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

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

-I and -E may now take one environment variable in enclosed in %s as arguments.
This is done since CMD.EXE don't expand env.vars like 4OS2 does.

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