DialRules.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:11k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: DialRules.c++,v 1.1.1.1 2005/11/11 21:32:03 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1993-1996 Sam Leffler
  4.  * Copyright (c) 1993-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. /*
  27.  * HylaFAX Dialing String Rule Support.
  28.  */
  29. #include "DialRules.h"
  30. #include "REArray.h"
  31. #include "Dictionary.h"
  32. #include <ctype.h>
  33. /*
  34.  * Interpolation points in replacement strings have
  35.  * the META bit or'd together with their match identifier.
  36.  * That is ``1'' is META|1, ``2'' is META|2, etc.
  37.  * The ``&'' interpolation is treated as ``''.
  38.  */
  39. const u_int META = 0200; // interpolation marker
  40. struct DialRule {
  41.     REPtr pat; // pattern to match
  42.     fxStr replace; // replacement string
  43.     DialRule();
  44.     ~DialRule();
  45.     int compare(const DialRule *) const;
  46. };
  47. DialRule::DialRule(){}
  48. DialRule::~DialRule(){}
  49. int DialRule::compare(const DialRule*) const { return 0; }
  50. fxDECLARE_ObjArray(RuleArray, DialRule)
  51. fxIMPLEMENT_ObjArray(RuleArray, DialRule)
  52. fxDECLARE_StrKeyDictionary(VarDict, fxStr)
  53. fxIMPLEMENT_StrKeyObjValueDictionary(VarDict, fxStr)
  54. fxDECLARE_StrKeyDictionary(RulesDict, RuleArrayPtr)
  55. fxIMPLEMENT_StrKeyObjValueDictionary(RulesDict, RuleArrayPtr)
  56. DialStringRules::DialStringRules(const char* file) : filename(file)
  57. {
  58.     verbose = false;
  59.     fp = NULL;
  60.     vars = new VarDict;
  61.     regex = new REArray;
  62.     rules = new RulesDict;
  63. }
  64. DialStringRules::~DialStringRules()
  65. {
  66.     delete rules;
  67.     delete regex;
  68.     delete vars;
  69. }
  70. void DialStringRules::setVerbose(bool b) { verbose = b; }
  71. /*
  72.  * Parse the dialing string rules file
  73.  * to create the in-memory rules.
  74.  */
  75. bool
  76. DialStringRules::parse(bool shouldExist)
  77. {
  78.     bool ok = false;
  79.     lineno = 0;
  80.     fp = fopen(filename, "r");
  81.     if (fp) {
  82. ok = parseRules();
  83. fclose(fp);
  84.     } else if (shouldExist)
  85. parseError("Cannot open file "%s" for reading",
  86. (const char*) filename);
  87.     return (ok);
  88. }
  89. void
  90. DialStringRules::def(const fxStr& var, const fxStr& value)
  91. {
  92.     if (verbose)
  93. traceParse("Define %s = "%s"",
  94.     (const char*) var, (const char*) value);
  95.     (*vars)[var] = value;
  96. }
  97. void
  98. DialStringRules::undef(const fxStr& var)
  99. {
  100.     if (verbose)
  101. traceParse("Undefine %s", (const char*) var);
  102.     vars->remove(var);
  103. }
  104. bool
  105. DialStringRules::parseRules()
  106. {
  107.     char line[1024];
  108.     char* cp;
  109.     while ((cp = nextLine(line, sizeof (line)))) {
  110. // collect token
  111. if (!isalpha(*cp)) {
  112.     parseError("Syntax error, expecting identifier");
  113.     return (false);
  114. }
  115. const char* tp = cp;
  116. for (cp++; isalnum(*cp); cp++)
  117.     ;
  118. fxStr var(tp, cp-tp);
  119. while (isspace(*cp))
  120.     cp++;
  121. if (*cp == ':' && cp[1] == '=') { // rule set definition
  122.     for (cp += 2; *cp != '['; cp++)
  123. if (*cp == '') {
  124.     parseError("Missing '[' while parsing rule set");
  125.     return (false);
  126. }
  127.     if (verbose)
  128. traceParse("%s := [", (const char*) var);
  129.     RuleArray* ra = new RuleArray;
  130.     if (!parseRuleSet(*ra)) {
  131. delete ra;
  132. return (false);
  133.     }
  134.     (*rules)[var] = ra;
  135.     if (verbose)
  136. traceParse("]");
  137. } else if (*cp == '=') { // variable definition
  138.     fxStr value;
  139.     if (parseToken(cp+1, value) == NULL)
  140. return (false);
  141.     def(var, value);
  142. } else { // an error
  143.     parseError("Missing '=' or ':=' after "%s"", (const char*) var);
  144.     return (false);
  145. }
  146.     }
  147.     if (verbose) {
  148. RuleArray* ra = (*rules)["CanonicalNumber"];
  149. if (ra == 0)
  150.     traceParse("Warning, no "CanonicalNumber" rules.");
  151. ra = (*rules)["DialString"];
  152. if (ra == 0)
  153.     traceParse("Warning, no "DialString" rules.");
  154.     }
  155.     return (true);
  156. }
  157. /*
  158.  * Locate the next non-blank line in the file
  159.  * and return a pointer to the first non-whitespace
  160.  * character on the line.  Leading white space and
  161.  * comments are stripped.  NULL is returned on EOF.
  162.  */
  163. char*
  164. DialStringRules::nextLine(char* line, int lineSize)
  165. {
  166.     char* cp;
  167.     do {
  168. if (!fgets(line, lineSize, fp))
  169.     return (NULL);
  170. lineno++;
  171. for (cp = line; (cp = strchr(cp, '!')); cp++)
  172.     if (cp == line || cp[-1] != '\')
  173. break;
  174. if (cp)
  175.     *cp = '';
  176. else if ((cp = strchr(line, 'n')))
  177.     *cp = '';
  178. for (cp = line; isspace(*cp); cp++)
  179.     ;
  180.     } while (*cp == '');
  181.     return (cp);
  182. }
  183. /*
  184.  * Parse the next token on the input line and return
  185.  * the result in the string v.  Variable references
  186.  * are interpolated here, though they might be better
  187.  * done at a later time.
  188.  */
  189. const char*
  190. DialStringRules::parseToken(const char* cp, fxStr& v)
  191. {
  192.     while (isspace(*cp))
  193. cp++;
  194.     const char* tp;
  195.     if (*cp == '"') { // "..."
  196. tp = ++cp;
  197. for (;;) {
  198.     if (*cp == '') {
  199. parseError("String with unmatched '"'");
  200. return (NULL);
  201.     }
  202.     if (*cp == '\' && cp[1] == '') {
  203. parseError("Bad '\' escape sequence");
  204. return (NULL);
  205.     }
  206.     if (*cp == '"' && (cp == tp || cp[-1] != '\'))
  207. break;
  208.     cp++;
  209. }
  210. v = fxStr(tp, cp-tp);
  211. cp++; // skip trailing ``"''
  212.     } else { // token terminated by white space
  213. for (tp = cp; *cp != ''; cp++) {
  214.     if (*cp == '\' && cp[1] == '') {
  215. parseError("Bad '\' escape sequence");
  216. return (NULL);
  217.     }
  218.     if (isspace(*cp) && (cp == tp || cp[-1] != '\'))
  219. break;
  220. }
  221. v = fxStr(tp, cp-tp);
  222.     }
  223.     for (u_int i = 0, n = v.length(); i < n; i++) {
  224. if (v[i] == '$' && (i+1<n && v[i+1] == '{')) {
  225.     /*
  226.      * Handle variable reference.
  227.      */
  228.     u_int l = v.next(i, '}');
  229.     if (l >= v.length()) {
  230. parseError("Missing '}' for variable reference");
  231. return (NULL);
  232.     }
  233.     fxStr var = v.cut(i+2,l-(i+2));// variable name
  234.     v.remove(i, 3); // remove ${}
  235.     const fxStr& value = (*vars)[var];
  236.     v.insert(value, i); // insert variable's value
  237.     n = v.length(); // adjust string length
  238.     i += value.length()-1; // don't scan substituted string
  239. } else if (v[i] == '\') // pass <char> escapes unchanged
  240.     i++;
  241.     }
  242.     return cp;
  243. }
  244. /*
  245.  * Handle escapes for a token
  246.  * that appears on the RHS of a rewriting rule.
  247.  */
  248. void
  249. DialStringRules::subRHS(fxStr& v)
  250. {
  251.     /*
  252.      * Replace `&' and n items with (META|<n>) to mark spots
  253.      * in the token where matches should be interpolated.
  254.      */
  255.     for (u_int i = 0, n = v.length(); i < n; i++) {
  256. if (v[i] == '\') { // process <char> escapes
  257.     v.remove(i), n--;
  258.     if (isdigit(v[i]))
  259. v[i] = META | (v[i] - '0');
  260. } else if (v[i] == '&')
  261.     v[i] = META | 0;
  262.     }
  263. }
  264. /*
  265.  * Parse a set of rules and construct the array of
  266.  * rules (regular expressions + replacement strings).
  267.  */
  268. bool
  269. DialStringRules::parseRuleSet(RuleArray& rules)
  270. {
  271.     for (;;) {
  272. char line[1024];
  273. const char* cp = nextLine(line, sizeof (line));
  274. if (!cp) {
  275.     parseError("Missing ']' while parsing rule set");
  276.     return (false);
  277. }
  278. if (*cp == ']')
  279.     return (true);
  280. // new rule
  281. fxStr pat;
  282. if ((cp = parseToken(cp, pat)) == NULL)
  283.     return (false);
  284. while (isspace(*cp))
  285.     cp++;
  286. if (*cp != '=') {
  287.     parseError("Rule pattern without '='");
  288.     return (false);
  289. }
  290. DialRule r;
  291. if (parseToken(cp+1, r.replace) == NULL)
  292.     return (false);
  293. if (verbose)
  294.     traceParse("  "%s" = "%s"",
  295. (const char*) pat, (const char*) r.replace);
  296. subRHS(r.replace);
  297. u_int i = 0;
  298. u_int n = regex->length();
  299. while (i < n) {
  300.     RE* re = (*regex)[i];
  301.     if (strcmp(re->pattern(), pat) == 0)
  302. break;
  303.     i++;
  304. }
  305. if (i >= n) {
  306.     r.pat = new RE(pat);
  307.     if (r.pat->getErrorCode() > REG_NOMATCH) {
  308. fxStr emsg;
  309. r.pat->getError(emsg);
  310. parseError(pat | ": " | emsg);
  311.     }
  312.     regex->append(r.pat);
  313. } else
  314.     r.pat = (*regex)[i];
  315. rules.append(r);
  316.     }
  317. }
  318. fxStr
  319. DialStringRules::applyRules(const fxStr& name, const fxStr& s)
  320. {
  321.     if (verbose)
  322. traceRules("Apply %s rules to "%s"",
  323.     (const char*) name, (const char*) s);
  324.     fxStr result(s);
  325.     RuleArray* ra = (*rules)[name];
  326.     if (ra) {
  327. for (u_int i = 0, n = (*ra).length(); i < n; i++) {
  328.     DialRule& rule = (*ra)[i];
  329.     u_int off = 0;
  330.     while (rule.pat->Find(result, off)) {
  331. /*
  332.  * Regular expression match.
  333.  */
  334. int ix = rule.pat->StartOfMatch();
  335. int len = rule.pat->EndOfMatch() - ix;
  336. if (len == 0) // avoid looping on zero-length matches
  337.     break;
  338. /*
  339.  * Do ``&'' and ``n'' interpolations in the
  340.  * replacement.  NB: & is treated as .
  341.  */
  342. fxStr replace(rule.replace);
  343. for (u_int ri = 0, rlen = replace.length(); ri < rlen; ri++)
  344.     if (replace[ri] & META) {
  345. u_char mn = replace[ri] &~ META;
  346. int ms = rule.pat->StartOfMatch(mn);
  347. int mlen = rule.pat->EndOfMatch(mn) - ms;
  348. replace.remove(ri); // delete & or n
  349. replace.insert(result.extract(ms, mlen), ri);
  350. rlen = replace.length();// adjust string length ...
  351. ri += mlen - 1; // ... and scan index
  352.     }
  353. /*
  354.  * Paste interpolated replacement string over match.
  355.  */
  356. result.remove(ix, len);
  357. result.insert(replace, ix);
  358. off = ix + replace.length(); // skip replace when searching
  359. if (verbose)
  360.     traceRules("--> match rule "%s", result now "%s"",
  361. rule.pat->pattern(), (const char*) result);
  362.     }
  363. }
  364.     }
  365.     if (verbose)
  366. traceRules("--> return result "%s"", (const char*) result);
  367.     return result;
  368. }
  369. #include <stdarg.h>
  370. void
  371. DialStringRules::parseError(const char* fmt ...)
  372. {
  373.     va_list ap;
  374.     va_start(ap, fmt);
  375.     fprintf(stderr, "%s: line %u: ", (const char*) filename, lineno); 
  376.     vfprintf(stderr, fmt, ap);
  377.     va_end(ap);
  378.     putc('n', stderr);
  379. }
  380. void
  381. DialStringRules::traceParse(const char* fmt ...)
  382. {
  383.     va_list ap;
  384.     va_start(ap, fmt);
  385.     fprintf(stdout, "%s: line %u: ", (const char*) filename, lineno); 
  386.     vfprintf(stdout, fmt, ap);
  387.     va_end(ap);
  388.     putc('n', stdout);
  389. }
  390. void
  391. DialStringRules::traceRules(const char* fmt ...)
  392. {
  393.     va_list ap;
  394.     va_start(ap, fmt);
  395.     vfprintf(stdout, fmt, ap);
  396.     va_end(ap);
  397.     putc('n', stdout);
  398. }
  399. /*
  400.  * Apply the canonical number rule set to a string.
  401.  */
  402. fxStr DialStringRules::canonicalNumber(const fxStr& s)
  403.     { return applyRules("CanonicalNumber", s); }
  404. /*
  405.  * Apply the dial string rule set to a string.
  406.  */
  407. fxStr DialStringRules::dialString(const fxStr& s)
  408.     { return applyRules("DialString", s); }
  409. /*
  410.  * Apply the display number rule set to a string.
  411.  */
  412. fxStr DialStringRules::displayNumber(const fxStr& s)
  413.     { return applyRules("DisplayNumber", s); }