1
/***************************************************************/
5
/* Contains routines for parsing the reminder file and */
6
/* classifying the tokens parsed. */
8
/* This file is part of REMIND. */
9
/* Copyright (C) 1992-1998 by David F. Skoll */
10
/* Copyright (C) 1999-2000 by Roaring Penguin Software Inc. */
12
/***************************************************************/
15
static char const RCSID[] = "$Id: token.c,v 1.9 2000/02/18 03:46:10 dfs Exp $";
34
/* The macro PARSENUM parses a char pointer as an integer. It simply
35
executes 'return' if an initial non-numeric char is found. */
36
#define PARSENUM(var, string) \
37
if (!isdigit(*(string))) return; \
39
while (isdigit(*(string))) { \
41
var += *(string) - '0'; \
45
#define UPPER(c) (islower(c) ? toupper(c) : c)
47
/* The big array holding all recognized (literal) tokens in reminder file.
48
Keep this array sorted, or software will not work. */
50
/* NAME MINLEN TYPE VALUE */
52
{ "after", 3, T_Skip, AFTER_SKIP },
53
{ "april", 3, T_Month, 3 },
55
{ "august", 3, T_Month, 7 },
56
{ "banner", 3, T_Banner, 0 },
57
{ "before", 3, T_Skip, BEFORE_SKIP },
58
{ "cal", 3, T_RemType, CAL_TYPE },
59
{ "clear-omit-context", 5, T_Clr, 0 },
60
{ "debug", 5, T_Debug, 0 },
61
{ "december", 3, T_Month, 11 },
62
{ "dumpvars", 4, T_Dumpvars, 0 },
63
{ "duration", 3, T_Duration, 0 },
64
{ "else", 4, T_Else, 0 },
65
{ "endif", 5, T_EndIf, 0 },
66
{ "errmsg", 6, T_ErrMsg, 0 },
67
{ "exit", 4, T_Exit, 0 },
68
{ "february", 3, T_Month, 1 },
69
{ "flush", 5, T_Flush, 0 },
70
{ "friday", 3, T_WkDay, 4 },
71
{ "fset", 4, T_Fset, 0 },
73
{ "iftrig", 6, T_IfTrig, 0 },
74
{ "include", 3, T_Include, 0 },
75
{ "january", 3, T_Month, 0 },
76
{ "july", 3, T_Month, 6 },
77
{ "june", 3, T_Month, 5 },
78
{ "march", 3, T_Month, 2 },
79
{ "may", 3, T_Month, 4 },
80
{ "monday", 3, T_WkDay, 0 },
81
{ "msf", 3, T_RemType, MSF_TYPE },
82
{ "msg", 3, T_RemType, MSG_TYPE },
83
{ "november", 3, T_Month, 10 },
84
{ "october", 3, T_Month, 9 },
85
{ "omit", 3, T_Omit, 0 },
86
{ "once", 3, T_Once, 0 },
87
{ "pop-omit-context", 3, T_Pop, 0 },
88
{ "preserve", 8, T_Preserve, 0 },
89
{ "priority", 8, T_Priority, 0 },
90
{ "ps", 2, T_RemType, PS_TYPE },
91
{ "psfile", 6, T_RemType, PSF_TYPE },
92
{ "push-omit-context", 4, T_Push, 0 },
93
{ "rem", 3, T_Rem, 0 },
94
{ "run", 3, T_RemType, RUN_TYPE },
95
{ "satisfy", 7, T_RemType, SAT_TYPE },
96
{ "saturday", 3, T_WkDay, 5 },
97
{ "scanfrom", 4, T_Scanfrom, 0 },
98
{ "sched", 5, T_Sched, 0 },
99
{ "september", 3, T_Month, 8 },
100
{ "set", 3, T_Set, 0 },
101
{ "skip", 3, T_Skip, SKIP_SKIP },
102
{ "special", 7, T_RemType, PASSTHRU_TYPE },
103
{ "sunday", 3, T_WkDay, 6 },
104
{ "tag", 3, T_Tag, 0 },
105
{ "thursday", 3, T_WkDay, 3 },
106
{ "tuesday", 3, T_WkDay, 1 },
107
{ "unset", 5, T_UnSet, 0 },
108
{ "until", 3, T_Until, 0 },
109
{ "warn", 4, T_Warn, 0 },
110
{ "wednesday", 3, T_WkDay, 2 }
113
/* If language != English, we must also search the following... */
115
Token NonEnglishToks[] = {
116
/* NAME MINLEN TYPE VALUE */
118
{ L_MONDAY, 3, T_WkDay, 0 },
119
{ L_TUESDAY, 3, T_WkDay, 1 },
120
{ L_WEDNESDAY, 3, T_WkDay, 2 },
121
{ L_THURSDAY, 3, T_WkDay, 3 },
122
{ L_FRIDAY, 3, T_WkDay, 4 },
123
{ L_SATURDAY, 3, T_WkDay, 5 },
124
{ L_SUNDAY, 3, T_WkDay, 6 },
125
{ L_JAN, 3, T_Month, 0 },
126
{ L_FEB, 3, T_Month, 1 },
127
{ L_MAR, 3, T_Month, 2 },
128
{ L_APR, 3, T_Month, 3 },
129
{ L_MAY, 3, T_Month, 4 },
130
{ L_JUN, 3, T_Month, 5 },
131
{ L_JUL, 3, T_Month, 6 },
132
{ L_AUG, 3, T_Month, 7 },
133
{ L_SEP, 3, T_Month, 8 },
134
{ L_OCT, 3, T_Month, 9 },
135
{ L_NOV, 3, T_Month, 10 },
136
{ L_DEC, 3, T_Month, 11 }
140
PRIVATE int TokStrCmp ARGS((const Token *t, const char *s));
142
/***************************************************************/
144
/* FindInitialToken */
146
/* Find the initial token on the command line. If it's a */
147
/* left square bracket, return a T_Illegal type. */
149
/***************************************************************/
151
PUBLIC char *FindInitialToken(Token *tok, char *s)
153
char *FindInitialToken(tok, s)
161
tok->type = T_Illegal;
163
while (isspace(*s)) s++;
165
while (*s && !isspace(*s)) {
166
if (DBufPutc(&buf, *s++) != OK) return s;
169
FindToken(DBufValue(&buf), tok);
176
/***************************************************************/
180
/* Given a string, which token is it? */
182
/***************************************************************/
184
PUBLIC void FindToken(const char *s, Token *tok)
186
void FindToken(s, tok)
191
register int top, bot, mid, r;
194
tok->type = T_Illegal;
200
if (*s == '#' || *s == ';') {
201
tok->type = T_Comment;
205
/* Quickly give up the search if first char not a letter */
206
if ( ! isalpha(*s)) {
207
FindNumericToken(s, tok);
213
/* Ignore trailing commas */
214
if (l > 0 && s[l-1] == ',') {
218
top = sizeof(TokArray) / sizeof(TokArray[0]) - 1;
221
mid = (top + bot) / 2;
222
r = TokStrCmp(&TokArray[mid], s);
224
if (l >= TokArray[mid].MinLen) {
225
tok->type = TokArray[mid].type;
226
tok->val = TokArray[mid].val;
229
while (mid && !TokStrCmp(&TokArray[mid-1],s)) mid--;
230
while (!TokStrCmp(&TokArray[mid], s) && l < TokArray[mid].MinLen)
232
if (!TokStrCmp(&TokArray[mid], s)) {
233
tok->type = TokArray[mid].type;
234
tok->val = TokArray[mid].val;
240
if (r > 0) top = mid-1; else bot=mid+1;
243
/* If language is other than English, search the DayNames[] and MonthNames[]
246
for (r=0; r<(sizeof(NonEnglishToks) / sizeof(Token)); r++) {
247
if (l >= NonEnglishToks[r].MinLen &&
248
!TokStrCmp(&NonEnglishToks[r], s)) {
249
tok->type = NonEnglishToks[r].type;
250
tok->val = NonEnglishToks[r].val;
259
/***************************************************************/
261
/* FindNumericToken */
263
/* Parse a numeric token: */
264
/* Year - number between 1990 and 2085, or 90-99. */
265
/* Day - number between 1 and 31 */
270
/***************************************************************/
272
PUBLIC void FindNumericToken(const char *s, Token *t)
274
void FindNumericToken(s, t)
279
int mult = 1, hour, min;
286
/* If we hit a comma, swallow it. This allows stuff
290
/* Special hack - convert years between 90 and
291
99 to 1990 and 1999 */
292
if (t->val >= 90 && t->val <= 99) t->val += 1900;
294
/* Classify the number we've got */
295
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year;
296
else if (t->val >= 1 && t->val <= 31) t->type = T_Day;
297
else t->type = T_Number;
300
/* If we hit a colon or a period, we've probably got a time hr:min */
301
if (*s == ':' || *s == '.' || *s == TIMESEP) {
305
if (*s || hour > 23 || min > 59) return; /* Illegal time */
306
t->val = hour*60 + min; /* Convert to minutes past midnight */
311
/* If we hit a non-digit, error! */
314
/* Special hack - convert years between 90 and 99 to 1990 and 1999 */
315
if (t->val >= 90 && t->val <= 99) t->val += 1900;
317
/* Classify the number we've got */
318
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year;
319
else if (t->val >= 1 && t->val <= 31) t->type = T_Day;
320
else t->type = T_Number;
322
} else if (*s == '*') {
325
if (*s) return; /* Illegal token if followed by non-numeric char */
328
} else if (*s == '+') {
330
if (*s == '+') { mult = -1; s++; }
332
if (*s) return; /* Illegal token if followed by non-numeric char */
336
} else if (*s == '-') {
338
if (*s == '-') { mult = -1; s++; }
340
if (*s) return; /* Illegal token if followed by non-numeric char */
345
return; /* Unknown token type */
349
/***************************************************************/
353
/* Compare a token to a string. */
355
/***************************************************************/
357
PRIVATE int TokStrCmp(const Token *t, const char *s)
359
static int TokStrCmp(t, s)
366
while(*tk && *s && !(*s == ',' && *(s+1) == 0)) {
367
r = UPPER(*tk) - UPPER(*s);
372
/* Ignore trailing commas on s */
374
if (!*s || (*s == ',' && !*(s+1))) return 0;