17e71a8c5bd3b1eaeeec2d9f05da77c68668faa2
[oweals/cde.git] / cde / config / makedepend / ifparser.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * $XConsortium: ifparser.c /main/10 1996/09/28 16:15:18 rws $
25  *
26  * Copyright 1992 Network Computing Devices, Inc.
27  * 
28  * Permission to use, copy, modify, and distribute this software and its
29  * documentation for any purpose and without fee is hereby granted, provided
30  * that the above copyright notice appear in all copies and that both that
31  * copyright notice and this permission notice appear in supporting
32  * documentation, and that the name of Network Computing Devices may not be
33  * used in advertising or publicity pertaining to distribution of the software
34  * without specific, written prior permission.  Network Computing Devices makes
35  * no representations about the suitability of this software for any purpose.
36  * It is provided ``as is'' without express or implied warranty.
37  * 
38  * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
39  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
40  * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
41  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
42  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
43  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44  * PERFORMANCE OF THIS SOFTWARE.
45  * 
46  * Author:  Jim Fulton
47  *          Network Computing Devices, Inc.
48  * 
49  * Simple if statement processor
50  *
51  * This module can be used to evaluate string representations of C language
52  * if constructs.  It accepts the following grammar:
53  * 
54  *     EXPRESSION       :=      VALUE
55  *                       |      VALUE  BINOP    EXPRESSION
56  * 
57  *     VALUE            :=      '('  EXPRESSION  ')'
58  *                       |      '!'  VALUE
59  *                       |      '-'  VALUE
60  *                       |      'defined'  '('  variable  ')'
61  *                       |      'defined'  variable
62  *                       |      # variable '(' variable-list ')'
63  *                       |      variable
64  *                       |      number
65  * 
66  *     BINOP            :=      '*'     |  '/'  |  '%'
67  *                       |      '+'     |  '-'
68  *                       |      '<<'    |  '>>'
69  *                       |      '<'     |  '>'  |  '<='  |  '>='
70  *                       |      '=='    |  '!='
71  *                       |      '&'     |  '|'
72  *                       |      '&&'    |  '||'
73  * 
74  * The normal C order of precidence is supported.
75  * 
76  * 
77  * External Entry Points:
78  * 
79  *     ParseIfExpression                parse a string for #if
80  */
81
82 #include "ifparser.h"
83 #include <ctype.h>
84 #include <string.h>
85 #include <limits.h>
86
87 /****************************************************************************
88                    Internal Macros and Utilities for Parser
89  ****************************************************************************/
90
91 #define DO(val) if (!(val)) return NULL
92 #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
93 #define SKIPSPACE(ccc) while (isspace((int)*ccc)) ccc++
94 #define isvarfirstletter(ccc) (isalpha((int)ccc) || (ccc) == '_')
95
96
97 static const char *
98 parse_variable (g, cp, varp)
99     IfParser *g;
100     const char *cp;
101     const char **varp;
102 {
103     SKIPSPACE (cp);
104
105     if (!isvarfirstletter (*cp))
106         return CALLFUNC(g, handle_error) (g, cp, "variable name");
107
108     *varp = cp;
109     /* EMPTY */
110     for (cp++; isalnum((int)*cp) || *cp == '_'; cp++) ;
111     return cp;
112 }
113
114
115 static const char *
116 parse_number (g, cp, valp)
117     IfParser *g;
118     const char *cp;
119     long *valp;
120 {
121     long base = 10;
122     SKIPSPACE (cp);
123
124     if (!isdigit((int)*cp))
125         return CALLFUNC(g, handle_error) (g, cp, "number");
126
127     *valp = 0;
128
129     if (*cp == '0') {
130         cp++;
131         if ((*cp == 'x') || (*cp == 'X')) {
132             base = 16;
133             cp++;
134         } else {
135             base = 8;
136         }
137     }
138
139     /* Ignore overflows and assume ASCII, what source is usually written in */
140     while (1) {
141         int increment = -1;
142         if (base == 8) {
143             if ((*cp >= '0') && (*cp <= '7'))
144                 increment = *cp++ - '0';
145         } else if (base == 16) {
146             if ((*cp >= '0') && (*cp <= '9'))
147                 increment = *cp++ - '0';
148             else if ((*cp >= 'A') &&  (*cp <= 'F'))
149                 increment = *cp++ - ('A' - 10);
150             else if ((*cp >= 'a') && (*cp <= 'f'))
151                 increment = *cp++ - ('a' - 10);
152         } else {        /* Decimal */
153             if ((*cp >= '0') && (*cp <= '9'))
154                 increment = *cp++ - '0';
155         }
156         if (increment < 0)
157             break;
158         *valp = (*valp * base) + increment;
159     }
160
161     /* Skip trailing qualifiers */
162     while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
163     return cp;
164 }
165
166 static const char *
167 parse_character (g, cp, valp)
168     IfParser *g;
169     const char *cp;
170     long *valp;
171 {
172     char val;
173
174     SKIPSPACE (cp);
175     if (*cp == '\\')
176         switch (cp[1]) {
177         case 'n': val = '\n'; break;
178         case 't': val = '\t'; break;
179         case 'v': val = '\v'; break;
180         case 'b': val = '\b'; break;
181         case 'r': val = '\r'; break;
182         case 'f': val = '\f'; break;
183         case 'a': val = '\a'; break;
184         case '\\': val = '\\'; break;
185         case '?': val = '\?'; break;
186         case '\'': val = '\''; break;
187         case '\"': val = '\"'; break;
188         case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
189         default: val = (char) strtol (cp + 1, NULL, 8); break;
190         }
191     else
192         val = *cp;
193     while (*cp != '\'') cp++;
194     *valp = (long) val;
195     return cp;
196 }
197
198 static const char *
199 parse_value (g, cp, valp)
200     IfParser *g;
201     const char *cp;
202     long *valp;
203 {
204     const char *var;
205
206     *valp = 0;
207
208     SKIPSPACE (cp);
209     if (!*cp)
210         return cp;
211
212     switch (*cp) {
213       case '(':
214         DO (cp = ParseIfExpression (g, cp + 1, valp));
215         SKIPSPACE (cp);
216         if (*cp != ')') 
217             return CALLFUNC(g, handle_error) (g, cp, ")");
218
219         return cp + 1;                  /* skip the right paren */
220
221       case '!':
222         DO (cp = parse_value (g, cp + 1, valp));
223         *valp = !(*valp);
224         return cp;
225
226       case '-':
227         DO (cp = parse_value (g, cp + 1, valp));
228         *valp = -(*valp);
229         return cp;
230
231       case '#':
232         DO (cp = parse_variable (g, cp + 1, &var));
233         SKIPSPACE (cp);
234         if (*cp != '(')
235             return CALLFUNC(g, handle_error) (g, cp, "(");
236         do {
237             DO (cp = parse_variable (g, cp + 1, &var));
238             SKIPSPACE (cp);
239         } while (*cp && *cp != ')');
240         if (*cp != ')')
241             return CALLFUNC(g, handle_error) (g, cp, ")");
242         *valp = 1; /* XXX */
243         return cp + 1;
244
245       case '\'':
246         DO (cp = parse_character (g, cp + 1, valp));
247         if (*cp != '\'')
248             return CALLFUNC(g, handle_error) (g, cp, "'");
249         return cp + 1;
250
251       case 'd':
252         if (strncmp (cp, "defined", 7) == 0 && !isalnum((int)cp[7])) {
253             int paren = 0;
254             int len;
255
256             cp += 7;
257             SKIPSPACE (cp);
258             if (*cp == '(') {
259                 paren = 1;
260                 cp++;
261             }
262             DO (cp = parse_variable (g, cp, &var));
263             len = cp - var;
264             SKIPSPACE (cp);
265             if (paren && *cp != ')')
266                 return CALLFUNC(g, handle_error) (g, cp, ")");
267             *valp = (*(g->funcs.eval_defined)) (g, var, len);
268             return cp + paren;          /* skip the right paren */
269         }
270         /* fall out */
271     }
272
273     if (isdigit((int)*cp)) {
274         DO (cp = parse_number (g, cp, valp));
275     } else if (!isvarfirstletter(*cp))
276         return CALLFUNC(g, handle_error) (g, cp, "variable or number");
277     else {
278         DO (cp = parse_variable (g, cp, &var));
279         *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
280     }
281     
282     return cp;
283 }
284
285
286
287 static const char *
288 parse_product (g, cp, valp)
289     IfParser *g;
290     const char *cp;
291     long *valp;
292 {
293     long rightval;
294
295     DO (cp = parse_value (g, cp, valp));
296     SKIPSPACE (cp);
297
298     switch (*cp) {
299       case '*':
300         DO (cp = parse_product (g, cp + 1, &rightval));
301         *valp = (*valp * rightval);
302         break;
303
304       case '/':
305         DO (cp = parse_product (g, cp + 1, &rightval));
306         if (rightval)
307             *valp = (*valp / rightval);
308         else
309             *valp = LONG_MAX;
310         break;
311
312       case '%':
313         DO (cp = parse_product (g, cp + 1, &rightval));
314         *valp = (*valp % rightval);
315         break;
316     }
317     return cp;
318 }
319
320
321 static const char *
322 parse_sum (g, cp, valp)
323     IfParser *g;
324     const char *cp;
325     long *valp;
326 {
327     long rightval;
328
329     DO (cp = parse_product (g, cp, valp));
330     SKIPSPACE (cp);
331
332     switch (*cp) {
333       case '+':
334         DO (cp = parse_sum (g, cp + 1, &rightval));
335         *valp = (*valp + rightval);
336         break;
337
338       case '-':
339         DO (cp = parse_sum (g, cp + 1, &rightval));
340         *valp = (*valp - rightval);
341         break;
342     }
343     return cp;
344 }
345
346
347 static const char *
348 parse_shift (g, cp, valp)
349     IfParser *g;
350     const char *cp;
351     long *valp;
352 {
353     long rightval;
354
355     DO (cp = parse_sum (g, cp, valp));
356     SKIPSPACE (cp);
357
358     switch (*cp) {
359       case '<':
360         if (cp[1] == '<') {
361             DO (cp = parse_shift (g, cp + 2, &rightval));
362             *valp = (*valp << rightval);
363         }
364         break;
365
366       case '>':
367         if (cp[1] == '>') {
368             DO (cp = parse_shift (g, cp + 2, &rightval));
369             *valp = (*valp >> rightval);
370         }
371         break;
372     }
373     return cp;
374 }
375
376
377 static const char *
378 parse_inequality (g, cp, valp)
379     IfParser *g;
380     const char *cp;
381     long *valp;
382 {
383     long rightval;
384
385     DO (cp = parse_shift (g, cp, valp));
386     SKIPSPACE (cp);
387
388     switch (*cp) {
389       case '<':
390         if (cp[1] == '=') {
391             DO (cp = parse_inequality (g, cp + 2, &rightval));
392             *valp = (*valp <= rightval);
393         } else {
394             DO (cp = parse_inequality (g, cp + 1, &rightval));
395             *valp = (*valp < rightval);
396         }
397         break;
398
399       case '>':
400         if (cp[1] == '=') {
401             DO (cp = parse_inequality (g, cp + 2, &rightval));
402             *valp = (*valp >= rightval);
403         } else {
404             DO (cp = parse_inequality (g, cp + 1, &rightval));
405             *valp = (*valp > rightval);
406         }
407         break;
408     }
409     return cp;
410 }
411
412
413 static const char *
414 parse_equality (g, cp, valp)
415     IfParser *g;
416     const char *cp;
417     long *valp;
418 {
419     long rightval;
420
421     DO (cp = parse_inequality (g, cp, valp));
422     SKIPSPACE (cp);
423
424     switch (*cp) {
425       case '=':
426         if (cp[1] == '=')
427             cp++;
428         DO (cp = parse_equality (g, cp + 1, &rightval));
429         *valp = (*valp == rightval);
430         break;
431
432       case '!':
433         if (cp[1] != '=')
434             break;
435         DO (cp = parse_equality (g, cp + 2, &rightval));
436         *valp = (*valp != rightval);
437         break;
438     }
439     return cp;
440 }
441
442
443 static const char *
444 parse_band (g, cp, valp)
445     IfParser *g;
446     const char *cp;
447     long *valp;
448 {
449     long rightval;
450
451     DO (cp = parse_equality (g, cp, valp));
452     SKIPSPACE (cp);
453
454     switch (*cp) {
455       case '&':
456         if (cp[1] != '&') {
457             DO (cp = parse_band (g, cp + 1, &rightval));
458             *valp = (*valp & rightval);
459         }
460         break;
461     }
462     return cp;
463 }
464
465
466 static const char *
467 parse_bor (g, cp, valp)
468     IfParser *g;
469     const char *cp;
470     long *valp;
471 {
472     long rightval;
473
474     DO (cp = parse_band (g, cp, valp));
475     SKIPSPACE (cp);
476
477     switch (*cp) {
478       case '|':
479         if (cp[1] != '|') {
480             DO (cp = parse_bor (g, cp + 1, &rightval));
481             *valp = (*valp | rightval);
482         }
483         break;
484     }
485     return cp;
486 }
487
488
489 static const char *
490 parse_land (g, cp, valp)
491     IfParser *g;
492     const char *cp;
493     long *valp;
494 {
495     long rightval;
496
497     DO (cp = parse_bor (g, cp, valp));
498     SKIPSPACE (cp);
499
500     switch (*cp) {
501       case '&':
502         if (cp[1] != '&')
503             return CALLFUNC(g, handle_error) (g, cp, "&&");
504         DO (cp = parse_land (g, cp + 2, &rightval));
505         *valp = (*valp && rightval);
506         break;
507     }
508     return cp;
509 }
510
511
512 static const char *
513 parse_lor (g, cp, valp)
514     IfParser *g;
515     const char *cp;
516     long *valp;
517 {
518     long rightval;
519
520     DO (cp = parse_land (g, cp, valp));
521     SKIPSPACE (cp);
522
523     switch (*cp) {
524       case '|':
525         if (cp[1] != '|')
526             return CALLFUNC(g, handle_error) (g, cp, "||");
527         DO (cp = parse_lor (g, cp + 2, &rightval));
528         *valp = (*valp || rightval);
529         break;
530     }
531     return cp;
532 }
533
534
535 /****************************************************************************
536                              External Entry Points
537  ****************************************************************************/
538
539 const char *
540 ParseIfExpression (g, cp, valp)
541     IfParser *g;
542     const char *cp;
543     long *valp;
544 {
545     return parse_lor (g, cp, valp);
546 }
547
548