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

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

Added option: -o- which removes the object filename path.
Corrected the -Eall+ option.

File size: 49.0 KB
Line 
1/* $Id: fastdep.c,v 1.5 2000-03-15 15:02:11 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2000 knut st. osmundsen
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSERRORS
15#define INCL_FILEMGR
16
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <os2.h>
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25
26/*
27 * This following section is used while testing fastdep.
28 * stdio.h should be included; string.h never included.
29 */
30/*
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34*/
35
36#if 1
37#include <stdio.h>
38#else
39#include <string.h>
40#include <string.h>
41#endif
42
43/*
44 */ /* */ /*
45#include <string.h>
46 */
47#if 1
48# if 1
49 #if 0
50# include <string.h>
51 #else
52# if 1
53 #if 1
54 #if 0
55# include <string.h>
56 #else /* */ /*
57*/
58 # include <stdio.h>
59 #endif
60 #endif
61 #endif
62 #endif
63 #endif
64#endif
65
66/*******************************************************************************
67* Structures and Typedefs *
68*******************************************************************************/
69typedef struct _Options
70{
71 const char * pszInclude;
72 const char * pszExclude;
73 BOOL fExcludeAll;
74 const char * pszObjectExt;
75 const char * pszObjectDir;
76 BOOL fObjectDir; /* replace object directory? */
77 const char * pszRsrcExt;
78 BOOL fObjRule;
79 BOOL fNoObjectPath;
80 BOOL fSrcWhenObj;
81 BOOL fAppend; /* append to the output file, not overwrite it. */
82} OPTIONS, *POPTIONS;
83
84
85/*
86 * Language specific analysis functions type.
87 */
88typedef int ( _FNLANG) (FILE *phDep, const char *pszFilename, FILE *phFile,
89 BOOL fHeader, POPTIONS pOptions);
90typedef _FNLANG *PFNLANG;
91
92
93/**
94 * This struct holds the static configuration of the util.
95 */
96typedef struct _ConfigEntry
97{
98 const char **papszExts; /* Pointer to an array of pointer to extentions for this handler. */
99 /* If NULL this is the last entry. */
100 int iFirstHdr; /* Index into the papszExts array of the first headerfile/copybook. */
101 /* Set it to the NULL element of the array if no headers for this extention. */
102 /* A non-header file may get a object rule. */
103 PFNLANG pfn; /* Pointer to handler function. */
104} CONFIGENTRY, *PCONFIGENTRY;
105
106
107/*******************************************************************************
108* Internal Functions *
109*******************************************************************************/
110static void syntax(void);
111static int makeDependent(FILE *phDep, const char *pszFilename, POPTIONS pOptions);
112
113int langC_CPP(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions);
114int langAsm(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions);
115int langRC(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions);
116int langCOBOL(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions);
117
118
119/* string operations */
120static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
121
122/* file operations */
123char *filePath(const char *pszFilename, char *pszBuffer);
124char *filePathSlash(const char *pszFilename, char *pszBuffer);
125char *fileName(const char *pszFilename, char *pszBuffer);
126char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
127char *fileExt(const char *pszFilename, char *pszBuffer);
128
129/* pathlist operations */
130char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer);
131
132/* word operations */
133static char *findEndOfWord(char *psz);
134#if 0 /* not used */
135static char *findStartOfWord(char *psz, const char *pszStart);
136#endif
137
138/*******************************************************************************
139* Global Variables *
140*******************************************************************************/
141static const char pszDefaultDepFile[] = ".depend";
142static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
143static const char *apszExtAsm[] = {"asm", "inc", NULL};
144static const char *apszExtRC[] = {"rc", "dlg", NULL};
145static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", NULL};
146
147static CONFIGENTRY aConfig[] =
148{
149 {
150 apszExtC_CPP,
151 3,
152 langC_CPP,
153 },
154
155 {
156 apszExtAsm,
157 1,
158 langAsm,
159 },
160
161 {
162 apszExtRC,
163 1,
164 langRC,
165 },
166
167 {
168 apszExtCOBOL,
169 3,
170 langCOBOL,
171 },
172
173 /* terminating entry */
174 {
175 NULL,
176 -1,
177 NULL
178 }
179};
180
181
182/**
183 * Main function.
184 * @returns 0 on success.
185 * -n count of failiures.
186 * @param
187 * @param
188 * @equiv
189 * @precond
190 * @methdesc
191 * @result
192 * @time
193 * @sketch
194 * @algo
195 * @remark
196 */
197int main(int argc, char **argv)
198{
199 FILE *phDep = NULL;
200 int rc = 0;
201 int argi = 1;
202 const char *pszDepFile = pszDefaultDepFile;
203
204 static char szObjectDir[CCHMAXPATH];
205 static char szObjectExt[64] = "obj";
206 static char szRsrcExt[64] = "res";
207 static char szInclude[32768] = ";";
208 static char szExclude[32768] = ";";
209
210 OPTIONS options =
211 {
212 szInclude, /* pszInclude */
213 szExclude, /* pszExclude */
214 FALSE, /* fExcludeAll */
215 szObjectExt, /* pszObjectExt */
216 szObjectDir, /* pszObjectDir */
217 FALSE, /* fObjectDir */
218 szRsrcExt, /* pszRsrcExt */
219 TRUE, /* fObjRule */
220 FALSE, /* fNoObjectPath */
221 TRUE, /* fSrcWhenObj */
222 FALSE /* fAppend */
223 };
224
225 szObjectDir[0] = '\0';
226
227 if (argc == 1)
228 {
229 syntax();
230 return -87;
231 }
232
233 while (argi < argc)
234 {
235 if (argv[argi][0] == '-' || argv[argi][0] == '/')
236 {
237 /* parameters */
238 switch (argv[argi][1])
239 {
240 case 'A':
241 case 'a': /* Append to the output file */
242 options.fAppend = argv[argi][2] != '-';
243 break;
244
245 case 'D':
246 case 'd': /* "-d <filename>" */
247 if (argv[argi][2] != '\0')
248 pszDepFile = &argv[argi][2];
249 else
250 {
251 if (argi + 1 < argc)
252 pszDepFile = argv[++argi];
253 else
254 {
255 fprintf(stderr, "invalid parameter -d, filename missing!\n");
256 return -1;
257 }
258 }
259 if (phDep != NULL)
260 {
261 fclose(phDep);
262 phDep = NULL;
263 }
264 break;
265
266 case 'E': /* list of paths. If a file is found in one of these directories the */
267 case 'e': /* filename will be used without the directory path. */
268 /* Eall<[+]|-> ? */
269 if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
270 {
271 options.fExcludeAll = argv[argi][5] != '-';
272 break;
273 }
274 /* path or path list */
275 if (strlen(argv[argi]) > 2)
276 strcat(szExclude, &argv[argi][2]);
277 else
278 {
279 strcat(szExclude, argv[argi+1]);
280 argi++;
281 }
282 if (szExclude[strlen(szExclude)-1] != ';')
283 strcat(szExclude, ";");
284 break;
285
286 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
287 case 'i':
288 if (strlen(argv[argi]) > 2)
289 strcat(szInclude, &argv[argi][2]);
290 else
291 {
292 strcat(szInclude, argv[argi+1]);
293 argi++;
294 }
295 if (szInclude[strlen(szInclude)-1] != ';')
296 strcat(szInclude, ";");
297 break;
298
299 case 'n': /* no object path , -N<[+]|-> */
300 case 'N':
301 if (strlen(argv[argi]) <= 1+1+1)
302 options.fNoObjectPath = argv[argi][2] != '-';
303 else
304 {
305 fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
306 return -1;
307 }
308 break;
309
310 case 'o': /* object base directory, Obj or Obr<[+]|-> */
311 case 'O':
312 if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
313 {
314 options.fObjRule = argv[argi][4] != '-';
315 break;
316 }
317
318 if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
319 {
320 if (strlen(argv[argi]) > 4)
321 strcpy(szObjectExt, argv[argi]+4);
322 else
323 {
324 strcpy(szObjectExt, argv[argi+1]);
325 argi++;
326 }
327 break;
328 }
329
330 /* path: -o or -o- */
331 options.fObjectDir = TRUE;
332 if (strlen(argv[argi]) > 2)
333 {
334 if (argv[argi][2] == '-') /* no object path */
335 szObjectDir[0] = '\0';
336 else
337 strcpy(szObjectDir, argv[argi]+2);
338 }
339 else
340 {
341 strcpy(szObjectDir, argv[argi+1]);
342 argi++;
343 }
344 if (szObjectDir[0] != '\0'
345 && szObjectDir[strlen(szObjectDir)-1] != '\\'
346 && szObjectDir[strlen(szObjectDir)-1] != '/'
347 )
348 strcat(szObjectDir, "\\");
349 break;
350
351 case 'r':
352 case 'R':
353 if (strlen(argv[argi]) > 2)
354 strcpy(szObjectExt, argv[argi]+2);
355 else
356 {
357 strcpy(szObjectExt, argv[argi+1]);
358 argi++;
359 }
360 break;
361
362 case 'h':
363 case 'H':
364 case '?':
365 syntax();
366 return 1;
367
368 default:
369 fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
370 return -1;
371 }
372
373 }
374 else
375 { /* not a parameter! */
376 ULONG ulRc;
377 FILEFINDBUF3 filebuf;
378 HDIR hDir = HDIR_CREATE;
379 ULONG ulFound = 1;
380
381 memset(&filebuf, 0, sizeof(filebuf));
382
383 /*
384 * Open output file.
385 */
386 if (phDep == NULL)
387 {
388 phDep = fopen(pszDepFile, options.fAppend ? "a" : "w");
389 if (phDep == NULL)
390 {
391 fprintf(stderr, "error opening outputfile '%s'.\n", pszDepFile);
392 return 1;
393 }
394 }
395
396 /*
397 * Search for the files specified.
398 */
399 ulRc = DosFindFirst(argv[argi], &hDir,
400 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
401 &filebuf, sizeof(FILEFINDBUF3), &ulFound, FIL_STANDARD);
402 while (ulRc == NO_ERROR)
403 {
404 char *psz;
405 char szSource[CCHMAXPATH];
406
407 /*
408 * Make full path.
409 */
410 if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')))
411 {
412 strncpy(szSource, argv[argi], psz - argv[argi] + 1);
413 szSource[psz - argv[argi] + 1] = '\0';
414 }
415 else
416 szSource[0] = '\0';
417 strcat(szSource, filebuf.achName);
418
419 /*
420 * Analyse the file.
421 */
422 rc -= makeDependent(phDep, &szSource[0], &options);
423
424 /* next file */
425 ulRc = DosFindNext(hDir, &filebuf, sizeof(filebuf), &ulFound);
426 }
427 DosFindClose(hDir);
428 }
429 /* next */
430 argi++;
431 }
432
433 return rc;
434}
435
436
437/**
438 * Displays the syntax description for this util.
439 * @status completely implemented.
440 * @author knut st. osmundsen
441 */
442static void syntax(void)
443{
444 printf(
445 "FastDep v0.1\n"
446 "Quick and dirty dependant scanner. Creates a makefile readable depend file.\n"
447 "\n"
448 "Syntax: FastDep [-a<[+]|->] [-d <outputfn>] [-e <excludepath>] [-eall<[+]|->]\n"
449 " [-i <include>] [-n<[+]|->] [-o <objdir>] [-obr<[+]|->] <files>\n"
450 "\n"
451 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
452 " -d <outputfn> Output filename. Default: %s\n"
453 " -e excludepath Exclude paths. If a filename is found in any\n"
454 " of these paths only the filename is used, not\n"
455 " the path+filename (which is default) (don't work?).\n"
456 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
457 " -eall+: No path are added to the filename.\n"
458 " -eall-: The filename is appended the include path\n"
459 " was found in.\n"
460 " Default: eall-\n"
461 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
462 " -n<[+]|-> No path for object files in the rules.\n"
463 " -o <objdir> Path were object files are placed. This path replaces the\n"
464 " entire filename path\n"
465 " -o- No object path\n"
466 " -obr<[+]|-> -obr+: Object rule.\n"
467 " -obr-: No object rule, rule for source filename is generated.\n"
468 " -obj[ ]<objext> Object extention. Default: obj\n"
469 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
470 " <files> Files to scan. Wildchars are allowed.\n"
471 "\n",
472 pszDefaultDepFile
473 );
474}
475
476
477/**
478 * Generates depend info on this file, and fwrites it to phDep.
479 * @returns
480 * @param phDep Pointer to file struct for outfile.
481 * @param pszFilename Pointer to source filename.
482 * @param pOptions Pointer to options struct.
483 * @status completely implemented.
484 * @author knut st. osmundsen
485 */
486static int makeDependent(FILE *phDep, const char *pszFilename, POPTIONS pOptions)
487{
488 int rc = -1;
489 FILE *phFile;
490
491 phFile = fopen(pszFilename, "r");
492 if (phFile != NULL)
493 {
494 char szExt[CCHMAXPATH];
495 PCONFIGENTRY pCfg = &aConfig[0];
496 BOOL fHeader;
497
498 /*
499 * Find which filetype this is...
500 */
501 fileExt(pszFilename, szExt);
502 while (pCfg->papszExts != NULL)
503 {
504 const char **ppsz = pCfg->papszExts;
505 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
506 ppsz++;
507 if (*ppsz != NULL)
508 {
509 fHeader = &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
510 break;
511 }
512 pCfg++;
513 }
514
515 /* Found? */
516 if (pCfg->papszExts != NULL)
517 rc = (*pCfg->pfn)(phDep, pszFilename, phFile, fHeader, pOptions);
518 else
519 {
520 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
521 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
522 rc = 0;
523 }
524
525 fputs("\n", phDep);
526 fclose(phFile);
527 }
528 else
529 fprintf(stderr, "failed to open '%s'\n", pszFilename);
530
531 return rc;
532}
533
534
535/**
536 * Generates depend info on this C or C++ file, and writes it to phDep.
537 * @returns 0 on success.
538 * !0 on error.
539 * @param phDep Pointer to file struct for outfile.
540 * @param pszFilename Pointer to source filename.
541 * @param phFile Pointer to source file handle.
542 * @param pOptions Pointer to options struct.
543 * @status completely implemented.
544 * @author knut st. osmundsen
545 */
546int langC_CPP(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions)
547{
548 int iLine; /* Linenumber. */
549 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
550 BOOL fComment; /* TRUE when within a multiline comment. */
551 /* FALSE when not within a multiline comment. */
552 int iIfStack; /* StackPointer. */
553 struct IfStackEntry
554 {
555 int fIncluded : 1; /* TRUE: include this code;
556 * FALSE: excluded */
557 int fIf : 1; /* TRUE: #if part of the expression.
558 * FALSE: #else part of the expression. */
559 int fSupported : 1; /* TRUE: supported if/else statement
560 * FALSE: unsupported all else[<something>] are ignored
561 * All code is included.
562 */
563 } achIfStack[256];
564
565
566 /**********************************/
567 /* print file name to depend file */
568 /**********************************/
569 if (pOptions->fObjRule && !fHeader)
570 {
571 if (pOptions->fNoObjectPath)
572 fprintf(phDep, "%s.%s:", fileNameNoExt(pszFilename, szBuffer), pOptions->pszObjectExt);
573 else
574 fprintf(phDep, "%s%s.%s:",
575 pOptions->fObjectDir ?
576 pOptions->pszObjectDir : filePathSlash(pszFilename, szBuffer),
577 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
578 pOptions->pszObjectExt);
579
580 if (pOptions->fSrcWhenObj)
581 fprintf(phDep, " \\\n%4s %s", "",
582 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename
583 );
584 }
585 else
586 fprintf(phDep, "%s:", pszFilename);
587
588
589 /*******************/
590 /* find dependants */
591 /*******************/
592 /* Initiate the IF-stack, comment state and line number. */
593 iIfStack = 0;
594 achIfStack[iIfStack].fIf = TRUE;
595 achIfStack[iIfStack].fIncluded = TRUE;
596 achIfStack[iIfStack].fSupported = TRUE;
597 fComment = FALSE;
598 iLine = 0;
599 while (!feof(phFile)) /* line loop */
600 {
601 if (fgets(szBuffer, sizeof(szBuffer), phFile) != NULL)
602 {
603 /* search for #include */
604 register char *pszC;
605 int cbLen;
606 int i = 0;
607 iLine++;
608
609 /* skip blank chars */
610 cbLen = strlen(szBuffer);
611 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
612 i++;
613
614 /* preprocessor statement? */
615 if (!fComment && szBuffer[i] == '#')
616 {
617 /*
618 * Preprocessor checks
619 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
620 * Depending on the word afterwards we'll take some different actions.
621 * So we'll start of by extracting that word and make a string swich on it.
622 * Note that there might be some blanks between the hash and the word.
623 */
624 int cchWord;
625 char * pszEndWord;
626 char * pszArgument;
627 i++; /* skip hash ('#') */
628 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
629 i++;
630 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
631 cchWord = pszEndWord - &szBuffer[i];
632
633 /*
634 * Find the argument by skipping the blanks.
635 */
636 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
637 pszArgument++;
638
639 /*
640 * string switch.
641 */
642 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
643 {
644 /*
645 * #include
646 *
647 * Are we in a state where this file is to be included?
648 */
649 if (achIfStack[iIfStack].fIncluded)
650 {
651 char szFullname[CCHMAXPATH];
652 char *psz;
653 BOOL f = FALSE;
654 int j;
655
656 /* extract info between "" or <> */
657 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
658 i++;
659 i++; /* skip '"' or '<' */
660
661 /* if invalid statement then continue with the next line! */
662 if (!f) continue;
663
664 /* find end */
665 j = f = 0;
666 while (i + j < cbLen && j < CCHMAXPATH &&
667 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
668 j++;
669
670 /* if invalid statement then continue with the next line! */
671 if (!f) continue;
672
673 /* copy filename */
674 strncpy(szFullname, &szBuffer[i], j);
675 szFullname[j] = '\0'; /* ensure terminatition. */
676
677 /* find include file! */
678 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
679 if (psz == NULL)
680 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
681
682 /* did we find the include? */
683 if (psz != NULL)
684 {
685 char szBuffer2[CCHMAXPATH];
686 if (pOptions->fExcludeAll ||
687 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
688 )
689 strcpy(szBuffer, szFullname);
690 fprintf(phDep, " \\\n%4.s %s", "", szBuffer);
691 }
692 else
693 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
694 pszFilename, iLine, szFullname);
695 }
696 }
697 else
698 /*
699 * #if
700 */
701 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
702 { /* #if 0 and #if <1-9> are supported */
703 pszEndWord = findEndOfWord(pszArgument);
704 iIfStack++;
705 if ((pszEndWord - pszArgument) == 1
706 && *pszArgument >= '0' && *pszArgument <= '9')
707 {
708 if (*pszArgument != '0')
709 achIfStack[iIfStack].fIncluded = TRUE;
710 else
711 achIfStack[iIfStack].fIncluded = FALSE;
712 }
713 else
714 achIfStack[iIfStack].fSupported = FALSE;
715 achIfStack[iIfStack].fIncluded = TRUE;
716 achIfStack[iIfStack].fIf = TRUE;
717 }
718 else
719 /*
720 * #else
721 */
722 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
723 {
724 if (achIfStack[iIfStack].fSupported)
725 {
726 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
727 achIfStack[iIfStack].fIncluded = FALSE;
728 else
729 achIfStack[iIfStack].fIncluded = TRUE;
730 }
731 achIfStack[iIfStack].fIf = FALSE;
732 }
733 else
734 /*
735 * #endif
736 */
737 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
738 { /* Pop the if-stack. */
739 if (iIfStack > 0)
740 iIfStack--;
741 else
742 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
743 }
744 /*
745 * general if<something> and elseif<something> implementations
746 */
747 else
748 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
749 {
750 achIfStack[iIfStack].fSupported = FALSE;
751 achIfStack[iIfStack].fIncluded = TRUE;
752 }
753 else
754 if (strncmp(&szBuffer[i], "if", 2) == 0)
755 {
756 iIfStack++;
757 achIfStack[iIfStack].fIf = TRUE;
758 achIfStack[iIfStack].fSupported = FALSE;
759 achIfStack[iIfStack].fIncluded = TRUE;
760 }
761 /* The rest of them aren't implemented yet.
762 else if (strncmp(&szBuffer[i], "if") == 0)
763 {
764 }
765 */
766 }
767
768 /*
769 * Comment checks.
770 * -Start at first non-blank.
771 * -Loop thru the line since we might have more than one
772 * comment statement on a single line.
773 */
774 pszC = &szBuffer[i];
775 while (pszC != NULL && *pszC != '\0')
776 {
777 if (fComment)
778 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
779 else
780 {
781 char *pszLC;
782 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
783 pszC = strstr(pszC, "/*"); /* look for start comment mark */
784 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
785 break; /* muliline comment mark we'll ignore the multiline mark. */
786 }
787
788 /* Comment mark found? */
789 if (pszC != NULL)
790 {
791 fComment = !fComment;
792 pszC += 2; /* skip comment mark */
793
794 /* debug */
795 /*
796 if (fComment)
797 fprintf(stderr, "starts at line %d\n", iLine);
798 else
799 fprintf(stderr, "ends at line %d\n", iLine);
800 */
801 }
802 }
803 }
804 else
805 break;
806 } /*while*/
807 fputs("\n", phDep);
808
809 return 0;
810}
811
812
813/**
814 * Generates depend info on this file, and fwrites it to phDep.
815 * @returns 0 on success.
816 * !0 on error.
817 * @param phDep Pointer to file struct for outfile.
818 * @param pszFilename Pointer to source filename.
819 * @param phFile Pointer to source file handle.
820 * @param pOptions Pointer to options struct.
821 * @status completely implemented.
822 * @author knut st. osmundsen
823 */
824int langAsm(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions)
825{
826 char szBuffer[4096]; /* max line length */
827 int iLine;
828
829
830 /**********************************/
831 /* print file name to depend file */
832 /**********************************/
833 if (pOptions->fObjRule && !fHeader)
834 {
835 if (pOptions->fNoObjectPath)
836 fprintf(phDep, "%s.%s:", fileNameNoExt(pszFilename, szBuffer), pOptions->pszObjectExt);
837 else
838 fprintf(phDep, "%s%s.%s:",
839 pOptions->fObjectDir ?
840 pOptions->pszObjectDir : filePathSlash(pszFilename, szBuffer),
841 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
842 pOptions->pszObjectExt);
843
844 if (pOptions->fSrcWhenObj)
845 fprintf(phDep, " \\\n%4s %s", "",
846 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename
847 );
848 }
849 else
850 fprintf(phDep, "%s:", pszFilename);
851
852
853 /*******************/
854 /* find dependants */
855 /*******************/
856 iLine = 0;
857 while (!feof(phFile)) /* line loop */
858 {
859 if (fgets(szBuffer, sizeof(szBuffer), phFile) != NULL)
860 {
861 /* search for include */
862 int cbLen;
863 int i = 0;
864 iLine++;
865
866 /* skip blank chars */
867 cbLen = strlen(szBuffer);
868 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
869 i++;
870
871 /* is this an include? */
872 if (strnicmp(&szBuffer[i], "include", 7) == 0
873 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
874 )
875 {
876 char szFullname[CCHMAXPATH];
877 char *psz;
878 int j;
879
880 /* skip to first no blank char */
881 i += 7;
882 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
883 i++;
884
885 /* comment check - if comment found, no filename was given. continue. */
886 if (szBuffer[i] == ';') continue;
887
888 /* find end */
889 j = 0;
890 while (i + j < cbLen
891 && j < CCHMAXPATH
892 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
893 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
894 )
895 j++;
896
897 /* copy filename */
898 strncpy(szFullname, &szBuffer[i], j);
899 szFullname[j] = '\0'; /* ensure terminatition. */
900
901 /* find include file! */
902 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
903 if (psz == NULL)
904 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
905
906 /* Did we find the include? */
907 if (psz != NULL)
908 {
909 char szBuffer2[CCHMAXPATH];
910 if (pOptions->fExcludeAll ||
911 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
912 )
913 strcpy(szBuffer, szFullname);
914 fprintf(phDep, " \\\n%4.s %s", "", szBuffer);
915 }
916 else
917 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
918 pszFilename, iLine, szFullname);
919 }
920 }
921 else
922 break;
923 } /*while*/
924 fputs("\n", phDep);
925
926 return 0;
927}
928
929
930/**
931 * Generates depend info on this Resource file, and writes it to phDep.
932 * @returns 0 on success.
933 * !0 on error.
934 * @param phDep Pointer to file struct for outfile.
935 * @param pszFilename Pointer to source filename.
936 * @param phFile Pointer to source file handle.
937 * @param pOptions Pointer to options struct.
938 * @status completely implemented.
939 * @author knut st. osmundsen
940 */
941int langRC(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions)
942{
943 char szBuffer[4096]; /* max line length */
944 int iLine;
945
946 /**********************************/
947 /* print file name to depend file */
948 /**********************************/
949 if (pOptions->fObjRule && !fHeader)
950 {
951 if (pOptions->fNoObjectPath)
952 fprintf(phDep, "%s.%s:", fileNameNoExt(pszFilename, szBuffer), pOptions->pszRsrcExt);
953 else
954 fprintf(phDep, "%s%s.res:",
955 pOptions->fObjectDir ?
956 pOptions->pszObjectDir : filePathSlash(pszFilename, szBuffer),
957 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
958 pOptions->pszRsrcExt);
959
960 if (pOptions->fSrcWhenObj)
961 fprintf(phDep, " \\\n%4s %s", "",
962 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename
963 );
964 }
965 else
966 fprintf(phDep, "%s:", pszFilename);
967
968
969 /*******************/
970 /* find dependants */
971 /*******************/
972 iLine = 0;
973 while (!feof(phFile)) /* line loop */
974 {
975 if (fgets(szBuffer, sizeof(szBuffer), phFile) != NULL)
976 {
977 /* search for #include */
978 int cbLen;
979 int i = 0;
980 iLine++;
981
982 /* skip blank chars */
983 cbLen = strlen(szBuffer);
984 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
985 i++;
986
987 /* is this an include? */
988 if ( strncmp(&szBuffer[i], "#include", 8) == 0
989 || strncmp(&szBuffer[i], "RCINCLUDE", 9) == 0
990 || strncmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
991 )
992 {
993 char szFullname[CCHMAXPATH];
994 char *psz;
995 BOOL f = FALSE;
996 int j;
997
998 /* extract info between "" or <> */
999 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1000 i++;
1001 i++; /* skip '"' or '<' */
1002
1003 /* if invalid statement then continue with the next line! */
1004 if (!f) continue;
1005
1006 /* find end */
1007 j = f = 0;
1008 while (i + j < cbLen && j < CCHMAXPATH &&
1009 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1010 j++;
1011
1012 /* if invalid statement then continue with the next line! */
1013 if (!f) continue;
1014
1015 /* copy filename */
1016 strncpy(szFullname, &szBuffer[i], j);
1017 szFullname[j] = '\0'; /* ensure terminatition. */
1018
1019 /* find include file! */
1020 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
1021 if (psz == NULL)
1022 psz = pathlistFindFile(getenv("INCLUDE"), szFullname, szBuffer);
1023
1024 /* did we find the include? */
1025 if (psz != NULL)
1026 {
1027 char szBuffer2[CCHMAXPATH];
1028 if (pOptions->fExcludeAll ||
1029 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
1030 )
1031 strcpy(szBuffer, szFullname);
1032 fprintf(phDep, " \\\n%4.s %s", "", szBuffer);
1033 }
1034 else
1035 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1036 pszFilename, iLine, szFullname);
1037 }
1038 }
1039 else
1040 break;
1041 } /*while*/
1042 fputs("\n", phDep);
1043
1044 return 0;
1045}
1046
1047
1048/**
1049 * Generates depend info on this COBOL file, and writes it to phDep.
1050 * @returns 0 on success.
1051 * !0 on error.
1052 * @param phDep Pointer to file struct for outfile.
1053 * @param pszFilename Pointer to source filename.
1054 * @param phFile Pointer to source file handle.
1055 * @param pOptions Pointer to options struct.
1056 * @status completely implemented.
1057 * @author knut st. osmundsen
1058 */
1059int langCOBOL(FILE *phDep, const char *pszFilename, FILE *phFile, BOOL fHeader, POPTIONS pOptions)
1060{
1061 char szBuffer[4096]; /* max line length */
1062 int iLine;
1063
1064 /**********************************/
1065 /* print file name to depend file */
1066 /**********************************/
1067 if (pOptions->fObjRule && !fHeader)
1068 {
1069 if (pOptions->fNoObjectPath)
1070 fprintf(phDep, "%s.%s:", fileNameNoExt(pszFilename, szBuffer), pOptions->pszObjectExt);
1071 else
1072 fprintf(phDep, "%s%s.%s:",
1073 pOptions->fObjectDir ?
1074 pOptions->pszObjectDir : filePathSlash(pszFilename, szBuffer),
1075 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1076 pOptions->pszObjectExt);
1077
1078 if (pOptions->fSrcWhenObj)
1079 fprintf(phDep, " \\\n%4s %s", "",
1080 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : pszFilename
1081 );
1082 }
1083 else
1084 fprintf(phDep, "%s:", pszFilename);
1085
1086
1087 /*******************/
1088 /* find dependants */
1089 /*******************/
1090 iLine = 0;
1091 while (!feof(phFile)) /* line loop */
1092 {
1093 if (fgets(szBuffer, sizeof(szBuffer), phFile) != NULL)
1094 {
1095 /* search for #include */
1096 int cbLen;
1097 int i = 0;
1098 int i1, i2;
1099 iLine++;
1100
1101 /* check for comment mark (column 7) */
1102 if (szBuffer[6] == '*')
1103 continue;
1104
1105 /* skip blank chars */
1106 cbLen = strlen(szBuffer);
1107 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1108 i++;
1109
1110 /* is this an include? */
1111 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
1112 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
1113 )
1114 {
1115 char szFullname[CCHMAXPATH];
1116 char *psz;
1117 int j;
1118
1119 /* skip statement */
1120 i += 4;
1121 if (i1 != 0)
1122 {
1123 int y = 2; /* skip two words */
1124 do
1125 {
1126 /* skip blanks */
1127 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1128 i++;
1129 /* skip word */
1130 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
1131 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
1132 i++;
1133 y--;
1134 } while (y > 0);
1135 }
1136
1137 /* check for blank */
1138 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
1139 continue;
1140
1141 /* skip blanks */
1142 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1143 i++;
1144
1145 /* if invalid statement then continue with the next line! */
1146 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
1147 continue;
1148
1149 /* find end */
1150 j = 0;
1151 while (i + j < cbLen && j < CCHMAXPATH
1152 && szBuffer[i+j] != '.'
1153 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1154 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
1155 )
1156 j++;
1157
1158 /* if invalid statement then continue with the next line! */
1159 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
1160 continue;
1161
1162 /* copy filename */
1163 strncpy(szFullname, &szBuffer[i], j);
1164 szFullname[j] = '\0'; /* ensure terminatition. */
1165
1166 /* add extention .cpy - hardcoded for the moment. */
1167 strcat(szFullname, ".cpy");
1168
1169 /* find include file! */
1170 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer);
1171
1172 /* did we find the include? */
1173 if (psz != NULL)
1174 {
1175 char szBuffer2[CCHMAXPATH];
1176 if (pOptions->fExcludeAll ||
1177 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2) != NULL
1178 )
1179 strcpy(szBuffer, szFullname);
1180 fprintf(phDep, " \\\n%4.s %s", "", szBuffer);
1181 }
1182 else
1183 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1184 pszFilename, iLine, szFullname);
1185 }
1186 }
1187 else
1188 break;
1189 } /*while*/
1190 fputs("\n", phDep);
1191
1192 return 0;
1193}
1194
1195#define upcase(ch) \
1196 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
1197
1198/**
1199 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
1200 * @returns 0 equal. (same as strnicmp)
1201 * @param pszS1 String 1
1202 * @param pszS2 String 2
1203 * @param cch Length to compare (relative to string 1)
1204 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1205 */
1206static int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
1207{
1208 do
1209 {
1210 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
1211 pszS1++, pszS2++, cch--;
1212
1213 /* blank test and skipping */
1214 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
1215 {
1216 while (cch > 0 && *pszS1 == ' ')
1217 pszS1++, cch--;
1218
1219 while (*pszS2 == ' ')
1220 pszS2++;
1221 }
1222 else
1223 break;
1224 } while (cch > 0);
1225
1226 return cch == 0 ? 0 : *pszS1 - *pszS2;
1227}
1228
1229/**
1230 * Copies the path part (excluding the slash) into pszBuffer and returns
1231 * a pointer to the buffer.
1232 * If no path is found "" is returned.
1233 * @returns Pointer to pszBuffer with path.
1234 * @param pszFilename Pointer to readonly filename.
1235 * @param pszBuffer Pointer to output Buffer.
1236 * @status completely implemented.
1237 * @author knut st. osmundsen
1238 */
1239char *filePath(const char *pszFilename, char *pszBuffer)
1240{
1241 char *psz = strrchr(pszFilename, '\\');
1242 if (psz == NULL)
1243 psz = strrchr(pszFilename, '/');
1244
1245 if (psz == NULL)
1246 *pszBuffer = '\0';
1247 else
1248 {
1249 strncpy(pszBuffer, pszFilename, psz - pszFilename - 1);
1250 pszBuffer[psz - pszFilename - 1] = '\0';
1251 }
1252
1253 return pszBuffer;
1254}
1255
1256
1257/**
1258 * Copies the path part including the slash into pszBuffer and returns
1259 * a pointer to the buffer.
1260 * If no path is found "" is returned.
1261 * @returns Pointer to pszBuffer with path.
1262 * @param pszFilename Pointer to readonly filename.
1263 * @param pszBuffer Pointer to output Buffer.
1264 * @status completely implemented.
1265 * @author knut st. osmundsen
1266 */
1267char *filePathSlash(const char *pszFilename, char *pszBuffer)
1268{
1269 char *psz = strrchr(pszFilename, '\\');
1270 if (psz == NULL)
1271 psz = strrchr(pszFilename, '/');
1272
1273 if (psz == NULL)
1274 *pszBuffer = '\0';
1275 else
1276 {
1277 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1278 pszBuffer[psz - pszFilename + 1] = '\0';
1279 }
1280
1281 return pszBuffer;
1282}
1283
1284
1285/**
1286 * Copies the filename (with extention) into pszBuffer and returns
1287 * a pointer to the buffer.
1288 * @returns Pointer to pszBuffer with path.
1289 * @param pszFilename Pointer to readonly filename.
1290 * @param pszBuffer Pointer to output Buffer.
1291 * @status completely implemented.
1292 * @author knut st. osmundsen
1293 */
1294char *fileName(const char *pszFilename, char *pszBuffer)
1295{
1296 char *psz = strrchr(pszFilename, '\\');
1297 if (psz == NULL)
1298 psz = strrchr(pszFilename, '/');
1299
1300 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1301
1302 return pszBuffer;
1303}
1304
1305
1306/**
1307 * Copies the name part with out extention into pszBuffer and returns
1308 * a pointer to the buffer.
1309 * If no name is found "" is returned.
1310 * @returns Pointer to pszBuffer with path.
1311 * @param pszFilename Pointer to readonly filename.
1312 * @param pszBuffer Pointer to output Buffer.
1313 * @status completely implemented.
1314 * @author knut st. osmundsen
1315 */
1316char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
1317{
1318 char *psz = strrchr(pszFilename, '\\');
1319 if (psz == NULL)
1320 psz = strrchr(pszFilename, '/');
1321
1322 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1323
1324 psz = strrchr(pszBuffer, '.');
1325 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
1326 *psz = '\0';
1327
1328 return pszBuffer;
1329}
1330
1331
1332/**
1333 * Copies the extention part into pszBuffer and returns
1334 * a pointer to the buffer.
1335 * If no extention is found "" is returned.
1336 * The dot ('.') is not included!
1337 * @returns Pointer to pszBuffer with path.
1338 * @param pszFilename Pointer to readonly filename.
1339 * @param pszBuffer Pointer to output Buffer.
1340 * @status completely implemented.
1341 * @author knut st. osmundsen
1342 */
1343char *fileExt(const char *pszFilename, char *pszBuffer)
1344{
1345 char *psz = strrchr(pszFilename, '.');
1346 if (psz != NULL)
1347 {
1348 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
1349 *pszBuffer = '\0';
1350 else
1351 strcpy(pszBuffer, psz + 1);
1352 }
1353 else
1354 *pszBuffer = '\0';
1355
1356 return pszBuffer;
1357}
1358
1359
1360
1361
1362/**
1363 * Finds a filename in a specified pathlist.
1364 * @returns Pointer to a filename consiting of the path part + the given filename.
1365 * (pointer into pszBuffer)
1366 * NULL if file is not found. ("" in buffer)
1367 * @param pszPathList Path list to search for filename.
1368 * @parma pszFilename Filename to find.
1369 * @parma pszBuffer Ouput Buffer.
1370 * @status completely implemented.
1371 * @author knut st. osmundsen
1372 */
1373char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
1374{
1375 const char *psz = pszPathList;
1376 const char *pszNext = NULL;
1377
1378 *pszBuffer = '\0';
1379
1380 if (pszPathList == NULL)
1381 return NULL;
1382
1383 while (*psz != '\0')
1384 {
1385 /* find end of this path */
1386 pszNext = strchr(psz, ';');
1387 if (pszNext == NULL)
1388 pszNext = psz + strlen(psz);
1389
1390 if (pszNext - psz > 0)
1391 {
1392 HDIR hDir = HDIR_CREATE;
1393 ULONG cFiles = 1UL;
1394 FILEFINDBUF3 FileFindBuf;
1395 APIRET rc;
1396 char szFile[CCHMAXPATH];
1397
1398 /* make search statment */
1399 strncpy(szFile, psz, pszNext - psz);
1400 szFile[pszNext - psz] = '\0';
1401 if (szFile[pszNext - psz - 1] != '\\' && szFile[pszNext - psz - 1] != '/')
1402 strcpy(&szFile[pszNext - psz], "\\");
1403 strcat(szFile, pszFilename);
1404
1405 /* search for file */
1406 rc = DosFindFirst(szFile, &hDir, FILE_NORMAL, &FileFindBuf, sizeof(FileFindBuf),
1407 &cFiles, FIL_STANDARD);
1408 DosFindClose(hDir);
1409 if (rc == NO_ERROR)
1410 {
1411 strncpy(pszBuffer, psz, pszNext - psz);
1412 pszBuffer[pszNext - psz] = '\0';
1413 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
1414 strcpy(&pszBuffer[pszNext - psz], "\\");
1415 strcat(pszBuffer, pszFilename);
1416 break;
1417 }
1418 }
1419
1420 /* next */
1421 if (*pszNext != ';')
1422 break;
1423 psz = pszNext + 1;
1424 }
1425
1426 return *pszBuffer == '\0' ? NULL : pszBuffer;
1427}
1428
1429
1430/**
1431 * Finds the first char after word.
1432 * @returns Pointer to the first char after word.
1433 * @param psz Where to start.
1434 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1435 */
1436static char *findEndOfWord(char *psz)
1437{
1438
1439 while (*psz != '\0' &&
1440 (
1441 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1442 ||
1443 (*psz >= '0' && *psz <= '9')
1444 ||
1445 *psz == '_'
1446 )
1447 )
1448 ++psz;
1449 return (char *)psz;
1450}
1451
1452#if 0 /* not used */
1453/**
1454 * Find the starting char of a word
1455 * @returns Pointer to first char in word.
1456 * @param psz Where to start.
1457 * @param pszStart Where to stop.
1458 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1459 */
1460static char *findStartOfWord(const char *psz, const char *pszStart)
1461{
1462 const char *pszR = psz;
1463 while (psz >= pszStart &&
1464 (
1465 (*psz >= 'A' && *psz <= 'Z')
1466 || (*psz >= 'a' && *psz <= 'z')
1467 || (*psz >= '0' && *psz <= '9')
1468 || *psz == '_'
1469 )
1470 )
1471 pszR = psz--;
1472 return (char*)pszR;
1473}
1474#endif
1475
1476/*
1477 * Testin purpose.
1478 */
1479#include <os2.h>
1480
Note: See TracBrowser for help on using the repository browser.