1 /* $Id: fastdep.c 2413 2010-09-11 17:43:04Z bird $
2  *
3  * Fast dependents. (Fast = Quick and Dirty!)
4  *
5  * Copyright (c) 1999-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
6  *
7  * GPL
8  *
9  */
10 
11 /*******************************************************************************
12 *   Defined Constants And Macros                                               *
13 *******************************************************************************/
14 #define INCL_DOSERRORS
15 #define INCL_FILEMGR
16 #define INCL_DOSMISC
17 
18 
19 /*
20  * Size of the \n charater (forget '\r').
21  * If you're compiling this under a UNICODE system this may perhaps change,
22  * but I doubd that fastdep will work at all under a UNICODE system. ;-)
23  */
24 #if defined(UNICODE) && !defined(__WIN32OS2__)
25 #define CBNEWLINE       (2)
26 #else
27 #define CBNEWLINE       (1)
28 #endif
29 
30 
31 /*
32  * Time stamp size.
33  */
34 #define TS_SIZE         (48)
35 
36 
37 /*******************************************************************************
38 *   Header Files                                                               *
39 *******************************************************************************/
40 #if defined(OS2FAKE)
41 #include "os2fake.h"
42 #else
43 #include <os2.h>
44 #endif
45 
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <direct.h>
50 #include <assert.h>
51 
52 #include "avl.h"
53 
54 #ifdef __WIN32OS2__
55 #   define WIN32API
56 #   include <odinbuild.h>
57 #else
58 #   define ODIN32_BUILD_NR -1
59 #endif
60 
61 #ifndef INLINE
62 #   if defined(__IBMC__)
63 #       define INLINE _Inline
64 #   elif defined(__IBMCPP__)
65 #       define INLINE inline
66 #   elif defined(__WATCOMC__)
67 #       define INLINE __inline
68 #   elif defined(__WATCOM_CPLUSPLUS__)
69 #       define INLINE inline
70 #   else
71 #       error message("unknown compiler - inline keyword unknown!")
72 #   endif
73 #endif
74 
75 /*
76  * This following section is used while testing fastdep.
77  * stdio.h should be included; string.h never included.
78  */
79 /*
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 */
84 
85 #if 1
86 #include <stdio.h>
87 #else
88 #include <string.h>
89 #include <string.h>
90 #endif
91 
92 /*
93  */ /* */ /*
94 #include <string.h>
95  */
96 #if 1
97 #    if 1
98         #if 0
99 # include <string.h>
100         #else
101 #            if 1
102                 #if 1
103                     #if 0
104 # include <string.h>
105                     #else /* */ /*
106 */
107  # include <stdio.h>
108                     #endif
109                 #endif
110             #endif
111         #endif
112     #endif
113 #endif
114 
115 /*******************************************************************************
116 *   Structures and Typedefs                                                    *
117 *******************************************************************************/
118 typedef struct _Options
119 {
120     const char *    pszInclude;
121     const char *    pszExclude;
122     BOOL            fExcludeAll;
123     const char *    pszObjectExt;
124     const char *    pszObjectDir;
125     BOOL            fObjectDir;         /* replace object directory? */
126     const char *    pszRsrcExt;
127     BOOL            fObjRule;
128     BOOL            fNoObjectPath;
129     BOOL            fSrcWhenObj;
130     BOOL            fAppend;            /* append to the output file, not overwrite it. */
131     BOOL            fCheckCyclic;       /* allways check for cylic dependency before inserting an dependent. */
132     BOOL            fCacheSearchDirs;   /* cache entire search dirs. */
133     const char *    pszExcludeFiles;    /* List of excluded files. */
134     BOOL            fForceScan;         /* Force scan of all files. */
135 } OPTIONS, *POPTIONS;
136 
137 
138 /*
139  * Language specific analysis functions type.
140  */
141 typedef int ( _FNLANG)  (const char *pszFilename, const char *pszNormFilename,
142                          const char *pszTS, BOOL fHeader, void **ppvRule);
143 typedef _FNLANG    *PFNLANG;
144 
145 
146 /**
147  * This struct holds the static configuration of the util.
148  */
149 typedef struct _ConfigEntry
150 {
151     char         szId[16];              /* Config ID. */
152     const char **papszExts;             /* Pointer to an array of pointer to extentions for this handler. */
153                                         /* If NULL this is the last entry. */
154     int          iFirstHdr;             /* Index into the papszExts array of the first headerfile/copybook. */
155                                         /* Set it to the NULL element of the array if no headers for this extention. */
156                                         /* A non-header file may get a object rule. */
157     PFNLANG      pfn;                   /* Pointer to handler function. */
158     char        *pszzAddDeps;           /* Pointer to an string of string of additional dependencies. */
159 } CONFIGENTRY, *PCONFIGENTRY;
160 
161 
162 /**
163  * Dependant Rule
164  */
165 typedef struct _DepRule
166 {
167     AVLNODECORE     avlCore;
168     char *          pszRule;            /* Pointer to rule name */
169     int             cDeps;              /* Entries in the dependant array. */
170     char **         papszDep;           /* Pointer to an array of pointers to dependants. */
171     BOOL            fUpdated;           /* If we have updated this entry during the run. */
172     char            szTS[TS_SIZE];      /* Time stamp. */
173 } DEPRULE, *PDEPRULE;
174 
175 
176 /**
177  * Filename cache entry.
178  */
179 #define FCACHEENTRY     AVLNODECORE
180 #define PFCACHEENTRY    PAVLNODECORE
181 
182 
183 /*******************************************************************************
184 *   Internal Functions                                                         *
185 *******************************************************************************/
186 static void syntax(void);
187 static int makeDependent(const char *pszFilename, const char *pszTS);
188 
189 static int langC_CPP(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
190 static int langAsm(  const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
191 static int langRC(   const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
192 static int langCOBOL(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
193 static int langIPF(  const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader, void **ppvRule);
194 
195 
196 /* string operations */
197 static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
198 
199 /* file operations */
200 static char *fileNormalize(char *pszFilename);
201 static char *fileNormalize2(const char *pszFilename, char *pszBuffer);
202        char *filePath(const char *pszFilename, char *pszBuffer);
203 static char *filePathSlash(const char *pszFilename, char *pszBuffer);
204 static char *filePathSlash2(const char *pszFilename, char *pszBuffer);
205 static char *fileName(const char *pszFilename, char *pszBuffer);
206 static char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
207 static char *fileExt(const char *pszFilename, char *pszBuffer);
208 
209 /* filecache operations */
210 static BOOL filecacheAddFile(const char *pszFilename);
211 static BOOL filecacheAddDir(const char *pszDir);
212 INLINE BOOL filecacheFind(const char *pszFilename);
213 INLINE BOOL filecacheIsDirCached(const char *pszDir);
214 static char*filecacheFileExist(const char *pszFilename, char *pszBuffer);
215 
216 /* pathlist operations */
217 static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer);
218 static BOOL  pathlistFindFile2(const char *pszPathList, const char *pszFilename);
219 
220 /* word operations */
221 static char *findEndOfWord(char *psz);
222 #if 0 /* not used */
223 static char *findStartOfWord(char *psz, const char *pszStart);
224 #endif
225 
226 /* file helpers */
227 static signed long fsize(FILE *phFile);
228 
229 /* text helpers */
230 INLINE char *trim(char *psz);
231 INLINE char *trimR(char *psz);
232 INLINE char *trimQuotes(char *psz);
233 
234 /* preprocessors */
235 static char *PreProcessLine(char *pszOut, const char *pszIn);
236 
237 /* textbuffer */
238 static void *textbufferCreate(const char *pszFilename);
239 static void  textbufferDestroy(void *pvBuffer);
240 static char *textbufferNextLine(void *pvBuffer, char *psz);
241 static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
242 
243 /* depend workers */
244 static BOOL  depReadFile(const char *pszFilename, BOOL fAppend);
245 static BOOL  depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly);
246 static void  depRemoveAll(void);
247 static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName);
248 static BOOL  depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName);
249 static int   depNameToReal(char *pszName);
250 static int   depNameToMake(char *pszName, int cchName, const char *pszSrc);
251 static void  depMarkNotFound(void *pvRule);
252 static BOOL  depCheckCyclic(PDEPRULE pdepRule, const char *pszDep);
253 static BOOL  depValidate(PDEPRULE pdepRule);
254 INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3);
255 static void  depAddSrcAddDeps(void *pvRule, const char *pszz);
256 
257 
258 /*******************************************************************************
259 *   Global Variables                                                           *
260 *******************************************************************************/
261 /*
262  * Pointer to the list of dependencies.
263  */
264 static PDEPRULE pdepTree = NULL;
265 
266 
267 /*
268  * Filecache - tree starts here.
269  */
270 static PFCACHEENTRY pfcTree = NULL;
271 static unsigned     cfcNodes = 0;
272 static PFCACHEENTRY pfcDirTree = NULL;
273 
274 
275 /*
276  * Current directory stuff
277  */
278 static char     szCurDir[CCHMAXPATH];
279 static int      aiSlashes[CCHMAXPATH];
280 static int      cSlashes;
281 
282 
283 /*
284  * Environment variables used.
285  * (These has the correct case.)
286  */
287 static char *   pszIncludeEnv;
288 
289 
290 /*
291  * Configuration stuff.
292  */
293 static const char pszDefaultDepFile[] = ".depend";
294 static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
295 static const char *apszExtAsm[]   = {"asm", "inc", NULL};
296 static const char *apszExtRC[]    = {"rc",  "dlg", NULL};
297 static const char *apszExtORC[]   = {"orc", "dlg", NULL};
298 static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", "wbl", NULL};
299 static const char *apszExtIPF[]   = {"ipf", "man", NULL};
300 static const char *apszExtIPP[]   = {"ipp", NULL};
301 static CONFIGENTRY aConfig[] =
302 {
303     {
304         "CX",
305         apszExtC_CPP,
306         3,
307         langC_CPP,
308         NULL,
309     },
310 
311     {
312         "AS",
313         apszExtAsm,
314         1,
315         langAsm,
316         NULL,
317     },
318 
319     {
320         "RC",
321         apszExtRC,
322         1,
323         langRC,
324         NULL,
325     },
326 
327     {
328         "ORC",
329         apszExtORC,
330         1,
331         langRC,
332         NULL,
333     },
334 
335     {
336         "COB",
337         apszExtCOBOL,
338         -1,
339         langCOBOL,
340         NULL,
341     },
342 
343     {
344         "IPF",
345         apszExtIPF,
346         -1,
347         langIPF,
348         NULL,
349     },
350 
351     {
352         "IPP",
353         apszExtIPP,
354         -1,
355         langC_CPP,
356         NULL,
357     },
358 
359     /* terminating entry */
360     {
361         "",
362         NULL,
363         -1,
364         NULL,
365         NULL
366     }
367 };
368 
369 
370 static char szObjectDir[CCHMAXPATH];
371 static char szObjectExt[64] = "obj";
372 static char szRsrcExt[64]   = "res";
373 static char szInclude[32768] = ";";
374 static char szExclude[32768] = ";";
375 static char szExcludeFiles[65536] = "";
376 
377 OPTIONS options =
378 {
379     szInclude,       /* pszInclude */
380     szExclude,       /* pszExclude */
381     FALSE,           /* fExcludeAll */
382     szObjectExt,     /* pszObjectExt */
383     szObjectDir,     /* pszObjectDir */
384     FALSE,           /* fObjectDir */
385     szRsrcExt,       /* pszRsrcExt */
386     TRUE,            /* fObjRule */
387     FALSE,           /* fNoObjectPath */
388     TRUE,            /* fSrcWhenObj */
389     FALSE,           /* fAppend */
390     TRUE,            /* fCheckCyclic */
391     TRUE,            /* fCacheSearchDirs */
392     szExcludeFiles,  /* pszExcludeFiles */
393     FALSE            /* fForceScan */
394 };
395 
396 
397 /**
398  * Main function.
399  * @returns   0 on success.
400  *           -n count of failiures.
401  * @param
402  * @param
403  * @equiv
404  * @precond
405  * @methdesc
406  * @result
407  * @time
408  * @sketch
409  * @algo
410  * @remark
411  */
main(int argc,char ** argv)412 int main(int argc, char **argv)
413 {
414     int         rc   = 0;
415     int         argi = 1;
416     int         i;
417     char *      psz;
418     char *      psz2;
419     const char *pszDepFile = pszDefaultDepFile;
420     char        achBuffer[4096];
421 
422     szObjectDir[0] = '\0';
423 
424     if (argc == 1)
425     {
426         syntax();
427         return -87;
428     }
429 
430     /*
431      * Initiate current directory stuff
432      */
433     if (_getcwd(szCurDir, sizeof(szCurDir)) == NULL)
434     {
435         fprintf(stderr, "fatal error: failed to get current directory\n");
436         return -88;
437     }
438     strlwr(szCurDir);
439     aiSlashes[0] = 0;
440     for (i = 1, cSlashes; szCurDir[i] != '\0'; i++)
441     {
442         if (szCurDir[i] == '/')
443             szCurDir[i] = '\\';
444         if (szCurDir[i] == '\\')
445             aiSlashes[cSlashes++] = i;
446     }
447     if (szCurDir[i-1] != '\\')
448     {
449         aiSlashes[cSlashes] = i;
450         szCurDir[i++] = '\\';
451         szCurDir[i] = '\0';
452     }
453 
454 
455     /*
456      * Initiate environment variables used: INCLUDE
457      */
458     psz = getenv("INCLUDE");
459     if (psz != NULL)
460     {
461         pszIncludeEnv = strdup(psz);
462         strlwr(pszIncludeEnv);
463     }
464     else
465         pszIncludeEnv = "";
466 
467 
468     /*
469      * Disable hard errors.
470      */
471     DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
472 
473 
474     /*
475      * parse arguments
476      */
477     while (argi < argc)
478     {
479         if (argv[argi][0] == '-' || argv[argi][0] == '/')
480         {
481             /* parameters */
482             switch (argv[argi][1])
483             {
484                 case 'A':
485                 case 'a': /* Append to the output file */
486                     options.fAppend = argv[argi][2] != '-';
487                     break;
488 
489                 case 'D':
490                 case 'd': /* "-d <filename>" */
491                 {
492                     const char *pszOld = pszDepFile;
493                     if (argv[argi][2] != '\0')
494                         pszDepFile = &argv[argi][2];
495                     else
496                     {
497                         argi++;
498                         if (argi < argc)
499                             pszDepFile = argv[argi];
500                         else
501                         {
502                             fprintf(stderr, "invalid parameter -d, filename missing!\n");
503                             return -1;
504                         }
505                     }
506 
507                     /* if dependencies are generated we'll flush them to the old filename */
508                     if (pdepTree != NULL && pszOld != pszDepFile)
509                     {
510                         if (!depWriteFile(pszOld, !options.fAppend))
511                             fprintf(stderr, "error: failed to write (flush) dependencies.\n");
512                         depRemoveAll();
513                     }
514                     break;
515                 }
516 
517                 case 'C': /* forced directory cache  'ca' or cylic check 'cy'*/
518                 case 'c':
519                     if (argv[argi][2] == 'a' || argv[argi][2] == 'A')
520                         options.fCacheSearchDirs = TRUE;
521                     else if ((argv[argi][2] == 'y' || argv[argi][2] == 'Y'))
522                         options.fCheckCyclic = argv[argi][3] != '-';
523                     break;
524 
525                 case 'E': /* list of paths. If a file is found in one of these directories the */
526                 case 'e': /* filename will be used without the directory path. */
527                     /* Eall<[+]|-> ? */
528                     if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
529                     {
530                         options.fExcludeAll = argv[argi][5] != '-';
531                         break;
532                     }
533                     /* path or path list */
534                     if (strlen(argv[argi]) > 2)
535                         psz = &argv[argi][2];
536                     else
537                     {
538                         if (++argi >= argc)
539                         {
540                             fprintf(stderr, "syntax error! Option -e.\n");
541                             return 1;
542                         }
543                         psz = argv[argi];
544                     }
545                     /* check if enviroment variable */
546                     if (*psz == '%')
547                     {
548                         psz2 = strdup(psz+1);
549                         if (psz2 != NULL && *psz2 != '\0')
550                         {
551                             if (psz2[strlen(psz2)-1] == '%')
552                                 psz2[strlen(psz2)-1] = '\0';
553                             psz = getenv(psz2);
554                             free(psz2);
555                             if (psz == NULL)
556                                 break;
557                         }
558                         else
559                         {
560                             fprintf(stderr, "error: -E% is not an valid argument!\n");
561                             return -1;
562                         }
563                     }
564                     if (psz != NULL)
565                     {
566                         strcat(szExclude, psz);
567                         strlwr(szExclude);
568                         if (szExclude[strlen(szExclude)-1] != ';')
569                             strcat(szExclude, ";");
570                     }
571                     break;
572 
573                 case 'f':
574                 case 'F': /* force scan of all files. */
575                     options.fForceScan = argv[argi][2] != '-';
576                     break;
577 
578                 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
579                 case 'i':
580                     if (strlen(argv[argi]) > 2)
581                         psz = &argv[argi][2];
582                     else
583                     {
584                         if (++argi >= argc)
585                         {
586                             fprintf(stderr, "syntax error! Option -i.\n");
587                             return 1;
588                         }
589                         psz = argv[argi];
590                     }
591                     /* check if enviroment variable */
592                     if (*psz == '%')
593                     {
594                         psz2 = strdup(psz+1);
595                         if (psz2 != NULL && *psz2 != '\0')
596                         {
597                             if (psz2[strlen(psz2)-1] == '%')
598                                 psz2[strlen(psz2)-1] = '\0';
599                             psz = getenv(psz2);
600                             free(psz2);
601                             if (psz == NULL)
602                                 break;
603                         }
604                         else
605                         {
606                             fprintf(stderr, "error: -I% is not an valid argument!\n");
607                             return -1;
608                         }
609                     }
610                     if (psz != NULL)
611                     {
612                         strcat(szInclude, psz);
613                         strlwr(szInclude);
614                         if (szInclude[strlen(szInclude)-1] != ';')
615                             strcat(szInclude, ";");
616                     }
617                     break;
618 
619                 case 'n': /* no object path , -N<[+]|-> */
620                 case 'N':
621                     if (strlen(argv[argi]) <= 1+1+1)
622                         options.fNoObjectPath = argv[argi][2] != '-';
623                     else
624                     {
625                         fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
626                         return -1;
627                     }
628                     break;
629 
630                 case 'o': /* object base directory, Obj or Obr<[+]|-> */
631                 case 'O':
632                     if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
633                     {
634                         options.fObjRule = argv[argi][4] != '-';
635                         break;
636                     }
637 
638                     if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
639                     {
640                         if (strlen(argv[argi]) > 4)
641                             strcpy(szObjectExt, argv[argi]+4);
642                         else
643                         {
644                             if (++argi >= argc)
645                             {
646                                 fprintf(stderr, "syntax error! Option -obj.\n");
647                                 return 1;
648                             }
649                             strcpy(szObjectExt, argv[argi]);
650                         }
651                         break;
652                     }
653 
654                     /* path: -o or -o- */
655                     options.fObjectDir = TRUE;
656                     if (strlen(argv[argi]) > 2)
657                     {
658                         if (argv[argi][2] == '-')  /* no object path */
659                             szObjectDir[0] = '\0';
660                         else
661                             strcpy(szObjectDir, argv[argi]+2);
662                     }
663                     else
664                     {
665                         if (++argi >= argc)
666                         {
667                             fprintf(stderr, "syntax error! Option -o.\n");
668                             return 1;
669                         }
670                         strcpy(szObjectDir, argv[argi]);
671                     }
672                     if (szObjectDir[0] != '\0'
673                         && szObjectDir[strlen(szObjectDir)-1] != '\\'
674                         && szObjectDir[strlen(szObjectDir)-1] != '/'
675                         )
676                         strcat(szObjectDir, "\\");
677                     break;
678 
679                 case 'r':
680                 case 'R':
681                     if (strlen(argv[argi]) > 2)
682                         strcpy(szRsrcExt, argv[argi]+2);
683                     else
684                     {
685                         if (++argi >= argc)
686                         {
687                             fprintf(stderr, "syntax error! Option -r.\n");
688                             return 1;
689                         }
690                         strcpy(szRsrcExt, argv[argi]);
691                     }
692                     break;
693 
694                 case 's':
695                 case 'S':
696                     if (!strnicmp(argv[argi]+1, "srcadd", 6))
697                     {
698                         if (strlen(argv[argi]) > 7)
699                             psz = argv[argi]+2;
700                         else
701                         {
702                             if (++argi >= argc)
703                             {
704                                 fprintf(stderr, "syntax error! Option -srcadd.\n");
705                                 return 1;
706                             }
707                             psz = argv[argi];
708                         }
709                         if (!(psz2 = strchr(psz, ':')))
710                         {
711                             fprintf(stderr, "syntax error! Option -srcadd malformed!\n");
712                             return 1;
713                         }
714                         for (i = 0; aConfig[i].pfn; i++)
715                         {
716                             if (    !strnicmp(aConfig[i].szId, psz, psz2 - psz)
717                                 &&  !aConfig[i].szId[psz2 - psz])
718                             {
719                                 int cch, cch2;
720                                 if (!*++psz2)
721                                 {
722                                     fprintf(stderr, "error: Option -srcadd no additioanl dependancy!\n",
723                                             psz2 - psz, psz);
724                                     return 1;
725                                 }
726                                 cch = 0;
727                                 psz = aConfig[i].pszzAddDeps;
728                                 if (psz)
729                                 {
730                                     do
731                                     {
732                                         cch += (cch2 = strlen(psz)) + 1;
733                                         psz += cch2 + 1;
734                                     } while (*psz);
735                                 }
736                                 cch2 = strlen(psz2);
737                                 aConfig[i].pszzAddDeps = realloc(aConfig[i].pszzAddDeps, cch + cch2 + 2);
738                                 if (!aConfig[i].pszzAddDeps)
739                                 {
740                                     fprintf(stderr, "error: Out of memory!\n");
741                                     return 1;
742                                 }
743                                 strcpy(aConfig[i].pszzAddDeps + cch, psz2);
744                                 aConfig[i].pszzAddDeps[cch + cch2 + 1] = '\0';
745                                 psz = NULL;
746                                 break;
747                             }
748                         }
749                         if (psz)
750                         {
751                             fprintf(stderr, "error: Option -srcadd, invalid language id '%.*s%'\n",
752                                     psz2 - psz, psz);
753                             return 1;
754                         }
755                     }
756                     else
757                     {
758                         fprintf(stderr, "syntax error! Invalid option %s\n", argv[argi]);
759                         return 1;
760                     }
761                     break;
762 
763                 case 'x':
764                 case 'X': /* Exclude files */
765                     psz = &achBuffer[CCHMAXPATH+8];
766                     if (strlen(argv[argi]) > 2)
767                         strcpy(psz, &argv[argi][2]);
768                     else
769                     {
770                         if (++argi >= argc)
771                         {
772                             fprintf(stderr, "syntax error! Option -x.\n");
773                             return 1;
774                         }
775                         strcpy(psz, argv[argi]);
776                     }
777                     while (psz != NULL && *psz != ';')
778                     {
779                         char *  pszNext = strchr(psz, ';');
780                         int     cch = strlen(szExcludeFiles);
781                         if (pszNext)
782                             *pszNext++ = '\0';
783                         if (DosQueryPathInfo(psz, FIL_QUERYFULLNAME, &szExcludeFiles[cch], CCHMAXPATH))
784                         {
785                             fprintf(stderr, "error: Invalid exclude name\n");
786                             return -1;
787                         }
788                         strlwr(&szExcludeFiles[cch]);
789                         strcat(&szExcludeFiles[cch], ";");
790                         psz = pszNext;
791                     }
792                     break;
793 
794                 case 'h':
795                 case 'H':
796                 case '?':
797                     syntax();
798                     return 1;
799 
800                 default:
801                     fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
802                     return -1;
803             }
804 
805         }
806         else if (argv[argi][0] == '@')
807         {   /*
808              * Parameter file (debugger parameter length restrictions led to this):
809              *    Create a textbuffer.
810              *    Parse the file and create a new parameter vector.
811              *    Set argv to the new parameter vector, argi to 0 and argc to
812              *    the parameter count.
813              *    Restrictions: Parameters enclosed in "" is not implemented.
814              *                  No commandline parameters are processed after the @file
815              */
816             char *pszBuffer = (char*)textbufferCreate(&argv[argi][1]); /* !ASSUMS! that pvBuffer is the file string! */
817             if (pszBuffer != NULL)
818             {
819                 char **apszArgs = NULL;
820                 char *psz = pszBuffer;
821                 int  i = 0;
822 
823                 while (*psz != '\0')
824                 {
825                     /* find end of parameter word */
826                     char *pszEnd = psz + 1;
827                     char  ch = *pszEnd;
828                     while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != '\0')
829                         ch = *++pszEnd;
830 
831                     /* allocate more arg array space? */
832                     if ((i % 512) == 0)
833                     {
834                         apszArgs = realloc(apszArgs, sizeof(char*) * 512);
835                         if (apszArgs == NULL)
836                         {
837                             fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
838                             return -8;
839                         }
840                     }
841                     *pszEnd = '\0';
842                     apszArgs[i++] = psz;
843 
844                     /* next */
845                     psz = pszEnd + 1;
846                     ch = *psz;
847                     while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
848                         ch = *++psz;
849                 }
850 
851                 argc = i;
852                 argi = 0;
853                 argv = apszArgs;
854                 continue;
855             }
856             else
857             {
858                 fprintf(stderr, "error: could not open parameter file\n");
859                 return -1;
860             }
861         }
862         else
863         {   /* not a parameter! */
864             ULONG           ulRc;
865             PFILEFINDBUF3   pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
866             HDIR            hDir = HDIR_CREATE;
867             ULONG           cFiles = ~0UL;
868             int             i;
869 
870 
871             /*
872              * If append option is or if the forcescan option isn't is
873              * we'll have to read the existing dep file before starting
874              * adding new dependencies.
875              */
876             if (pdepTree == NULL && (options.fAppend || !options.fForceScan))
877                 depReadFile(pszDepFile, options.fAppend);
878 
879             /*
880              * Search for the files specified.
881              */
882             ulRc = DosFindFirst(argv[argi], &hDir,
883                                 FILE_READONLY |  FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
884                                 pfindbuf3, sizeof(achBuffer), &cFiles, FIL_STANDARD);
885             if (!options.fCacheSearchDirs)
886                 options.fCacheSearchDirs = cFiles > 25;
887             while (ulRc == NO_ERROR)
888             {
889                 for (i = 0;
890                      i < cFiles;
891                      i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
892                      )
893                 {
894                     const char *    psz;
895                     char            szSource[CCHMAXPATH];
896                     BOOL            fExcluded;
897                     char            szTS[TS_SIZE];
898 
899                     /*
900                      * Make full path.
901                      */
902                     if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')) || (*(psz = &argv[argi][1]) == ':'))
903                     {
904                         strncpy(szSource, argv[argi], psz - argv[argi] + 1);
905                         szSource[psz - argv[argi] + 1]  = '\0';
906                     }
907                     else
908                         szSource[0]  = '\0';
909                     strcat(szSource, pfindbuf3->achName);
910                     strlwr(szSource);
911                     fileNormalize(szSource);
912 
913                     /*
914                      * Check if this is an excluded file.
915                      */
916                     fExcluded = FALSE;
917                     psz = options.pszExcludeFiles;
918                     while (*psz != '\0' && *psz != ';')
919                     {
920                         const char * pszNext = strchr(psz, ';');
921                         if (strlen(szSource) == pszNext - psz && strncmp(szSource, psz, pszNext - psz) == 0)
922                             fExcluded = TRUE;
923                         psz = pszNext + 1;
924                     }
925                     if (fExcluded)
926                         continue;
927 
928                     /*
929                      * Analyse the file.
930                      */
931                     depMakeTS(szTS, pfindbuf3);
932                     rc -= makeDependent(&szSource[0], szTS);
933                 }
934 
935                 /* next file */
936                 cFiles = ~0UL;
937                 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
938                 ulRc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
939             }
940             DosFindClose(hDir);
941         }
942         /* next */
943         argi++;
944     }
945 
946     /* Write the depend file! */
947     if (!depWriteFile(pszDepFile, !options.fAppend))
948         fprintf(stderr, "error: failed to write dependencies file!\n");
949     #if 0
950     printf("cfcNodes=%d\n", cfcNodes);
951     #endif
952 
953     return rc;
954 }
955 
956 
957 /**
958  * Displays the syntax description for this util.
959  * @status    completely implemented.
960  * @author    knut st. osmundsen
961  */
syntax(void)962 void syntax(void)
963 {
964     printf(
965         "FastDep v0.48 (build %d)\n"
966         "Dependency scanner. Creates a makefile readable depend file.\n"
967         " - was quick and dirty, now it's just quick -\n"
968         "\n"
969         "Syntax: FastDep [options] <files> [more options [more files [...]]]\n"
970         "    or\n"
971         "        FastDep [options] @<parameterfile>\n"
972         "\n"
973         "Options:\n"
974         "   -a<[+]|->       Append to the output file.            Default: Overwrite.\n"
975         "   -ca             Force search directory caching.\n"
976         "                   Default: cache if more that 25 files are to be searched.\n"
977         "                            (more than 25 in the first file expression.)\n"
978         "   -cy<[+]|->      Check for cylic dependencies.         Default: -cy-\n"
979         "   -d <outputfn>   Output filename.                      Default: %s\n"
980         "   -e excludepath  Exclude paths. If a filename is found in any\n"
981         "                   of these paths only the filename is used, not\n"
982         "                   the path+filename (which is default).\n"
983         "   -eall<[+]|->    Include and source filenames, paths or no paths.\n"
984         "                   -eall+: No path are added to the filename.\n"
985         "                   -eall-: The filename is appended the include path\n"
986         "                           was found in.\n"
987         "                   Default: eall-\n"
988         "   -f<[+]|->       Force scanning of all files. If disabled we'll only scan\n"
989         "                   files which are younger or up to one month older than the\n"
990         "                   dependancy file (if it exists).       Default: disabled\n"
991         "   -i <include>    Additional include paths. INCLUDE is searched after this.\n"
992         "   -n<[+]|->       No path for object files in the rules.\n"
993         "   -o <objdir>     Path were object files are placed. This path replaces the\n"
994         "                   entire filename path\n"
995         "   -o-             No object path\n"
996         "   -obr<[+]|->     -obr+: Object rule.\n"
997         "                   -obr-: No object rule, rule for source filename is generated.\n"
998         "   -obj[ ]<objext> Object extention.                     Default: obj\n"
999         "   -srcadd[ ]<langid>:<dep>\n"
1000         "                   Additional dependants for source file of the given language\n"
1001         "                   type. langid: AS,CX,RC,ORC,COB,IPF\n"
1002         "                   This is very usfull for compiler configuration files.\n"
1003         "   -r[ ]<rsrcext>  Resource binary extention.            Default: res\n"
1004         "   -x[ ]<f1[;f2]>  Files to exclude. Only exact filenames.\n"
1005         "   <files>         Files to scan. Wildchars are allowed.\n"
1006         "\n"
1007         "Options and files could be mixed.\n"
1008         " copyright (c) 1999-2010 knut st. osmundsen (bird-kBuild-spamx@anduin.net)\n",
1009         ODIN32_BUILD_NR,
1010         pszDefaultDepFile
1011         );
1012 }
1013 
1014 
1015 /**
1016  * Generates depend info on this file, these are stored internally
1017  * and written to file later.
1018  * @returns
1019  * @param   pszFilename     Pointer to source filename. Correct case is assumed!
1020  * @param   pszTS           File time stamp.
1021  * @status  completely implemented.
1022  * @author  knut st. osmundsen
1023  */
makeDependent(const char * pszFilename,const char * pszTS)1024 int makeDependent(const char *pszFilename, const char *pszTS)
1025 {
1026     int    rc = -1;
1027 
1028     char            szExt[CCHMAXPATH];
1029     PCONFIGENTRY    pCfg = &aConfig[0];
1030     BOOL            fHeader;
1031 
1032     /*
1033      * Find which filetype this is...
1034      */
1035     fileExt(pszFilename, szExt);
1036     while (pCfg->papszExts != NULL)
1037     {
1038         const char **ppsz = pCfg->papszExts;
1039         while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
1040             ppsz++;
1041         if (*ppsz != NULL)
1042         {
1043             fHeader = pCfg->iFirstHdr > 0 && &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
1044             break;
1045         }
1046         pCfg++;
1047     }
1048 
1049     /* Found? */
1050     if (pCfg->papszExts != NULL)
1051     {
1052         void *  pvRule = NULL;
1053         char    szNormFile[CCHMAXPATH];
1054         fileNormalize2(pszFilename, szNormFile);
1055         rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pszTS, fHeader, &pvRule);
1056         if (!rc && pvRule)
1057         {
1058             if (!fHeader && pCfg->pszzAddDeps)
1059                 depAddSrcAddDeps(pvRule, pCfg->pszzAddDeps);
1060         }
1061     }
1062     else
1063     {
1064         if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
1065             fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
1066         rc = 0;
1067     }
1068 
1069 
1070     return rc;
1071 }
1072 
1073 
1074 /**
1075  * Generates depend info on this C or C++ file, these are stored internally
1076  * and written to file later.
1077  * @returns 0 on success.
1078  *          !0 on error.
1079  * @param   pszFilename         Pointer to source filename. Correct case is assumed!
1080  * @param   pszNormFilename     Pointer to normalized source filename.
1081  * @param   pszTS               File time stamp.
1082  * @parma   fHeader             True if header file is being scanned.
1083  * @param   ppvRule             Variabel to return any new rule handle.
1084  * @status  completely implemented.
1085  * @author  knut st. osmundsen
1086  */
langC_CPP(const char * pszFilename,const char * pszNormFilename,const char * pszTS,BOOL fHeader,void ** ppvRule)1087 int langC_CPP(const char *pszFilename, const char *pszNormFilename,
1088               const char *pszTS, BOOL fHeader, void **ppvRule)
1089 {
1090     void *  pvFile;                     /* Text buffer pointer. */
1091     void *  pvRule;                     /* Handle to the current rule. */
1092     char    szBuffer[4096];             /* Max line length is 4096... should not be a problem. */
1093     int     iLine;                      /* Linenumber. */
1094     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
1095     BOOL    fComment;                   /* TRUE when within a multiline comment. */
1096                                         /* FALSE when not within a multiline comment. */
1097     int     iIfStack;                   /* StackPointer. */
1098     struct  IfStackEntry
1099     {
1100         int fIncluded : 1;              /* TRUE:  include this code;
1101                                          * FALSE: excluded */
1102         int fIf : 1;                    /* TRUE:  #if part of the expression.
1103                                          * FALSE: #else part of the expression. */
1104         int fSupported : 1;             /* TRUE:  supported if/else statement
1105                                          * FALSE: unsupported all else[<something>] are ignored
1106                                          *        All code is included.
1107                                          */
1108     } achIfStack[256];
1109     char    szSrcDir[CCHMAXPATH];
1110     filePath(pszNormFilename, szSrcDir);/* determin source code directory. */
1111 
1112     /**********************************/
1113     /* Add the depend rule            */
1114     /**********************************/
1115     if (options.fObjRule && !fHeader)
1116     {
1117         if (options.fNoObjectPath)
1118             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
1119         else
1120             pvRule = depAddRule(options.fObjectDir ?
1121                                     options.pszObjectDir :
1122                                     filePathSlash(pszFilename, szBuffer),
1123                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1124                                 options.pszObjectExt, pszTS, FALSE);
1125 
1126         if (options.fSrcWhenObj && pvRule)
1127             depAddDepend(pvRule,
1128                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1129                             fileName(pszFilename, szBuffer) : pszNormFilename,
1130                          options.fCheckCyclic,
1131                          FALSE);
1132     }
1133     else
1134         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1135                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1136 
1137     /* duplicate rule? */
1138     *ppvRule = pvRule;
1139     if (pvRule == NULL)
1140         return 0;
1141 
1142 
1143     /********************/
1144     /* Make file buffer */
1145     /********************/
1146     pvFile = textbufferCreate(pszFilename);
1147     if (!pvFile)
1148     {
1149         fprintf(stderr, "failed to open '%s'\n", pszFilename);
1150         return -1;
1151     }
1152 
1153 
1154     /*******************/
1155     /* find dependants */
1156     /*******************/
1157     /* Initiate the IF-stack, comment state and line number. */
1158     iIfStack = 0;
1159     achIfStack[iIfStack].fIf = TRUE;
1160     achIfStack[iIfStack].fIncluded = TRUE;
1161     achIfStack[iIfStack].fSupported = TRUE;
1162     fComment = FALSE;
1163     iLine = 0;
1164     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1165     {
1166         /* search for #include */
1167         register char *pszC;
1168         int cbLen;
1169         int i = 0;
1170         iLine++;
1171 
1172         /* skip blank chars */
1173         cbLen = strlen(szBuffer);
1174         while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1175             i++;
1176 
1177         /* preprocessor statement? */
1178         if (!fComment && szBuffer[i] == '#')
1179         {
1180             /*
1181              * Preprocessor checks
1182              * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1183              * Depending on the word afterwards we'll take some different actions.
1184              * So we'll start of by extracting that word and make a string swich on it.
1185              * Note that there might be some blanks between the hash and the word.
1186              */
1187             int     cchWord;
1188             char *  pszEndWord;
1189             char *  pszArgument;
1190             i++;                /* skip hash ('#') */
1191             while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1192                 i++;
1193             pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1194             cchWord = pszEndWord - &szBuffer[i];
1195 
1196             /*
1197              * Find the argument by skipping the blanks.
1198              */
1199             while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1200                 pszArgument++;
1201 
1202             /*
1203              * string switch.
1204              */
1205             if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1206             {
1207                 /*
1208                  * #include
1209                  *
1210                  * Are we in a state where this file is to be included?
1211                  */
1212                 if (achIfStack[iIfStack].fIncluded)
1213                 {
1214                     char    szFullname[CCHMAXPATH];
1215                     char *  psz;
1216                     BOOL    f = FALSE;
1217                     int     j;
1218                     BOOL    fQuote;
1219 
1220                     /* extract info between "" or <> */
1221                     while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1222                         i++;
1223                     fQuote = szBuffer[i] == '"';
1224                     i++; /* skip '"' or '<' */
1225 
1226                     /* if invalid statement then continue with the next line! */
1227                     if (!f) continue;
1228 
1229                     /* find end */
1230                     j = f = 0;
1231                     while (i + j < cbLen &&  j < CCHMAXPATH &&
1232                            !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1233                         j++;
1234 
1235                     /* if invalid statement then continue with the next line! */
1236                     if (!f) continue;
1237 
1238                     /* copy filename */
1239                     strncpy(szFullname, &szBuffer[i], j);
1240                     szFullname[j] = '\0'; /* ensure terminatition. */
1241                     strlwr(szFullname);
1242 
1243                     /* find include file! */
1244                     psz = fQuote ? pathlistFindFile(szSrcDir, szFullname, szBuffer) : NULL;
1245                     if (psz == NULL)
1246                         psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1247                     if (psz == NULL)
1248                         psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1249 
1250                     /* did we find the include? */
1251                     if (psz != NULL)
1252                     {
1253                         if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1254                         {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1255                             if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1256                                 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1257                             else
1258                                 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1259                                         pszFilename, iLine, szFullname);
1260                         }
1261                         else
1262                             depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1263                     }
1264                     else
1265                     {
1266                         fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1267                                 pszFilename, iLine, szFullname);
1268                         depMarkNotFound(pvRule);
1269                     }
1270                 }
1271             }
1272             else
1273                 /*
1274                  * #if
1275                  */
1276                 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1277             {   /* #if 0 and #if <1-9> are supported */
1278                 pszEndWord = findEndOfWord(pszArgument);
1279                 iIfStack++;
1280                 if ((pszEndWord - pszArgument) == 1
1281                     && *pszArgument >= '0' && *pszArgument <= '9')
1282                 {
1283                     if (*pszArgument != '0')
1284                         achIfStack[iIfStack].fIncluded =  TRUE;
1285                     else
1286                         achIfStack[iIfStack].fIncluded =  FALSE;
1287                 }
1288                 else
1289                     achIfStack[iIfStack].fSupported = FALSE;
1290                 achIfStack[iIfStack].fIncluded = TRUE;
1291                 achIfStack[iIfStack].fIf = TRUE;
1292             }
1293             else
1294                 /*
1295                  * #else
1296                  */
1297                 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1298             {
1299                 if (achIfStack[iIfStack].fSupported)
1300                 {
1301                     if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1302                         achIfStack[iIfStack].fIncluded = FALSE;
1303                     else
1304                         achIfStack[iIfStack].fIncluded = TRUE;
1305                 }
1306                 achIfStack[iIfStack].fIf = FALSE;
1307             }
1308             else
1309                 /*
1310                  * #endif
1311                  */
1312                 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1313             {   /* Pop the if-stack. */
1314                 if (iIfStack > 0)
1315                     iIfStack--;
1316                 else
1317                     fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1318             }
1319             /*
1320              * general if<something> and elseif<something> implementations
1321              */
1322             else
1323                 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1324             {
1325                 achIfStack[iIfStack].fSupported = FALSE;
1326                 achIfStack[iIfStack].fIncluded = TRUE;
1327             }
1328             else
1329                 if (strncmp(&szBuffer[i], "if", 2) == 0)
1330             {
1331                 iIfStack++;
1332                 achIfStack[iIfStack].fIf = TRUE;
1333                 achIfStack[iIfStack].fSupported = FALSE;
1334                 achIfStack[iIfStack].fIncluded = TRUE;
1335             }
1336             /* The rest of them aren't implemented yet.
1337             else if (strncmp(&szBuffer[i], "if") == 0)
1338             {
1339             }
1340             */
1341         }
1342 
1343         /*
1344          * Comment checks.
1345          *  -Start at first non-blank.
1346          *  -Loop thru the line since we might have more than one
1347          *   comment statement on a single line.
1348          */
1349         pszC = &szBuffer[i];
1350         while (pszC != NULL && *pszC != '\0')
1351         {
1352             if (fComment)
1353                 pszC = strstr(pszC, "*/");  /* look for end comment mark. */
1354             else
1355             {
1356                 char *pszLC;
1357                 pszLC= strstr(pszC, "//");  /* look for single line comment mark. */
1358                 pszC = strstr(pszC, "/*");  /* look for start comment mark */
1359                 if (pszLC && pszLC < pszC)  /* if there is an single line comment mark before the */
1360                     break;                  /* muliline comment mark we'll ignore the multiline mark. */
1361             }
1362 
1363             /* Comment mark found? */
1364             if (pszC != NULL)
1365             {
1366                 fComment = !fComment;
1367                 pszC += 2;          /* skip comment mark */
1368 
1369                 /* debug */
1370                 /*
1371                 if (fComment)
1372                     fprintf(stderr, "starts at line %d\n", iLine);
1373                 else
1374                     fprintf(stderr, "ends   at line %d\n", iLine);
1375                     */
1376             }
1377         }
1378     } /*while*/
1379 
1380     textbufferDestroy(pvFile);
1381 
1382     return 0;
1383 }
1384 
1385 
1386 /**
1387  * Generates depend info on this file, these are stored internally
1388  * and written to file later.
1389  * @returns 0 on success.
1390  *          !0 on error.
1391  * @param   pszFilename         Pointer to source filename. Correct case is assumed!
1392  * @param   pszNormFilename     Pointer to normalized source filename.
1393  * @param   pszTS               File time stamp.
1394  * @parma   fHeader             True if header file is being scanned.
1395  * @param   ppvRule             Variabel to return any new rule handle.
1396  * @status  completely implemented.
1397  * @author  knut st. osmundsen
1398  */
langAsm(const char * pszFilename,const char * pszNormFilename,const char * pszTS,BOOL fHeader,void ** ppvRule)1399 int langAsm(const char *pszFilename, const char *pszNormFilename,
1400             const char *pszTS, BOOL fHeader, void **ppvRule)
1401 {
1402     void *  pvFile;                     /* Text buffer pointer. */
1403     void *  pvRule;                     /* Handle to the current rule. */
1404     char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
1405     int     iLine;                      /* current line number */
1406     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
1407 
1408 
1409     /**********************************/
1410     /* Add the depend rule            */
1411     /**********************************/
1412     if (options.fObjRule && !fHeader)
1413     {
1414         if (options.fNoObjectPath)
1415             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
1416         else
1417             pvRule = depAddRule(options.fObjectDir ?
1418                                     options.pszObjectDir :
1419                                     filePathSlash(pszFilename, szBuffer),
1420                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1421                                 options.pszObjectExt, pszTS, FALSE);
1422 
1423         if (options.fSrcWhenObj && pvRule)
1424             depAddDepend(pvRule,
1425                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1426                             fileName(pszFilename, szBuffer) : pszNormFilename,
1427                          options.fCheckCyclic, FALSE);
1428     }
1429     else
1430         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1431                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1432 
1433     /* duplicate rule? */
1434     *ppvRule = pvRule;
1435     if (pvRule == NULL)
1436         return 0;
1437 
1438 
1439     /********************/
1440     /* Make file buffer */
1441     /********************/
1442     pvFile = textbufferCreate(pszFilename);
1443     if (!pvFile)
1444     {
1445         fprintf(stderr, "failed to open '%s'\n", pszFilename);
1446         return -1;
1447     }
1448 
1449 
1450     /*******************/
1451     /* find dependants */
1452     /*******************/
1453     iLine = 0;
1454     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1455     {
1456         /* search for include */
1457         int cbLen;
1458         int i = 0;
1459         iLine++;
1460 
1461         /* skip blank chars */
1462         cbLen = strlen(szBuffer);
1463         while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1464             i++;
1465 
1466         /* is this an include? */
1467         if (strnicmp(&szBuffer[i], "include", 7) == 0
1468             && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1469             )
1470         {
1471             char szFullname[CCHMAXPATH];
1472             char *psz;
1473             int  j;
1474 
1475             /* skip to first no blank char  */
1476             i += 7;
1477             while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1478                 i++;
1479 
1480             /* comment check - if comment found, no filename was given. continue. */
1481             if (szBuffer[i] == ';') continue;
1482 
1483             /* find end */
1484             j = 0;
1485             while (i + j < cbLen
1486                    &&  j < CCHMAXPATH
1487                    && szBuffer[i+j] != ' '  && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1488                    && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';'  && szBuffer[i+j] != '\r'
1489                    )
1490                 j++;
1491 
1492             /* copy filename */
1493             strncpy(szFullname, &szBuffer[i], j);
1494             szFullname[j] = '\0'; /* ensure terminatition. */
1495             strlwr(szFullname);
1496 
1497             /* find include file! */
1498             psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1499             if (psz == NULL)
1500                 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1501 
1502             /* Did we find the include? */
1503             if (psz != NULL)
1504             {
1505                 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1506                 {   /* include sys/stats.inc makes trouble, check for '/' and '\'. */
1507                     if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1508                         depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1509                     else
1510                         fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1511                                 pszFilename, iLine, szFullname);
1512                 }
1513                 else
1514                     depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1515             }
1516             else
1517             {
1518                 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1519                         pszFilename, iLine, szFullname);
1520                 depMarkNotFound(pvRule);
1521             }
1522         }
1523     } /*while*/
1524 
1525     textbufferDestroy(pvFile);
1526 
1527     return 0;
1528 }
1529 
1530 
1531 /**
1532  * Generates depend info on this Resource file, these are stored internally
1533  * and written to file later.
1534  * @returns 0 on success.
1535  *          !0 on error.
1536  * @param   pszFilename         Pointer to source filename. Correct case is assumed!
1537  * @param   pszNormFilename     Pointer to normalized source filename.
1538  * @param   pszTS               File time stamp.
1539  * @parma   fHeader             True if header file is being scanned.
1540  * @status  completely implemented.
1541  * @author  knut st. osmundsen
1542  */
1543 #if 0
1544 int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader)
1545 {
1546     void *  pvFile;                     /* Text buffer pointer. */
1547     void *  pvRule;                     /* Handle to the current rule. */
1548     char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
1549     int     iLine;                      /* current line number */
1550     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
1551 
1552 
1553     /**********************************/
1554     /* Add the depend rule            */
1555     /**********************************/
1556     if (options.fObjRule && !fHeader)
1557     {
1558         if (options.fNoObjectPath)
1559             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
1560         else
1561             pvRule = depAddRule(options.fObjectDir ?
1562                                     options.pszObjectDir :
1563                                     filePathSlash(pszFilename, szBuffer),
1564                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1565                                 options.pszRsrcExt, pszTS, FALSE);
1566 
1567         if (options.fSrcWhenObj && pvRule)
1568             depAddDepend(pvRule,
1569                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1570                             fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1571                          options.fCheckCyclic,
1572                          FALSE);
1573     }
1574     else
1575         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1576                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1577 
1578     /* duplicate rule? */
1579     *ppvRule = pvRule;
1580     if (pvRule == NULL)
1581         return 0;
1582 
1583 
1584     /********************/
1585     /* Make file buffer */
1586     /********************/
1587     pvFile = textbufferCreate(pszFilename);
1588     if (!pvFile)
1589     {
1590         fprintf(stderr, "failed to open '%s'\n", pszFilename);
1591         return -1;
1592     }
1593 
1594 
1595     /*******************/
1596     /* find dependants */
1597     /*******************/
1598     iLine = 0;
1599     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1600     {
1601         /* search for #include */
1602         int cbLen;
1603         int i = 0;
1604         int i1;
1605         iLine++;
1606 
1607         /* skip blank chars */
1608         cbLen = strlen(szBuffer);
1609         while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1610             i++;
1611 
1612         /* is this an include? */
1613         i1 = 1;
1614         if (   strncmp(&szBuffer[i], "#include", 8) == 0
1615             || (i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1616             || strnicmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1617             )
1618         {
1619             char szFullname[CCHMAXPATH];
1620             char *psz;
1621             BOOL f = FALSE;
1622             int  j;
1623 
1624             if (i1 != 0)
1625             {   /*
1626                  * #include <file.h>,  #include "file.h" or DLGINCLUDE 1 "file.h"
1627                  *
1628                  * extract info between "" or <>
1629                  */
1630                 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1631                     i++;
1632                 i++; /* skip '"' or '<' */
1633 
1634                 /* if invalid statement then continue with the next line! */
1635                 if (!f) continue;
1636 
1637                 /* find end */
1638                 j = f = 0;
1639                 while (i + j < cbLen &&  j < CCHMAXPATH &&
1640                        !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1641                     j++;
1642 
1643                 /* if invalid statement then continue with the next line! */
1644                 if (!f) continue;
1645             }
1646             else
1647             {   /*
1648                  * RCINCLUDE ["]filename.dlg["]
1649                  * Extract filename.
1650                  */
1651 
1652                 /* skip to filename.dlg start - if eol will continue to loop. */
1653                 i += 9;
1654                 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1655                     i++;
1656                 if (szBuffer[i] == '\0')
1657                     continue;
1658 
1659                 /* search to end of filename. */
1660                 j = i+1;
1661                 while (   szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1662                        && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1663                     j++;
1664             }
1665 
1666             /* copy filename */
1667             strncpy(szFullname, &szBuffer[i], j);
1668             szFullname[j] = '\0'; /* ensure terminatition. */
1669             strlwr(szFullname);
1670 
1671             /* find include file! */
1672             psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1673             if (psz == NULL)
1674                 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1675 
1676             /* did we find the include? */
1677             if (psz != NULL)
1678             {
1679                 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1680                 {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1681                     if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1682                         depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1683                     else
1684                         fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1685                                 pszFilename, iLine, szFullname);
1686                 }
1687                 else
1688                     depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1689             }
1690             else
1691             {
1692                 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1693                         pszFilename, iLine, szFullname);
1694                 depMarkNotFound(pvRule);
1695             }
1696         }
1697     } /*while*/
1698 
1699     textbufferDestroy(pvFile);
1700     return 0;
1701 }
1702 #else
langRC(const char * pszFilename,const char * pszNormFilename,const char * pszTS,BOOL fHeader,void ** ppvRule)1703 int langRC(const char *pszFilename, const char *pszNormFilename,
1704            const char *pszTS, BOOL fHeader, void **ppvRule)
1705 {
1706     void *  pvFile;                     /* Text buffer pointer. */
1707     void *  pvRule;                     /* Handle to the current rule. */
1708     char    szBuffer[4096];             /* Max line length is 4096... should not be a problem. */
1709     int     iLine;                      /* Linenumber. */
1710     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
1711     BOOL    fComment;                   /* TRUE when within a multiline comment. */
1712                                         /* FALSE when not within a multiline comment. */
1713     int     iIfStack;                   /* StackPointer. */
1714     struct  IfStackEntry
1715     {
1716         int fIncluded : 1;              /* TRUE:  include this code;
1717                                          * FALSE: excluded */
1718         int fIf : 1;                    /* TRUE:  #if part of the expression.
1719                                          * FALSE: #else part of the expression. */
1720         int fSupported : 1;             /* TRUE:  supported if/else statement
1721                                          * FALSE: unsupported all else[<something>] are ignored
1722                                          *        All code is included.
1723                                          */
1724     } achIfStack[256];
1725 
1726 
1727     /**********************************/
1728     /* Add the depend rule            */
1729     /**********************************/
1730     if (options.fObjRule && !fHeader)
1731     {
1732         if (options.fNoObjectPath)
1733             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
1734         else
1735             pvRule = depAddRule(options.fObjectDir ?
1736                                     options.pszObjectDir :
1737                                     filePathSlash(pszFilename, szBuffer),
1738                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1739                                 options.pszRsrcExt, pszTS, FALSE);
1740 
1741         if (options.fSrcWhenObj && pvRule)
1742             depAddDepend(pvRule,
1743                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1744                             fileName(pszFilename, szBuffer) : pszNormFilename,
1745                          options.fCheckCyclic,
1746                          FALSE);
1747     }
1748     else
1749         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1750                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1751 
1752     /* duplicate rule? */
1753     *ppvRule = pvRule;
1754     if (pvRule == NULL)
1755         return 0;
1756 
1757 
1758     /********************/
1759     /* Make file buffer */
1760     /********************/
1761     pvFile = textbufferCreate(pszFilename);
1762     if (!pvFile)
1763     {
1764         fprintf(stderr, "failed to open '%s'\n", pszFilename);
1765         return -1;
1766     }
1767 
1768 
1769     /*******************/
1770     /* find dependants */
1771     /*******************/
1772     /* Initiate the IF-stack, comment state and line number. */
1773     iIfStack = 0;
1774     achIfStack[iIfStack].fIf = TRUE;
1775     achIfStack[iIfStack].fIncluded = TRUE;
1776     achIfStack[iIfStack].fSupported = TRUE;
1777     fComment = FALSE;
1778     iLine = 0;
1779     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1780     {
1781         register char * pszC;
1782         char            szFullname[CCHMAXPATH];
1783         int             cbLen;
1784         int             i1 = 1;
1785         int             i = 0;
1786         iLine++;
1787 
1788         /* skip blank chars */
1789         cbLen = strlen(szBuffer);
1790         while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1791             i++;
1792 
1793         /* preprocessor statement? */
1794         if (!fComment && szBuffer[i] == '#')
1795         {
1796             /*
1797              * Preprocessor checks
1798              * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1799              * Depending on the word afterwards we'll take some different actions.
1800              * So we'll start of by extracting that word and make a string swich on it.
1801              * Note that there might be some blanks between the hash and the word.
1802              */
1803             int     cchWord;
1804             char *  pszEndWord;
1805             char *  pszArgument;
1806             i++;                /* skip hash ('#') */
1807             while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1808                 i++;
1809             pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1810             cchWord = pszEndWord - &szBuffer[i];
1811 
1812             /*
1813              * Find the argument by skipping the blanks.
1814              */
1815             while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1816                 pszArgument++;
1817 
1818             /*
1819              * string switch.
1820              */
1821             if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1822             {
1823                 /*
1824                  * #include
1825                  *
1826                  * Are we in a state where this file is to be included?
1827                  */
1828                 if (achIfStack[iIfStack].fIncluded)
1829                 {
1830                     char *psz;
1831                     BOOL f = FALSE;
1832                     int  j;
1833 
1834                     /* extract info between "" or <> */
1835                     while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1836                         i++;
1837                     i++; /* skip '"' or '<' */
1838 
1839                     /* if invalid statement then continue with the next line! */
1840                     if (!f) continue;
1841 
1842                     /* find end */
1843                     j = f = 0;
1844                     while (i + j < cbLen &&  j < CCHMAXPATH &&
1845                            !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1846                         j++;
1847 
1848                     /* if invalid statement then continue with the next line! */
1849                     if (!f) continue;
1850 
1851                     /* copy filename */
1852                     strncpy(szFullname, &szBuffer[i], j);
1853                     szFullname[j] = '\0'; /* ensure terminatition. */
1854                     strlwr(szFullname);
1855 
1856                     /* find include file! */
1857                     psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1858                     if (psz == NULL)
1859                         psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1860 
1861                     /* did we find the include? */
1862                     if (psz != NULL)
1863                     {
1864                         if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1865                         {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1866                             if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1867                                 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1868                             else
1869                                 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1870                                         pszFilename, iLine, szFullname);
1871                         }
1872                         else
1873                             depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1874                     }
1875                     else
1876                     {
1877                         fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1878                                 pszFilename, iLine, szFullname);
1879                         depMarkNotFound(pvRule);
1880                     }
1881                 }
1882             }
1883             else
1884                 /*
1885                  * #if
1886                  */
1887                 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1888             {   /* #if 0 and #if <1-9> are supported */
1889                 pszEndWord = findEndOfWord(pszArgument);
1890                 iIfStack++;
1891                 if ((pszEndWord - pszArgument) == 1
1892                     && *pszArgument >= '0' && *pszArgument <= '9')
1893                 {
1894                     if (*pszArgument != '0')
1895                         achIfStack[iIfStack].fIncluded =  TRUE;
1896                     else
1897                         achIfStack[iIfStack].fIncluded =  FALSE;
1898                 }
1899                 else
1900                     achIfStack[iIfStack].fSupported = FALSE;
1901                 achIfStack[iIfStack].fIncluded = TRUE;
1902                 achIfStack[iIfStack].fIf = TRUE;
1903             }
1904             else
1905                 /*
1906                  * #else
1907                  */
1908                 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1909             {
1910                 if (achIfStack[iIfStack].fSupported)
1911                 {
1912                     if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1913                         achIfStack[iIfStack].fIncluded = FALSE;
1914                     else
1915                         achIfStack[iIfStack].fIncluded = TRUE;
1916                 }
1917                 achIfStack[iIfStack].fIf = FALSE;
1918             }
1919             else
1920                 /*
1921                  * #endif
1922                  */
1923                 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1924             {   /* Pop the if-stack. */
1925                 if (iIfStack > 0)
1926                     iIfStack--;
1927                 else
1928                     fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1929             }
1930             /*
1931              * general if<something> and elseif<something> implementations
1932              */
1933             else
1934                 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1935             {
1936                 achIfStack[iIfStack].fSupported = FALSE;
1937                 achIfStack[iIfStack].fIncluded = TRUE;
1938             }
1939             else
1940                 if (strncmp(&szBuffer[i], "if", 2) == 0)
1941             {
1942                 iIfStack++;
1943                 achIfStack[iIfStack].fIf = TRUE;
1944                 achIfStack[iIfStack].fSupported = FALSE;
1945                 achIfStack[iIfStack].fIncluded = TRUE;
1946             }
1947             /* The rest of them aren't implemented yet.
1948             else if (strncmp(&szBuffer[i], "if") == 0)
1949             {
1950             }
1951             */
1952         } else
1953             /*
1954              * Check for resource compiler directives.
1955              */
1956             if (    !fComment
1957                 &&  !strchr(&szBuffer[i], ',')
1958                 &&  (   !strnicmp(&szBuffer[i], "ICON", 4)
1959                      || !strnicmp(&szBuffer[i], "FONT", 4)
1960                      || !strnicmp(&szBuffer[i], "BITMAP", 6)
1961                      || !strnicmp(&szBuffer[i], "POINTER", 7)
1962                      || !strnicmp(&szBuffer[i], "RESOURCE", 8)
1963                      || !(i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9))
1964                    /*|| !strnicmp(&szBuffer[i], "DLGINCLUDE", 10) - only used by the dlgeditor */
1965                      || !strnicmp(&szBuffer[i], "DEFAULTICON", 11)
1966                      )
1967                 )
1968         {
1969             /*
1970              * RESOURCE 123 1 ["]filename.ext["]
1971              */
1972             char    szLine[1024];
1973             char *  pszFile;
1974             char    chQuote = ' ';
1975 
1976             PreProcessLine(szLine, &szBuffer[i]);
1977 
1978             pszFile = &szLine[strlen(szLine)-1];
1979             if (*pszFile == '\"' || *pszFile == '\'')
1980             {
1981                 chQuote = *pszFile;
1982                 *pszFile-- = '\0';
1983             }
1984             while (*pszFile != chQuote)
1985                 pszFile--;
1986             *pszFile++ = '\0'; /* We now have extracted the filename - pszFile. */
1987             strlwr(pszFile);
1988 
1989             /* Add filename to the dependencies. */
1990             if (i1)
1991                 depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
1992             else
1993             {
1994                 char *psz;
1995                 /* find include file! */
1996                 psz = pathlistFindFile(options.pszInclude, pszFile, szFullname);
1997                 if (psz == NULL)
1998                     psz = pathlistFindFile(pszIncludeEnv, pszFile, szFullname);
1999 
2000                 /* did we find the include? */
2001                 if (psz != NULL)
2002                 {
2003                     if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szFullname))
2004                     {   /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
2005                         if (!strchr(pszFile, '/') && !strchr(pszFile, '\\'))
2006                             depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
2007                         else
2008                             fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
2009                                     pszFilename, iLine, pszFile);
2010                     }
2011                     else
2012                         depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
2013                 }
2014                 else
2015                 {
2016                     fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
2017                             pszFilename, iLine, pszFile);
2018                     depMarkNotFound(pvRule);
2019                 }
2020             }
2021         }
2022 
2023 
2024         /*
2025          * Comment checks.
2026          *  -Start at first non-blank.
2027          *  -Loop thru the line since we might have more than one
2028          *   comment statement on a single line.
2029          */
2030         pszC = &szBuffer[i];
2031         while (pszC != NULL && *pszC != '\0')
2032         {
2033             if (fComment)
2034                 pszC = strstr(pszC, "*/");  /* look for end comment mark. */
2035             else
2036             {
2037                 char *pszLC;
2038                 pszLC= strstr(pszC, "//");  /* look for single line comment mark. */
2039                 pszC = strstr(pszC, "/*");  /* look for start comment mark */
2040                 if (pszLC && pszLC < pszC)  /* if there is an single line comment mark before the */
2041                     break;                  /* muliline comment mark we'll ignore the multiline mark. */
2042             }
2043 
2044             /* Comment mark found? */
2045             if (pszC != NULL)
2046             {
2047                 fComment = !fComment;
2048                 pszC += 2;          /* skip comment mark */
2049 
2050                 /* debug */
2051                 /*
2052                 if (fComment)
2053                     fprintf(stderr, "starts at line %d\n", iLine);
2054                 else
2055                     fprintf(stderr, "ends   at line %d\n", iLine);
2056                     */
2057             }
2058         }
2059     } /*while*/
2060 
2061     textbufferDestroy(pvFile);
2062 
2063     return 0;
2064 }
2065 #endif
2066 
2067 
2068 /**
2069  * Generates depend info on this COBOL file, these are stored internally
2070  * and written to file later.
2071  * @returns 0 on success.
2072  *          !0 on error.
2073  * @param   pszFilename         Pointer to source filename. Correct case is assumed!
2074  * @param   pszNormFilename     Pointer to normalized source filename.
2075  * @param   pszTS               File time stamp.
2076  * @parma   fHeader             True if header file is being scanned.
2077  * @param   ppvRule             Variabel to return any new rule handle.
2078  * @status  completely implemented.
2079  * @author  knut st. osmundsen
2080  */
langCOBOL(const char * pszFilename,const char * pszNormFilename,const char * pszTS,BOOL fHeader,void ** ppvRule)2081 int langCOBOL(const char *pszFilename, const char *pszNormFilename,
2082               const char *pszTS, BOOL fHeader, void **ppvRule)
2083 {
2084     void *  pvFile;                     /* Text buffer pointer. */
2085     void *  pvRule;                     /* Handle to the current rule. */
2086     char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
2087     int     iLine;                      /* current line number */
2088     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
2089 
2090 
2091     /**********************************/
2092     /* Add the depend rule            */
2093     /**********************************/
2094     if (options.fObjRule && !fHeader)
2095     {
2096         if (options.fNoObjectPath)
2097             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
2098         else
2099             pvRule = depAddRule(options.fObjectDir ?
2100                                     options.pszObjectDir :
2101                                     filePathSlash(pszFilename, szBuffer),
2102                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
2103                                 options.pszObjectExt, pszTS, FALSE);
2104 
2105         if (options.fSrcWhenObj && pvRule)
2106             depAddDepend(pvRule,
2107                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
2108                             ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
2109                          options.fCheckCyclic,
2110                          FALSE);
2111     }
2112     else
2113         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
2114                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
2115 
2116     /* duplicate rule? */
2117     *ppvRule = pvRule;
2118     if (pvRule == NULL)
2119         return 0;
2120 
2121 
2122     /********************/
2123     /* Make file buffer */
2124     /********************/
2125     pvFile = textbufferCreate(pszFilename);
2126     if (!pvFile)
2127     {
2128         fprintf(stderr, "failed to open '%s'\n", pszFilename);
2129         return -1;
2130     }
2131 
2132 
2133     /*******************/
2134     /* find dependants */
2135     /*******************/
2136     iLine = 0;
2137     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2138     {
2139         /* search for #include */
2140         int cbLen;
2141         int i = 0;
2142         int i1, i2;
2143         iLine++;
2144 
2145         /* check for comment mark (column 7) */
2146         if (szBuffer[6] == '*')
2147             continue;
2148 
2149         /* skip blank chars */
2150         cbLen = strlen(szBuffer);
2151         while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
2152             i++;
2153 
2154         /* is this an include? */
2155         if (   (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
2156             || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
2157             )
2158         {
2159             char szFullname[CCHMAXPATH];
2160             char *psz;
2161             int  j;
2162 
2163             /* skip statement */
2164             i += 4;
2165             if (i1 != 0)
2166             {
2167                 int y = 2; /* skip two words */
2168                 do
2169                 {
2170                     /* skip blanks */
2171                     while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2172                         i++;
2173                     /* skip word */
2174                     while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
2175                            && szBuffer[i] != '\0' && szBuffer[i] != '\n')
2176                         i++;
2177                     y--;
2178                 } while (y > 0);
2179             }
2180 
2181             /* check for blank */
2182             if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
2183                 continue;
2184 
2185             /* skip blanks */
2186             while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2187                 i++;
2188 
2189             /* if invalid statement then continue with the next line! */
2190             if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
2191                 continue;
2192 
2193             /* find end */
2194             j = 0;
2195             while (i + j < cbLen && j < CCHMAXPATH
2196                    && szBuffer[i+j] != '.'
2197                    && szBuffer[i+j] != ' '  && szBuffer[i+j] != '\t'
2198                    && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
2199                    )
2200                 j++;
2201 
2202             /* if invalid statement then continue with the next line! */
2203             if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
2204                 continue;
2205 
2206             /* copy filename */
2207             strncpy(szFullname, &szBuffer[i], j);
2208             szFullname[j] = '\0'; /* ensure terminatition. */
2209             strlwr(szFullname);
2210 
2211             /* add extention .cpy - hardcoded for the moment. */
2212             strcpy(&szFullname[j], ".cbl");
2213 
2214             /* find include file! */
2215             psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2216             if (!psz)
2217             {
2218                 strcpy(&szFullname[j], ".cpy");
2219                 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2220             }
2221 
2222             /* did we find the include? */
2223             if (psz != NULL)
2224             {
2225                 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2226                     depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
2227                 else
2228                     depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
2229             }
2230             else
2231             {
2232                 szFullname[j] = '\0';
2233                 fprintf(stderr, "%s(%d): warning copybook '%s' was not found!\n",
2234                         pszFilename, iLine, szFullname);
2235                 depMarkNotFound(pvRule);
2236             }
2237         }
2238     } /*while*/
2239 
2240     textbufferDestroy(pvFile);
2241 
2242     return 0;
2243 }
2244 
2245 
2246 /**
2247  * Generates depend info on this IPF file, these are stored internally
2248  * and written to file later.
2249  * @returns 0 on success.
2250  *          !0 on error.
2251  * @param   pszFilename         Pointer to source filename. Correct case is assumed!
2252  * @param   pszNormFilename     Pointer to normalized source filename.
2253  * @param   pszTS               File time stamp.
2254  * @param   fHeader             True if header file is being scanned.
2255  * @param   ppvRule             Variabel to return any new rule handle.
2256  * @status  completely implemented.
2257  * @author  knut st. osmundsen
2258  */
langIPF(const char * pszFilename,const char * pszNormFilename,const char * pszTS,BOOL fHeader,void ** ppvRule)2259 int langIPF(  const char *pszFilename, const char *pszNormFilename,
2260               const char *pszTS, BOOL fHeader, void **ppvRule)
2261 {
2262     void *  pvFile;                     /* Text buffer pointer. */
2263     void *  pvRule;                     /* Handle to the current rule. */
2264     char    szBuffer[4096];             /* Temporary buffer (max line lenght size...) */
2265     int     iLine;                      /* current line number */
2266     void *  pv = NULL;                  /* An index used by textbufferGetNextLine. */
2267 
2268 
2269     /**********************************/
2270     /* Add the depend rule            */
2271     /**********************************/
2272     /*if (options.fObjRule && !fHeader)
2273     {
2274         if (options.fNoObjectPath)
2275             pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
2276         else
2277             pvRule = depAddRule(options.fObjectDir ?
2278                                     options.pszObjectDir :
2279                                     filePathSlash(pszFilename, szBuffer),
2280                                 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
2281                                 options.pszObjectExt, pszTS, FALSE);
2282 
2283         if (options.fSrcWhenObj && pvRule)
2284             depAddDepend(pvRule,
2285                          options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
2286                             ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
2287                          options.fCheckCyclic,
2288                          FALSE);
2289     }
2290     else */
2291         pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
2292                             fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
2293 
2294     /* duplicate rule? */
2295     *ppvRule = pvRule;
2296     if (pvRule == NULL)
2297         return 0;
2298 
2299 
2300     /********************/
2301     /* Make file buffer */
2302     /********************/
2303     pvFile = textbufferCreate(pszFilename);
2304     if (!pvFile)
2305     {
2306         fprintf(stderr, "failed to open '%s'\n", pszFilename);
2307         return -1;
2308     }
2309 
2310 
2311     /*******************/
2312     /* find dependants */
2313     /*******************/
2314     iLine = 0;
2315     while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2316     {
2317         iLine++;
2318 
2319         /* is this an imbed statement? */
2320         if (!strncmp(&szBuffer[0], ".im", 3))
2321         {
2322             char    szFullname[CCHMAXPATH];
2323             char *  psz;
2324             int     i;
2325             int     j;
2326             char    chQuote = 0;
2327 
2328             /* skip statement and blanks */
2329             i = 4;
2330             while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2331                 i++;
2332 
2333             /* check for quotes */
2334             if (szBuffer[i] == '\'' || szBuffer[i] == '\"')
2335                 chQuote = szBuffer[i++];
2336 
2337             /* find end */
2338             j = 0;
2339             if (chQuote != 0)
2340             {
2341                 while (szBuffer[i+j] != chQuote && szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2342                     j++;
2343             }
2344             else
2345             {
2346                 while (szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2347                     j++;
2348             }
2349 
2350             /* find end */
2351             if (j >= CCHMAXPATH)
2352             {
2353                 fprintf(stderr, "%s(%d) warning: Filename too long ignored.\n", pszFilename, iLine);
2354                 continue;
2355             }
2356 
2357             /* copy filename */
2358             strncpy(szFullname, &szBuffer[i], j);
2359             szFullname[j] = '\0'; /* ensure terminatition. */
2360             strlwr(szFullname);
2361 
2362             /* find include file! */
2363             psz = filecacheFileExist(szFullname, szBuffer);
2364 
2365             /* did we find the include? */
2366             if (psz != NULL)
2367             {
2368                 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2369                     depAddDepend(pvRule, fileName(szFullname, szBuffer), options.fCheckCyclic, FALSE);
2370                 else
2371                     depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
2372             }
2373             else
2374             {
2375                 fprintf(stderr, "%s(%d): warning imbeded file '%s' was not found!\n",
2376                         pszFilename, iLine, szFullname);
2377                 depMarkNotFound(pvRule);
2378             }
2379         }
2380     } /*while*/
2381 
2382     textbufferDestroy(pvFile);
2383     fHeader = fHeader;
2384 
2385     return 0;
2386 }
2387 
2388 
2389 #define upcase(ch)   \
2390      (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
2391 
2392 /**
2393  * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
2394  * @returns   0 equal. (same as strnicmp)
2395  * @param     pszS1  String 1
2396  * @param     pszS2  String 2
2397  * @param     cch    Length to compare (relative to string 1)
2398  */
strnicmpwords(const char * pszS1,const char * pszS2,int cch)2399 int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
2400 {
2401     do
2402     {
2403         while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
2404             pszS1++, pszS2++, cch--;
2405 
2406         /* blank test and skipping */
2407         if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
2408         {
2409             while (cch > 0 && *pszS1 == ' ')
2410                 pszS1++, cch--;
2411 
2412             while (*pszS2 == ' ')
2413                 pszS2++;
2414         }
2415         else
2416             break;
2417     } while (cch > 0);
2418 
2419     return cch == 0 ? 0 : *pszS1 - *pszS2;
2420 }
2421 
2422 
2423 /**
2424  * Normalizes the path slashes for the filename. It will partially expand paths too.
2425  * @returns   pszFilename
2426  * @param     pszFilename  Pointer to filename string. Not empty string!
2427  *                         Much space to play with.
2428  */
fileNormalize(char * pszFilename)2429 char *fileNormalize(char *pszFilename)
2430 {
2431     char *psz = pszFilename;
2432 
2433     /* correct slashes */
2434     while ((pszFilename = strchr(pszFilename, '//')) != NULL)
2435         *pszFilename++ = '\\';
2436 
2437     /* expand path? */
2438     pszFilename = psz;
2439     if (pszFilename[1] != ':')
2440     {   /* relative path */
2441         int     iSlash;
2442         char    szFile[CCHMAXPATH];
2443         char *  psz = szFile;
2444 
2445         strcpy(szFile, pszFilename);
2446         iSlash = *psz == '\\' ? 1 : cSlashes;
2447         while (*psz != '\0')
2448         {
2449             if (*psz == '.' && psz[1] == '.'  && psz[2] == '\\')
2450             {   /* up one directory */
2451                 if (iSlash > 0)
2452                     iSlash--;
2453                 psz += 3;
2454             }
2455             else if (*psz == '.' && psz[1] == '\\')
2456             {   /* no change */
2457                 psz += 2;
2458             }
2459             else
2460             {   /* completed expantion! */
2461                 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
2462                 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
2463                 break;
2464             }
2465         }
2466     }
2467     /* else: assume full path */
2468 
2469     return psz;
2470 }
2471 
2472 
2473 /**
2474  * Normalizes the path slashes for the filename. It will partially expand paths too.
2475  * Makes name all lower case too.
2476  * @returns   pszFilename
2477  * @param     pszFilename  Pointer to filename string. Not empty string!
2478  *                         Much space to play with.
2479  * @param     pszBuffer    Pointer to output buffer.
2480  */
fileNormalize2(const char * pszFilename,char * pszBuffer)2481 char *fileNormalize2(const char *pszFilename, char *pszBuffer)
2482 {
2483     char *  psz = pszBuffer;
2484     int     iSlash;
2485 
2486     if (pszFilename[1] != ':')
2487     {
2488         /* iSlash */
2489         if (*pszFilename == '\\' || *pszFilename == '/')
2490             iSlash = 1;
2491         else
2492             iSlash = cSlashes;
2493 
2494         /* interpret . and .. */
2495         while (*pszFilename != '\0')
2496         {
2497             if (*pszFilename == '.' && pszFilename[1] == '.'  && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
2498             {   /* up one directory */
2499                 if (iSlash > 0)
2500                     iSlash--;
2501                 pszFilename += 3;
2502             }
2503             else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
2504             {   /* no change */
2505                 pszFilename += 2;
2506             }
2507             else
2508             {   /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
2509                 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
2510                 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
2511                 break;
2512             }
2513         }
2514     }
2515     else
2516     {   /* have drive letter specified - assume ok (TODO)*/
2517         strcpy(pszBuffer, pszFilename);
2518     }
2519 
2520     /* correct slashes */
2521     while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
2522         *pszBuffer++ = '\\';
2523 
2524     /* lower case it */
2525     /*strlwr(psz);*/
2526 
2527     return psz;
2528 }
2529 
2530 
2531 /**
2532  * Copies the path part (excluding the slash) into pszBuffer and returns
2533  * a pointer to the buffer.
2534  * If no path is found "" is returned.
2535  * @returns   Pointer to pszBuffer with path.
2536  * @param     pszFilename  Pointer to readonly filename.
2537  * @param     pszBuffer    Pointer to output Buffer.
2538  * @status    completely implemented.
2539  * @author    knut st. osmundsen
2540  */
filePath(const char * pszFilename,char * pszBuffer)2541 char *filePath(const char *pszFilename, char *pszBuffer)
2542 {
2543     char *psz = strrchr(pszFilename, '\\');
2544     if (psz == NULL)
2545         psz = strrchr(pszFilename, '/');
2546 
2547     if (psz == NULL)
2548         *pszBuffer = '\0';
2549     else
2550     {
2551         strncpy(pszBuffer, pszFilename, psz - pszFilename);
2552         pszBuffer[psz - pszFilename] = '\0';
2553     }
2554 
2555     return pszBuffer;
2556 }
2557 
2558 
2559 /**
2560  * Copies the path part including the slash into pszBuffer and returns
2561  * a pointer to the buffer.
2562  * If no path is found "" is returned.
2563  * @returns   Pointer to pszBuffer with path.
2564  * @param     pszFilename  Pointer to readonly filename.
2565  * @param     pszBuffer    Pointer to output Buffer.
2566  * @status    completely implemented.
2567  * @author    knut st. osmundsen
2568  */
filePathSlash(const char * pszFilename,char * pszBuffer)2569 char *filePathSlash(const char *pszFilename, char *pszBuffer)
2570 {
2571     char *psz = strrchr(pszFilename, '\\');
2572     if (psz == NULL)
2573         psz = strrchr(pszFilename, '/');
2574 
2575     if (psz == NULL)
2576         *pszBuffer = '\0';
2577     else
2578     {
2579         strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2580         pszBuffer[psz - pszFilename + 1] = '\0';
2581     }
2582 
2583     return pszBuffer;
2584 }
2585 
2586 
2587 /**
2588  * Copies the path part including the slash into pszBuffer and returns
2589  * a pointer to the buffer. If no path is found "" is returned.
2590  * The path is normalized to only use '\\'.
2591  * @returns   Pointer to pszBuffer with path.
2592  * @param     pszFilename  Pointer to readonly filename.
2593  * @param     pszBuffer    Pointer to output Buffer.
2594  * @status    completely implemented.
2595  * @author    knut st. osmundsen
2596  */
filePathSlash2(const char * pszFilename,char * pszBuffer)2597 char *filePathSlash2(const char *pszFilename, char *pszBuffer)
2598 {
2599     char *psz = strrchr(pszFilename, '\\');
2600     if (psz == NULL)
2601         psz = strrchr(pszFilename, '/');
2602 
2603     if (psz == NULL)
2604         *pszBuffer = '\0';
2605     else
2606     {
2607         strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2608         pszBuffer[psz - pszFilename + 1] = '\0';
2609 
2610         /* normalize all '/' to '\\' */
2611         psz = pszBuffer;
2612         while ((psz = strchr(psz, '/')) != NULL)
2613                *psz++ = '\\';
2614     }
2615 
2616     return pszBuffer;
2617 }
2618 
2619 
2620 /**
2621  * Copies the filename (with extention) into pszBuffer and returns
2622  * a pointer to the buffer.
2623  * @returns   Pointer to pszBuffer with path.
2624  * @param     pszFilename  Pointer to readonly filename.
2625  * @param     pszBuffer    Pointer to output Buffer.
2626  * @status    completely implemented.
2627  * @author    knut st. osmundsen
2628  */
fileName(const char * pszFilename,char * pszBuffer)2629 char *fileName(const char *pszFilename, char *pszBuffer)
2630 {
2631     char *psz = strrchr(pszFilename, '\\');
2632     if (psz == NULL)
2633         psz = strrchr(pszFilename, '/');
2634 
2635     strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2636 
2637     return pszBuffer;
2638 }
2639 
2640 
2641 /**
2642  * Copies the name part with out extention into pszBuffer and returns
2643  * a pointer to the buffer.
2644  * If no name is found "" is returned.
2645  * @returns   Pointer to pszBuffer with path.
2646  * @param     pszFilename  Pointer to readonly filename.
2647  * @param     pszBuffer    Pointer to output Buffer.
2648  * @status    completely implemented.
2649  * @author    knut st. osmundsen
2650  */
fileNameNoExt(const char * pszFilename,char * pszBuffer)2651 char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
2652 {
2653     char *psz = strrchr(pszFilename, '\\');
2654     if (psz == NULL)
2655         psz = strrchr(pszFilename, '/');
2656 
2657     strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2658 
2659     psz = strrchr(pszBuffer, '.');
2660     if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
2661         *psz = '\0';
2662 
2663     return pszBuffer;
2664 }
2665 
2666 
2667 /**
2668  * Copies the extention part into pszBuffer and returns
2669  * a pointer to the buffer.
2670  * If no extention is found "" is returned.
2671  * The dot ('.') is not included!
2672  * @returns   Pointer to pszBuffer with path.
2673  * @param     pszFilename  Pointer to readonly filename.
2674  * @param     pszBuffer    Pointer to output Buffer.
2675  * @status    completely implemented.
2676  * @author    knut st. osmundsen
2677  */
fileExt(const char * pszFilename,char * pszBuffer)2678 char *fileExt(const char *pszFilename, char *pszBuffer)
2679 {
2680     char *psz = strrchr(pszFilename, '.');
2681     if (psz != NULL)
2682     {
2683         if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
2684             *pszBuffer = '\0';
2685         else
2686             strcpy(pszBuffer, psz + 1);
2687     }
2688     else
2689         *pszBuffer = '\0';
2690 
2691     return pszBuffer;
2692 }
2693 
2694 
2695 /**
2696  * Adds a file to the cache.
2697  * @returns   Success indicator.
2698  * @param     pszFilename   Name of the file which is to be added. (with path!)
2699  */
filecacheAddFile(const char * pszFilename)2700 BOOL filecacheAddFile(const char *pszFilename)
2701 {
2702     PFCACHEENTRY pfcNew;
2703 
2704     /* allocate new block and fill in data */
2705     pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
2706     if (pfcNew == NULL)
2707     {
2708         fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2709         return FALSE;
2710     }
2711     pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2712     strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
2713     if (!AVLInsert(&pfcTree, pfcNew))
2714     {
2715         free(pfcNew);
2716         return TRUE;
2717     }
2718     cfcNodes++;
2719 
2720     return TRUE;
2721 }
2722 
2723 
2724 /**
2725  * Adds a file to the cache.
2726  * @returns   Success indicator.
2727  * @param     pszDir   Name of the path which is to be added. (with slash!)
2728  */
filecacheAddDir(const char * pszDir)2729 BOOL filecacheAddDir(const char *pszDir)
2730 {
2731     PFCACHEENTRY    pfcNew;
2732     APIRET          rc;
2733     char            szDir[CCHMAXPATH];
2734     int             cchDir;
2735     char            achBuffer[32768];
2736     PFILEFINDBUF3   pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2737     HDIR            hDir = HDIR_CREATE;
2738     ULONG           cFiles = 0xFFFFFFF;
2739     int             i;
2740 
2741     /* Make path */
2742     filePathSlash2(pszDir, szDir);
2743     /*strlwr(szDir);*/ /* Convert name to lower case to allow faster searchs! */
2744     cchDir = strlen(szDir);
2745 
2746 
2747     /* Add directory to pfcDirTree. */
2748     pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2749     if (pfcNew == NULL)
2750     {
2751         fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2752         DosFindClose(hDir);
2753         return FALSE;
2754     }
2755     pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2756     strcpy((char*)(unsigned)pfcNew->Key, szDir);
2757     AVLInsert(&pfcDirTree, pfcNew);
2758 
2759 
2760     /* Start to search directory - all files */
2761     strcat(szDir + cchDir, "*");
2762     rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2763                       pfindbuf3, sizeof(achBuffer),
2764                       &cFiles, FIL_STANDARD);
2765     while (rc == NO_ERROR)
2766     {
2767         for (i = 0;
2768              i < cFiles;
2769              i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2770              )
2771         {
2772             pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2773             if (pfcNew == NULL)
2774             {
2775                 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2776                 DosFindClose(hDir);
2777                 return FALSE;
2778             }
2779             pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2780             strcpy((char*)(unsigned)pfcNew->Key, szDir);
2781             strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2782             strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2783             if (!AVLInsert(&pfcTree, pfcNew))
2784                 free(pfcNew);
2785             else
2786                 cfcNodes++;
2787         }
2788 
2789         /* next */
2790         cFiles = 0xFFFFFFF;
2791         pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2792         rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2793     }
2794 
2795     DosFindClose(hDir);
2796 
2797     return TRUE;
2798 }
2799 
2800 
2801 /**
2802  * Checks if pszFilename is exists in the cache.
2803  * @return    TRUE if found. FALSE if not found.
2804  * @param     pszFilename   Name of the file to be found. (with path!)
2805  *                          This is in lower case!
2806  */
filecacheFind(const char * pszFilename)2807 INLINE BOOL filecacheFind(const char *pszFilename)
2808 {
2809     return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2810 }
2811 
2812 
2813 /**
2814  * Checks if pszFilename is exists in the cache.
2815  * @return    TRUE if found. FALSE if not found.
2816  * @param     pszFilename   Name of the file to be found. (with path!)
2817  *                          This is in lower case!
2818  */
filecacheIsDirCached(const char * pszDir)2819 INLINE BOOL filecacheIsDirCached(const char *pszDir)
2820 {
2821     return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2822 }
2823 
2824 
2825 /**
2826  * Checks if a file exist, uses file cache if possible.
2827  * @returns   Pointer to a filename consiting of the path part + the given filename.
2828  *            (pointer into pszBuffer)
2829  *            NULL if file is not found. ("" in buffer)
2830  * @parma     pszFilename  Filename to find.
2831  * @parma     pszBuffer    Ouput Buffer.
2832  * @status    completely implemented.
2833  * @author    knut st. osmundsen
2834  */
filecacheFileExist(const char * pszFilename,char * pszBuffer)2835 char *filecacheFileExist(const char *pszFilename, char *pszBuffer)
2836 {
2837     APIRET          rc;
2838 
2839     *pszBuffer = '\0';
2840 
2841     fileNormalize2(pszFilename, pszBuffer);
2842 
2843     /*
2844      * Search for the file in this directory.
2845      *   Search cache first
2846      */
2847     if (!filecacheFind(pszBuffer))
2848     {
2849         char szDir[CCHMAXPATH];
2850 
2851         filePathSlash(pszBuffer, szDir);
2852         if (!filecacheIsDirCached(szDir))
2853         {
2854             /*
2855              * If caching of entire dirs are enabled, we'll
2856              * add the directory to the cache and search it.
2857              */
2858             if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2859             {
2860                 if (filecacheFind(pszBuffer))
2861                     return pszBuffer;
2862             }
2863             else
2864             {
2865                 FILESTATUS3 fsts3;
2866 
2867                 /* ask the OS */
2868                 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2869                 if (rc == NO_ERROR)
2870                 {   /* add file to cache. */
2871                     filecacheAddFile(pszBuffer);
2872                     return pszBuffer;
2873                 }
2874             }
2875         }
2876     }
2877     else
2878         return pszBuffer;
2879 
2880     return NULL;
2881 }
2882 
2883 
2884 /**
2885  * Finds a filename in a specified pathlist.
2886  * @returns   Pointer to a filename consiting of the path part + the given filename.
2887  *            (pointer into pszBuffer)
2888  *            NULL if file is not found. ("" in buffer)
2889  * @param     pszPathList  Path list to search for filename.
2890  * @parma     pszFilename  Filename to find.
2891  * @parma     pszBuffer    Ouput Buffer.
2892  * @status    completely implemented.
2893  * @author    knut st. osmundsen
2894  */
pathlistFindFile(const char * pszPathList,const char * pszFilename,char * pszBuffer)2895 char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
2896 {
2897     const char *psz = pszPathList;
2898     const char *pszNext = NULL;
2899 
2900     *pszBuffer = '\0';
2901 
2902     if (pszPathList == NULL)
2903         return NULL;
2904 
2905     while (*psz != '\0')
2906     {
2907         /* find end of this path */
2908         pszNext = strchr(psz, ';');
2909         if (pszNext == NULL)
2910             pszNext = psz + strlen(psz);
2911 
2912         if (pszNext - psz > 0)
2913         {
2914             APIRET          rc;
2915 
2916             /* make search statment */
2917             strncpy(pszBuffer, psz, pszNext - psz);
2918             pszBuffer[pszNext - psz] = '\0';
2919             if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2920                 strcpy(&pszBuffer[pszNext - psz], "\\");
2921             strcat(pszBuffer, pszFilename);
2922             fileNormalize(pszBuffer);
2923 
2924             /*
2925              * Search for the file in this directory.
2926              *   Search cache first
2927              */
2928             if (!filecacheFind(pszBuffer))
2929             {
2930                 char szDir[CCHMAXPATH];
2931 
2932                 filePathSlash(pszBuffer, szDir);
2933                 if (!filecacheIsDirCached(szDir))
2934                 {
2935                     /*
2936                      * If caching of entire dirs are enabled, we'll
2937                      * add the directory to the cache and search it.
2938                      */
2939                     if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2940                     {
2941                         if (filecacheFind(pszBuffer))
2942                             return pszBuffer;
2943                     }
2944                     else
2945                     {
2946                         FILESTATUS3 fsts3;
2947 
2948                         /* ask the OS */
2949                         rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2950                         if (rc == NO_ERROR)
2951                         {   /* add file to cache. */
2952                             filecacheAddFile(pszBuffer);
2953                             return pszBuffer;
2954                         }
2955                     }
2956                 }
2957             }
2958             else
2959                 return pszBuffer;
2960         }
2961 
2962         /* next */
2963         if (*pszNext != ';')
2964             break;
2965         psz = pszNext + 1;
2966     }
2967 
2968     return NULL;
2969 }
2970 
2971 
2972 /**
2973  * Checks if the given filename may exist within any of the given paths.
2974  * This check only matches the filename path agianst the paths in the pathlist.
2975  * @returns   TRUE: if exists.
2976  *            FALSE: don't exist.
2977  * @param     pszPathList  Path list to search for filename.
2978  * @parma     pszFilename  Filename to find. The filename should be normalized!
2979  * @status    completely implemented.
2980  * @author    knut st. osmundsen
2981  */
pathlistFindFile2(const char * pszPathList,const char * pszFilename)2982 BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
2983 {
2984     const char *psz = pszPathList;
2985     const char *pszNext = NULL;
2986     char        szBuffer[CCHMAXPATH];
2987     char        szBuffer2[CCHMAXPATH];
2988     char       *pszPathToFind = &szBuffer2[0];
2989 
2990     /*
2991      * Input checking
2992      */
2993     if (pszPathList == NULL)
2994         return FALSE;
2995 
2996     /*
2997      * Normalize the filename and get it's path.
2998      */
2999     filePath(pszFilename, pszPathToFind);
3000 
3001 
3002     /*
3003      * Loop thru the path list.
3004      */
3005     while (*psz != '\0')
3006     {
3007         /* find end of this path */
3008         pszNext = strchr(psz, ';');
3009         if (pszNext == NULL)
3010             pszNext = psz + strlen(psz);
3011 
3012         if (pszNext - psz > 0)
3013         {
3014             char *  pszPath = &szBuffer[0];
3015 
3016             /*
3017              * Extract and normalize the path
3018              */
3019             strncpy(pszPath, psz, pszNext - psz);
3020             pszPath[pszNext - psz] = '\0';
3021             if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
3022                 pszPath[pszNext - psz - 1] = '\0';
3023             fileNormalize(pszPath);
3024 
3025             /*
3026              * Check if it matches the path of the filename
3027              */
3028             if (strcmp(pszPath, pszPathToFind) == 0)
3029                 return TRUE;
3030         }
3031 
3032         /*
3033          * Next part of the path list.
3034          */
3035         if (*pszNext != ';')
3036             break;
3037         psz = pszNext + 1;
3038     }
3039 
3040     return FALSE;
3041 }
3042 
3043 
3044 /**
3045  * Finds the first char after word.
3046  * @returns   Pointer to the first char after word.
3047  * @param     psz  Where to start.
3048  */
findEndOfWord(char * psz)3049 char *findEndOfWord(char *psz)
3050 {
3051 
3052     while (*psz != '\0' &&
3053             (
3054               (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
3055               ||
3056               (*psz >= '0' && *psz <= '9')
3057               ||
3058               *psz == '_'
3059             )
3060           )
3061         ++psz;
3062     return (char *)psz;
3063 }
3064 
3065 #if 0 /* not used */
3066 /**
3067  * Find the starting char of a word
3068  * @returns   Pointer to first char in word.
3069  * @param     psz       Where to start.
3070  * @param     pszStart  Where to stop.
3071  */
3072 char *findStartOfWord(const char *psz, const char *pszStart)
3073 {
3074     const char *pszR = psz;
3075     while (psz >= pszStart &&
3076             (
3077                  (*psz >= 'A' && *psz <= 'Z')
3078               || (*psz >= 'a' && *psz <= 'z')
3079               || (*psz >= '0' && *psz <= '9')
3080               || *psz == '_'
3081              )
3082           )
3083         pszR = psz--;
3084     return (char*)pszR;
3085 }
3086 #endif
3087 
3088 /**
3089  * Find the size of a file.
3090  * @returns   Size of file. -1 on error.
3091  * @param     phFile  File handle.
3092  */
fsize(FILE * phFile)3093 signed long fsize(FILE *phFile)
3094 {
3095     int ipos;
3096     signed long cb;
3097 
3098     if ((ipos = ftell(phFile)) < 0
3099         ||
3100         fseek(phFile, 0, SEEK_END) != 0
3101         ||
3102         (cb = ftell(phFile)) < 0
3103         ||
3104         fseek(phFile, ipos, SEEK_SET) != 0
3105         )
3106         cb = -1;
3107     return cb;
3108 }
3109 
3110 
3111 /**
3112  * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
3113  * @returns   Pointer to first not space or tab char in the string.
3114  * @param     psz   Pointer to the string which is to be trimmed.
3115  * @status    completely implmented.
3116  */
trim(char * psz)3117 INLINE char *trim(char *psz)
3118 {
3119     int i;
3120     if (psz == NULL)
3121         return NULL;
3122     while (*psz == ' ' || *psz == '\t')
3123         psz++;
3124     i = strlen(psz) - 1;
3125     while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3126         i--;
3127     psz[i+1] = '\0';
3128     return psz;
3129 }
3130 
3131 
3132 /**
3133  * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
3134  * @returns   Pointer to the string passed in.
3135  * @param     psz   Pointer to the string which is to be right trimmed.
3136  * @status    completely implmented.
3137  */
trimR(char * psz)3138 INLINE char *trimR(char *psz)
3139 {
3140     int i;
3141     if (psz == NULL)
3142         return NULL;
3143     i = strlen(psz) - 1;
3144     while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3145         i--;
3146     psz[i+1] = '\0';
3147     return psz;
3148 }
3149 
3150 
3151 /**
3152  * Trims any quotes of a possibly quoted string.
3153  * @returns   Pointer to the string passed in.
3154  * @param     psz   Pointer to the string which is to be quote-trimmed.
3155  * @status    completely implmented.
3156  */
trimQuotes(char * psz)3157 INLINE char *trimQuotes(char *psz)
3158 {
3159     int i;
3160     if (psz == NULL)
3161         return NULL;
3162 
3163     if (*psz == '\"' || *psz == '\'')
3164         psz++;
3165     i = strlen(psz) - 1;
3166     if (psz[i] == '\"' || psz[i] == '\'')
3167         psz[i] = '\0';
3168 
3169     return psz;
3170 }
3171 
3172 
3173 /**
3174  * C/C++ preprocess a single line. Assumes that we're not starting
3175  * with at comment.
3176  * @returns Pointer to output buffer.
3177  * @param   pszOut  Ouput (preprocessed) string.
3178  * @param   pszIn   Input string.
3179  */
PreProcessLine(char * pszOut,const char * pszIn)3180 char *PreProcessLine(char *pszOut, const char *pszIn)
3181 {
3182     char *  psz = pszOut;
3183     BOOL    fComment = FALSE;
3184     BOOL    fQuote = FALSE;
3185 
3186     /*
3187      * Loop thru the string.
3188      */
3189     while (*pszIn != '\0')
3190     {
3191         if (fQuote)
3192         {
3193             *psz++ = *pszIn;
3194             if (*pszIn == '\\')
3195             {
3196                 *psz++ = *++pszIn;
3197                 pszIn++;
3198             }
3199             else if (*pszIn++ == '"')
3200                 fQuote = FALSE;
3201         }
3202         else if (fComment)
3203         {
3204             if (*pszIn == '*' && pszIn[1] == '/')
3205             {
3206                 fComment = FALSE;
3207                 pszIn += 2;
3208             }
3209             else
3210                 pszIn++;
3211         }
3212         else
3213         {
3214             if (   (*pszIn == '/' && pszIn[1] == '/')
3215                 ||  *pszIn == '\0')
3216             {   /* End of line. */
3217                 break;
3218             }
3219 
3220             if (*pszIn == '/' && pszIn[1] == '*')
3221             {   /* Start comment */
3222                 fComment = TRUE;
3223                 pszIn += 2;
3224             }
3225             else
3226                 *psz++ = *pszIn++;
3227         }
3228     }
3229 
3230     /*
3231      * Trim right.
3232      */
3233     psz--;
3234     while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
3235         psz--;
3236     psz[1] = '\0';
3237 
3238     return pszOut;
3239 }
3240 
3241 
3242 /**
3243  * Creates a memory buffer for a text file.
3244  * @returns   Pointer to file memoryblock. NULL on error.
3245  * @param     pszFilename  Pointer to filename string.
3246  * @remark    This function is the one using most of the execution
3247  *            time (DosRead + DosOpen) - about 70% of the execution time!
3248  */
textbufferCreate(const char * pszFilename)3249 void *textbufferCreate(const char *pszFilename)
3250 {
3251     void *pvFile = NULL;
3252     FILE *phFile;
3253 
3254     phFile = fopen(pszFilename, "rb");
3255     if (phFile != NULL)
3256     {
3257         signed long cbFile = fsize(phFile);
3258         if (cbFile >= 0)
3259         {
3260             pvFile = malloc(cbFile + 1);
3261             if (pvFile != NULL)
3262             {
3263                 memset(pvFile, 0, cbFile + 1);
3264                 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
3265                 {   /* failed! */
3266                     free(pvFile);
3267                     pvFile = NULL;
3268                 }
3269             }
3270             else
3271                 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
3272         }
3273         fclose(phFile);
3274     }
3275     return pvFile;
3276 }
3277 
3278 
3279 /**
3280  * Destroys a text textbuffer.
3281  * @param     pvBuffer   Buffer handle.
3282  */
textbufferDestroy(void * pvBuffer)3283 void textbufferDestroy(void *pvBuffer)
3284 {
3285     free(pvBuffer);
3286 }
3287 
3288 
3289 /**
3290  * Gets the next line from an textbuffer.
3291  * @returns   Pointer to the next line.
3292  * @param     pvBuffer  Buffer handle.
3293  * @param     psz       Pointer to current line.
3294  *                      NULL is passed in to get the first line.
3295  */
textbufferNextLine(void * pvBuffer,register char * psz)3296 char *textbufferNextLine(void *pvBuffer, register char *psz)
3297 {
3298     register char ch;
3299 
3300     /* if first line psz is NULL. */
3301     if (psz == NULL)
3302         return (char*)pvBuffer;
3303 
3304     /* skip till end of file or end of line. */
3305     ch = *psz;
3306     while (ch != '\0' && ch != '\n' && ch != '\r')
3307         ch = *++psz;
3308 
3309     /* skip line end */
3310     if (ch == '\r')
3311         ch = *++psz;
3312     if (ch == '\n')
3313         psz++;
3314 
3315     return psz;
3316 }
3317 
3318 
3319 /**
3320  * Gets the next line from an textbuffer.
3321  * (fgets for textbuffer)
3322  * @returns   Pointer to pszOutBuffer. NULL when end of file.
3323  * @param     pvBuffer  Buffer handle.
3324  * @param     ppv       Pointer to a buffer index pointer. (holds the current buffer index)
3325  *                      Pointer to a null pointer is passed in to get the first line.
3326  * @param     pszLineBuffer  Output line buffer. (!= NULL)
3327  * @param     cchLineBuffer  Size of the output line buffer. (> 0)
3328  * @remark    '\n' and '\r' are removed!
3329  */
textbufferGetNextLine(void * pvBuffer,void ** ppv,char * pszLineBuffer,int cchLineBuffer)3330 char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
3331 {
3332     char *          pszLine = pszLineBuffer;
3333     char *          psz = *(char**)(void*)ppv;
3334     register char   ch;
3335 
3336     /* first line? */
3337     if (psz == NULL)
3338         psz = pvBuffer;
3339 
3340     /* Copy to end of the line or end of the linebuffer. */
3341     ch = *psz;
3342     cchLineBuffer--; /* reserve space for '\0' */
3343     while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
3344     {
3345         *pszLine++ = ch;
3346         ch = *++psz;
3347     }
3348     *pszLine = '\0';
3349 
3350     /* skip line end */
3351     if (ch == '\r')
3352         ch = *++psz;
3353     if (ch == '\n')
3354         psz++;
3355 
3356     /* check if position has changed - if unchanged it's the end of file! */
3357     if (*ppv == (void*)psz)
3358         pszLineBuffer = NULL;
3359 
3360     /* store current position */
3361     *ppv = (void*)psz;
3362 
3363     return pszLineBuffer;
3364 }
3365 
3366 
3367 /**
3368  * Appends a depend file to the internal file.
3369  * This will update the date in the option struct.
3370  */
depReadFile(const char * pszFilename,BOOL fAppend)3371 BOOL  depReadFile(const char *pszFilename, BOOL fAppend)
3372 {
3373     void *      pvFile;
3374     char *      pszNext;
3375     char *      pszPrev;                /* Previous line, only valid when finding new rule. */
3376     BOOL        fMoreDeps = FALSE;
3377     void *      pvRule = NULL;
3378 
3379 
3380     /* read depend file */
3381     pvFile = textbufferCreate(pszFilename);
3382     if (pvFile == NULL)
3383         return FALSE;
3384 
3385     /* parse the original depend file */
3386     pszPrev = NULL;
3387     pszNext = pvFile;
3388     while (*pszNext != '\0')
3389     {
3390         int   i;
3391         int   cch;
3392         char *psz;
3393 
3394         /* get the next line. */
3395         psz = pszNext;
3396         pszNext = textbufferNextLine(pvFile, pszNext);
3397 
3398         /*
3399          * Process the current line:
3400          *   Start off by terminating the line.
3401          *   Trim the line,
3402          *   Skip empty lines.
3403          *   If not looking for more deps Then
3404          *     Check if new rule starts here.
3405          *   Endif
3406          *
3407          *   If more deps to last rule Then
3408          *     Get dependant name.
3409          *   Endif
3410          */
3411         i = -1;
3412         while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
3413             pszNext[i--] = '\0';
3414         trimR(psz);
3415         cch = strlen(psz);
3416         if (cch == 0)
3417         {
3418             fMoreDeps = FALSE;
3419             continue;
3420         }
3421 
3422         if (*psz == '#')
3423         {
3424             pszPrev = psz;
3425             continue;
3426         }
3427 
3428         /* new rule? */
3429         if (!fMoreDeps)
3430         {
3431             if (*psz != ' ' && *psz != '\t' && *psz != '\0')
3432             {
3433                 i = 0;
3434                 while (psz[i] != '\0')
3435                 {
3436                     if (psz[i] == ':'
3437                         && (psz[i+1] == ' '
3438                             || psz[i+1] == '\t'
3439                             || psz[i+1] == '\0'
3440                             || (psz[i+1] == '\\' && psz[i+2] == '\0')
3441                             )
3442                         )
3443                     {
3444                         char    szTS[TS_SIZE];
3445                         char *  pszCont = strchr(&psz[i], '\\');
3446                         fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
3447 
3448                         /* read evt. timestamp. */
3449                         szTS[0] = '\0';
3450                         if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
3451                             strcpy(szTS, pszPrev + 2);
3452 
3453                         psz[i] = '\0';
3454                         pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS, TRUE);
3455                         if (pvRule)
3456                             ((PDEPRULE)pvRule)->fUpdated = fAppend;
3457                         psz += i + 1;
3458                         cch -= i + 1;
3459                         break;
3460                     }
3461                     i++;
3462                 }
3463             }
3464             pszPrev = NULL;
3465         }
3466 
3467 
3468         /* more dependants */
3469         if (fMoreDeps)
3470         {
3471             if (cch > 0 && psz[cch-1] == '\\')
3472             {
3473                 fMoreDeps = TRUE;
3474                 psz[cch-1] = '\0';
3475             }
3476             else
3477                 fMoreDeps = FALSE;
3478 
3479             /* if not duplicate rule */
3480             if (pvRule != NULL)
3481             {
3482                 psz = trimQuotes(trim(psz));
3483                 if (*psz != '\0')
3484                     depAddDepend(pvRule, psz, options.fCheckCyclic, TRUE);
3485             }
3486         }
3487     } /* while */
3488 
3489 
3490     /* return succesfully */
3491     textbufferDestroy(pvFile);
3492     return TRUE;
3493 }
3494 
3495 /**
3496  *
3497  * @returns   Success indicator.
3498  * @param     pszFilename           Pointer to name of the output file.
3499  * @param     fWriteUpdatedOnly     If set we'll only write updated rules.
3500  */
depWriteFile(const char * pszFilename,BOOL fWriteUpdatedOnly)3501 BOOL  depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
3502 {
3503     FILE *phFile;
3504     phFile = fopen(pszFilename, "w");
3505     if (phFile != NULL)
3506     {
3507         AVLENUMDATA EnumData;
3508         PDEPRULE    pdep;
3509         static char szBuffer[0x10000];
3510         int         iBuffer = 0;
3511         int         cch;
3512 
3513         /*
3514          * Write warning on top of file.
3515          */
3516         fputs("#\n"
3517               "# This file was automatically generated by FastDep.\n"
3518               "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3519               "#\n"
3520               "# THIS FILE SHOULD   N O T   BE EDITED MANUALLY!!!\n"
3521               "#\n"
3522               "# (As this may possibly make it unreadable for fastdep\n"
3523               "#  and ruin the caching methods of FastDep.)\n"
3524               "#\n"
3525               "\n",
3526               phFile);
3527 
3528         /* normal dependency output */
3529         pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3530         while (pdep != NULL)
3531         {
3532             if (!fWriteUpdatedOnly || pdep->fUpdated)
3533             {
3534                 int cchTS = strlen(pdep->szTS);
3535                 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3536 
3537                 /* Write rule. Flush the buffer first if necessary. */
3538                 cch = strlen(pdep->pszRule);
3539                 if (iBuffer + cch*3 + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3540                 {
3541                     fwrite(szBuffer, iBuffer, 1, phFile);
3542                     iBuffer = 0;
3543                 }
3544 
3545                 memcpy(szBuffer + iBuffer, "# ", 2);
3546                 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3547                 iBuffer += cchTS + 2;
3548                 szBuffer[iBuffer++] = '\n';
3549 
3550                 if (fQuoted) szBuffer[iBuffer++] = '"';
3551                 iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, pdep->pszRule);
3552                 if (fQuoted) szBuffer[iBuffer++] = '"';
3553                 strcpy(szBuffer + iBuffer++, ":");
3554 
3555                 /* write rule dependants. */
3556                 if (pdep->papszDep != NULL)
3557                 {
3558                     char **ppsz = pdep->papszDep;
3559                     while (*ppsz != NULL)
3560                     {
3561                         /* flush buffer? */
3562                         fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3563                         cch = strlen(*ppsz);
3564                         if (iBuffer + cch*3 + fQuoted * 2 + 20 >= sizeof(szBuffer))
3565                         {
3566                             fwrite(szBuffer, iBuffer, 1, phFile);
3567                             iBuffer = 0;
3568                         }
3569                         strcpy(szBuffer + iBuffer, " \\\n    ");
3570                         iBuffer += 7;
3571                         if (fQuoted) szBuffer[iBuffer++] = '"';
3572                         iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, *ppsz);
3573                         if (fQuoted) szBuffer[iBuffer++] = '"';
3574 
3575                         /* next dependant */
3576                         ppsz++;
3577                     }
3578                 }
3579 
3580                 /* Add two new lines. Flush buffer first if necessary. */
3581                 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3582                 {
3583                     fwrite(szBuffer, iBuffer, 1, phFile);
3584                     iBuffer = 0;
3585                 }
3586 
3587                 /* add 2 linefeeds */
3588                 strcpy(szBuffer + iBuffer, "\n\n");
3589                 iBuffer += CBNEWLINE*2;
3590             }
3591 
3592             /* next rule */
3593             pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3594         }
3595 
3596 
3597         /* flush buffer. */
3598         fwrite(szBuffer, iBuffer, 1, phFile);
3599 
3600         fclose(phFile);
3601         return TRUE;
3602     }
3603 
3604     return FALSE;
3605 }
3606 
3607 
3608 /**
3609  * Removes all nodes in the tree of dependencies. (pdepTree)
3610  */
depRemoveAll(void)3611 void  depRemoveAll(void)
3612 {
3613     AVLENUMDATA EnumData;
3614     PDEPRULE    pdep;
3615 
3616     pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3617     while (pdep != NULL)
3618     {
3619         /* free this */
3620         if (pdep->papszDep != NULL)
3621         {
3622             char ** ppsz = pdep->papszDep;
3623             while (*ppsz != NULL)
3624                 free(*ppsz++);
3625             free(pdep->papszDep);
3626         }
3627         free(pdep);
3628 
3629         /* next */
3630         pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3631     }
3632     pdepTree = NULL;
3633 }
3634 
3635 
3636 /**
3637  * Adds a rule to the list of dependant rules.
3638  * @returns   Rule handle. NULL if rule exists/error.
3639  * @param     pszRulePath   Pointer to rule text. Empty strings are banned!
3640  *                          This string might only contain the path of the rule. (with '\\')
3641  * @param     pszName       Name of the rule.
3642  *                          NULL if pszRulePath contains the entire rule.
3643  * @param     pszExt        Extention (without '.')
3644  *                          NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3645  * @param     fConvertName  If set we'll convert from makefile name to realname.
3646  */
depAddRule(const char * pszRulePath,const char * pszName,const char * pszExt,const char * pszTS,BOOL fConvertName)3647 void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName)
3648 {
3649     char     szRule[CCHMAXPATH*2];
3650     PDEPRULE pNew;
3651     int      cch;
3652 
3653     /* make rulename */
3654     strcpy(szRule, pszRulePath);
3655     cch = strlen(szRule);
3656     if (pszName != NULL)
3657     {
3658         strcpy(szRule + cch, pszName);
3659         cch += strlen(szRule + cch);
3660     }
3661     if (pszExt != NULL)
3662     {
3663         strcat(szRule + cch++, ".");
3664         strcat(szRule + cch, pszExt);
3665         cch += strlen(szRule + cch);
3666     }
3667     if (fConvertName)
3668         cch = depNameToReal(szRule);
3669 
3670     /*
3671      * Allocate a new rule structure and fill in data
3672      * Note. One block for both the DEPRULE and the pszRule string.
3673      */
3674     pNew = malloc(sizeof(DEPRULE) + cch + 1);
3675     if (pNew == NULL)
3676     {
3677         fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3678         return NULL;
3679     }
3680     pNew->pszRule = (char*)(void*)(pNew + 1);
3681     strcpy(pNew->pszRule, szRule);
3682     pNew->cDeps = 0;
3683     pNew->papszDep = NULL;
3684     pNew->fUpdated = TRUE;
3685     pNew->avlCore.Key = pNew->pszRule;
3686     strcpy(pNew->szTS, pszTS);
3687 
3688     /* Insert the rule */
3689     if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3690     {   /*
3691          * The rule existed.
3692          * If it's allready touched (updated) during this session
3693          *   there is nothing to be done.
3694          * If not force scan and it's newer than depfile-1month then
3695          *   we'll use the information we've got.
3696          * Reuse the node in the tree.
3697          */
3698         PDEPRULE    pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3699         assert(pOld);
3700         free(pNew);
3701         if (pOld->fUpdated)
3702             return NULL;
3703 
3704         pOld->fUpdated = TRUE;
3705         if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3706             return NULL;
3707         strcpy(pOld->szTS, pszTS);
3708 
3709         if (pOld->papszDep)
3710         {
3711             free(pOld->papszDep);
3712             pOld->papszDep = NULL;
3713         }
3714         pOld->cDeps = 0;
3715 
3716         return pOld;
3717     }
3718 
3719     return pNew;
3720 }
3721 
3722 
3723 /**
3724  * Adds a dependant to a rule.
3725  * @returns   Successindicator. TRUE = success.
3726  *            FALSE = cyclic or out of memory.
3727  * @param     pvRule        Rule handle.
3728  * @param     pszDep        Pointer to dependant name
3729  * @param     fCheckCyclic  When set we'll check that we're not creating an cyclic dependency.
3730  * @param     fConvertName  If set we'll convert from makefile name to realname.
3731  */
depAddDepend(void * pvRule,const char * pszDep,BOOL fCheckCyclic,BOOL fConvertName)3732 BOOL  depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName)
3733 {
3734     PDEPRULE    pdep = (PDEPRULE)pvRule;
3735     int         cchDep;
3736 
3737     if (pszDep[0] == '\0')
3738     {
3739         fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3740                 pdep->pszRule);
3741         /* __interrupt(3); */
3742         return FALSE;
3743     }
3744 
3745     if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3746     {
3747         fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3748                 pszDep, pdep->pszRule);
3749         return FALSE;
3750     }
3751 
3752     /* allocate more array space */
3753     if (((pdep->cDeps) % 48) == 0)
3754     {
3755         pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3756         if (pdep->papszDep == NULL)
3757         {
3758             pdep->cDeps = 0;
3759             fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3760             return FALSE;
3761         }
3762     }
3763 
3764     /* allocate string space and copy pszDep */
3765     cchDep = strlen(pszDep) + 1;
3766     if ((pdep->papszDep[pdep->cDeps] = malloc(cchDep)) == NULL)
3767     {
3768         fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3769         return FALSE;
3770     }
3771     strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3772 
3773     /* convert ^# and other stuff */
3774     if (fConvertName)
3775         depNameToReal(pdep->papszDep[pdep->cDeps]);
3776 
3777     /* terminate array and increment dep count */
3778     pdep->papszDep[++pdep->cDeps] = NULL;
3779 
3780     /* successful! */
3781     return TRUE;
3782 }
3783 
3784 
3785 /**
3786  * Converts from makefile filename to real filename.
3787  * @returns New name length.
3788  * @param   pszName     Pointer to the string to make real.
3789  */
depNameToReal(char * pszName)3790 int depNameToReal(char *pszName)
3791 {
3792     int cchNewName = strlen(pszName);
3793     int iDisplacement = 0;
3794 
3795     /*
3796      * Look for '^' and '$$'.
3797      */
3798     while (*pszName)
3799     {
3800         if (    *pszName == '^'
3801             ||  (*pszName == '$' && pszName[1] == '$'))
3802         {
3803             iDisplacement--;
3804             pszName++;
3805             cchNewName--;
3806         }
3807         if (iDisplacement)
3808             pszName[iDisplacement] = *pszName;
3809         pszName++;
3810     }
3811     pszName[iDisplacement] = '\0';
3812 
3813     return cchNewName;
3814 }
3815 
3816 
3817 /**
3818  * Converts from real filename to makefile filename.
3819  * @returns New name length.
3820  * @param   pszName     Output name buffer.
3821  * @param   cchName     Size of name buffer.
3822  * @param   pszSrc      Input name.
3823  */
depNameToMake(char * pszName,int cchName,const char * pszSrc)3824 int   depNameToMake(char *pszName, int cchName, const char *pszSrc)
3825 {
3826     char *pszNameOrg = pszName;
3827 
3828     /*
3829      * Convert real name to makefile name.
3830      */
3831     while (*pszSrc)
3832     {
3833         if (    *pszSrc == '#'
3834             ||  *pszSrc == '!'
3835             ||  (*pszSrc == '$' && pszSrc[1] != '(')
3836             ||  *pszSrc == '@'
3837             ||  *pszSrc == '-'
3838             ||  *pszSrc == '^'
3839            /* ||  *pszSrc == '('
3840             ||  *pszSrc == ')'
3841             ||  *pszSrc == '{'
3842             ||  *pszSrc == '}'*/)
3843         {
3844             if (!cchName--)
3845             {
3846                 fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
3847                 return pszName - pszNameOrg + strlen(pszName);
3848             }
3849             *pszName++ = '^';
3850         }
3851         if (!cchName--)
3852         {
3853             fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
3854             return pszName - pszNameOrg + strlen(pszName);
3855         }
3856         *pszName++ = *pszSrc++;
3857     }
3858     *pszName = '\0';
3859 
3860     return pszName - pszNameOrg;
3861 }
3862 
3863 
3864 
3865 /**
3866  * Marks the file as one which is to be rescanned next time
3867  * since not all dependencies was found...
3868  * @param   pvRule  Rule handle...
3869  */
depMarkNotFound(void * pvRule)3870 void  depMarkNotFound(void *pvRule)
3871 {
3872     ((PDEPRULE)pvRule)->szTS[0] = '\0';
3873 }
3874 
3875 
3876 /**
3877  * Checks if adding this dependent will create a cyclic dependency.
3878  * @returns   TRUE: Cyclic.
3879  *            FALSE: Non-cylic.
3880  * @param     pdepRule  Rule pszDep is to be inserted in.
3881  * @param     pszDep    Depend name.
3882  */
depCheckCyclic(PDEPRULE pdepRule,const char * pszDep)3883 BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3884 {
3885 #define DEPTH_FIRST 1
3886 #ifdef DEPTH_FIRST
3887     #define DEPTH 32
3888 #else
3889     #define DEPTH 128
3890 #endif
3891     #define HISTORY 256
3892     char *  pszRule = pdepRule->pszRule;
3893     char ** appsz[DEPTH];
3894 #if HISTORY
3895     char *  apszHistory[HISTORY];
3896     int     iHistory;
3897     int     j;
3898     int     iStart;
3899     int     iEnd;
3900     int     iCmp;
3901 #endif
3902     PDEPRULE pdep;
3903     int     i;
3904 
3905     /* self check */
3906     if (strcmp(pdepRule->pszRule, pszDep) == 0)
3907         return TRUE;
3908 
3909     /* find rule for the dep. */
3910     if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3911         || pdep->papszDep == NULL)
3912         return FALSE; /* no rule, or no dependents, not cyclic */
3913 
3914     i = 1;
3915     appsz[0] = pdep->papszDep;
3916 #ifdef HISTORY
3917     iHistory = 1;
3918     apszHistory[0] = pdep->pszRule;
3919 #endif
3920     while (i > 0)
3921     {
3922         /* pop off element */
3923         register char **  ppsz = appsz[--i];
3924 
3925         while (*ppsz != NULL)
3926         {
3927             /* check if equal to the main rule */
3928             if (strcmp(pszRule, *ppsz) == 0)
3929                 return TRUE;
3930 
3931             /* push onto stack (ppsz is incremented in this test!) */
3932             if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3933                 && pdep->papszDep != NULL)
3934             {
3935                 if (i >= DEPTH)
3936                 {
3937                     fprintf(stderr, "error: too deep chain (%d). pszRule=%s  pszDep=%s\n",
3938                             i, pszRule, pszDep);
3939                     return FALSE;
3940                 }
3941 #ifdef HISTORY
3942                 /*
3943                  * Check if in history, if so we'll skip it.
3944                  */
3945                 #if 0
3946                 for (j = 0;  j < iHistory; j++)
3947                     if (!strcmp(apszHistory[j], pdep->pszRule))
3948                         break;
3949                 if (j != iHistory)
3950                     continue;           /* found */
3951 
3952                 /*
3953                  * Push into history - might concider make this binary sorted one day.
3954                  */
3955                 if (iHistory < HISTORY)
3956                     apszHistory[iHistory++] = pdep->pszRule;
3957 
3958                 #else
3959 
3960                 /*
3961                  * Check if in history, if so we'll skip it.
3962                  *  (Binary search)
3963                  * ASSUMES: Always something in the history!
3964                  */
3965                 iEnd = iHistory - 1;
3966                 iStart = 0;
3967                 j = iHistory / 2;
3968                 while (    (iCmp = strcmp(pdep->pszRule, apszHistory[j])) != 0
3969                        &&   iEnd != iStart)
3970                 {
3971                     if (iCmp < 0)
3972                         iEnd = j - 1;
3973                     else
3974                         iStart = j + 1;
3975                     if (iStart > iEnd)
3976                         break;
3977                     j = (iStart + iEnd) / 2;
3978                 }
3979 
3980                 if (!iCmp)
3981                     continue;           /* found */
3982 
3983                 /*
3984                  * Push into history - might concider make this binary sorted one day.
3985                  */
3986                 if (iHistory < HISTORY)
3987                 {
3988                     int k;
3989                     if (iCmp > 0)       /* Insert after. */
3990                         j++;
3991                     for (k = iHistory; k > j; k--)
3992                         apszHistory[k] = apszHistory[k - 1];
3993                     apszHistory[j] = pdep->pszRule;
3994                     iHistory++;
3995                 }
3996 
3997                 #endif
3998 
3999 #endif
4000                 /*
4001                  * Push on to the stack.
4002                  */
4003                 #ifdef DEPTH_FIRST
4004                 /* dept first */
4005                 appsz[i++] = ppsz;      /* save current posistion */
4006                 ppsz = pdep->papszDep;  /* process new node */
4007                 #else
4008                 /* complete current node first. */
4009                 appsz[i++] = pdep->papszDep;
4010                 #endif
4011             }
4012         }
4013     }
4014 
4015     return FALSE;
4016 }
4017 
4018 
4019 /**
4020  * Validates that the dependencies for the file exists
4021  * in the given locations. Dependants without path is ignored.
4022  * @returns TRUE if all ok.
4023  *          FALSE if one (or possibly more) dependants are non-existing.
4024  * @param   pdepRule    Pointer to rule we're to validate.
4025  */
depValidate(PDEPRULE pdepRule)4026 BOOL depValidate(PDEPRULE pdepRule)
4027 {
4028     int i;
4029 
4030     for (i = 0; i < pdepRule->cDeps; i++)
4031     {
4032         char *psz = pdepRule->papszDep[i];
4033         if (    !strchr(psz, '$')
4034             &&
4035             (   psz[1] == ':'
4036             ||  strchr(psz, '\\')
4037             ||  strchr(psz, '/')
4038                  )
4039             )
4040         {
4041             /*
4042              * Check existance of the file.
4043              *   Search cache first
4044              */
4045             if (!filecacheFind(psz))
4046             {
4047                 char szDir[CCHMAXPATH];
4048 
4049                 filePathSlash(psz, szDir);
4050                 if (!filecacheIsDirCached(szDir))
4051                 {
4052                     /*
4053                      * If caching of entire dirs are enabled, we'll
4054                      * add the directory to the cache and search it.
4055                      */
4056                     if (options.fCacheSearchDirs && filecacheAddDir(szDir))
4057                     {
4058                         if (!filecacheFind(psz))
4059                             return FALSE;
4060                     }
4061                     else
4062                     {
4063                         FILESTATUS3 fsts3;
4064 
4065                         /* ask the OS */
4066                         if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
4067                             return FALSE;
4068                         /* add file to cache. */
4069                         filecacheAddFile(psz);
4070                     }
4071                 }
4072                 /*
4073                  * Dir was cached, hence the file doesn't exist
4074                  * and the we should rescan the source file.
4075                  */
4076                 else
4077                     return FALSE;
4078             }
4079         }
4080     }
4081 
4082     return TRUE;
4083 }
4084 
4085 
4086 /**
4087  * Make a timestamp from the file data provided thru the
4088  * search API.
4089  * @returns Pointer to pszTS
4090  * @param   pszTS       Pointer to timestamp (output).
4091  * @param   pfindbuf3   Pointer to search result.
4092  */
depMakeTS(char * pszTS,PFILEFINDBUF3 pfindbuf3)4093 INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
4094 {
4095     sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
4096             pfindbuf3->fdateLastWrite.year + 1980,
4097             pfindbuf3->fdateLastWrite.month,
4098             pfindbuf3->fdateLastWrite.day,
4099             pfindbuf3->ftimeLastWrite.hours,
4100             pfindbuf3->ftimeLastWrite.minutes,
4101             pfindbuf3->ftimeLastWrite.twosecs * 2,
4102             (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
4103             (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
4104             pfindbuf3->cbFile);
4105     return pszTS;
4106 }
4107 
4108 
4109 /**
4110  * Adds the src additioanl dependenies to a rule.
4111  * @param   pvRule  Rule to add them to.
4112  * @param   pszz    Pointer to the string of strings of extra dependencies.
4113  */
depAddSrcAddDeps(void * pvRule,const char * pszz)4114 void depAddSrcAddDeps(void *pvRule, const char *pszz)
4115 {
4116     while (*pszz)
4117     {
4118         depAddDepend(pvRule, pszz, FALSE, FALSE);
4119         pszz += strlen(pszz) + 1;
4120     }
4121 }
4122 
4123 
4124 
4125 
4126 
4127 /*
4128  * Testing purpose.
4129  */
4130 
4131 #if !defined(OS2FAKE)
4132 #include <os2.h>
4133 #endif
4134 #ifdef OLEMANN
4135 #include "olemann.h"
4136 #endif
4137