xref: /reactos/sdk/tools/hpp/hpp.c (revision dc0433f0)
1 /*
2  * COPYRIGHT:             See COPYING in the top level directory
3  * PROJECT:               Header preprocessor
4  * PURPOSE:               Generates header files from other header files
5  * PROGRAMMER;            Timo Kreuzer
6  *
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <ctype.h>
14 
15 //#define DBG 1
16 
17 #if DBG
18 #define trace printf
19 #else
20 #define trace if (0) printf
21 #endif
22 
23 typedef struct _DEFINE
24 {
25     struct _DEFINE *pNext;
26     int val;
27     char *pszName;
28     unsigned int cchName;
29     char *pszValue;
30     unsigned int cchValue;
31     char achBuffer[1];
32 } DEFINE, *PDEFINE;
33 
34 DEFINE *gpDefines = 0;
35 int iLine;
36 const char *gpszCurFile;
37 
38 char*
39 convert_path(const char* origpath)
40 {
41     char* newpath;
42     int i;
43 
44     newpath = strdup(origpath);
45 
46     i = 0;
47     while (newpath[i] != 0)
48     {
49 #ifdef UNIX_PATHS
50         if (newpath[i] == '\\')
51         {
52             newpath[i] = '/';
53         }
54 #else
55 #ifdef DOS_PATHS
56         if (newpath[i] == '/')
57         {
58             newpath[i] = '\\';
59         }
60 #endif
61 #endif
62         i++;
63     }
64     return newpath;
65 }
66 
67 char*
68 GetFolder(const char* pszFullPath)
69 {
70     return ".";
71 }
72 
73 void*
74 LoadFile(const char* pszFileName, size_t* pFileSize)
75 {
76     FILE* file;
77     void* pFileData = NULL;
78     int iFileSize;
79 
80     trace("Loading file...");
81 
82     file = fopen(pszFileName, "rb");
83     if (!file)
84     {
85         trace("Could not open file\n");
86         return NULL;
87     }
88 
89     fseek(file, 0L, SEEK_END);
90     iFileSize = ftell(file);
91     fseek(file, 0L, SEEK_SET);
92     *pFileSize = iFileSize;
93     trace("ok. Size is %d\n", iFileSize);
94 
95     pFileData = malloc(iFileSize + 1);
96 
97     if (pFileData != NULL)
98     {
99         if (iFileSize != fread(pFileData, 1, iFileSize, file))
100         {
101             free(pFileData);
102             pFileData = NULL;
103         }
104     }
105     else
106     {
107         trace("Could not allocate memory for file\n");
108     }
109 
110     fclose(file);
111 
112     return pFileData;
113 }
114 
115 
116 int
117 error(char *format, ...)
118 {
119     va_list valist;
120     int res;
121     va_start(valist, format);
122     res = vfprintf(stderr, format, valist);
123     va_end(valist);
124     return res;
125 }
126 
127 char*
128 GetNextChar(const char *psz)
129 {
130     while (*psz == ' ' || *psz == '\t') psz++;
131     return (char*)psz;
132 }
133 
134 char*
135 GetNextLine(char *pszLine)
136 {
137     /* Walk to the end of the line */
138     while (*pszLine != 13 && *pszLine != 10 && *pszLine != 0) pszLine++;
139 
140     /* Skip one CR/LF */
141     if (pszLine[0] == 13 && pszLine[1] == 10)
142         pszLine += 2;
143     else if (pszLine[0] == 13 || pszLine[0] == 10)
144         pszLine++;
145 
146     if (*pszLine == 0)
147     {
148         return 0;
149     }
150 
151     return pszLine;
152 }
153 
154 int
155 strxlen(const char *psz)
156 {
157     int len = 0;
158     while (isalnum(*psz) || *psz == '_')
159     {
160         psz++;
161         len++;
162     }
163     return len;
164 }
165 
166 PDEFINE
167 FindDefine(const char *p, char **pNext)
168 {
169     PDEFINE pDefine;
170     int cchName;
171 
172     cchName = strxlen(p);
173     if (pNext)
174         *pNext = (char*)p + cchName;
175 
176     /* search for the define in the global list */
177     pDefine = gpDefines;
178     while (pDefine != 0)
179     {
180         trace("found a define: %s\n", pDefine->pszName);
181         if (pDefine->cchName == cchName)
182         {
183             if (strncmp(p, pDefine->pszName, cchName) == 0)
184             {
185                 return pDefine;
186             }
187         }
188         pDefine = pDefine->pNext;
189     }
190     return 0;
191 }
192 
193 void
194 WriteLine(char *pchLine, FILE *fileOut)
195 {
196     char *pch, *pchLineEnd, *pchVariable;
197     int len;
198     PDEFINE pDefine;
199 
200     pchLineEnd = strchr(pchLine, '\n');
201     if (pchLineEnd == 0)
202         return;
203 
204     len = pchLineEnd - pchLine + 1;
205 
206     pch = pchLine;
207     while (len > 0)
208     {
209         /* Check if there is a $ variable in the line */
210         pchVariable = strchr(pch, '$');
211         if (pchVariable && (pchVariable < pchLineEnd))
212         {
213             /* Write all characters up to the $ */
214             fwrite(pch, 1, pchVariable - pch, fileOut);
215 
216             /* Try to find the define */
217             pDefine = FindDefine(pchVariable + 1, &pch);
218             if (pDefine != 0)
219             {
220                 /* We have a define, write the value */
221                 fwrite(pDefine->pszValue, 1, pDefine->cchValue, fileOut);
222             }
223             else
224             {
225                 len = strxlen(pchVariable + 1) + 1;
226                 error("Could not find variable '%.*s'\n", len, pchVariable);
227                 fwrite(pchVariable, 1, pch - pchVariable, fileOut);
228             }
229 
230             len = pchLineEnd - pch + 1;
231         }
232         else
233         {
234             fwrite(pch, 1, len, fileOut);
235             break;
236         }
237     }
238 }
239 
240 int
241 EvaluateConstant(const char *p, char **pNext)
242 {
243     PDEFINE pDefine;
244 
245     pDefine = FindDefine(p, pNext);
246     if (!pDefine)
247         return 0;
248 
249     return pDefine->val;
250 }
251 
252 int
253 EvaluateExpression(char *pExpression, char **pNext)
254 {
255     char *p, *pstart;
256     int inv, thisval, val = 0;
257 
258     trace("evaluating expression\n");
259 
260     pstart = GetNextChar(pExpression);
261     if (*pstart != '(')
262     {
263         error("Parse error: expected '(' \n");
264         return -1;
265     }
266 
267     while (1)
268     {
269         /* Get the start of the real expression */
270         p = pstart;
271         if ((p[0] == '&' && p[1] == '&') ||
272             (p[0] == '|' && p[1] == '|'))
273         {
274             p++;
275         }
276         p = GetNextChar(p + 1);
277 
278         /* Check for inversion modifier */
279         if (*p == '!')
280         {
281             inv = 1;
282             p = GetNextChar(p + 1);
283         }
284         else
285             inv = 0;
286 
287         /* Beginning of a new subexpression? */
288         if (*p == '(')
289         {
290             /* Evaluate subexpression */
291             thisval = EvaluateExpression(p, &p);
292         }
293         else if (isdigit(*p))
294         {
295             thisval = strtod(p, &p);
296             trace("found a num: %d\n", thisval);
297         }
298         else if (isalpha(*p) || *p == '_')
299         {
300             thisval = EvaluateConstant(p, &p);
301         }
302         else
303         {
304             error("..Parse error, expected '(' or constant in line %d\n",
305                   iLine);
306             return -1;
307         }
308 
309         if (inv)
310             thisval = !thisval;
311 
312         /* Check how to combine the current value */
313         if (pstart[0] == '(')
314         {
315             val = thisval;
316         }
317         else if (pstart[0] == '&' && pstart[1] == '&')
318         {
319             val = val && thisval;
320         }
321         else if (pstart[0] == '&' && pstart[1] != '&')
322         {
323             val = val & thisval;
324         }
325         else if (pstart[0] == '|' && pstart[1] == '|')
326         {
327             trace("found || val = %d, thisval = %d\n", val, thisval);
328             val = val || thisval;
329         }
330         else if (pstart[0] == '|' && pstart[1] != '|')
331         {
332             val = val | thisval;
333         }
334         else if (pstart[0] == '+')
335         {
336             val = val + thisval;
337         }
338         else
339         {
340             error("+Parse error: expected '(' or operator in Line %d, got %c\n",
341                   iLine, pstart[0]);
342             return -1;
343         }
344 
345         p = GetNextChar(p);
346 
347         /* End of current subexpression? */
348         if (*p == ')')
349         {
350             if (pNext)
351             {
352                 *pNext = p + 1;
353             }
354             return val;
355         }
356 
357         /* Continue with a new start position */
358         pstart = p;
359     }
360 
361     return val;
362 }
363 
364 int
365 ParseInputFile(const char *pszInFile, FILE *fileOut)
366 {
367     char* pInputData, *pCurrentLine, *p1, *p2;
368     size_t cbInFileLenth;
369     int iIfLevel, iCopyLevel;
370 
371     trace("parsing input file: %s\n", pszInFile);
372 
373     /* Set the global file name */
374     gpszCurFile = pszInFile;
375 
376     /* Load the input file into memory */
377     pInputData = LoadFile(pszInFile, &cbInFileLenth);
378     if (!pInputData)
379     {
380         error("Could not load input file %s\n", pszInFile);
381         return -1;
382     }
383 
384     /* Zero terminate the file */
385     pInputData[cbInFileLenth] = 0;
386 
387     pCurrentLine = pInputData;
388     iLine = 1;
389     iCopyLevel = iIfLevel = 0;
390 
391     /* The main processing loop */
392     do
393     {
394         trace("line %d: ", iLine);
395 
396         /* If this is a normal line ... */
397         if (pCurrentLine[0] != '$')
398         {
399             /* Check if we are to copy this line */
400             if (iCopyLevel == iIfLevel)
401             {
402                 trace("copying\n");
403                 WriteLine(pCurrentLine, fileOut);
404             }
405             else
406                 trace("skipping\n");
407 
408             /* Continue with next line */
409             continue;
410         }
411 
412         /* Check for $endif */
413         if (strncmp(pCurrentLine, "$endif", 6) == 0)
414         {
415             trace("found $endif\n");
416             if (iIfLevel <= 0)
417             {
418                 error("Parse error: $endif without $if in %s:%d\n", pszInFile, iLine);
419                 return -1;
420             }
421             if (iCopyLevel == iIfLevel)
422             {
423                 iCopyLevel--;
424             }
425             iIfLevel--;
426 
427             /* Continue with next line */
428             continue;
429         }
430 
431         /* The rest is only parsed when we are in a true block */
432         if (iCopyLevel < iIfLevel)
433         {
434             trace("skipping\n");
435 
436             /* Continue with next line */
437             continue;
438         }
439 
440         /* Check for $define */
441         if (strncmp(pCurrentLine, "$define", 7) == 0)
442         {
443             PDEFINE pDefine;
444             char *pchName, *pchValue;
445             size_t cchName, cchValue;
446 
447             trace("found $define\n");
448             p1 = GetNextChar(pCurrentLine + 7);
449             if (*p1 != '(')
450             {
451                 error("Parse error: expected '(' at %s:%d\n",
452                       pszInFile, iLine);
453                 return -1;
454             }
455 
456             pchName = GetNextChar(p1 + 1);
457             cchName = strxlen(pchName);
458             p1 = GetNextChar(pchName + cchName);
459 
460             /* Check for assignment */
461             if (*p1 == '=')
462             {
463                 trace("found $define with assignment\n");
464                 pchValue = GetNextChar(p1 + 1);
465                 cchValue = strxlen(pchValue);
466                 p1 = GetNextChar(pchValue + cchValue);
467             }
468             else
469             {
470                 pchValue = 0;
471                 cchValue = 0;
472             }
473 
474             /* Allocate a DEFINE structure */
475             pDefine = malloc(sizeof(DEFINE) + cchName + cchValue + 2);
476             if (pDefine == 0)
477             {
478                 error("Failed to allocate %u bytes\n",
479                       sizeof(DEFINE) + cchName + cchValue + 2);
480                 return -1;
481             }
482 
483             pDefine->pszName = pDefine->achBuffer;
484             strncpy(pDefine->pszName, pchName, cchName);
485             pDefine->pszName[cchName] = 0;
486             pDefine->cchName = cchName;
487             pDefine->val = 1;
488 
489             if (pchValue != 0)
490             {
491                 pDefine->pszValue = &pDefine->achBuffer[cchName + 1];
492                 strncpy(pDefine->pszValue, pchValue, cchValue);
493                 pDefine->pszValue[cchValue] = 0;
494                 pDefine->cchValue = cchValue;
495             }
496             else
497             {
498                 pDefine->pszValue = 0;
499                 pDefine->cchValue = 0;
500             }
501 
502             /* Insert the new define into the global list */
503             pDefine->pNext = gpDefines;
504             gpDefines = pDefine;
505 
506             /* Check for closing ')' */
507             if (*p1 != ')')
508             {
509                 error("Parse error: expected ')' at %s:%d\n",
510                       pszInFile, iLine);
511                 return -1;
512             }
513         }
514 
515         /* Check for $if */
516         else if (strncmp(pCurrentLine, "$if", 3) == 0)
517         {
518             int val;
519 
520             trace("found $if\n");
521             /* Increase the if-level */
522             iIfLevel++;
523 
524             /* Get beginning of the expression */
525             p1 = GetNextChar(pCurrentLine + 3);
526 
527             /* evaluate the expression */
528             val = EvaluateExpression(p1, 0);
529 
530             if (val)
531             {
532                 iCopyLevel = iIfLevel;
533             }
534             else if (val == -1)
535             {
536                 /* Parse error */
537                 return -1;
538             }
539         }
540 
541         /* Check for $include */
542         else if (strncmp(pCurrentLine, "$include", 8) == 0)
543         {
544             int ret;
545 
546             trace("found $include\n");
547             p1 = GetNextChar(pCurrentLine + 8);
548             if (*p1 != '(')
549             {
550                 error("Parse error: expected '(' at %s:%d, found '%c'\n",
551                       pszInFile, iLine, *p1);
552                 return -1;
553             }
554             p1++;
555             p2 = strchr(p1, ')');
556             *p2 = 0;
557 
558             /* Parse the included file */
559             ret = ParseInputFile(p1, fileOut);
560 
561             /* Restore the global file name */
562             gpszCurFile = pszInFile;
563 
564             /* Restore the zeroed character */
565             *p2 = ')';
566 
567             if (ret == -1)
568             {
569                 return -1;
570             }
571         }
572 
573         /* Check for $$ comment */
574         else if (strncmp(pCurrentLine, "$$", 2) == 0)
575         {
576             trace("$$ ignored\n");
577             /* continue with next line */
578             continue;
579         }
580 
581         else
582         {
583             trace("wot:%s\n", pCurrentLine);
584         }
585 
586         /* Continue with next line */
587     }
588     while (pCurrentLine = GetNextLine(pCurrentLine),
589            iLine++,
590            pCurrentLine != 0);
591 
592     /* Free the file data */
593     free(pInputData);
594 
595     trace("Done with file.\n\n");
596 
597     return 0;
598 }
599 
600 
601 int
602 main(int argc, char* argv[])
603 {
604     char *pszInFile, *pszOutFile;
605     FILE* fileOut;
606     int ret;
607 
608     if (argc != 3)
609     {
610         error("Usage: hpp <inputfile> <outputfile>\n");
611         exit(1);
612     }
613 
614     pszInFile = convert_path(argv[1]);
615     pszOutFile = convert_path(argv[2]);
616 
617     fileOut = fopen(pszOutFile, "wb");
618     if (fileOut == NULL)
619     {
620         error("Cannot open output file %s", pszOutFile);
621         exit(1);
622     }
623 
624     ret = ParseInputFile(pszInFile, fileOut);
625 
626     fclose(fileOut);
627     free(pszInFile);
628     free(pszOutFile);
629 
630     return ret;
631 }
632