Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / lexit.c
1 /* $TOG: lexit.c /main/5 1998/04/06 13:13:30 mgreess $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9 /*
10  * handcoded lexer to get rid of slow yylook routines
11  * produced by lex.
12  */
13
14
15 #include <EUSCompat.h>
16 #include <ctype.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/param.h>
23 #include <sys/stat.h>
24
25 #include "rtable4.h"
26 #include "parser.h"
27 #include "ansi_c.h"
28
29 extern char *pgname;
30 int yyylineno;
31
32 int externNumberVal;
33 char *externQuotedString;
34 Period_4 externPeriod;
35 Appt_Status_4 externApptStatus;
36 Tag_4 externTag;
37 Privacy_Level_4 externPrivacy;
38
39 static int      hash_string     P((register char *));
40 static char     *strescapes     P((char *));
41
42 static int len;
43 static caddr_t start_of_mmapped_area;
44 static caddr_t current_ptr;
45 static caddr_t end;
46
47 #define input_char()    ((end == current_ptr)?0:(*(current_ptr++)))
48 #define unput_char()    (((current_ptr>start_of_mmapped_area)?(current_ptr--):0))
49                           
50 #define ALPHA   1
51 #define DIGIT   2
52 #define IGNORE  3
53 #define SENDIT  4
54 #define EOFILE  5
55 #define QUOTE   6
56 #define COMMENT 7
57 #define NEWLINE 8
58 #define DASH    9
59
60 /* 
61   the following token entry allows us to combine the
62   hash table, token id and action on token recog 
63   together.
64   */
65
66 typedef struct token_data {
67   const char * name;            /* lexical token representation */
68   short first_token;            /* index into tokens.  points to
69                                    first token that hashes to this spot.
70                                    subsequent tokens may be found by following
71                                    next_token field IN first_token. */
72   short  next_token;
73   void * info_to_change;        /* ptr to 4 byte value to change when token 
74                                    is found.  Ignored if NULL */
75   int token_value;              /* value to set above to.  */
76   int return_value;             /* value to return from yyylex when this token
77                                    is found */
78 } token_data_t;
79
80 static u_char parse_buffer[BUFSIZ*3];
81 static u_char initial_mask[255];
82 static u_int  sendit_value[255];
83
84 /*
85   some macros to save my fingers
86  */
87
88 #define PVAL(a,b) { #a,-1, -1, &externPeriod.period,b,PERIODVAL}
89 #define AVAL(a,b) { #a,-1, -1, &externApptStatus,b,APPTSTATUSVAL}
90 #define TVAL(a,b) { #a,-1, -1, &externTag.tag,b,TAGSVAL }
91 #define SVAL(a,b) { #a,-1, -1, &externPrivacy,b,PRIVACYVAL}
92
93 #define EMP(a,b) {#a,-1, -1, NULL,NULL,b}
94
95 token_data_t tokens[] = {
96   PVAL(single, single_4),
97   PVAL(daily, daily_4),
98   PVAL(weekly, weekly_4),
99   PVAL(biweekly, biweekly_4),
100   PVAL(monthly, monthly_4),
101   PVAL(yearly, yearly_4),
102   PVAL(nthWeekday, nthWeekday_4),
103   PVAL(everyNthDay, everyNthDay_4),
104   PVAL(everyNthWeek, everyNthWeek_4),
105   PVAL(everyNthMonth, everyNthMonth_4),
106   PVAL(monThruFri, monThruFri_4),
107   PVAL(monWedFri, monWedFri_4),
108   PVAL(tueThur, tueThur_4),
109   PVAL(daysOfWeek, daysOfWeek_4),
110
111   AVAL(active, active_4),
112   AVAL(pendingAdd, pendingAdd_4),
113   AVAL(pendingDelete, pendingDelete_4),
114   AVAL(committed, committed_4),
115   AVAL(cancelled, cancelled_4),
116   AVAL(completed, completed_4),
117
118   TVAL(appointment, appointment_4),
119   TVAL(reminder, reminder_4),
120   TVAL(otherTag, otherTag_4),
121   TVAL(holiday, holiday_4),
122   TVAL(toDo, toDo_4),
123
124   SVAL(public, public_4),
125   SVAL(private, private_4),
126   SVAL(semiprivate, semiprivate_4),
127
128   EMP(attributes,ATTRIBUTES_4),
129   EMP(attributelist,ATTRIBUTES_5),
130   EMP(author,AUTHOR),
131   EMP(Version, VERSION),
132   EMP(calendarattributes,CALATTRS),
133   EMP(duration,DURATION),
134   EMP(period,PERIOD),
135   EMP(nth,NTH),
136   EMP(enddate,ENDDATE),
137   EMP(ntimes,NTIMES),
138   EMP(what,WHAT),
139   EMP(details,DETAILS),
140   EMP(mailto,MAILTO),
141   EMP(exceptions,EXCEPTION),
142   EMP(key,KEY),
143   EMP(read,READ),
144   EMP(write,WRITE),
145   EMP(delete,DELETE),
146   EMP(exec,EXEC),
147   EMP(apptstat,APPTSTATUS),
148   EMP(tags,TAGS),
149   EMP(privacy,PRIVACY),
150   EMP(add,ADD),
151   EMP(remove,REMOVE),
152   EMP(access,ACCESS),
153   EMP(entrytable, TABLE),
154   EMP(hashedattributes, HASHEDATTRS),
155   EMP(deny,DENY)};
156
157
158 #define NUMTOKES (sizeof(tokens)/sizeof(tokens[0]))
159
160 /*
161   load parser masks & build
162   token hash tables...
163   we could make this staticly,
164   inititialized, but it would
165   be harder to maintain.
166   */
167
168 /*
169 static u_char initial_mask[255];
170 static u_int  sendit_value[255];
171 */
172
173 static void
174 init()
175 {
176   int i;
177
178   for(i=0;i<255;i++)
179     if(isascii(i)) {
180       if(isalpha(i))
181         initial_mask[i] = ALPHA;
182       else if(isdigit(i))
183         initial_mask[i] = DIGIT;
184       else if(isspace(i))
185         initial_mask[i] = IGNORE;
186       else {
187         switch(i) {
188         case '-':
189           initial_mask[i] = DASH;
190           break;
191         case '*':
192           initial_mask[i] = COMMENT;
193           break;
194         case ' ':
195           initial_mask[i] = IGNORE;
196           break;
197         case '"':
198           initial_mask[i] = QUOTE;
199           break;
200         case '(':
201           initial_mask[i] = SENDIT;
202           sendit_value[i] = OPENPAREN;
203           break;
204         case ')':
205           initial_mask[i] = SENDIT;
206           sendit_value[i] = CLOSEPAREN;
207           break;
208         case ',':
209           initial_mask[i] = SENDIT;
210           sendit_value[i] = COMMA;
211           break;
212         case ':':
213           initial_mask[i] = SENDIT;
214           sendit_value[i] = COLON;
215           break;
216         }
217       }
218     }
219     else if(isspace(i))
220       initial_mask[i] = IGNORE;
221
222   initial_mask[0] = EOFILE;
223   initial_mask['\n'] = NEWLINE;
224
225   /*
226     build token hash table
227     */
228
229   for(i=0;i<NUMTOKES;i++) {
230     int bucket = hash_string((char *) tokens[i].name);
231     
232     if(tokens[bucket].first_token == -1)
233       tokens[bucket].first_token = i;
234     else {
235       int j = 0;
236       bucket = tokens[bucket].first_token;
237       while(tokens[bucket].next_token != -1)
238         j++, bucket = tokens[bucket].next_token;
239       tokens[bucket].next_token = i;
240     }
241   }
242 }
243
244 extern int
245 yyylex()
246 {
247   static int first_time=1;
248
249   if(first_time) {
250     init();
251     first_time = 0;
252   }
253
254   while(1) {
255     register u_char * ptr = parse_buffer;
256     register u_char c;
257
258     switch(initial_mask[ *ptr = input_char()]) {
259
260     case IGNORE:
261       continue;
262
263     case NEWLINE:
264       yyylineno++;
265       break;
266
267     case SENDIT:
268       *(ptr+1) = 0;
269       return(sendit_value[*ptr]);
270
271     case DASH:
272       /* make sure next input is a number */
273       if( initial_mask[*(++ptr)=input_char()] != DIGIT) {
274          fprintf(stderr, "%s: Unsupported char %c (ascii %d) found in callog file\n",
275               pgname, *ptr, *ptr);
276          return(0);
277       }
278       /* fall through */
279
280     case DIGIT:
281       while(initial_mask[*(++ptr)=input_char()] == DIGIT)
282         ;
283
284       unput_char();
285
286       *ptr = NULL;
287
288       externNumberVal=atoi((char *)parse_buffer); 
289       return(NUMBER);
290
291     case ALPHA:
292       {
293         register int bucket;
294         register token_data_t * t;
295
296         while(initial_mask[*(++ptr)=input_char()] == ALPHA)
297           ;
298
299         unput_char();
300
301         *ptr = NULL;
302
303         if ((bucket = tokens[hash_string((char *) parse_buffer)].first_token)
304             == -1) {
305                 fprintf(stderr, "%s: cannot lex %s in callog file\n",
306                         pgname, parse_buffer);
307                 return(0);
308         }
309
310         while(strcmp(tokens[bucket].name, (char*)parse_buffer) != 0)
311           if((bucket = tokens[bucket].next_token) == -1) /* end of chain */ {
312             fprintf(stderr, "%s: cannot lex %s in callog file\n", 
313                     pgname, parse_buffer);
314             return(0);
315           }
316         t = tokens + bucket;
317
318         if(t->info_to_change)
319           *((int *) t->info_to_change) = t->token_value;
320         return(t->return_value);
321       }
322
323     case QUOTE:       /* note that code removes leading and trailing quotes
324                                                 */
325       while(1) {
326         switch(*ptr = input_char())
327           {
328           case '\\':
329             *++ptr = input_char(); /* load next char in any case */
330                 ptr++; 
331             break;
332           case '"':
333             *ptr = 0;
334             strescapes((char *) parse_buffer); /* process any escape sequences */
335             externQuotedString = (char *) strdup((char *)parse_buffer);
336             return(QUOTEDSTRING);
337           case EOFILE:
338           case '\n':
339           case '\r':
340             fprintf(stderr, "%s: missing matching \" in callog file\n", pgname);
341             return(0);
342           default:
343             ptr++;
344           }
345       }
346       
347       
348       
349     case EOFILE:
350       return(0);
351
352     case COMMENT:
353       {
354         int i;
355         for(i=0;i<3;i++) /* look for 4 **** as comment lead-in */
356           if(initial_mask[*(++ptr)=input_char()]!= COMMENT) {
357             *(++ptr) = 0;
358             fprintf(stderr, "%s: cannot lex %s in callog file\n",
359                     pgname, parse_buffer);
360             return(0);
361           }
362
363         while(input_char() != '\n') /* eat up rest of comment */
364           ;
365         yyylineno++; /* since we eat the newline here.... */
366
367         continue;
368       }
369
370
371     default:
372       fprintf(stderr, "%s: Unsupported char %c (ascii %d) found in callog file\n",
373               pgname, *ptr, *ptr);
374       return(0);
375     }
376   }
377 }
378
379   
380 extern int
381 yyywrap(FILE *f)
382 {
383   fclose (f);
384   munmap(start_of_mmapped_area, len);
385   return (1);
386 }
387
388 extern int
389 setinput (FILE* f)
390 {
391   struct stat buff;
392
393   if(fstat(fileno(f), & buff) < 0) {
394     perror("fstat");
395     return(-1);
396   }
397   
398   if((start_of_mmapped_area = 
399       mmap(NULL,  
400            len = buff.st_size,
401            PROT_READ,
402            MAP_PRIVATE,
403            fileno(f),
404            0)) == (caddr_t) -1) { /* mmap failed??? */
405         perror("mmap");
406         return(-1);
407       }
408
409 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
410   /* no madvise so we lose this optimization */
411   madvise(start_of_mmapped_area, len, MADV_SEQUENTIAL);
412 #endif
413
414   end = start_of_mmapped_area + len;
415   current_ptr = start_of_mmapped_area;
416   yyylineno = 1;        
417   
418   return(0);
419 }
420
421
422
423 static int 
424 hash_string(register char *s)
425 {
426   register unsigned result = 0;
427   register int sum;
428   
429   while(sum = *s++)
430     result = (result << 4) + sum;
431   
432   return(result % NUMTOKES);
433 }
434
435 /*---------------------------------------------------------------------------
436   Strescapes performs escape character processing in a manner similar to the C
437   compiler.  It processes a character string passed to it and performs the
438   following substitutions in place:
439   
440   \\    ->   \
441   \"    ->   "
442   \e    ->   033
443   \t    ->   011
444   \n    ->   newline
445   \0nn  ->   0nn
446   \r    ->   cr
447   
448   A single \ in front of other characters is ignored(&removed).  As with
449   most string routines, strescapes returns it's argument.  Note that
450   inserting a \0 will end the string at that location, but the remainder of
451   the string will be processed.
452   
453   ---------------------------------------------------------------------------*/
454
455
456 static char * 
457 strescapes(char *s)
458 {
459   register char * in=s, * out=s;
460   
461   while(*in)
462     {
463       if(*in == '\\')
464         switch(*(in+1))
465           {
466           case 'e':  /* an escape character */
467             *out++ = '\033';
468             in += 2;
469             break;
470             
471           case 't':  /* a tab character */
472             *out++ = '\t';
473             in += 2;
474             break;
475             
476           case 'n':  /* a newline */
477             *out++ = '\n';
478             in += 2;
479             break;
480           case 'r':  /* a carriage return */
481             *out++ = '\r';
482             in += 2;
483             break;
484           case '\\': /* a backslash */
485             *out++ = '\\';
486             in += 2;
487             break;
488           case '0':  /* a octal constant */
489             {
490               register int i,result;
491               in++;
492               for(result=i=0; (i<=3) && (*in >='0') && (*in <= '7') ; )
493                 {
494                   result <<= 3;
495                   result += (*in++) - '0';
496                 }
497               *out++ = result & 0377;
498               break;
499             }
500             
501           default:  /* not used as escape.... make it disappear */
502             in++;   /* this also handles nulls */
503             break;
504           }
505       else
506         *out++ = * in++;
507     }
508   *out='\0';
509   return(s);
510 }
511
512
513 #ifdef TEST
514
515 main(int argc, char ** argv)
516 {
517   FILE * f;
518   int d;
519
520   if(argc < 2) {
521     printf("usage: %s filename\n", argv[0]);
522     exit(1);
523   }
524
525   if((f = fopen(argv[1], "r")) == NULL) {
526     perror("fopen");
527     exit(2);
528   }
529
530   if(setinput(f) < 0) {
531     exit(3);
532   }
533
534   while(d=yyylex()) {
535     if(argc == 2)
536       printf("received %d, text <%s>\n", d, parse_buffer);
537   }
538
539   exit(0);
540 }
541     
542 #endif