home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / datime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  38.6 KB  |  1,265 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Andrew Lowry
  8. */
  9. /* TODO: Finish dtdst, figure out why always GMT */
  10.  
  11. /* datime
  12. **
  13. ** Utilities to deal with times and dates.  Two routines are machine-
  14. ** specific and must be written for each machine individually.  These
  15. ** are dtnow, which fills in a datime structure with the current
  16. ** date and time, and dttzone, which returns the local timezone in
  17. ** minutes west of Greenwich and a timezone id, if available.
  18. **
  19. ** Required structure and constant declarations are to be found in datime.h.
  20. ** tzone.h contains declarations of timezone information, separated from
  21. ** this source for ease in modifying the rules.
  22. **/
  23.  
  24. #include "ccmdlib.h"
  25. #include "tzone.h"            /* declare timezone info structures */
  26. #include "dtpat.h"            /* get date patterns */
  27.  
  28. /* A few useful character constants */
  29.  
  30. #define NULCHAR    '\000'
  31. #define TAB    '\011'
  32. #define SPACE    '\040'
  33.  
  34.  
  35.  
  36. /* dtnow
  37. **
  38. ** Purpose:
  39. **   Return, via a datime structure supplied by the caller, the current
  40. **   date and time, using local timezone information.
  41. **
  42. ** Input arguments:
  43. **   dtblk - Pointer to a datime block to be filled in.
  44. **
  45. ** Output arguments:
  46. **   dtblk - Fields filled in with current info.
  47. **
  48. ** Returns: Nothing.
  49. **/
  50.  
  51. #ifdef MSDOS
  52. /* #include <dos.h> */
  53.  
  54. /* macro to convert from 2-digit BCD to integer */
  55.  
  56. #define bcd2int(x) (((x)&0xf)+(10*(((x)>>4)&0xf)))
  57.  
  58. dtnow(dtblk)
  59. datime *dtblk;
  60. {
  61.   union REGS inregs, outregs;    /* structures for communicating with BIOS */
  62.   int junk;
  63.  
  64.   inregs.h.ah = 2;        /* read the time */
  65.   int86(0x1a,&inregs,&outregs);
  66.   dtblk->_dthr = bcd2int(outregs.h.ch); /* transfer values */
  67.   dtblk->_dtmin = bcd2int(outregs.h.cl);
  68.   dtblk->_dtsec = bcd2int(outregs.h.dh);
  69.  
  70.   inregs.h.ah = 4;        /* read the date */
  71.   int86(0x1a,&inregs,&outregs);
  72.   dtblk->_dtmon = bcd2int(outregs.h.dh)-1; /* transfer values */
  73.   dtblk->_dtday = bcd2int(outregs.h.dl)-1;
  74.   dtblk->_dtyr = bcd2int(outregs.h.cl)+100*bcd2int(outregs.h.ch);
  75.                 /* compute day-of-week */
  76.   dtblk->_dtdow = dtdow(dtblk->_dtmon,dtblk->_dtday,dtblk->_dtyr);
  77.   dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
  78.   dtblk->_dtdst = dtdst(dtblk); /* compute daylight savings time */
  79. }
  80. #endif
  81.  
  82. #if unix
  83.  
  84. dtnow(dtblk)
  85. datime *dtblk;
  86. {
  87.   long t;
  88.   struct tm *localtime(), *x;
  89.   t = time(NULL);            /* get time */
  90.   x = localtime(&t);            /* convert it */
  91.   dtblk->_dthr = x->tm_hour;        /* hour */
  92.   dtblk->_dtmin = x->tm_min;        /* minute */
  93.   dtblk->_dtsec = x->tm_sec;        /* second */
  94.   dtblk->_dtmon = x->tm_mon;        /* month */
  95.   dtblk->_dtday = x->tm_mday -1;    /* day of month */
  96.   dtblk->_dtyr = x->tm_year+1900;    /* year */
  97.   dtblk->_dtdow = x->tm_wday;        /* week day */
  98.   dttzone(&dtblk->_dttz,&dtblk->_dttzc);/* time zone */
  99.   dtblk->_dtdst = dtdst(dtblk);        /* daylight savings time */
  100. }
  101. #endif /* unix */
  102.  
  103.  
  104. /* dttzone
  105. **
  106. ** Purpose:
  107. **   Returns the local timezone, as minutes west of Greenwich.  Daylight
  108. **   savings time is not considered.  If a timezone ID is known, it is
  109. **   returned as well.
  110. **
  111. ** Input arguments: None.
  112. ** Output arguments:
  113. **   offset - Number of minutes west of Greenwich, in range -7199 to 7200
  114. **     (that is, the international dateline is considered 12 hours WEST,
  115. **     not 12 hours EAST of Greenwich)
  116. **   code - Timezone code number if known, or -1
  117. **
  118. ** Returns: Nothing
  119. **/
  120.  
  121. #if MSDOS
  122. dttzone(offset,code)
  123. int *offset,*code;
  124. {
  125.   *offset = LCL_TZOFF;        /* get offset defined in datime.h */
  126. #ifdef LCL_TZCODE
  127.   *code = LCL_TZCODE;        /* and code from datime.h if there is one */
  128. #else
  129.   *code = -1;            /* otherwise signal no code */
  130. #endif
  131. }
  132. #endif
  133.  
  134. #if (BSD)
  135. dttzone(offset, code)
  136. int *offset, *code;
  137. {
  138.   struct timeval tp;
  139.   struct timezone tz;
  140.   gettimeofday(&tp,&tz);        /* get time of day */
  141.   *offset = tz.tz_minuteswest;
  142. #ifdef LCL_TZCODE
  143.   *code = LCL_TZCODE;           /* and code from datime.h if there is one */
  144. #else
  145.   *code = -1;                /* otherwise signal no code */
  146. #endif
  147. }  
  148. #endif /*  BSD */
  149.  
  150. #ifdef SYSV
  151. dttzone(offset, code)
  152. int *offset, *code;
  153. {
  154.   *offset = timezone / 60;        /* get the correct timezone */
  155. #ifdef LCL_TZCODE
  156.   *code = LCL_TZCODE;           /* and code from datime.h if there is one */
  157. #else
  158.   *code = -1;                /* otherwise signal no code */
  159. #endif
  160. }  
  161. #endif /*  SYSV */
  162.  
  163.  
  164.  
  165.  
  166. /* dttzinf
  167. **
  168. ** Purpose:
  169. **   Return a pointer to a timezone information structure, given
  170. **   a timezone code or an offset from Greenwich.  The former style
  171. **   of call is identified by the input argument being a positive
  172. **   value with flag TZ_CODE set.  Anything else is taken to be
  173. **   a signed number of minutes west of Greenwich.  In the former
  174. **   case, the tzinfo array is scanned for a timezone with the given
  175. **   code, and a pointer to the corresponding tzinf structure is
  176. **   returned.  When an offset is given, the first timezone with
  177. **   the given offset is returned (so timezones with different
  178. **   dst rules, or in different hemispheres, cannot be distinguished
  179. **   by means of their offsets).  In either case, if no matching
  180. **   timezone is located, NULL is returned.
  181. **
  182. ** Input arguments:
  183. **   key - Signed offset in minutes west of Greenwich, or positive
  184. **     timezone code value with the TZ_CODE flag turned on.
  185. **
  186. ** Output arguments: None.
  187. ** Returns: Pointer to first matching tzinf structure, or NULL if none found.
  188. **/
  189.  
  190. tzinf *
  191. dttzinf(key)
  192. int key;
  193. {
  194.   int bycode;                /* TRUE if looking up by code */
  195.   int i;
  196.  
  197.   if ((key > 0) && (key & TZ_CODE)) {    /* lookup by code? */
  198.     bycode = TRUE;            /* set flag */
  199.     key &= ~TZ_CODE;            /* and isolate the code value */
  200.   }
  201.   else
  202.     bycode = FALSE;            /* otherwise clear flag */
  203.  
  204.   for (i = 0; i < TZCOUNT; i++)        /* loop through the table */
  205.     if (key == (bycode ? tzinfo[i]._tzcode : tzinfo[i]._tzoff)) 
  206.       return(&tzinfo[i]);        /* found indicated zone */
  207.   return(NULL);                /* zone not in table */
  208. }
  209.  
  210.  
  211.  
  212. /* dtdow
  213. **
  214. ** Purpose:
  215. **   Given a date, compute the day of week on which the date falls,
  216. **   according to the Gregorian calendar system.  The calculation
  217. **   assumes that the Gregorian system has been in effect since
  218. **   1-Jan-0000, which is not true.  In general, correct results
  219. **   will be given for dates after 1923, by which time all the
  220. **   major cultures had adopted the calendar.  See a world almanac
  221. **   or other source for details of who joined the system when.
  222. **
  223. ** Input arguments:
  224. **   mon - The month for the date in question, 0 = January
  225. **   day - The day of the month, with 0 = the first day.
  226. **   yr - The year in question, with no conversion for years
  227. **     less than 100.
  228. ** 
  229. ** Output arguments: None.
  230. ** Returns: Day of week for given date, with 0 = Sunday, 6 = Saturday.
  231. **/
  232.  
  233. /* Useful macro to decide if a year is a leap year */
  234.  
  235. #define LEAP(y)    (((y)%400 == 0) || (((y)%100 != 0) && ((y)%4 == 0)))
  236.  
  237. /* lengths of the months, assuming a leap year */
  238. static int mthlens[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  239.  
  240. int
  241. dtdow(mon,day,yr)
  242. int mon,day,yr;
  243. {
  244.   int dow;                /* calculated day of week */
  245.   int i;
  246.   
  247.   /* First calculate day-of-week relative to 1-Jan-0000, that is,
  248.   ** the number of days since that date, modulo 7.  A first approximation
  249.   ** accounts for years prior to the given year, by calculating (365*yr) % 7.
  250.   ** But since 365%7 is 1, this is the same as (1*yr) % 7, or yr % 7.
  251.   ** Adjustments are then made for leap years, and then for preceding months
  252.   ** in the given year, and preceding days in the given month.
  253.   **/
  254.   dow = yr % 7;                /* start with days in prior years */
  255.   dow += (yr+3) / 4;            /* adjust for preceding leap years */
  256.   dow -= (yr+99) / 100;            /* most centuries are non-leap */
  257.   dow += (yr+399) / 400;        /* but every fourth one is */
  258.   for (i = 0; i < mon; i++)
  259.     dow += mthlens[i];            /* add preceding months this year */
  260.   if ((mon > 1) && !LEAP(yr))        /* after non-leap Feb? */
  261.     dow -= 1;                /* yup, we added to much */
  262.   dow += day;                /* now adjust for date within month */
  263.   dow += 6;                /* Greg 1-Jan-0000 was a Saturday */
  264.   dow %= 7;                /* get dow in range 0-7 */
  265.   return(dow);                /* give result to caller */
  266. }
  267.  
  268.  
  269.  
  270. /* dtdst
  271. **
  272. ** Purpose:
  273. **   Given a datime structure filled in with date, time, and timezone
  274. **   information, compute the daylight-savings-time adjustment that
  275. **   should be in effect according to whatever rules we have for the
  276. **   given timezone.  If no appropriate rule exists, assume no adjustment
  277. **   (that is, an adjustment of zero).  Generally, when dst is in effect,
  278. **   the adjustment will be -60, meaning that to convert to standard time,
  279. **   60 minutes should be subtracted from the given time.  If a timezone
  280. **   code is present in the datime structure, it is used to find the dst
  281. **   rules.  Otherwise, the timezone offset is used.  In the latter case,
  282. **   the wrong rules may be applied since timezones with identical offsets
  283. **   but different dst rules will not be distinguished.
  284. **
  285. ** Input arguments:
  286. **   dtblk - A pointer to the datime block holding the date, time, and zone
  287. **     information to use.
  288. **
  289. ** Output arguments: None.  (In particular, the _dtdst field of the given
  290. **   datime block is NOT modified.)
  291. **
  292. ** Returns: Number of minutes to add to given time in order to convert
  293. **   to standard time in its timezone (so adding this value as well
  294. **   as the _dttz value will result in Greenwich Mean Time).
  295. **
  296. **/
  297.  
  298. int
  299. dtdst(dtblk)
  300. datime *dtblk;
  301. {
  302.   tzinf *tzi;            /* time zone info block */
  303.   dstrule *rule;        /* applicable dst rule */
  304.   int julian, dston, dstoff, i;
  305.  
  306.   if (dtblk->_dttzc != -1)
  307.     tzi = dttzinf(TZ_CODE | dtblk->_dttzc); /* look up by code if set */
  308.   else
  309.     tzi = dttzinf(dtblk->_dttz); /* otherwise look up by offset */
  310.  
  311.   if (tzi == NULL) 
  312.     return(0);            /* no adjustment if tz not known */
  313.  
  314.   /* calculate julian date of current date */
  315.   for (julian = dtblk->_dtday+1, i = 0; i < dtblk->_dtmon; i++)
  316.     julian += mthlens[i];
  317.   if ((dtblk->_dtmon > 1) && !LEAP(dtblk->_dtyr))
  318.     julian--;
  319.   
  320.   for (rule = tzi->_tzdst; ; rule++) {
  321.     if (dtblk->_dtyr < rule->_dsyr1 || dtblk->_dtyr > rule->_dsyrn) {
  322.       if (rule->_dsyr1 > 0)
  323.     continue;
  324.       else return 0;
  325.     }
  326.     
  327.     /* get appropximate dates of dst on/off */
  328.     dston = rule->_dsdow;    /* XXX struct member is mislabeled */
  329.     dstoff = rule->_dsday;    /* XXX struct member is mislabeled */
  330.     
  331.     /* account for feb 29th */
  332.     if (!LEAP(dtblk->_dtyr)) {
  333.       if (dston >= 59)
  334.     dston--, dstoff--;
  335.       else
  336.     if (dstoff >= 59)
  337.       dstoff--;
  338.     }
  339.     
  340.     /*
  341.      * get actual julian dates for dst on/off, assumed to be Sunday
  342.      * previous to the approximate date.
  343.      */
  344.     
  345.     /* get days from Jan 1st to first Sunday */
  346.     i = (julian - 1 + 7 - dtblk->_dtdow) % 7;
  347.     /* subtract number of days between dston and previous Sunday */
  348.     dston -= ((((dston - 1) % 7) - i) + 7) % 7;
  349.     dstoff -= ((((dstoff - 1) % 7) - i) + 7) % 7;
  350.     
  351.  
  352.     if ((julian < dston) || (julian > dstoff) ||
  353.     (julian == dston && (dtblk->_dthr < 2)) ||
  354.     (julian == dstoff && (dtblk->_dthr >= (tzi->_tzsth ? 2 : 1)))) {
  355.       if (rule->_dsyr1)
  356.         continue;
  357.       else return (0);
  358.     }
  359.     return (- tzi->_tzdadj);
  360.   }
  361. }
  362.  
  363.  
  364.  
  365. /* dtparse
  366. **
  367. ** Purpose:
  368. **   Attempt to interpret a given input string as a date/time specification.
  369. **   If successful, fill the results into a given datime block.  A successful
  370. **   parse need not consume all the available input.
  371. **   
  372. **   The input must match one of the patterns declared in dtpat.h, which
  373. **   cover a wide range of formats.  Flags may be passed to constrain
  374. **   which patterns may be used, or to indicate that either the time or
  375. **   the date portion is not to be parsed.  The patterns are ranked so
  376. **   that a pattern of a higher rank will be chosen when there is
  377. **   ambiguity in the input.  The ranking can be supplemented by a set of
  378. **   adjustments that depend on the flags that are specified in the parse,
  379. **   so that the ranking can depend on the flags.  For details of how
  380. **   the patterns are constructed, read the comments in dtpat.h.
  381. **
  382. **   Parsing succeeds if, of all the patterns matching the current input,
  383. **   there is a single matching pattern of highest rank.  The input is
  384. **   decoded according to that pattern, checked for acceptability, and
  385. **   then returned via the  datime structure supplied by the caller. 
  386. **
  387. **   Completion text is set by some pattern elements when they encounter
  388. **   the end of the input.
  389. **
  390. ** Input arguments:
  391. **   flags - Flags to control the parse, as defined in datime.h
  392. **   text - A pointer to the text to be parsed.
  393. **   textlen - The number of characters in the text to be parsed.
  394. **
  395. ** Output arguments:
  396. **   dtblk - Filled in with date/time info from parse if successful.
  397. **     If time is not given, 00:00:01 is used, with the local timezone
  398. **     and daylight savings computed by the rules.  If date is not
  399. **     given, the current date is filled in.  Day of week is computed
  400. **     from the date, and if the input specifies a day of week, it
  401. **     must match the computed day in order to succeed.
  402. **   parselen - The number of characters consumed by a successful parse.
  403. **   completion - A pointer to text that can be used to complete
  404. **     the current input (generally only partial completion results)
  405. **     if the date/time specification is incomplete.
  406. **   incomplete - TRUE iff the parse failed due to lack of sufficient
  407. **     input.
  408. **
  409. ** Returns: TRUE iff the parse succeeds.
  410. **/
  411.  
  412. /* Define TRACE here if a pattern match trace is desired */
  413.  
  414. /* #define TRACE */
  415.  
  416. /* Global variables used by the pattern matching routines */
  417.  
  418. static char *dttext;        /* pointer to input text */
  419. static int dtlen;        /* # of chars left to parse */
  420. static int dtinc;        /* parse failed for lack of text */
  421. static char *dtcmp;        /* pointer to completion text after */
  422.                 /*  incomplete parse */
  423.  
  424. int
  425. dtparse(flags,text,textlen,dtblk,parselen,completion,incomplete)
  426. int flags,textlen,*parselen,*incomplete;
  427. char *text,**completion;
  428. datime *dtblk;
  429. {
  430.   datime curdt;            /* current time/date go here */
  431.   dtpat *pats;            /* pattern array selected by flags */
  432.  
  433.   dttext = text;        /* set up global variables */
  434.   dtlen = textlen;
  435.   dtinc = *incomplete = FALSE;    /* assume not incomplete */
  436.  
  437.   if (flags & DTP_NDA)        /* check exclusions from parse */
  438.     if (flags & DTP_NTI)
  439.       return(FALSE);        /* cannot exlude both time and date */
  440.     else
  441.       pats = timpats;        /* exclude date but not time */
  442.   else
  443.     if (flags & DTP_NTI)
  444.       pats = datpats;        /* exclude time but not date */
  445.     else
  446.       pats = dtpats;        /* exclude neither */
  447.   if (!match(pats,flags)) {    /* attempt a parse */
  448.     if (dtinc) {        /* reached end of input? */
  449.       *incomplete = TRUE;    /* yup, let them know */    
  450.       *completion = dtcmp;    /* and pass on completion text */
  451.     }
  452.     return(FALSE);        /* give a failure return */
  453.   }
  454.   if (flags & DTP_NDA) {    /* need current date info? */
  455.     dtnow(&curdt);        /* get it */
  456.     dtblk->_dtmon = curdt._dtmon; /* and transfer date info */
  457.     dtblk->_dtday = curdt._dtday;
  458.     dtblk->_dtyr = curdt._dtyr;
  459.     dtblk->_dtdow = curdt._dtdow;
  460.   }
  461.   else {
  462.     dtblk->_dtmon = mon;    /* otherwise transfer parsed values */
  463.     dtblk->_dtday = day;
  464.     dtblk->_dtyr = yr;
  465.     dtblk->_dtdow = dow;
  466.   }
  467.   
  468.   if (flags & DTP_NTI) {    /* time not parsed? */
  469.     dtblk->_dthr = 0;        /* fill in one second past midnight */
  470.     dtblk->_dtmin = 0;
  471.     dtblk->_dtsec = 1;
  472.     dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
  473.     dstspec = -1;        /* need dst computation */
  474.   }
  475.   else {            /* parsed time - fill in values */
  476.     dtblk->_dthr = hr;
  477.     dtblk->_dtmin = min;
  478.     dtblk->_dtsec = sec;
  479.     if (tzspec != -1) {        /* timezone given? */
  480.       dtblk->_dttz = tz;    /* then transfer it */
  481.       dtblk->_dttzc = tzc;
  482.     }
  483.     else {
  484.       dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* otherwise get local zone */
  485.       dstspec = -1;        /* and no dst indicator given */
  486.     }
  487.   }
  488.   if (dstspec == -1)        /* dst indicator not given? */
  489.     dtblk->_dtdst = dtdst(dtblk); /* then compute it by the rules */
  490.   else
  491.     dtblk->_dtdst = dst;    /* else set value from parse */
  492.  
  493.   *parselen = dttext - text;    /* set # of chars consumed */
  494.   
  495.   return(TRUE);            /* and succeed */
  496. }
  497.  
  498.  
  499. /* The pattern matcher routines */
  500.  
  501. #define MAXVAL    2
  502. static int patval[MAXVAL];        /* values set by pattern elements */
  503.  
  504. /* match
  505. ** 
  506. ** Purpose:
  507. **   Apply a given set of patterns to the text currently pointed to by
  508. **   global variable 'dttext'.  If there is a single succeeding pattern
  509. **   of highest rank, consume the characters and return TRUE.  Otherwise
  510. **   return FALSE.  As soon as a highest-rank failing pattern sets the 
  511. **   dtinc flag, a FALSE return is made immediately without attempting 
  512. **   other patterns.  On success, the index of the succeeding pattern in 
  513. **   the pattern array is returned via the patval array.
  514. **
  515. ** Input arguments:
  516. **   pats - Pointer to the first pattern in an array of patterns to attempt,
  517. **     terminated by a pattern whose pattern string is NULL.
  518. **   flags - The flags specified in the FDB for the parse.
  519. **
  520. ** Output arguments: None
  521. ** Returns: TRUE if matching succeeds, FALSE otherwise.
  522. **
  523. ** Global variables:
  524. **   dttext - Pointer to the text to be matched, updated after a successful 
  525. **     match
  526. **   dtlen - Number of characters in input text, updated after success
  527. **   dtinc - TRUE after a failing match that failed because of insufficient
  528. **     input text.
  529. **   dtcmp - After an incomplete match, points to appropriate completion 
  530. **     text, or NULL if there is none.
  531. **/
  532.  
  533.  
  534. #ifdef TRACE
  535. static int tracelev = 0;                /* indentation level for trace */
  536. #endif
  537.  
  538. static int
  539. match(pats,flags)
  540. dtpat *pats;
  541. int flags;
  542. {
  543. #ifdef TRACE
  544.   int ret;                /* results of match operation */
  545.   tracelev +=2;
  546.   trindent();fprintf(stderr,"Matching: ");trshow();trnl();
  547.   tracelev +=2;
  548.   ret = match1(pats,flags);
  549.   tracelev -= 2;
  550.   trindent();fprintf(stderr,"Match result: %s\n",(ret ? "SUCCESS":"FAILURE"));
  551.   tracelev -= 2;
  552.   return(ret);
  553. }
  554. static int
  555. match1(pats,flags)
  556. dtpat *pats;
  557. int flags;
  558. {  
  559. #endif
  560.  
  561.   char *origtext = dttext;        /* save input parameters */
  562.   int origlen = dtlen;            /*  to be restored on failure */
  563.   char *savtext;            /* input params after a good match */
  564.   int savlen;
  565.   int rank;                /* ranks of individual patterns */
  566.   int hirank;                /* highest pattern rank */
  567.   dtpat *p;                /* for stepping through patterns */
  568.   int matched = FALSE;            /* TRUE if a pattern succeeds */
  569.   
  570.   hirank = -1;                /* assume no patterns eligible */
  571.   for (p = pats; p->_dppat != NULL; p++) { /* step through the patterns */
  572.     rank = patrank(p,flags);        /* compute ranks */
  573.     if (rank > hirank)
  574.       hirank = rank;            /* and remember highest rank */
  575.   }
  576.   
  577.   while (hirank >= 0) {            /* step down through eligible ranks */
  578.     for (p = pats; p->_dppat != NULL; p++) /* loop through pat at that rank */
  579.       if (patrank(p,flags) == hirank) {
  580.     if (matchpat(p,flags))        /* good match? */
  581. #ifdef TRACE
  582.         { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN SUCCEEDED\n");
  583. #endif
  584.       if (!matched) {        /* first one? */
  585.         matched = TRUE;        /* then say we matched */
  586.         savtext = dttext;        /* save the input parameters */
  587.         savlen = dtlen;
  588.         set1val(p - pats);        /* set index of good pattern for */
  589.                     /*  return value for %r call */
  590.           }
  591.       else
  592.         return(FALSE);        /* multiple highest-rank matches */
  593. #ifdef TRACE
  594.         }
  595. #endif
  596.         else if (dtinc)            /* incomplete match? */
  597. #ifdef TRACE
  598.           { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED (INC)\n");
  599. #endif
  600.       return(FALSE);        /* then quit now */
  601. #ifdef TRACE
  602.           }
  603.     else {
  604.       tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED\n");
  605.     }
  606. #endif
  607.  
  608.         dttext = origtext;        /* restore input for next pattern */
  609.     dtlen = origlen;
  610.       }
  611.     if (matched) {            /* single match at this rank? */
  612.       dttext = savtext;            /* fix global vars to consume input */
  613.       dtlen = savlen;
  614.       return(TRUE);            /* and succeed */
  615.     }
  616.     else
  617.       hirank--;                /* no match, try next lower rank */
  618.   }
  619.  
  620.   return(FALSE);            /* no success at any rank */
  621. }
  622.  
  623.  
  624.  
  625. /* patrank
  626. **
  627. ** Purpose:
  628. **   Compute the rank for a parse pattern with a given set of flags.
  629. **   If any of the flags disqualifies the pattern (via its _dpqal field),
  630. **   -1 is returned.  Otherwise, rank adjustments are applied, to the
  631. **   base rank, and the adjusted rank is returned.
  632. **
  633. ** Input args:
  634. **   pat - A pointer to the pattern to be ranked.
  635. **   flags - Parse flags active for the parse.
  636. **
  637. ** Output args: None
  638. ** Returns: Adjusted rank.
  639. **/
  640.  
  641. static int
  642. patrank(pat,flags)
  643. dtpat *pat;
  644. int flags;
  645. {
  646.   int rank;                /* base rank for the pattern */
  647.   rankadj *adj;                /* pointer to adjustment array */
  648.  
  649.   if (flags & pat->_dpqal)        /* any disqualifying flags active? */
  650.     return(-1);                /* yup, throw it out */
  651.  
  652.   rank = pat->_dprnk;            /* get the base rank */
  653.   adj = pat->_dpraj;            /* and point to adjustments array */
  654.   if (adj != NULL)
  655.     while (adj->_raflg != -1) {        /* loop through adjustments */
  656.       if (adj->_raflg & flags)        /* adjustment indicated by flags? */
  657.     rank += adj->_raadj;        /* yup, adjust the rank */
  658.       adj++;                /* and move to next adjustment */
  659.     }
  660.   return(rank);                /* give back adjusted rank */
  661. }
  662.  
  663.  
  664.  
  665. /* matchpat
  666. **
  667. ** Purpose:
  668. **   Apply a given pattern to the current input.  Return TRUE if all the
  669. **   required special pattern elements succeed and if intervening pattern 
  670. **   characters match the input.  Otherwise return FALSE.  After a success,
  671. **   the global input parameters (dttext and dtlen) will be left so as to
  672. **   consume the input characters that participated in the match.  On
  673. **   failure caused by insufficient input text, global variable dtinc
  674. **   will be set TRUE, and dtcmp may point to completion text to be
  675. **   filled in if completion is requested.
  676. **
  677. ** Input args:
  678. **   pat - A pointer to the pattern to be matched.
  679. **   flags - The flags in effect for the parse.
  680. **
  681. ** Output args: None
  682. ** Returns: TRUE for a successful match, FALSE for a failure.
  683. **/
  684.  
  685. static int
  686. matchpat(pat,flags)
  687. dtpat *pat;
  688. int flags;
  689. {
  690.   int **patarg = pat->_dparg;        /* point to pattern args */
  691.   char *pats = pat->_dppat;        /* and to pattern string */
  692.   char pc,tc;                /* chars from pat & dttext input */
  693.   char *opttext;            /* vars to save input state */
  694.   int optlen;                /*  before an optional pat element */
  695.   int opt;                /* TRUE iff during optional pat elt */
  696.   int nogood;                /* TRUE after a failing pattern elt */
  697.   char *delims;                /* pointer to a %d arg string */
  698.   int i;
  699.   
  700. #ifdef TRACE
  701.   trindent();fprintf(stderr,"Pattern [%s] Input [",pats);
  702.   trshow();fprintf(stderr,"]\n");
  703.   tracelev += 2;
  704. #endif
  705.  
  706.   clearval();                /* clear the pattern values */
  707.  
  708.   while ((pc = *pats++) != NULCHAR) {    /* scan through pattern string */
  709.     if (pc == SPACE)
  710.       continue;                /* skip spaces in pattern */
  711.     if (pc != '%') {            /* normal character */
  712. #ifdef TRACE
  713.       trindent();fprintf(stderr,"Char [%c] ",pc);
  714. #endif
  715.       if (dtlen == 0) {        /* reached end of input */
  716.     dtinc = TRUE;            /* signal incomplete match */
  717.     dtcmp = NULL;            /* no completion on normal chars */
  718. #ifdef TRACE
  719.         fprintf(stderr,"INC\n");
  720. #endif
  721.     return(FALSE);            /* and fail */
  722.       }
  723.       tc = *dttext++;            /* get next input char */
  724.       dtlen--;                /* and consume it */
  725.       if (islower(tc))            /* convert to upper case */
  726.     tc = toupper(tc);
  727.       if (islower(pc))
  728.     pc = toupper(pc);
  729.       if (pc != tc)
  730. #ifdef TRACE
  731.         { fprintf(stderr,"NO\n");
  732. #endif
  733.     return(FALSE);            /* fail if chars do not match */
  734. #ifdef TRACE
  735.         }
  736.       else
  737.         fprintf(stderr,"OK\n");
  738. #endif
  739.       continue;                /* otherwise continue scanning */
  740.     }
  741.     pc = *pats++;            /* get special element char */
  742. #ifdef TRACE
  743.     trindent();
  744. #endif
  745.     if (pc == '?') {            /* this element optional? */
  746. #ifdef TRACE
  747.       fprintf(stderr,"Optional ");
  748. #endif
  749.       opt = TRUE;            /* flag it */
  750.       opttext = dttext;            /* and save input state */
  751.       optlen = dtlen;
  752.       pc = *pats++;            /* and get next pattern char */
  753.     }
  754.     else
  755.       opt = FALSE;            /* else clear optional flag */
  756. #ifdef TRACE
  757.     fprintf(stderr,"Special [%c] Args [",pc); trargs();
  758.     fprintf(stderr,"] Input [");trshow();
  759.     fprintf(stderr,"] ");
  760. #endif
  761.     nogood = FALSE;            /* assume pat element will succeed */
  762.  
  763.     switch(pc) {            /* now dispatch to element handler */
  764.       case '=':                /* assign value from prior element */
  765.     **(patarg++) = patval[0];    /* assign value, move to next arg */
  766.     for (i = 1; i < MAXVAL; i++)    /* cycle the pattern value array */
  767.       patval[i-1] = patval[i];
  768.     patval[MAXVAL-1] = -1;        /* and clear the last value */
  769.     break;
  770.  
  771.       case '%':                /* match a percent sign */
  772.     if (dtlen-- == 0)        /* insufficient input? */
  773.       dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  774.         else if ((*dttext++) != '%')
  775.       nogood = TRUE;        /* check for next char match */
  776.     else
  777.       set1val(0);            /* returns 0 on success */
  778.     break;
  779.       
  780.       case SPACE:            /* match a space */
  781.     if (dtlen-- == 0)        /* insufficient input? */
  782.        dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  783.         else if ((*dttext++) != SPACE)
  784.       nogood = TRUE;        /* next char not a space */
  785.         else
  786.       set1val(0);            /* good match */
  787.     break;
  788.     
  789.       case 'c':                /* match next pattern char */
  790.       case 'C':
  791. #ifdef TRACE
  792.         fprintf(stderr,"Char [%c] ",*(pats+1));
  793. #endif
  794.     if (dtlen-- == 0)        /* insufficient input? */
  795.        dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  796.         else if ((*dttext++) != *pats++)
  797.       nogood = TRUE;        /* next char does not match */
  798.         else
  799.       set1val(0);            /* good match */
  800.     break;    
  801.  
  802.       case 'k':                /* keyword match */
  803.       case 'K':
  804.     nogood = !matchkey(*patarg++);  /* attempt the match */
  805.     break;
  806.  
  807.       case '#':                /* one of the numeric patterns */
  808.     nogood = !matchnum(*pats++);    /* attempt numeric parse */
  809.         break;
  810.  
  811.       case 'p':                /* next char punctuation */
  812.       case 'P':
  813.     if (dtlen <= 0) {        /* insufficient input? */
  814.       dtinc = nogood = TRUE;    /* then set incomplete failure */
  815.       dtcmp = " ";        /* complete with a space */
  816.         }
  817.     else {
  818.       tc = *dttext;            /* get next char, do not consume it */
  819.       if (isalnum(tc))        /* not a punctuation char? */
  820.         nogood = TRUE;        /* then signal failure */
  821.           else
  822.         set1val((int) tc);        /* return ASCII code of char */
  823.         }
  824.     break;
  825.     
  826.       case 'w':                /* match a run of whitespace */
  827.       case 'W':
  828.     if (dtlen <= 0) {        /* insufficient chars? */
  829.       dtinc = nogood = TRUE;    /* signal incomplete failure */
  830.       dtcmp = " ";            /* complete with a space */
  831.       break;
  832.         }
  833.     tc = *dttext;            /* get next input char, unquoted */
  834.     if ((tc != SPACE) && (tc != TAB)) /* first char not whitespace? */
  835.       nogood = TRUE;
  836.         else {
  837.       while ((--dtlen) > 0) {    /* loop through nonempty run */
  838.         tc = *(++dttext);
  839.         if ((tc != SPACE) && (tc != TAB))
  840.           break;            /* until non-whitespace encountered */
  841.           }
  842.       if (dtlen == 0)        /* if exited loop for lack of input */
  843.         dttext++;            /*  pointer was not bumped enough */
  844.           set1val(0);            /* always return zero */
  845.         }
  846.         break;
  847.  
  848.       case 'd':                /* match a delimiter char */
  849.       case 'D':
  850.     delims = (char *) *patarg++;    /* point to delimiter set */
  851. #ifdef TRACE
  852.         fprintf(stderr,"Delims [%s] ",delims);
  853. #endif
  854.     if (dtlen <= 0) {        /* no input left? */
  855.       nogood = dtinc = TRUE;    /* signal incomplete failure */
  856.       dtcmp = NULL;        /* no completion */
  857.         }
  858.     else {
  859.       nogood = TRUE;        /* assume first char not delimiter */
  860.       tc = *dttext++;            /* get next input char */
  861.       dtlen--;            /* and count it */
  862.       for (i = 0; delims[i] != NULCHAR; i++) /* loop through delimiters */
  863.         if (delims[i] == tc) {    /* found a match? */
  864.           nogood = FALSE;        /* signal success */    
  865.           set1val((int) i);        /* return index of matching delim */
  866.           break;
  867.             }
  868.         }
  869.     break;
  870.     
  871.       case 'r':                /* recursively invoke a pattern set */
  872.       case 'R':
  873. #ifdef TRACE
  874.         trnl();
  875. #endif
  876.     nogood = !match(*patarg++,flags); /* invoke the pattern matcher */
  877. #ifdef TRACE
  878.         trindent(); fprintf(stderr,"Result for %r: ");
  879. #endif
  880.     break;
  881.  
  882.       case 'f':                /* invoke a function */
  883.       case 'F':
  884.     nogood = !(*((int (*)()) *patarg++))(&i); /* get return value in i */
  885.     if (!nogood)
  886.       set1val(i);            /* set return value on success */
  887.     break;
  888.     
  889.       case 'z':                /* store a zero value */
  890.       case 'Z':
  891.     *(*patarg++) = 0;        /* set the value, move to next arg */
  892.     break;
  893.     
  894.       case 'n':                /* store a -1 value */
  895.       case 'N':
  896.     *(*patarg++) = -1;        /* set the value, move to next arg */
  897.     break;
  898.     
  899.       case '+':                /* skip next argument */
  900.     patarg++;            /* bump the pointer */
  901.     break;
  902.     
  903.       default:                /* anything else is an error */
  904.     nogood = TRUE;
  905.         break;
  906.     }
  907.     
  908.     if (dtinc)                /* incomplete parse? */
  909. #ifdef TRACE
  910.       { fprintf(stderr,"INC\n");
  911. #endif
  912.       return(FALSE);            /* yes, fail immediately */
  913. #ifdef TRACE
  914.       }
  915. #endif
  916.  
  917.     if (nogood)                /* pattern element failed? */
  918.       if (opt) {            /* optional element? */
  919. #ifdef TRACE
  920.         fprintf(stderr,"NO (OK)\n");
  921. #endif
  922.     clearval();            /* yes, all values to -1 */
  923.     dttext = opttext;        /* restore input pointers */
  924.     dtlen = optlen;
  925.       }
  926.       else
  927. #ifdef TRACE
  928.         { fprintf(stderr,"NO\n");
  929. #endif
  930.     return(FALSE);            /* fail if failing elt not optional */
  931. #ifdef TRACE
  932.         }
  933.     else
  934.        fprintf(stderr,"OK\n");
  935. #endif
  936.  
  937.   }
  938.   return(TRUE);                /* got through entire pattern */
  939. }
  940.  
  941.  
  942.  
  943. /* matchkey
  944. **
  945. ** Purpose:
  946. **   Implement the %k special pattern element.  Input characters are matched
  947. **   against the keywords in the given array until a matching key is located,
  948. **   or until they have all failed to match.  In the former case, the index
  949. **   of the matching keyword is set into the patval array as the value
  950. **   returned by this pattern element, and TRUE is returned.  In the latter
  951. **   case, FALSE is returned.  If matching fails on any keyword as a result
  952. **   of insufficient input, dtinc is set and an immediate failure is given.
  953. **   A keyword matches the input only if the input contains all the characters
  954. **   contained in the keyword up to a vertical bar in the keyword.  After 
  955. **   that, additional matching characters are consumed as well, up to the
  956. **   first nonmatching character or the end of the keyword.  Case is ignored
  957. **   when matching letters.
  958. **
  959. **   In the case that the end of the input is reached (causing dtinc to be
  960. **   set), if the current input uniquely matches exactly one of the keywords,
  961. **   the remainder of that keyword is set as the completion text, even if
  962. **   the input has not reached the vertical bar marker for that keyword.
  963. ** Input arguments:
  964. **   keys - Pointer to an array of keyword strings, terminated by NULL.
  965. **
  966. ** Output arguments: None.
  967. ** Returns: TRUE for success, FALSE for failure or incomplete.
  968. **/
  969.  
  970.  
  971. static int
  972. matchkey(keys)
  973. char **keys;
  974. {
  975.   int len;                /* length of individual key match */
  976.   char *key;                /* pointer to individual key */
  977.   int sawbar;                /* TRUE if bar seen in matching key */
  978.   int i,j,k;
  979. #define KEYCMPLEN 15            /* size of following buffer */
  980.   static char keycmp[KEYCMPLEN];    /* keyword completion text buffer */
  981.  
  982. #ifdef TRACE
  983.   fprintf(stderr,"Keys [%s...] ",keys[0]);
  984. #endif
  985.   if (dtlen == 0) {            /* no input to match? */
  986.     dtinc = TRUE;            /* set incomplete flag */
  987.     dtcmp = NULL;            /* and no completion */
  988.     return(FALSE);            /* and fail immediately */
  989.   }
  990.       
  991.   for (i = 0; (key = keys[i]) != NULL; i++) { /* loop through the keys */
  992.     len = matchk1(dttext,dtlen,key,&sawbar); /* try to match current key */
  993.     if (dtinc || sawbar)
  994.       break;                /* exit on success or end of input */
  995.   }
  996.   
  997.   if (dtinc) {                /* ran out of input? */
  998.     key += len;                /* point to remainder of keyword */
  999.     if (sawbar) key++;            /* account for bar if seen */
  1000.     for (j = k = 0; k < KEYCMPLEN-1; j++) /* copy it to our buffer */
  1001.       if (key[j] == '|')
  1002.     continue;            /* skip the bar */
  1003.       else if (key[j] == NULCHAR)
  1004.     break;                /* exit at end of key */
  1005.       else
  1006.     keycmp[k++] = key[j];        /* copy other chars */
  1007.     keycmp[k] = NULCHAR;        /* tie off completion buffer */
  1008.     dtcmp = keycmp;            /* assume this is our completion */
  1009.     if (!sawbar)            /* if less than minimum match */
  1010.       while ((key = keys[++i]) != NULL)    /*  then look for a 2nd match */
  1011.         if (matchk1(dttext,dtlen,key,&sawbar) == len) { 
  1012.       dtcmp = NULL;            /* if found, then just beep */
  1013.       break;
  1014.         }
  1015.     return(FALSE);            /* fail the match in any case */
  1016.   }
  1017.   else if (sawbar) {            /* successful match? */
  1018.     dttext += len;            /* update input pointers */
  1019.     dtlen -= len;
  1020.     set1val(i);                /* set pattern return value */
  1021.     return(TRUE);            /* and succeed */
  1022.   }
  1023.   else                    /* all keys failed outright */
  1024.     return(FALSE);            /* so just fail */
  1025. }
  1026.  
  1027.  
  1028.  
  1029. /* matchk1 - Aux routine for matchkey
  1030. **
  1031. ** Purpose:
  1032. **   Determine how much of the input text matches a given keyword, and
  1033. **   return the number of matching chars.   Set a return flag indicating
  1034. **   whether or not a vertical bar was passed during the match (meaning
  1035. **   that the text should be accepted as matching the keyword).
  1036. **
  1037. ** Input arguments:
  1038. **   text - A pointer to the text to be matched.
  1039. **   textlen - The length of the text to be matched.  Guaranteed not
  1040. **     to be zero.
  1041. **   key - A pointer to the null-terminated keyword text.
  1042. **
  1043. ** Output arguments:
  1044. **   sawbar - Set TRUE if a vertical bar is passed while matching
  1045. **     the key.  Otherwise, FALSE is set.
  1046. **/
  1047.  
  1048. static int
  1049. matchk1(text,textlen,key,sawbar)
  1050. char *text,*key;
  1051. int textlen,*sawbar;
  1052. {
  1053.   char tc,kc;                /* individual chars to compare */
  1054.   int len = 0;                /* number of chars matched */
  1055.  
  1056.   *sawbar = FALSE;            /* bar not seen yet */
  1057.   while (textlen != 0) {        /* loop through text */
  1058.     tc = *text++;            /* get next char */
  1059.     textlen--;                /* and count it */
  1060.     if ((kc = *key++) == NULCHAR) 
  1061.       break;                /* exit at end of key */
  1062.     if (kc == '|') {
  1063.       *sawbar = TRUE;            /* there's the bar! */
  1064.       if ((kc = *key++) == NULCHAR)    /* move to next char */
  1065.     break;
  1066.     }
  1067.     if (islower(tc))            /* convert to upper case */
  1068.       tc = toupper(tc);
  1069.     if (islower(kc))
  1070.       kc = toupper(kc);
  1071.     if (tc != kc)            /* chars don't match? */
  1072.       return(len);            /* return length of match */
  1073.     else
  1074.       len++;                /* count matching chars */
  1075.   }
  1076.  
  1077.   if (kc == '|')            /* input just barely met minimum? */
  1078.     *sawbar = TRUE;            /* yup, set flag */
  1079.   else if (kc != NULCHAR)        /* reached end of input? */
  1080.     dtinc = TRUE;            /* set incomplete flag */
  1081.   return(len);                /* give number of matching chars */
  1082. }
  1083.  
  1084.  
  1085.  
  1086. /* matchnum
  1087. **
  1088. ** Purpose:
  1089. **   Parse a decimal number in the current input, and check the value
  1090. **   according to the number type indicated by the passed character
  1091. **   (one of the characters that can follow "%#" in a pattern).
  1092. **   Set return value(s) according to the type, as well.
  1093. **
  1094. ** Input arguments:
  1095. **   type - Character selecting the treatment required for the parsed number.
  1096. **
  1097. ** Output args: None.
  1098. ** Returns: TRUE if successful parse, FALSE otherwise.
  1099. **/
  1100.  
  1101. static int
  1102. matchnum(type)
  1103. char type;
  1104. {
  1105.   int val = 0;                /* value parsed from data */
  1106.   char tc;                /* individual chars from input */
  1107.   int digcnt = 0;            /* number of digits in number */
  1108.  
  1109. #ifdef TRACE
  1110.   fprintf(stderr,"Type [%c] ",type);
  1111. #endif
  1112.   if (dtlen <= 0) {            /* no input left? */
  1113.     dtinc = TRUE;            /* set incomplete flag */
  1114.     dtcmp = NULL;            /* no completion text */
  1115.     return(FALSE);            /* and fail */
  1116.   }
  1117.   tc = *dttext;                /* get first char */
  1118.   if (!isdigit(tc))
  1119.     return(FALSE);            /* no digits to parse */
  1120.   while (isdigit(tc)) {            /* loop through digits */
  1121.     val = (val*10) + tc - '0';        /* add digits into value */
  1122.     digcnt++;                /* and count them */
  1123.     tc = *(++dttext);            /* grab the next character */
  1124.     if ((--dtlen) <= 0)            /* and count the last one */
  1125.       break;                /* exit loop if no more chars */
  1126.   }
  1127.  
  1128.   switch(type) {            /* now validate the number */
  1129.     case 'm':                /* month number, range 1-12 */
  1130.       if ((digcnt > 2) || (val < 1) || (val > 12))
  1131.     return(FALSE);            /* out of range */
  1132.       val--;                /* drop to zero origin */
  1133.       break;
  1134.     case 'd':                /* day number, range 1-31 */
  1135.       if ((digcnt > 2) || (val < 1) || (val > 31))
  1136.     return(FALSE);            /* out of range */
  1137.       val--;                /* drop to zero origin */
  1138.       break;
  1139.     case 'y':                /* 2-digit year number */
  1140.       if ((digcnt != 2) || (val > 99))    /* value out of range? */
  1141.     return(FALSE);
  1142.       break;                /* leave good value as is */
  1143.     case 'Y':                /* 4-digit year number */
  1144.       if ((digcnt < 3) || (digcnt > 4) || (val > 9999))
  1145.     return(FALSE);            /* out of range */
  1146.       break;                /* leave good value as is */
  1147.     case 'h':                /* hour in range 1-12 */
  1148.       if ((digcnt > 2) || (val < 1) || (val > 12))
  1149.     return(FALSE);            /* out of range */
  1150.       break;                /* no modificaton to value */
  1151.     case 'H':                /* hour in range 0-24 */
  1152.       if ((digcnt > 2) || (val > 24))
  1153.     return(FALSE);            /* out of range */
  1154.       break;                /* no modification to value */
  1155.     case 'M':                /* 4-digit hours-and-minutes */
  1156.       if ((digcnt < 3) || (digcnt > 4) || (val > 2400))
  1157.     return(FALSE);            /* out of range */
  1158.       if ((val % 100) > 59)
  1159.     return(FALSE);            /* minutes out of range */
  1160.       set2val(val/100,val%100);        /* break up the input */
  1161.       return(TRUE);            /* and succeed */
  1162.     case 's':                /* seconds or minutes, range 0-59 */
  1163.       if ((digcnt != 2) || (val > 59))
  1164.     return(FALSE);            /* out of range */
  1165.       break;                /* good value is left as is */
  1166.     default:                /* unknown type */
  1167.       return(FALSE);            /* just fail */
  1168.   }
  1169.   
  1170.   set1val(val);                /* set return value */
  1171.   return(TRUE);                /* and succeed */
  1172. }
  1173.  
  1174.  
  1175.  
  1176. /* Routines for manipulating return value array:
  1177. **   clearval - Set all values to -1.
  1178. **   set1val - Set first value, clear the rest to -1.
  1179. **   set2val - Set first two values, clear the rest to -1.
  1180. **
  1181. ** Input arguments:
  1182. **   val1 - First value to set (set1val and set2val only)
  1183. **   val2 - Second value to set (set2val only)
  1184. **
  1185. ** Output arguments: None.
  1186. ** Returns: Nothing.
  1187. **/
  1188.  
  1189. static
  1190. clearval()
  1191. {
  1192.   int i;
  1193.   for (i = 0; i < MAXVAL; i++)        /* loop through value array */
  1194.     patval[i] = -1;            /* and clear the entries */
  1195. }
  1196.  
  1197. static
  1198. set1val(val1)
  1199. int val1;
  1200. {
  1201.   int i;
  1202.   for (i = 1; i < MAXVAL; i++)        /* clear all but first entry */
  1203.     patval[i] = -1;
  1204.   patval[0] = val1;            /* and set given value */
  1205. }
  1206.  
  1207. static
  1208. set2val(val1,val2)
  1209. int val1,val2;
  1210. {
  1211.   int i;
  1212.   for (i = 2; i < MAXVAL; i++)        /* clear all but first two entries */
  1213.     patval[i] = -1;
  1214.   patval[0] = val1;            /* and set given values */
  1215.   patval[1] = val2;
  1216. }
  1217.  
  1218.  
  1219.  
  1220.  
  1221. #ifdef TRACE
  1222.  
  1223. /* Routines to print out trace information during the pattern match */
  1224.  
  1225. static
  1226. trindent()                /* indent to current trace level */
  1227. {
  1228.   int i;
  1229.   for (i = 0; i < tracelev; i++)
  1230.     fprintf(stderr," ");
  1231. }
  1232.  
  1233. static
  1234. trshow()                /* show remaining unparsed input */
  1235. {
  1236.   int i;
  1237.   char c;
  1238.   for (i = 0; i < dtlen; i++) {
  1239.     c = dt[i];
  1240.     if ((c == '\177') || (c < SPACE))
  1241.       fprintf(stderr,"^%c",c^'\100');
  1242.     else
  1243.       fprintf(stderr,"%c",c);
  1244.   }
  1245. }
  1246.  
  1247. static
  1248. trargs()                /* print contents of patval array */
  1249. {
  1250.   int i;
  1251.   for (i = 0; i < MAXVAL; i++) {
  1252.     if (i != 0)
  1253.       fprintf(stderr," ");
  1254.     fprintf(stderr,"%d",patval[i]);
  1255.   }
  1256. }
  1257.  
  1258. static
  1259. trnl()                    /* print a newline */
  1260. {
  1261.   fprintf(stderr,"\n");
  1262. }
  1263.  
  1264. #endif
  1265.