source: trunk/tools/fastdep/fastdep.c@ 3129

Last change on this file since 3129 was 3129, checked in by bird, 25 years ago

Speed optimizations. Using an AVL tree to cache the files which we have found,
we'll check this tree before we issue a DosQueryPathInfo. The function
pathlistFindFile is still the slowest function...

File size: 64.0 KB
Line 
1/* $Id: fastdep.c,v 1.9 2000-03-16 21:10:11 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2000 knut st. osmundsen
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSERRORS
15#define INCL_FILEMGR
16
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <os2.h>
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25
26#include "avl.h"
27
28#ifndef INLINE
29# if defined(__IBMC__)
30# define INLINE _Inline
31# elif defined(__IBMCPP__)
32# define INLINE inline
33# else
34# error "unknown compiler - inline keyword unknown!"
35# endif
36#endif
37
38/*
39 * This following section is used while testing fastdep.
40 * stdio.h should be included; string.h never included.
41 */
42/*
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46*/
47
48#if 1
49#include <stdio.h>
50#else
51#include <string.h>
52#include <string.h>
53#endif
54
55/*
56 */ /* */ /*
57#include <string.h>
58 */
59#if 1
60# if 1
61 #if 0
62# include <string.h>
63 #else
64# if 1
65 #if 1
66 #if 0
67# include <string.h>
68 #else /* */ /*
69*/
70 # include <stdio.h>
71 #endif
72 #endif
73 #endif
74 #endif
75 #endif
76#endif
77
78/*******************************************************************************
79* Structures and Typedefs *
80*******************************************************************************/
81typedef struct _Options
82{
83 const char * pszInclude;
84 const char * pszExclude;
85 BOOL fExcludeAll;
86 const char * pszObjectExt;
87 const char * pszObjectDir;
88 BOOL fObjectDir; /* replace object directory? */
89 const char * pszRsrcExt;
90 BOOL fObjRule;
91 BOOL fNoObjectPath;
92 BOOL fSrcWhenObj;
93 BOOL fAppend; /* append to the output file, not overwrite it. */
94} OPTIONS, *POPTIONS;
95
96
97/*
98 * Language specific analysis functions type.
99 */
100typedef int ( _FNLANG) (const char *pszFilename, void *pvFile,
101 BOOL fHeader, POPTIONS pOptions);
102typedef _FNLANG *PFNLANG;
103
104
105/**
106 * This struct holds the static configuration of the util.
107 */
108typedef struct _ConfigEntry
109{
110 const char **papszExts; /* Pointer to an array of pointer to extentions for this handler. */
111 /* If NULL this is the last entry. */
112 int iFirstHdr; /* Index into the papszExts array of the first headerfile/copybook. */
113 /* Set it to the NULL element of the array if no headers for this extention. */
114 /* A non-header file may get a object rule. */
115 PFNLANG pfn; /* Pointer to handler function. */
116} CONFIGENTRY, *PCONFIGENTRY;
117
118
119/**
120 * Dependant Rule
121 */
122typedef struct _DepRule
123{
124 char * pszRule; /* Pointer to rule name */
125 int cDeps; /* Entries in the dependant array. */
126 char ** papszDep; /* Pointer to an array of pointers to dependants. */
127 struct _DepRule *pNext; /* Pointer to the next rule */
128} DEPRULE, *PDEPRULE;
129
130
131/**
132 * Filename cache entry.
133 */
134#define FCACHEENTRY AVLNODECORE
135#define PFCACHEENTRY PAVLNODECORE
136
137
138/*******************************************************************************
139* Internal Functions *
140*******************************************************************************/
141static void syntax(void);
142static int makeDependent(const char *pszFilename, POPTIONS pOptions);
143
144int langC_CPP(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
145int langAsm(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
146int langRC(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
147int langCOBOL(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
148
149
150/* string operations */
151static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
152
153/* file operations */
154char *filePath(const char *pszFilename, char *pszBuffer);
155char *filePathSlash(const char *pszFilename, char *pszBuffer);
156char *fileName(const char *pszFilename, char *pszBuffer);
157char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
158char *fileExt(const char *pszFilename, char *pszBuffer);
159
160/* filecache operations */
161static BOOL filecacheAdd(const char *pszFilename);
162static BOOL filecacheFind(const char *pszFilename);
163
164/* pathlist operations */
165static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer);
166
167/* word operations */
168static char *findEndOfWord(char *psz);
169#if 0 /* not used */
170static char *findStartOfWord(char *psz, const char *pszStart);
171#endif
172
173/* file helpers */
174static signed long fsize(FILE *phFile);
175
176/* text helpers */
177INLINE char *trim(char *psz);
178INLINE char *trimR(char *psz);
179
180/* textbuffer */
181static void *textbufferCreate(const char *pszFilename);
182static void textbufferDestroy(void *pvBuffer);
183static char *textbufferNextLine(void *pvBuffer, char *psz);
184static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
185
186/* depend workers */
187static BOOL depReadFile(const char *pszFilename);
188static BOOL depWriteFile(const char *pszFilename);
189static void depRemoveAll(void);
190static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt);
191static BOOL depAddDepend(void *pvRule, const char *pszDep);
192#if 0 /* not used */
193static BOOL depCleanFile(const char *pszFilename);
194#endif
195
196
197/*******************************************************************************
198* Global Variables *
199*******************************************************************************/
200/*
201 * Pointer to the list of dependencies.
202 */
203static PDEPRULE pdepList = NULL;
204
205/*
206 * Filecache - tree starts here.
207 */
208static PFCACHEENTRY pfcTree = NULL;
209static unsigned cfcNodes = 0;
210
211
212/*
213 * Configuration stuff.
214 */
215static const char pszDefaultDepFile[] = ".depend";
216static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
217static const char *apszExtAsm[] = {"asm", "inc", NULL};
218static const char *apszExtRC[] = {"rc", "dlg", NULL};
219static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", NULL};
220static CONFIGENTRY aConfig[] =
221{
222 {
223 apszExtC_CPP,
224 3,
225 langC_CPP,
226 },
227
228 {
229 apszExtAsm,
230 1,
231 langAsm,
232 },
233
234 {
235 apszExtRC,
236 1,
237 langRC,
238 },
239
240 {
241 apszExtCOBOL,
242 3,
243 langCOBOL,
244 },
245
246 /* terminating entry */
247 {
248 NULL,
249 -1,
250 NULL
251 }
252};
253
254
255/**
256 * Main function.
257 * @returns 0 on success.
258 * -n count of failiures.
259 * @param
260 * @param
261 * @equiv
262 * @precond
263 * @methdesc
264 * @result
265 * @time
266 * @sketch
267 * @algo
268 * @remark
269 */
270int main(int argc, char **argv)
271{
272 int rc = 0;
273 int argi = 1;
274 const char *pszDepFile = pszDefaultDepFile;
275
276 static char szObjectDir[CCHMAXPATH];
277 static char szObjectExt[64] = "obj";
278 static char szRsrcExt[64] = "res";
279 static char szInclude[32768] = ";";
280 static char szExclude[32768] = ";";
281
282 OPTIONS options =
283 {
284 szInclude, /* pszInclude */
285 szExclude, /* pszExclude */
286 FALSE, /* fExcludeAll */
287 szObjectExt, /* pszObjectExt */
288 szObjectDir, /* pszObjectDir */
289 FALSE, /* fObjectDir */
290 szRsrcExt, /* pszRsrcExt */
291 TRUE, /* fObjRule */
292 FALSE, /* fNoObjectPath */
293 TRUE, /* fSrcWhenObj */
294 FALSE /* fAppend */
295 };
296
297 szObjectDir[0] = '\0';
298
299 if (argc == 1)
300 {
301 syntax();
302 return -87;
303 }
304
305 while (argi < argc)
306 {
307 if (argv[argi][0] == '-' || argv[argi][0] == '/')
308 {
309 /* parameters */
310 switch (argv[argi][1])
311 {
312 case 'A':
313 case 'a': /* Append to the output file */
314 options.fAppend = argv[argi][2] != '-';
315 break;
316
317 case 'D':
318 case 'd': /* "-d <filename>" */
319 {
320 const char *pszOld = pszDepFile;
321 if (argv[argi][2] != '\0')
322 pszDepFile = &argv[argi][2];
323 else
324 {
325 if (argi + 1 < argc)
326 pszDepFile = argv[++argi];
327 else
328 {
329 fprintf(stderr, "invalid parameter -d, filename missing!\n");
330 return -1;
331 }
332 }
333
334 /* if dependencies are generated we'll flush them to the old filename */
335 if (pdepList != NULL && pszOld != pszDepFile)
336 {
337 if (!depWriteFile(pszOld))
338 fprintf(stderr, "error: failed to write (flush) dependencies.\n");
339 depRemoveAll();
340 }
341 break;
342 }
343
344 case 'E': /* list of paths. If a file is found in one of these directories the */
345 case 'e': /* filename will be used without the directory path. */
346 /* Eall<[+]|-> ? */
347 if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
348 {
349 options.fExcludeAll = argv[argi][5] != '-';
350 break;
351 }
352 /* path or path list */
353 if (strlen(argv[argi]) > 2)
354 strcat(szExclude, &argv[argi][2]);
355 else
356 {
357 strcat(szExclude, argv[argi+1]);
358 argi++;
359 }
360 if (szExclude[strlen(szExclude)-1] != ';')
361 strcat(szExclude, ";");
362 break;
363
364 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
365 case 'i':
366 if (strlen(argv[argi]) > 2)
367 strcat(szInclude, &argv[argi][2]);
368 else
369 {
370 strcat(szInclude, argv[argi+1]);
371 argi++;
372 }
373 if (szInclude[strlen(szInclude)-1] != ';')
374 strcat(szInclude, ";");
375 break;
376
377 case 'n': /* no object path , -N<[+]|-> */
378 case 'N':
379 if (strlen(argv[argi]) <= 1+1+1)
380 options.fNoObjectPath = argv[argi][2] != '-';
381 else
382 {
383 fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
384 return -1;
385 }
386 break;
387
388 case 'o': /* object base directory, Obj or Obr<[+]|-> */
389 case 'O':
390 if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
391 {
392 options.fObjRule = argv[argi][4] != '-';
393 break;
394 }
395
396 if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
397 {
398 if (strlen(argv[argi]) > 4)
399 strcpy(szObjectExt, argv[argi]+4);
400 else
401 {
402 strcpy(szObjectExt, argv[argi+1]);
403 argi++;
404 }
405 break;
406 }
407
408 /* path: -o or -o- */
409 options.fObjectDir = TRUE;
410 if (strlen(argv[argi]) > 2)
411 {
412 if (argv[argi][2] == '-') /* no object path */
413 szObjectDir[0] = '\0';
414 else
415 strcpy(szObjectDir, argv[argi]+2);
416 }
417 else
418 {
419 strcpy(szObjectDir, argv[argi+1]);
420 argi++;
421 }
422 if (szObjectDir[0] != '\0'
423 && szObjectDir[strlen(szObjectDir)-1] != '\\'
424 && szObjectDir[strlen(szObjectDir)-1] != '/'
425 )
426 strcat(szObjectDir, "\\");
427 break;
428
429 case 'r':
430 case 'R':
431 if (strlen(argv[argi]) > 2)
432 strcpy(szObjectExt, argv[argi]+2);
433 else
434 {
435 strcpy(szObjectExt, argv[argi+1]);
436 argi++;
437 }
438 break;
439
440 case 'h':
441 case 'H':
442 case '?':
443 syntax();
444 return 1;
445
446 default:
447 fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
448 return -1;
449 }
450
451 }
452 else
453 { /* not a parameter! */
454 ULONG ulRc;
455 FILEFINDBUF3 filebuf;
456 HDIR hDir = HDIR_CREATE;
457 ULONG ulFound = 1;
458
459 memset(&filebuf, 0, sizeof(filebuf));
460
461 /*
462 * If append option is specified we'll have to read the existing dep file
463 * before starting adding new dependencies.
464 */
465 if (pdepList == NULL && options.fAppend)
466 depReadFile(pszDepFile);
467
468 /*
469 * Search for the files specified.
470 */
471 ulRc = DosFindFirst(argv[argi], &hDir,
472 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
473 &filebuf, sizeof(FILEFINDBUF3), &ulFound, FIL_STANDARD);
474 while (ulRc == NO_ERROR)
475 {
476 char *psz;
477 char szSource[CCHMAXPATH];
478
479 /*
480 * Make full path.
481 */
482 if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')))
483 {
484 strncpy(szSource, argv[argi], psz - argv[argi] + 1);
485 szSource[psz - argv[argi] + 1] = '\0';
486 }
487 else
488 szSource[0] = '\0';
489 strcat(szSource, filebuf.achName);
490
491 /*
492 * Analyse the file.
493 */
494 rc -= makeDependent(&szSource[0], &options);
495
496 /* next file */
497 ulRc = DosFindNext(hDir, &filebuf, sizeof(filebuf), &ulFound);
498 }
499 DosFindClose(hDir);
500 }
501 /* next */
502 argi++;
503 }
504
505 /* Write the depend file! */
506 if (!depWriteFile(pszDepFile))
507 fprintf(stderr, "error: failed to write dependencies file!\n");
508 #if 0
509 printf("cfcNodes=%d\n", cfcNodes);
510 #endif
511
512 return rc;
513}
514
515
516/**
517 * Displays the syntax description for this util.
518 * @status completely implemented.
519 * @author knut st. osmundsen
520 */
521static void syntax(void)
522{
523 printf(
524 "FastDep v0.1\n"
525 "Quick and dirty dependant scanner. Creates a makefile readable depend file.\n"
526 "\n"
527 "Syntax: FastDep [-a<[+]|->] [-d <outputfn>] [-e <excludepath>] [-eall<[+]|->]\n"
528 " [-i <include>] [-n<[+]|->] [-o <objdir>] [-obr<[+]|->] <files>\n"
529 "\n"
530 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
531 " -d <outputfn> Output filename. Default: %s\n"
532 " -e excludepath Exclude paths. If a filename is found in any\n"
533 " of these paths only the filename is used, not\n"
534 " the path+filename (which is default) (don't work?).\n"
535 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
536 " -eall+: No path are added to the filename.\n"
537 " -eall-: The filename is appended the include path\n"
538 " was found in.\n"
539 " Default: eall-\n"
540 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
541 " -n<[+]|-> No path for object files in the rules.\n"
542 " -o <objdir> Path were object files are placed. This path replaces the\n"
543 " entire filename path\n"
544 " -o- No object path\n"
545 " -obr<[+]|-> -obr+: Object rule.\n"
546 " -obr-: No object rule, rule for source filename is generated.\n"
547 " -obj[ ]<objext> Object extention. Default: obj\n"
548 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
549 " <files> Files to scan. Wildchars are allowed.\n"
550 "\n",
551 pszDefaultDepFile
552 );
553}
554
555
556/**
557 * Generates depend info on this file, these are stored internally
558 * and written to file later.
559 * @returns
560 * @param pszFilename Pointer to source filename.
561 * @param pOptions Pointer to options struct.
562 * @status completely implemented.
563 * @author knut st. osmundsen
564 */
565static int makeDependent(const char *pszFilename, POPTIONS pOptions)
566{
567 int rc = -1;
568 void * pvFile;
569
570 pvFile = textbufferCreate(pszFilename);
571 if (pvFile != NULL)
572 {
573 char szExt[CCHMAXPATH];
574 PCONFIGENTRY pCfg = &aConfig[0];
575 BOOL fHeader;
576
577 /*
578 * Find which filetype this is...
579 */
580 fileExt(pszFilename, szExt);
581 while (pCfg->papszExts != NULL)
582 {
583 const char **ppsz = pCfg->papszExts;
584 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
585 ppsz++;
586 if (*ppsz != NULL)
587 {
588 fHeader = &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
589 break;
590 }
591 pCfg++;
592 }
593
594 /* Found? */
595 if (pCfg->papszExts != NULL)
596 rc = (*pCfg->pfn)(pszFilename, pvFile, fHeader, pOptions);
597 else
598 {
599 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
600 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
601 rc = 0;
602 }
603
604 textbufferDestroy(pvFile);
605 }
606 else
607 fprintf(stderr, "failed to open '%s'\n", pszFilename);
608
609 return rc;
610}
611
612
613/**
614 * Generates depend info on this C or C++ file, these are stored internally
615 * and written to file later.
616 * @returns 0 on success.
617 * !0 on error.
618 * @param pszFilename Pointer to source filename.
619 * @param pvFile Pointer to file textbuffer.
620 * @param pOptions Pointer to options struct.
621 * @status completely implemented.
622 * @author knut st. osmundsen
623 */
624int langC_CPP(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
625{
626 void * pvRule; /* Handle to the current rule. */
627 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
628 int iLine; /* Linenumber. */
629 void * pv = NULL; /* An index used by textbufferGetNextLine. */
630 BOOL fComment; /* TRUE when within a multiline comment. */
631 /* FALSE when not within a multiline comment. */
632 int iIfStack; /* StackPointer. */
633 struct IfStackEntry
634 {
635 int fIncluded : 1; /* TRUE: include this code;
636 * FALSE: excluded */
637 int fIf : 1; /* TRUE: #if part of the expression.
638 * FALSE: #else part of the expression. */
639 int fSupported : 1; /* TRUE: supported if/else statement
640 * FALSE: unsupported all else[<something>] are ignored
641 * All code is included.
642 */
643 } achIfStack[256];
644
645
646 /**********************************/
647 /* Add the depend rule */
648 /**********************************/
649 if (pOptions->fObjRule && !fHeader)
650 {
651 if (pOptions->fNoObjectPath)
652 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
653 else
654 pvRule = depAddRule(pOptions->fObjectDir ?
655 pOptions->pszObjectDir :
656 filePathSlash(pszFilename, szBuffer),
657 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
658 pOptions->pszObjectExt);
659
660 if (pOptions->fSrcWhenObj && pvRule)
661 depAddDepend(pvRule, pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename);
662 }
663 else
664 pvRule = depAddRule(pszFilename, NULL, NULL);
665
666 /* duplicate rule? */
667 if (pvRule == NULL)
668 return 0;
669
670
671 /*******************/
672 /* find dependants */
673 /*******************/
674 /* Initiate the IF-stack, comment state and line number. */
675 iIfStack = 0;
676 achIfStack[iIfStack].fIf = TRUE;
677 achIfStack[iIfStack].fIncluded = TRUE;
678 achIfStack[iIfStack].fSupported = TRUE;
679 fComment = FALSE;
680 iLine = 0;
681 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
682 {
683 /* search for #include */
684 register char *pszC;
685 int cbLen;
686 int i = 0;
687 iLine++;
688
689 /* skip blank chars */
690 cbLen = strlen(szBuffer);
691 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
692 i++;
693
694 /* preprocessor statement? */
695 if (!fComment && szBuffer[i] == '#')
696 {
697 /*
698 * Preprocessor checks
699 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
700 * Depending on the word afterwards we'll take some different actions.
701 * So we'll start of by extracting that word and make a string swich on it.
702 * Note that there might be some blanks between the hash and the word.
703 */
704 int cchWord;
705 char * pszEndWord;
706 char * pszArgument;
707 i++; /* skip hash ('#') */
708 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
709 i++;
710 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
711 cchWord = pszEndWord - &szBuffer[i];
712
713 /*
714 * Find the argument by skipping the blanks.
715 */
716 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
717 pszArgument++;
718
719 /*
720 * string switch.
721 */
722 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
723 {
724 /*
725 * #include
726 *
727 * Are we in a state where this file is to be included?
728 */
729 if (achIfStack[iIfStack].fIncluded)
730 {
731 char szFullname[CCHMAXPATH];
732 char *psz;
733 BOOL f = FALSE;
734 int j;
735
736 /* extract info between "" or <> */
737 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
738 i++;
739 i++; /* skip '"' or '<' */
740
741 /* if invalid statement then continue with the next line! */
742 if (!f) continue;
743
744 /* find end */
745 j = f = 0;
746 while (i + j < cbLen && j < CCHMAXPATH &&
747 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
748 j++;
749
750 /* if invalid statement then continue with the next line! */
751 if (!f) continue;
752
753 /* copy filename */
754 strncpy(szFullname, &szBuffer[i], j);
755 szFullname[j] = '\0'; /* ensure terminatition. */
756
757 /* find include file! */
758 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
759 if (psz == NULL)
760 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
761
762 /* did we find the include? */
763 if (psz != NULL)
764 {
765 char szBuffer2[CCHMAXPATH];
766 if (pOptions->fExcludeAll ||
767 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
768 )
769 depAddDepend(pvRule, szFullname);
770 else
771 depAddDepend(pvRule, szBuffer);
772 }
773 else
774 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
775 pszFilename, iLine, szFullname);
776 }
777 }
778 else
779 /*
780 * #if
781 */
782 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
783 { /* #if 0 and #if <1-9> are supported */
784 pszEndWord = findEndOfWord(pszArgument);
785 iIfStack++;
786 if ((pszEndWord - pszArgument) == 1
787 && *pszArgument >= '0' && *pszArgument <= '9')
788 {
789 if (*pszArgument != '0')
790 achIfStack[iIfStack].fIncluded = TRUE;
791 else
792 achIfStack[iIfStack].fIncluded = FALSE;
793 }
794 else
795 achIfStack[iIfStack].fSupported = FALSE;
796 achIfStack[iIfStack].fIncluded = TRUE;
797 achIfStack[iIfStack].fIf = TRUE;
798 }
799 else
800 /*
801 * #else
802 */
803 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
804 {
805 if (achIfStack[iIfStack].fSupported)
806 {
807 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
808 achIfStack[iIfStack].fIncluded = FALSE;
809 else
810 achIfStack[iIfStack].fIncluded = TRUE;
811 }
812 achIfStack[iIfStack].fIf = FALSE;
813 }
814 else
815 /*
816 * #endif
817 */
818 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
819 { /* Pop the if-stack. */
820 if (iIfStack > 0)
821 iIfStack--;
822 else
823 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
824 }
825 /*
826 * general if<something> and elseif<something> implementations
827 */
828 else
829 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
830 {
831 achIfStack[iIfStack].fSupported = FALSE;
832 achIfStack[iIfStack].fIncluded = TRUE;
833 }
834 else
835 if (strncmp(&szBuffer[i], "if", 2) == 0)
836 {
837 iIfStack++;
838 achIfStack[iIfStack].fIf = TRUE;
839 achIfStack[iIfStack].fSupported = FALSE;
840 achIfStack[iIfStack].fIncluded = TRUE;
841 }
842 /* The rest of them aren't implemented yet.
843 else if (strncmp(&szBuffer[i], "if") == 0)
844 {
845 }
846 */
847 }
848
849 /*
850 * Comment checks.
851 * -Start at first non-blank.
852 * -Loop thru the line since we might have more than one
853 * comment statement on a single line.
854 */
855 pszC = &szBuffer[i];
856 while (pszC != NULL && *pszC != '\0')
857 {
858 if (fComment)
859 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
860 else
861 {
862 char *pszLC;
863 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
864 pszC = strstr(pszC, "/*"); /* look for start comment mark */
865 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
866 break; /* muliline comment mark we'll ignore the multiline mark. */
867 }
868
869 /* Comment mark found? */
870 if (pszC != NULL)
871 {
872 fComment = !fComment;
873 pszC += 2; /* skip comment mark */
874
875 /* debug */
876 /*
877 if (fComment)
878 fprintf(stderr, "starts at line %d\n", iLine);
879 else
880 fprintf(stderr, "ends at line %d\n", iLine);
881 */
882 }
883 }
884 } /*while*/
885
886 return 0;
887}
888
889
890/**
891 * Generates depend info on this file, these are stored internally
892 * and written to file later.
893 * @returns 0 on success.
894 * !0 on error.
895 * @param pszFilename Pointer to source filename.
896 * @param pvFile Pointer to file textbuffer.
897 * @param pOptions Pointer to options struct.
898 * @status completely implemented.
899 * @author knut st. osmundsen
900 */
901int langAsm(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
902{
903 void * pvRule; /* Handle to the current rule. */
904 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
905 int iLine; /* current line number */
906 void * pv = NULL; /* An index used by textbufferGetNextLine. */
907
908
909 /**********************************/
910 /* Add the depend rule */
911 /**********************************/
912 if (pOptions->fObjRule && !fHeader)
913 {
914 if (pOptions->fNoObjectPath)
915 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
916 else
917 pvRule = depAddRule(pOptions->fObjectDir ?
918 pOptions->pszObjectDir :
919 filePathSlash(pszFilename, szBuffer),
920 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
921 pOptions->pszObjectExt);
922
923 if (pOptions->fSrcWhenObj && pvRule)
924 depAddDepend(pvRule, pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename);
925 }
926 else
927 pvRule = depAddRule(pszFilename, NULL, NULL);
928
929 /* duplicate rule? */
930 if (pvRule == NULL)
931 return 0;
932
933
934 /*******************/
935 /* find dependants */
936 /*******************/
937 iLine = 0;
938 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
939 {
940 /* search for include */
941 int cbLen;
942 int i = 0;
943 iLine++;
944
945 /* skip blank chars */
946 cbLen = strlen(szBuffer);
947 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
948 i++;
949
950 /* is this an include? */
951 if (strnicmp(&szBuffer[i], "include", 7) == 0
952 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
953 )
954 {
955 char szFullname[CCHMAXPATH];
956 char *psz;
957 int j;
958
959 /* skip to first no blank char */
960 i += 7;
961 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
962 i++;
963
964 /* comment check - if comment found, no filename was given. continue. */
965 if (szBuffer[i] == ';') continue;
966
967 /* find end */
968 j = 0;
969 while (i + j < cbLen
970 && j < CCHMAXPATH
971 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
972 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
973 )
974 j++;
975
976 /* copy filename */
977 strncpy(szFullname, &szBuffer[i], j);
978 szFullname[j] = '\0'; /* ensure terminatition. */
979
980 /* find include file! */
981 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
982 if (psz == NULL)
983 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
984
985 /* Did we find the include? */
986 if (psz != NULL)
987 {
988 char szBuffer2[CCHMAXPATH];
989 if (pOptions->fExcludeAll ||
990 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
991 )
992 depAddDepend(pvRule, szFullname);
993 else
994 depAddDepend(pvRule, szBuffer);
995 }
996 else
997 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
998 pszFilename, iLine, szFullname);
999 }
1000 } /*while*/
1001
1002 return 0;
1003}
1004
1005
1006/**
1007 * Generates depend info on this Resource file, these are stored internally
1008 * and written to file later.
1009 * @returns 0 on success.
1010 * !0 on error.
1011 * @param pszFilename Pointer to source filename.
1012 * @param pvFile Pointer to file textbuffer.
1013 * @param pOptions Pointer to options struct.
1014 * @status completely implemented.
1015 * @author knut st. osmundsen
1016 */
1017int langRC(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1018{
1019 void * pvRule; /* Handle to the current rule. */
1020 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1021 int iLine; /* current line number */
1022 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1023
1024
1025 /**********************************/
1026 /* Add the depend rule */
1027 /**********************************/
1028 if (pOptions->fObjRule && !fHeader)
1029 {
1030 if (pOptions->fNoObjectPath)
1031 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszRsrcExt);
1032 else
1033 pvRule = depAddRule(pOptions->fObjectDir ?
1034 pOptions->pszObjectDir :
1035 filePathSlash(pszFilename, szBuffer),
1036 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1037 pOptions->pszRsrcExt);
1038
1039 if (pOptions->fSrcWhenObj && pvRule)
1040 depAddDepend(pvRule, pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename);
1041 }
1042 else
1043 pvRule = depAddRule(pszFilename, NULL, NULL);
1044
1045 /* duplicate rule? */
1046 if (pvRule == NULL)
1047 return 0;
1048
1049
1050 /*******************/
1051 /* find dependants */
1052 /*******************/
1053 iLine = 0;
1054 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1055 {
1056 /* search for #include */
1057 int cbLen;
1058 int i = 0;
1059 iLine++;
1060
1061 /* skip blank chars */
1062 cbLen = strlen(szBuffer);
1063 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1064 i++;
1065
1066 /* is this an include? */
1067 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1068 || strncmp(&szBuffer[i], "RCINCLUDE", 9) == 0
1069 || strncmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1070 )
1071 {
1072 char szFullname[CCHMAXPATH];
1073 char *psz;
1074 BOOL f = FALSE;
1075 int j;
1076
1077 /* extract info between "" or <> */
1078 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1079 i++;
1080 i++; /* skip '"' or '<' */
1081
1082 /* if invalid statement then continue with the next line! */
1083 if (!f) continue;
1084
1085 /* find end */
1086 j = f = 0;
1087 while (i + j < cbLen && j < CCHMAXPATH &&
1088 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1089 j++;
1090
1091 /* if invalid statement then continue with the next line! */
1092 if (!f) continue;
1093
1094 /* copy filename */
1095 strncpy(szFullname, &szBuffer[i], j);
1096 szFullname[j] = '\0'; /* ensure terminatition. */
1097
1098 /* find include file! */
1099 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
1100 if (psz == NULL)
1101 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
1102
1103 /* did we find the include? */
1104 if (psz != NULL)
1105 {
1106 char szBuffer2[CCHMAXPATH];
1107 if (pOptions->fExcludeAll ||
1108 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
1109 )
1110 depAddDepend(pvRule, szFullname);
1111 else
1112 depAddDepend(pvRule, szBuffer);
1113 }
1114 else
1115 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1116 pszFilename, iLine, szFullname);
1117 }
1118 } /*while*/
1119
1120 return 0;
1121}
1122
1123
1124/**
1125 * Generates depend info on this COBOL file, these are stored internally
1126 * and written to file later.
1127 * @returns 0 on success.
1128 * !0 on error.
1129 * @param pszFilename Pointer to source filename.
1130 * @param pvFile Pointer to file textbuffer.
1131 * @param pOptions Pointer to options struct.
1132 * @status completely implemented.
1133 * @author knut st. osmundsen
1134 */
1135int langCOBOL(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1136{
1137 void * pvRule; /* Handle to the current rule. */
1138 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1139 int iLine; /* current line number */
1140 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1141
1142
1143 /**********************************/
1144 /* Add the depend rule */
1145 /**********************************/
1146 if (pOptions->fObjRule && !fHeader)
1147 {
1148 if (pOptions->fNoObjectPath)
1149 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1150 else
1151 pvRule = depAddRule(pOptions->fObjectDir ?
1152 pOptions->pszObjectDir :
1153 filePathSlash(pszFilename, szBuffer),
1154 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1155 pOptions->pszObjectExt);
1156
1157 if (pOptions->fSrcWhenObj && pvRule)
1158 depAddDepend(pvRule, pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename);
1159 }
1160 else
1161 pvRule = depAddRule(pszFilename, NULL, NULL);
1162
1163 /* duplicate rule? */
1164 if (pvRule == NULL)
1165 return 0;
1166
1167
1168 /*******************/
1169 /* find dependants */
1170 /*******************/
1171 iLine = 0;
1172 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1173 {
1174 /* search for #include */
1175 int cbLen;
1176 int i = 0;
1177 int i1, i2;
1178 iLine++;
1179
1180 /* check for comment mark (column 7) */
1181 if (szBuffer[6] == '*')
1182 continue;
1183
1184 /* skip blank chars */
1185 cbLen = strlen(szBuffer);
1186 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1187 i++;
1188
1189 /* is this an include? */
1190 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
1191 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
1192 )
1193 {
1194 char szFullname[CCHMAXPATH];
1195 char *psz;
1196 int j;
1197
1198 /* skip statement */
1199 i += 4;
1200 if (i1 != 0)
1201 {
1202 int y = 2; /* skip two words */
1203 do
1204 {
1205 /* skip blanks */
1206 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1207 i++;
1208 /* skip word */
1209 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
1210 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
1211 i++;
1212 y--;
1213 } while (y > 0);
1214 }
1215
1216 /* check for blank */
1217 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
1218 continue;
1219
1220 /* skip blanks */
1221 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1222 i++;
1223
1224 /* if invalid statement then continue with the next line! */
1225 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
1226 continue;
1227
1228 /* find end */
1229 j = 0;
1230 while (i + j < cbLen && j < CCHMAXPATH
1231 && szBuffer[i+j] != '.'
1232 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1233 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
1234 )
1235 j++;
1236
1237 /* if invalid statement then continue with the next line! */
1238 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
1239 continue;
1240
1241 /* copy filename */
1242 strncpy(szFullname, &szBuffer[i], j);
1243 szFullname[j] = '\0'; /* ensure terminatition. */
1244
1245 /* add extention .cpy - hardcoded for the moment. */
1246 strcat(szFullname, ".cpy");
1247
1248 /* find include file! */
1249 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
1250
1251 /* did we find the include? */
1252 if (psz != NULL)
1253 {
1254 char szBuffer2[CCHMAXPATH];
1255 if (pOptions->fExcludeAll ||
1256 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
1257 )
1258 depAddDepend(pvRule, szFullname);
1259 else
1260 depAddDepend(pvRule, szBuffer);
1261 }
1262 else
1263 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1264 pszFilename, iLine, szFullname);
1265 }
1266 } /*while*/
1267
1268 return 0;
1269}
1270
1271#define upcase(ch) \
1272 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
1273
1274/**
1275 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
1276 * @returns 0 equal. (same as strnicmp)
1277 * @param pszS1 String 1
1278 * @param pszS2 String 2
1279 * @param cch Length to compare (relative to string 1)
1280 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1281 */
1282static int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
1283{
1284 do
1285 {
1286 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
1287 pszS1++, pszS2++, cch--;
1288
1289 /* blank test and skipping */
1290 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
1291 {
1292 while (cch > 0 && *pszS1 == ' ')
1293 pszS1++, cch--;
1294
1295 while (*pszS2 == ' ')
1296 pszS2++;
1297 }
1298 else
1299 break;
1300 } while (cch > 0);
1301
1302 return cch == 0 ? 0 : *pszS1 - *pszS2;
1303}
1304
1305/**
1306 * Copies the path part (excluding the slash) into pszBuffer and returns
1307 * a pointer to the buffer.
1308 * If no path is found "" is returned.
1309 * @returns Pointer to pszBuffer with path.
1310 * @param pszFilename Pointer to readonly filename.
1311 * @param pszBuffer Pointer to output Buffer.
1312 * @status completely implemented.
1313 * @author knut st. osmundsen
1314 */
1315char *filePath(const char *pszFilename, char *pszBuffer)
1316{
1317 char *psz = strrchr(pszFilename, '\\');
1318 if (psz == NULL)
1319 psz = strrchr(pszFilename, '/');
1320
1321 if (psz == NULL)
1322 *pszBuffer = '\0';
1323 else
1324 {
1325 strncpy(pszBuffer, pszFilename, psz - pszFilename - 1);
1326 pszBuffer[psz - pszFilename - 1] = '\0';
1327 }
1328
1329 return pszBuffer;
1330}
1331
1332
1333/**
1334 * Copies the path part including the slash into pszBuffer and returns
1335 * a pointer to the buffer.
1336 * If no path is found "" is returned.
1337 * @returns Pointer to pszBuffer with path.
1338 * @param pszFilename Pointer to readonly filename.
1339 * @param pszBuffer Pointer to output Buffer.
1340 * @status completely implemented.
1341 * @author knut st. osmundsen
1342 */
1343char *filePathSlash(const char *pszFilename, char *pszBuffer)
1344{
1345 char *psz = strrchr(pszFilename, '\\');
1346 if (psz == NULL)
1347 psz = strrchr(pszFilename, '/');
1348
1349 if (psz == NULL)
1350 *pszBuffer = '\0';
1351 else
1352 {
1353 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1354 pszBuffer[psz - pszFilename + 1] = '\0';
1355 }
1356
1357 return pszBuffer;
1358}
1359
1360
1361/**
1362 * Copies the filename (with extention) into pszBuffer and returns
1363 * a pointer to the buffer.
1364 * @returns Pointer to pszBuffer with path.
1365 * @param pszFilename Pointer to readonly filename.
1366 * @param pszBuffer Pointer to output Buffer.
1367 * @status completely implemented.
1368 * @author knut st. osmundsen
1369 */
1370char *fileName(const char *pszFilename, char *pszBuffer)
1371{
1372 char *psz = strrchr(pszFilename, '\\');
1373 if (psz == NULL)
1374 psz = strrchr(pszFilename, '/');
1375
1376 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1377
1378 return pszBuffer;
1379}
1380
1381
1382/**
1383 * Copies the name part with out extention into pszBuffer and returns
1384 * a pointer to the buffer.
1385 * If no name is found "" is returned.
1386 * @returns Pointer to pszBuffer with path.
1387 * @param pszFilename Pointer to readonly filename.
1388 * @param pszBuffer Pointer to output Buffer.
1389 * @status completely implemented.
1390 * @author knut st. osmundsen
1391 */
1392char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
1393{
1394 char *psz = strrchr(pszFilename, '\\');
1395 if (psz == NULL)
1396 psz = strrchr(pszFilename, '/');
1397
1398 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1399
1400 psz = strrchr(pszBuffer, '.');
1401 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
1402 *psz = '\0';
1403
1404 return pszBuffer;
1405}
1406
1407
1408/**
1409 * Copies the extention part into pszBuffer and returns
1410 * a pointer to the buffer.
1411 * If no extention is found "" is returned.
1412 * The dot ('.') is not included!
1413 * @returns Pointer to pszBuffer with path.
1414 * @param pszFilename Pointer to readonly filename.
1415 * @param pszBuffer Pointer to output Buffer.
1416 * @status completely implemented.
1417 * @author knut st. osmundsen
1418 */
1419char *fileExt(const char *pszFilename, char *pszBuffer)
1420{
1421 char *psz = strrchr(pszFilename, '.');
1422 if (psz != NULL)
1423 {
1424 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
1425 *pszBuffer = '\0';
1426 else
1427 strcpy(pszBuffer, psz + 1);
1428 }
1429 else
1430 *pszBuffer = '\0';
1431
1432 return pszBuffer;
1433}
1434
1435
1436/**
1437 * Adds a file to the cache.
1438 * @returns Success indicator.
1439 * @param pszFilename Name of the file which is to be added. (with path!)
1440 */
1441static BOOL filecacheAdd(const char *pszFilename)
1442{
1443 PFCACHEENTRY pfcNew;
1444
1445 /* allocate new block and fill in data */
1446 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
1447 if (pfcNew == NULL)
1448 {
1449 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1450 return FALSE;
1451 }
1452 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1453 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
1454 if (!AVLInsert(&pfcTree, pfcNew))
1455 {
1456 free(pfcNew);
1457 return TRUE;
1458 }
1459
1460 cfcNodes++;
1461
1462 return TRUE;
1463}
1464
1465
1466
1467/**
1468 * Checks if pszFile is exists in the cache.
1469 * @return TRUE if found. FALSE if not found.
1470 * @param pszFilename Name of the file to be found. (with path!)
1471 */
1472static BOOL filecacheFind(const char *pszFilename)
1473{
1474 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
1475}
1476
1477
1478
1479/**
1480 * Finds a filename in a specified pathlist.
1481 * @returns Pointer to a filename consiting of the path part + the given filename.
1482 * (pointer into pszBuffer)
1483 * NULL if file is not found. ("" in buffer)
1484 * @param pszPathList Path list to search for filename.
1485 * @parma pszFilename Filename to find.
1486 * @parma pszBuffer Ouput Buffer.
1487 * @status completely implemented.
1488 * @author knut st. osmundsen
1489 * @remark need substantial optimizations. 95% of execution is spend here.
1490 */
1491static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
1492{
1493 const char *psz = pszPathList;
1494 const char *pszNext = NULL;
1495
1496 *pszBuffer = '\0';
1497
1498 if (pszPathList == NULL)
1499 return NULL;
1500
1501 while (*psz != '\0')
1502 {
1503 /* find end of this path */
1504 pszNext = strchr(psz, ';');
1505 if (pszNext == NULL)
1506 pszNext = psz + strlen(psz);
1507
1508 if (pszNext - psz > 0)
1509 {
1510 APIRET rc;
1511 char szFile[CCHMAXPATH];
1512
1513 /* make search statment */
1514 strncpy(szFile, psz, pszNext - psz);
1515 szFile[pszNext - psz] = '\0';
1516 if (szFile[pszNext - psz - 1] != '\\' && szFile[pszNext - psz - 1] != '/')
1517 strcpy(&szFile[pszNext - psz], "\\");
1518 strcat(szFile, pszFilename);
1519
1520 /*
1521 * Search for the file in this directory.
1522 * Search cache first
1523 */
1524 if (!filecacheFind(szFile))
1525 {
1526 FILESTATUS3 fsts3;
1527
1528 /* ask the OS */
1529 rc = DosQueryPathInfo(szFile, FIL_STANDARD, &fsts3, sizeof(fsts3));
1530 if (rc == NO_ERROR)
1531 { /* add file to cache. */
1532 filecacheAdd(szFile);
1533 }
1534 }
1535 else
1536 rc = NO_ERROR;
1537
1538 /* did we find it? */
1539 if (rc == NO_ERROR)
1540 {
1541 strncpy(pszBuffer, psz, pszNext - psz);
1542 pszBuffer[pszNext - psz] = '\0';
1543 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
1544 strcpy(&pszBuffer[pszNext - psz], "\\");
1545 strcat(pszBuffer, pszFilename);
1546 break;
1547 }
1548 }
1549
1550 /* next */
1551 if (*pszNext != ';')
1552 break;
1553 psz = pszNext + 1;
1554 }
1555
1556 return *pszBuffer == '\0' ? NULL : pszBuffer;
1557}
1558
1559
1560/**
1561 * Finds the first char after word.
1562 * @returns Pointer to the first char after word.
1563 * @param psz Where to start.
1564 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1565 */
1566static char *findEndOfWord(char *psz)
1567{
1568
1569 while (*psz != '\0' &&
1570 (
1571 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1572 ||
1573 (*psz >= '0' && *psz <= '9')
1574 ||
1575 *psz == '_'
1576 )
1577 )
1578 ++psz;
1579 return (char *)psz;
1580}
1581
1582#if 0 /* not used */
1583/**
1584 * Find the starting char of a word
1585 * @returns Pointer to first char in word.
1586 * @param psz Where to start.
1587 * @param pszStart Where to stop.
1588 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1589 */
1590static char *findStartOfWord(const char *psz, const char *pszStart)
1591{
1592 const char *pszR = psz;
1593 while (psz >= pszStart &&
1594 (
1595 (*psz >= 'A' && *psz <= 'Z')
1596 || (*psz >= 'a' && *psz <= 'z')
1597 || (*psz >= '0' && *psz <= '9')
1598 || *psz == '_'
1599 )
1600 )
1601 pszR = psz--;
1602 return (char*)pszR;
1603}
1604#endif
1605
1606/**
1607 * Find the size of a file.
1608 * @returns Size of file. -1 on error.
1609 * @param phFile File handle.
1610 */
1611static signed long fsize(FILE *phFile)
1612{
1613 int ipos;
1614 signed long cb;
1615
1616 if ((ipos = ftell(phFile)) < 0
1617 ||
1618 fseek(phFile, 0, SEEK_END) != 0
1619 ||
1620 (cb = ftell(phFile)) < 0
1621 ||
1622 fseek(phFile, ipos, SEEK_SET) != 0
1623 )
1624 cb = -1;
1625 return cb;
1626}
1627
1628
1629
1630/**
1631 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
1632 * @returns Pointer to first not space or tab char in the string.
1633 * @param psz Pointer to the string which is to be trimmed.
1634 * @status completely implmented.
1635 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1636 */
1637INLINE char *trim(char *psz)
1638{
1639 int i;
1640 if (psz == NULL)
1641 return NULL;
1642 while (*psz == ' ' || *psz == '\t')
1643 psz++;
1644 i = strlen(psz) - 1;
1645 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1646 i--;
1647 psz[i+1] = '\0';
1648 return psz;
1649}
1650
1651
1652/**
1653 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
1654 * @returns Pointer to the string passed in.
1655 * @param psz Pointer to the string which is to be right trimmed.
1656 * @status completely implmented.
1657 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1658 */
1659INLINE char *trimR(char *psz)
1660{
1661 int i;
1662 if (psz == NULL)
1663 return NULL;
1664 i = strlen(psz) - 1;
1665 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1666 i--;
1667 psz[i+1] = '\0';
1668 return psz;
1669}
1670
1671
1672/**
1673 * Creates a memory buffer for a text file.
1674 * @returns Pointer to file memoryblock. NULL on error.
1675 * @param pszFilename Pointer to filename string.
1676 */
1677static void *textbufferCreate(const char *pszFilename)
1678{
1679 void *pvFile = NULL;
1680 FILE *phFile;
1681
1682 phFile = fopen(pszFilename, "r");
1683 if (phFile != NULL)
1684 {
1685 signed long cbFile = fsize(phFile);
1686 if (cbFile > 0)
1687 {
1688 pvFile = malloc(cbFile + 1);
1689 if (pvFile != NULL)
1690 {
1691 memset(pvFile, 0, cbFile + 1);
1692 if (fread(pvFile, 1, cbFile, phFile) == 0)
1693 { /* failed! */
1694 free(pvFile);
1695 pvFile = NULL;
1696 }
1697 }
1698 else
1699 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
1700 }
1701 fclose(phFile);
1702 }
1703 return pvFile;
1704}
1705
1706
1707/**
1708 * Destroys a text textbuffer.
1709 * @param pvBuffer Buffer handle.
1710 */
1711static void textbufferDestroy(void *pvBuffer)
1712{
1713 free(pvBuffer);
1714}
1715
1716
1717/**
1718 * Gets the next line from an textbuffer.
1719 * @returns Pointer to the next line.
1720 * @param pvBuffer Buffer handle.
1721 * @param psz Pointer to current line.
1722 * NULL is passed in to get the first line.
1723 */
1724static char *textbufferNextLine(void *pvBuffer, register char *psz)
1725{
1726 register char ch;
1727
1728 /* if first line psz is NULL. */
1729 if (psz == NULL)
1730 return (char*)pvBuffer;
1731
1732 /* skip till end of file or end of line. */
1733 ch = *psz;
1734 while (ch != '\0' && ch != '\n' && ch != '\r')
1735 ch = *++psz;
1736
1737 /* skip line end */
1738 if (ch == '\n')
1739 psz++;
1740 if (*psz == '\r')
1741 psz++;
1742
1743 return psz;
1744}
1745
1746
1747/**
1748 * Gets the next line from an textbuffer.
1749 * (fgets for textbuffer)
1750 * @returns Pointer to pszOutBuffer. NULL when end of file.
1751 * @param pvBuffer Buffer handle.
1752 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
1753 * Pointer to a null pointer is passed in to get the first line.
1754 * @param pszLineBuffer Output line buffer. (!= NULL)
1755 * @param cchLineBuffer Size of the output line buffer. (> 0)
1756 * @remark '\n' and '\r' are removed!
1757 */
1758static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
1759{
1760 char * pszLine = pszLineBuffer;
1761 char * psz = *(char**)(void*)ppv;
1762 register char ch;
1763
1764 /* first line? */
1765 if (psz == NULL)
1766 psz = pvBuffer;
1767
1768 /* Copy to end of the line or end of the linebuffer. */
1769 ch = *psz;
1770 cchLineBuffer--; /* reserve space for '\0' */
1771 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
1772 {
1773 *pszLine++ = ch;
1774 ch = *++psz;
1775 }
1776 *pszLine = '\0';
1777
1778 /* skip line end */
1779 if (ch == '\n')
1780 ch = *++psz;
1781 if (ch == '\r')
1782 psz++;
1783
1784 /* check if position has changed - if unchanged it's the end of file! */
1785 if (*ppv == (void*)psz)
1786 pszLineBuffer = NULL;
1787
1788 /* store current position */
1789 *ppv = (void*)psz;
1790
1791 return pszLineBuffer;
1792}
1793
1794
1795/**
1796 * Appends a depend file to the internal file.
1797 */
1798static BOOL depReadFile(const char *pszFilename)
1799{
1800 void *pvFile;
1801 char *pszNext;
1802 BOOL fMoreDeps = FALSE;
1803 void *pvRule = NULL;
1804
1805 /* read depend file */
1806 pvFile = textbufferCreate(pszFilename);
1807 if (pvFile == NULL)
1808 return FALSE;
1809
1810 /* parse the original depend file */
1811 pszNext = pvFile;
1812 while (*pszNext != '\0')
1813 {
1814 int i;
1815 int cch;
1816 char *psz;
1817
1818 /* get the next line. */
1819 psz = pszNext;
1820 pszNext = textbufferNextLine(pvFile, pszNext);
1821
1822 /*
1823 * Process the current line:
1824 * Start off by terminating the line.
1825 * Trim the line,
1826 * Skip empty lines.
1827 * If not looking for more deps Then
1828 * Check if new rule starts here.
1829 * Endif
1830 *
1831 * If more deps to last rule Then
1832 * Get dependant name.
1833 * Endif
1834 */
1835 i = -1;
1836 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
1837 pszNext[i--] = '\0';
1838 trimR(psz);
1839 cch = strlen(psz);
1840 if (cch == 0)
1841 continue;
1842
1843 /* new rule? */
1844 if (!fMoreDeps && *psz != ' ' && *psz != '\t' && *psz != '\0')
1845 {
1846 i = 0;
1847 while (psz[i] != '\0')
1848 {
1849 if (psz[i] == ':'
1850 && (psz[i+1] == ' '
1851 || psz[i+1] == '\t'
1852 || psz[i+1] == '\0'
1853 || psz[i+1] == '\\'
1854 )
1855 )
1856 break;
1857 i++;
1858 }
1859
1860 if (psz[i] == ':')
1861 { /* new rule! */
1862 psz[i] = '\0';
1863 pvRule = depAddRule(trimR(psz), NULL, NULL);
1864 psz += i + 1;
1865 cch -= i + 1;
1866 fMoreDeps = TRUE;
1867 }
1868 }
1869
1870 /* more dependants */
1871 if (fMoreDeps)
1872 {
1873 if (cch > 0 && psz[cch-1] == '\\')
1874 {
1875 fMoreDeps = TRUE;
1876 psz[cch-1] = '\0';
1877 }
1878 else
1879 fMoreDeps = FALSE;
1880
1881 /* if not duplicate rule */
1882 if (pvRule != NULL)
1883 {
1884 psz = trim(psz);
1885 if (*psz != '\0')
1886 depAddDepend(pvRule, psz);
1887 }
1888 }
1889 } /* while */
1890
1891
1892 /* return succesfully */
1893 textbufferDestroy(pvFile);
1894 return TRUE;
1895}
1896
1897/**
1898 *
1899 * @returns Success indicator.
1900 * @params pszFilename Pointer to name of the output file.
1901 */
1902static BOOL depWriteFile(const char *pszFilename)
1903{
1904 FILE *phFile;
1905 phFile = fopen(pszFilename, "w");
1906 if (phFile != NULL)
1907 {
1908 char szBuffer[4096];
1909 int iBuffer = 0;
1910 int cch;
1911 PDEPRULE pdep = pdepList;
1912
1913 while (pdep != NULL)
1914 {
1915 /* Write rule. Flush the buffer first if necessary. */
1916 cch = strlen(pdep->pszRule);
1917 if (iBuffer + cch + 2 >= sizeof(szBuffer))
1918 {
1919 fwrite(szBuffer, iBuffer, 1, phFile);
1920 iBuffer = 0;
1921 }
1922 strcpy(szBuffer + iBuffer, pdep->pszRule);
1923 iBuffer += cch;
1924 strcpy(szBuffer + iBuffer++, ":");
1925
1926 /* write rule dependants. */
1927 if (pdep->papszDep != NULL)
1928 {
1929 char **ppsz = pdep->papszDep;
1930 while (*ppsz != NULL)
1931 {
1932 /* flush buffer? */
1933 if (iBuffer + strlen(*ppsz) + 20 >= sizeof(szBuffer))
1934 {
1935 fwrite(szBuffer, iBuffer, 1, phFile);
1936 iBuffer = 0;
1937 }
1938 iBuffer += sprintf(szBuffer + iBuffer, " \\\n %s", *ppsz);
1939
1940 /* next dependant */
1941 ppsz++;
1942 }
1943 }
1944
1945 /* Add two new lines. Flush buffer first if necessary. */
1946 if (iBuffer + 2 >= sizeof(szBuffer))
1947 {
1948 fwrite(szBuffer, iBuffer, 1, phFile);
1949 iBuffer = 0;
1950 }
1951 strcpy(szBuffer + iBuffer, "\n\n");
1952 iBuffer += 2;
1953
1954 /* next rule */
1955 pdep = pdep->pNext;
1956 }
1957
1958 /* flush buffer. */
1959 fwrite(szBuffer, iBuffer, 1, phFile);
1960
1961 fclose(phFile);
1962 return TRUE;
1963 }
1964
1965 return FALSE;
1966}
1967
1968
1969/**
1970 * Removes all entries in the list of dependencies. (pdepList)
1971 */
1972static void depRemoveAll(void)
1973{
1974 while (pdepList != NULL)
1975 {
1976 register PDEPRULE pdepToBeFree = pdepList;
1977 /* next */
1978 pdepList = pdepToBeFree->pNext;
1979
1980 /* free this */
1981 if (pdepToBeFree->papszDep != NULL)
1982 {
1983 char ** ppsz = pdepToBeFree->papszDep;
1984 while (*ppsz != NULL)
1985 free(*ppsz++);
1986 free(pdepToBeFree->papszDep);
1987 }
1988 free(pdepToBeFree);
1989 }
1990}
1991
1992
1993/**
1994 * Adds a rule to the list of dependant rules.
1995 * @returns Rule handle. NULL if rule exists/error.
1996 * @param pszRulePath Pointer to rule text. Empty strings are banned!
1997 * This string might only contain the path of the rule. (with '\\')
1998 * @param pszName Name of the rule.
1999 * NULL if pszRulePath contains the entire rule.
2000 * @param pszExt Extention (without '.')
2001 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
2002 */
2003static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt)
2004{
2005 char szRule[CCHMAXPATH*2];
2006 PDEPRULE pdepPrev = NULL;
2007 PDEPRULE pdep = pdepList;
2008 PDEPRULE pNew;
2009 int cch;
2010
2011 /* make rulename */
2012 strcpy(szRule, pszRulePath);
2013 cch = strlen(szRule);
2014 if (pszName != NULL)
2015 {
2016 strcpy(szRule + cch, pszName);
2017 cch += strlen(szRule + cch);
2018 }
2019 if (pszExt != NULL)
2020 {
2021 strcat(szRule + cch++, ".");
2022 strcat(szRule + cch, pszExt);
2023 cch += strlen(szRule + cch);
2024 }
2025
2026 /* find location */
2027 while (pdep != NULL && stricmp(pdep->pszRule, szRule) < 0)
2028 {
2029 pdepPrev = pdep;
2030 pdep = pdep->pNext;
2031 }
2032
2033 /* check if matching rule name */
2034 if (pdep != NULL && stricmp(pdep->pszRule, szRule) == 0)
2035 return NULL;
2036
2037 /*
2038 * Allocate a new rule structure and fill in data
2039 * Note. One block for both the DEPRULE and the pszRule string.
2040 */
2041 pNew = malloc(sizeof(DEPRULE) + cch + 1);
2042 if (pNew == NULL)
2043 return NULL;
2044 pNew->pszRule = (char*)(void*)(pNew + 1);
2045 strcpy(pNew->pszRule, szRule);
2046 pNew->cDeps = 0;
2047 pNew->papszDep = NULL;
2048
2049 /* link in module (before pdep) */
2050 pNew->pNext = pdep;
2051 if (pdepPrev == NULL)
2052 pdepList = pNew;
2053 else
2054 pdepPrev->pNext = pNew;
2055
2056 _heap_check();
2057 return pNew;
2058}
2059
2060
2061
2062/**
2063 * Adds a dependant to a rule.
2064 * @returns Successindicator.
2065 * @param pvRule Rule handle.
2066 * @param pszDep Pointer to dependant name
2067 */
2068static BOOL depAddDepend(void *pvRule, const char *pszDep)
2069{
2070 PDEPRULE pdep = (PDEPRULE)pvRule;
2071
2072 /* allocate more array space */
2073 if (((pdep->cDeps) % 48) == 0)
2074 {
2075 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
2076 if (pdep->papszDep == NULL)
2077 {
2078 pdep->cDeps = 0;
2079 return FALSE;
2080 }
2081 }
2082
2083 /* allocate string space and copy pszDep */
2084 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
2085 return FALSE;
2086 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
2087
2088 /* terminate array and increment dep count */
2089 pdep->papszDep[++pdep->cDeps] = NULL;
2090
2091 /* successful! */
2092 _heap_check();
2093 return TRUE;
2094}
2095
2096
2097
2098/*
2099 * Testing purpose.
2100 */
2101#include <os2.h>
2102
2103
2104
Note: See TracBrowser for help on using the repository browser.