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

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

Bugfixing...

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