FreeBSD 10 clang port
[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 <limits.h>
85
86 /****************************************************************************
87                    Internal Macros and Utilities for Parser
88  ****************************************************************************/
89
90 #define DO(val) if (!(val)) return NULL
91 #define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff))
92 #define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++
93 #define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_')
94
95
96 static const char *
97 parse_variable (g, cp, varp)
98     IfParser *g;
99     const char *cp;
100     const char **varp;
101 {
102     SKIPSPACE (cp);
103
104     if (!isvarfirstletter (*cp))
105         return CALLFUNC(g, handle_error) (g, cp, "variable name");
106
107     *varp = cp;
108     /* EMPTY */
109     for (cp++; isalnum(*cp) || *cp == '_'; cp++) ;
110     return cp;
111 }
112
113
114 static const char *
115 parse_number (g, cp, valp)
116     IfParser *g;
117     const char *cp;
118     long *valp;
119 {
120     long base = 10;
121     SKIPSPACE (cp);
122
123     if (!isdigit(*cp))
124         return CALLFUNC(g, handle_error) (g, cp, "number");
125
126     *valp = 0;
127
128     if (*cp == '0') {
129         cp++;
130         if ((*cp == 'x') || (*cp == 'X')) {
131             base = 16;
132             cp++;
133         } else {
134             base = 8;
135         }
136     }
137
138     /* Ignore overflows and assume ASCII, what source is usually written in */
139     while (1) {
140         int increment = -1;
141         if (base == 8) {
142             if ((*cp >= '0') && (*cp <= '7'))
143                 increment = *cp++ - '0';
144         } else if (base == 16) {
145             if ((*cp >= '0') && (*cp <= '9'))
146                 increment = *cp++ - '0';
147             else if ((*cp >= 'A') &&  (*cp <= 'F'))
148                 increment = *cp++ - ('A' - 10);
149             else if ((*cp >= 'a') && (*cp <= 'f'))
150                 increment = *cp++ - ('a' - 10);
151         } else {        /* Decimal */
152             if ((*cp >= '0') && (*cp <= '9'))
153                 increment = *cp++ - '0';
154         }
155         if (increment < 0)
156             break;
157         *valp = (*valp * base) + increment;
158     }
159
160     /* Skip trailing qualifiers */
161     while (*cp == 'U' || *cp == 'u' || *cp == 'L' || *cp == 'l') cp++;
162     return cp;
163 }
164
165 static const char *
166 parse_character (g, cp, valp)
167     IfParser *g;
168     const char *cp;
169     long *valp;
170 {
171     char val;
172
173     SKIPSPACE (cp);
174     if (*cp == '\\')
175         switch (cp[1]) {
176         case 'n': val = '\n'; break;
177         case 't': val = '\t'; break;
178         case 'v': val = '\v'; break;
179         case 'b': val = '\b'; break;
180         case 'r': val = '\r'; break;
181         case 'f': val = '\f'; break;
182         case 'a': val = '\a'; break;
183         case '\\': val = '\\'; break;
184         case '?': val = '\?'; break;
185         case '\'': val = '\''; break;
186         case '\"': val = '\"'; break;
187         case 'x': val = (char) strtol (cp + 2, NULL, 16); break;
188         default: val = (char) strtol (cp + 1, NULL, 8); break;
189         }
190     else
191         val = *cp;
192     while (*cp != '\'') cp++;
193     *valp = (long) val;
194     return cp;
195 }
196
197 static const char *
198 parse_value (g, cp, valp)
199     IfParser *g;
200     const char *cp;
201     long *valp;
202 {
203     const char *var;
204
205     *valp = 0;
206
207     SKIPSPACE (cp);
208     if (!*cp)
209         return cp;
210
211     switch (*cp) {
212       case '(':
213         DO (cp = ParseIfExpression (g, cp + 1, valp));
214         SKIPSPACE (cp);
215         if (*cp != ')') 
216             return CALLFUNC(g, handle_error) (g, cp, ")");
217
218         return cp + 1;                  /* skip the right paren */
219
220       case '!':
221         DO (cp = parse_value (g, cp + 1, valp));
222         *valp = !(*valp);
223         return cp;
224
225       case '-':
226         DO (cp = parse_value (g, cp + 1, valp));
227         *valp = -(*valp);
228         return cp;
229
230       case '#':
231         DO (cp = parse_variable (g, cp + 1, &var));
232         SKIPSPACE (cp);
233         if (*cp != '(')
234             return CALLFUNC(g, handle_error) (g, cp, "(");
235         do {
236             DO (cp = parse_variable (g, cp + 1, &var));
237             SKIPSPACE (cp);
238         } while (*cp && *cp != ')');
239         if (*cp != ')')
240             return CALLFUNC(g, handle_error) (g, cp, ")");
241         *valp = 1; /* XXX */
242         return cp + 1;
243
244       case '\'':
245         DO (cp = parse_character (g, cp + 1, valp));
246         if (*cp != '\'')
247             return CALLFUNC(g, handle_error) (g, cp, "'");
248         return cp + 1;
249
250       case 'd':
251         if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) {
252             int paren = 0;
253             int len;
254
255             cp += 7;
256             SKIPSPACE (cp);
257             if (*cp == '(') {
258                 paren = 1;
259                 cp++;
260             }
261             DO (cp = parse_variable (g, cp, &var));
262             len = cp - var;
263             SKIPSPACE (cp);
264             if (paren && *cp != ')')
265                 return CALLFUNC(g, handle_error) (g, cp, ")");
266             *valp = (*(g->funcs.eval_defined)) (g, var, len);
267             return cp + paren;          /* skip the right paren */
268         }
269         /* fall out */
270     }
271
272     if (isdigit(*cp)) {
273         DO (cp = parse_number (g, cp, valp));
274     } else if (!isvarfirstletter(*cp))
275         return CALLFUNC(g, handle_error) (g, cp, "variable or number");
276     else {
277         DO (cp = parse_variable (g, cp, &var));
278         *valp = (*(g->funcs.eval_variable)) (g, var, cp - var);
279     }
280     
281     return cp;
282 }
283
284
285
286 static const char *
287 parse_product (g, cp, valp)
288     IfParser *g;
289     const char *cp;
290     long *valp;
291 {
292     long rightval;
293
294     DO (cp = parse_value (g, cp, valp));
295     SKIPSPACE (cp);
296
297     switch (*cp) {
298       case '*':
299         DO (cp = parse_product (g, cp + 1, &rightval));
300         *valp = (*valp * rightval);
301         break;
302
303       case '/':
304         DO (cp = parse_product (g, cp + 1, &rightval));
305         if (rightval)
306             *valp = (*valp / rightval);
307         else
308             *valp = LONG_MAX;
309         break;
310
311       case '%':
312         DO (cp = parse_product (g, cp + 1, &rightval));
313         *valp = (*valp % rightval);
314         break;
315     }
316     return cp;
317 }
318
319
320 static const char *
321 parse_sum (g, cp, valp)
322     IfParser *g;
323     const char *cp;
324     long *valp;
325 {
326     long rightval;
327
328     DO (cp = parse_product (g, cp, valp));
329     SKIPSPACE (cp);
330
331     switch (*cp) {
332       case '+':
333         DO (cp = parse_sum (g, cp + 1, &rightval));
334         *valp = (*valp + rightval);
335         break;
336
337       case '-':
338         DO (cp = parse_sum (g, cp + 1, &rightval));
339         *valp = (*valp - rightval);
340         break;
341     }
342     return cp;
343 }
344
345
346 static const char *
347 parse_shift (g, cp, valp)
348     IfParser *g;
349     const char *cp;
350     long *valp;
351 {
352     long rightval;
353
354     DO (cp = parse_sum (g, cp, valp));
355     SKIPSPACE (cp);
356
357     switch (*cp) {
358       case '<':
359         if (cp[1] == '<') {
360             DO (cp = parse_shift (g, cp + 2, &rightval));
361             *valp = (*valp << rightval);
362         }
363         break;
364
365       case '>':
366         if (cp[1] == '>') {
367             DO (cp = parse_shift (g, cp + 2, &rightval));
368             *valp = (*valp >> rightval);
369         }
370         break;
371     }
372     return cp;
373 }
374
375
376 static const char *
377 parse_inequality (g, cp, valp)
378     IfParser *g;
379     const char *cp;
380     long *valp;
381 {
382     long rightval;
383
384     DO (cp = parse_shift (g, cp, valp));
385     SKIPSPACE (cp);
386
387     switch (*cp) {
388       case '<':
389         if (cp[1] == '=') {
390             DO (cp = parse_inequality (g, cp + 2, &rightval));
391             *valp = (*valp <= rightval);
392         } else {
393             DO (cp = parse_inequality (g, cp + 1, &rightval));
394             *valp = (*valp < rightval);
395         }
396         break;
397
398       case '>':
399         if (cp[1] == '=') {
400             DO (cp = parse_inequality (g, cp + 2, &rightval));
401             *valp = (*valp >= rightval);
402         } else {
403             DO (cp = parse_inequality (g, cp + 1, &rightval));
404             *valp = (*valp > rightval);
405         }
406         break;
407     }
408     return cp;
409 }
410
411
412 static const char *
413 parse_equality (g, cp, valp)
414     IfParser *g;
415     const char *cp;
416     long *valp;
417 {
418     long rightval;
419
420     DO (cp = parse_inequality (g, cp, valp));
421     SKIPSPACE (cp);
422
423     switch (*cp) {
424       case '=':
425         if (cp[1] == '=')
426             cp++;
427         DO (cp = parse_equality (g, cp + 1, &rightval));
428         *valp = (*valp == rightval);
429         break;
430
431       case '!':
432         if (cp[1] != '=')
433             break;
434         DO (cp = parse_equality (g, cp + 2, &rightval));
435         *valp = (*valp != rightval);
436         break;
437     }
438     return cp;
439 }
440
441
442 static const char *
443 parse_band (g, cp, valp)
444     IfParser *g;
445     const char *cp;
446     long *valp;
447 {
448     long rightval;
449
450     DO (cp = parse_equality (g, cp, valp));
451     SKIPSPACE (cp);
452
453     switch (*cp) {
454       case '&':
455         if (cp[1] != '&') {
456             DO (cp = parse_band (g, cp + 1, &rightval));
457             *valp = (*valp & rightval);
458         }
459         break;
460     }
461     return cp;
462 }
463
464
465 static const char *
466 parse_bor (g, cp, valp)
467     IfParser *g;
468     const char *cp;
469     long *valp;
470 {
471     long rightval;
472
473     DO (cp = parse_band (g, cp, valp));
474     SKIPSPACE (cp);
475
476     switch (*cp) {
477       case '|':
478         if (cp[1] != '|') {
479             DO (cp = parse_bor (g, cp + 1, &rightval));
480             *valp = (*valp | rightval);
481         }
482         break;
483     }
484     return cp;
485 }
486
487
488 static const char *
489 parse_land (g, cp, valp)
490     IfParser *g;
491     const char *cp;
492     long *valp;
493 {
494     long rightval;
495
496     DO (cp = parse_bor (g, cp, valp));
497     SKIPSPACE (cp);
498
499     switch (*cp) {
500       case '&':
501         if (cp[1] != '&')
502             return CALLFUNC(g, handle_error) (g, cp, "&&");
503         DO (cp = parse_land (g, cp + 2, &rightval));
504         *valp = (*valp && rightval);
505         break;
506     }
507     return cp;
508 }
509
510
511 static const char *
512 parse_lor (g, cp, valp)
513     IfParser *g;
514     const char *cp;
515     long *valp;
516 {
517     long rightval;
518
519     DO (cp = parse_land (g, cp, valp));
520     SKIPSPACE (cp);
521
522     switch (*cp) {
523       case '|':
524         if (cp[1] != '|')
525             return CALLFUNC(g, handle_error) (g, cp, "||");
526         DO (cp = parse_lor (g, cp + 2, &rightval));
527         *valp = (*valp || rightval);
528         break;
529     }
530     return cp;
531 }
532
533
534 /****************************************************************************
535                              External Entry Points
536  ****************************************************************************/
537
538 const char *
539 ParseIfExpression (g, cp, valp)
540     IfParser *g;
541     const char *cp;
542     long *valp;
543 {
544     return parse_lor (g, cp, valp);
545 }
546
547