Imported Upstream version 15.8a
[platform/upstream/cscope.git] / src / main.c
1 /*===========================================================================
2  Copyright (c) 1998-2000, The Santa Cruz Operation 
3  All rights reserved.
4  
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  *Redistributions of source code must retain the above copyright notice,
9  this list of conditions and the following disclaimer.
10
11  *Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14
15  *Neither name of The Santa Cruz Operation nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission. 
18
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  DAMAGE. 
31  =========================================================================*/
32
33
34 /*      cscope - interactive C symbol cross-reference
35  *
36  *      main functions
37  */
38
39 #include "global.h"
40
41 #include "build.h"
42 #include "vp.h"
43 #include "version.h"    /* FILEVERSION and FIXVERSION */
44 #include "scanner.h" 
45 #include "alloc.h"
46
47 #include <stdlib.h>     /* atoi */
48 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
49 #include <ncurses.h>
50 #else
51 #include <curses.h>
52 #endif
53 #include <sys/types.h>  /* needed by stat.h */
54 #include <sys/stat.h>   /* stat */
55 #include <signal.h>
56 #ifdef HAVE_GETOPT_LONG 
57 #include <getopt.h>
58 #endif
59
60 /* defaults for unset environment variables */
61 #define EDITOR  "vi"
62 #define HOME    "/"     /* no $HOME --> use root directory */
63 #define SHELL   "sh"
64 #define LINEFLAG "+%s"  /* default: used by vi and emacs */
65 #define TMPDIR  "/tmp"
66 #ifndef DFLT_INCDIR
67 #define DFLT_INCDIR "/usr/include"
68 #endif
69
70 static char const rcsid[] = "$Id: main.c,v 1.55 2011/07/04 13:41:17 nhorman Exp $";
71
72 /* note: these digraph character frequencies were calculated from possible 
73    printable digraphs in the cross-reference for the C compiler */
74 char    dichar1[] = " teisaprnl(of)=c"; /* 16 most frequent first chars */
75 char    dichar2[] = " tnerpla";         /* 8 most frequent second chars 
76                                            using the above as first chars */
77 char    dicode1[256];           /* digraph first character code */
78 char    dicode2[256];           /* digraph second character code */
79
80 char    *editor, *shell, *lineflag;     /* environment variables */
81 char    *home;                  /* Home directory */
82 BOOL    lineflagafterfile;
83 char    *argv0;                 /* command name */
84 BOOL    compress = YES;         /* compress the characters in the crossref */
85 BOOL    dbtruncated;            /* database symbols are truncated to 8 chars */
86 int     dispcomponents = 1;     /* file path components to display */
87 #if CCS
88 BOOL    displayversion;         /* display the C Compilation System version */
89 #endif
90 BOOL    editallprompt = YES;    /* prompt between editing files */
91 unsigned int fileargc;          /* file argument count */
92 char    **fileargv;             /* file argument values */
93 int     fileversion;            /* cross-reference file version */
94 BOOL    incurses = NO;          /* in curses */
95 BOOL    invertedindex;          /* the database has an inverted index */
96 BOOL    isuptodate;             /* consider the crossref up-to-date */
97 BOOL    kernelmode;             /* don't use DFLT_INCDIR - bad for kernels */
98 BOOL    linemode = NO;          /* use line oriented user interface */
99 BOOL    verbosemode = NO;       /* print extra information on line mode */
100 BOOL    recurse_dir = NO;       /* recurse dirs when searching for src files */
101 char    *namefile;              /* file of file names */
102 BOOL    ogs;                    /* display OGS book and subsystem names */
103 char    *prependpath;           /* prepend path to file names */
104 FILE    *refsfound;             /* references found file */
105 char    temp1[PATHLEN + 1];     /* temporary file name */
106 char    temp2[PATHLEN + 1];     /* temporary file name */
107 char    tempdirpv[PATHLEN + 1]; /* private temp directory */
108 long    totalterms;             /* total inverted index terms */
109 BOOL    trun_syms;              /* truncate symbols to 8 characters */
110 char    tempstring[TEMPSTRING_LEN + 1]; /* use this as a buffer, instead of 'yytext', 
111                                  * which had better be left alone */
112 char    *tmpdir;                /* temporary directory */
113
114 static  BOOL    onesearch;              /* one search only in line mode */
115 static  char    *reflines;              /* symbol reference lines file */
116
117 /* Internal prototypes: */
118 static  void    initcompress(void);
119 static  void    longusage(void);
120 static  void    skiplist(FILE *oldrefs);
121 static  void    usage(void);
122
123 #ifdef HAVE_FIXKEYPAD
124 void    fixkeypad();
125 #endif
126
127 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
128 void 
129 sigwinch_handler(int sig, siginfo_t *info, void *unused)
130 {
131     (void) sig;
132     (void) info;
133     (void) unused;
134     if(incurses == YES)
135         ungetch(KEY_RESIZE);
136 }
137 #endif
138
139 #ifdef HAVE_GETOPT_LONG
140 struct option lopts[] = {
141         {"help", 0, NULL, 'h'},
142         {"version", 0, NULL, 'V'},
143         {0, 0, 0, 0}
144 };
145
146 char ** parse_options(int *argc, char **argv)
147 {
148         int opt;
149         int longind;
150         char path[PATHLEN + 1];     /* file path */
151         char *s;
152         int argcc = *argc;
153         
154
155         while ((opt = getopt_long(argcc, argv,
156                "hVbcCdeF:f:I:i:kLl0:1:2:3:4:5:6:7:8:9:P:p:qRs:TUuv",
157                lopts, &longind)) != -1) {
158                 switch(opt) {
159
160                 case '?':
161                         usage();
162                         myexit(1);
163                         break;
164                 case '0':
165                 case '1':
166                 case '2':
167                 case '3':
168                 case '4':
169                 case '5':
170                 case '6':
171                 case '7':
172                 case '8':
173                 case '9':
174                         /* The input fields numbers for line mode operation */
175                         field = opt - '0';
176                         if (strlen(optarg) > PATHLEN) {
177                                     postfatal("\
178                                         cscope: pattern too long, cannot be > \
179                                         %d characters\n", PATLEN);
180                         }
181                         strcpy(Pattern, optarg);        
182                         break;
183                 case 'b':       /* only build the cross-reference */
184                         buildonly = YES;
185                         linemode  = YES;
186                         break;
187                 case 'c':       /* ASCII characters only in crossref */
188                         compress = NO;
189                         break;
190                 case 'C':       /* turn on caseless mode for symbol searches */
191                         caseless = YES;
192                         egrepcaseless(caseless); /* simulate egrep -i flag */
193                         break;
194                 case 'd':       /* consider crossref up-to-date */
195                         isuptodate = YES;
196                         break;
197                 case 'e':       /* suppress ^E prompt between files */
198                         editallprompt = NO;
199                         break;
200                 case 'h':
201                         longusage();
202                         myexit(1);
203                         break;
204                 case 'k':       /* ignore DFLT_INCDIR */
205                         kernelmode = YES;
206                         break;
207                 case 'L':
208                         onesearch = YES;
209                         /* FALLTHROUGH */
210                 case 'l':
211                         linemode = YES;
212                         break;
213                 case 'v':
214                         verbosemode = YES;
215                         break;
216                 case 'V':
217                         fprintf(stderr, "%s: version %d%s\n", argv0,
218                                 FILEVERSION, FIXVERSION);
219                         myexit(0);
220                         break;
221                 case 'q':       /* quick search */
222                         invertedindex = YES;
223                         break;
224                 case 'T':       /* truncate symbols to 8 characters */
225                         trun_syms = YES;
226                         break;
227                 case 'u':       /* unconditionally build the cross-reference */
228                         unconditional = YES;
229                         break;
230                 case 'U':       /* assume some files have changed */
231                         fileschanged = YES;
232                         break;
233                 case 'R':
234                         recurse_dir = YES;
235                         break;
236                 case 'f':       /* alternate cross-reference file */
237                         reffile = optarg;
238                         if (strlen(reffile) > sizeof(path) - 3) {
239                                 postfatal("\
240                                         cscope: reffile too long, cannot \
241                                         be > %d characters\n", sizeof(path) - 3);
242                                 /* NOTREACHED */
243                         }
244                         strcpy(path, reffile);
245
246                         s = path + strlen(path);
247                         strcpy(s, ".in");
248                         invname = my_strdup(path);
249                         strcpy(s, ".po");
250                         invpost = my_strdup(path);
251                         break;
252
253                 case 'F':       /* symbol reference lines file */
254                         reflines = optarg;
255                         break;
256                 case 'i':       /* file containing file names */
257                         namefile = optarg;
258                         break;
259                 case 'I':       /* #include file directory */
260                         includedir(optarg);
261                         break;
262                 case 'p':       /* file path components to display */
263                         dispcomponents = atoi(optarg);
264                         break;
265                 case 'P':       /* prepend path to file names */
266                         prependpath = optarg;
267                         break;
268                 case 's':       /* additional source file directory */
269                         sourcedir(optarg);
270                         break;
271                 }
272         }
273         /*
274          * This adjusts argv so that we only see the remaining 
275          * args.  Its ugly, but we need to do it so that the rest
276          * of the main routine doesn't get all confused
277          */
278         *argc = *argc - optind;
279         return &argv[optind];
280 }
281 #endif
282
283 int
284 main(int argc, char **argv)
285 {
286     FILE *names;                        /* name file pointer */
287     int oldnum;                 /* number in old cross-ref */
288     char path[PATHLEN + 1];     /* file path */
289     FILE *oldrefs;      /* old cross-reference file */
290     char *s;
291     int c;
292     unsigned int i;
293     pid_t pid;
294     struct stat stat_buf;
295 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
296     struct sigaction winch_action;
297 #endif
298     mode_t orig_umask;
299         
300     yyin = stdin;
301     yyout = stdout;
302     /* save the command name for messages */
303     argv0 = argv[0];
304
305     /* set the options */
306 #ifdef HAVE_GETOPT_LONG 
307         argv = parse_options(&argc, argv);
308 #else
309     while (--argc > 0 && (*++argv)[0] == '-') {
310         /* HBB 20030814: add GNU-style --help and --version options */
311         if (strequal(argv[0], "--help")
312             || strequal(argv[0], "-h")) {
313             longusage();
314             myexit(0);
315         }
316         if (strequal(argv[0], "--version")
317             || strequal(argv[0], "-V")) {
318 #if CCS
319             displayversion = YES;
320 #else
321             fprintf(stderr, "%s: version %d%s\n", argv0,
322                     FILEVERSION, FIXVERSION);
323             myexit(0);
324 #endif
325         }
326
327         for (s = argv[0] + 1; *s != '\0'; s++) {
328
329             /* look for an input field number */
330             if (isdigit((unsigned char) *s)) {
331                 field = *s - '0';
332                 if (field > 8) {
333                     field = 8;
334                 }
335                 if (*++s == '\0' && --argc > 0) {
336                     s = *++argv;
337                 }
338                 if (strlen(s) > PATLEN) {
339                     postfatal("\
340 cscope: pattern too long, cannot be > %d characters\n", PATLEN);
341                     /* NOTREACHED */
342                 }
343                 strcpy(Pattern, s);
344                 goto nextarg;
345             }
346             switch (*s) {
347             case '-':   /* end of options */
348                 --argc;
349                 ++argv;
350                 goto lastarg;
351             case 'b':   /* only build the cross-reference */
352                 buildonly = YES;
353                 linemode  = YES;
354                 break;
355             case 'c':   /* ASCII characters only in crossref */
356                 compress = NO;
357                 break;
358             case 'C':   /* turn on caseless mode for symbol searches */
359                 caseless = YES;
360                 egrepcaseless(caseless); /* simulate egrep -i flag */
361                 break;
362             case 'd':   /* consider crossref up-to-date */
363                 isuptodate = YES;
364                 break;
365             case 'e':   /* suppress ^E prompt between files */
366                 editallprompt = NO;
367                 break;
368             case 'k':   /* ignore DFLT_INCDIR */
369                 kernelmode = YES;
370                 break;
371             case 'L':
372                 onesearch = YES;
373                 /* FALLTHROUGH */
374             case 'l':
375                 linemode = YES;
376                 break;
377             case 'v':
378                 verbosemode = YES;
379                 break;
380             case 'o':   /* display OGS book and subsystem names */
381                 ogs = YES;
382                 break;
383             case 'q':   /* quick search */
384                 invertedindex = YES;
385                 break;
386             case 'T':   /* truncate symbols to 8 characters */
387                 trun_syms = YES;
388                 break;
389             case 'u':   /* unconditionally build the cross-reference */
390                 unconditional = YES;
391                 break;
392             case 'U':   /* assume some files have changed */
393                 fileschanged = YES;
394                 break;
395             case 'R':
396                 recurse_dir = YES;
397                 break;
398             case 'f':   /* alternate cross-reference file */
399             case 'F':   /* symbol reference lines file */
400             case 'i':   /* file containing file names */
401             case 'I':   /* #include file directory */
402             case 'p':   /* file path components to display */
403             case 'P':   /* prepend path to file names */
404             case 's':   /* additional source file directory */
405             case 'S':
406                 c = *s;
407                 if (*++s == '\0' && --argc > 0) {
408                     s = *++argv;
409                 }
410                 if (*s == '\0') {
411                     fprintf(stderr, "%s: -%c option: missing or empty value\n", 
412                             argv0, c);
413                     goto usage;
414                 }
415                 switch (c) {
416                 case 'f':       /* alternate cross-reference file */
417                     reffile = s;
418                     if (strlen(reffile) > sizeof(path) - 3) {
419                           postfatal("\
420 cscope: reffile too long, cannot be > %d characters\n", sizeof(path) - 3);
421                           /* NOTREACHED */
422                     }
423                     strcpy(path, s);
424 #ifdef SHORT_NAMES_ONLY
425                     /* System V has a 14 character limit */
426                     s = mybasename(path);
427                     if (strlen(s) > 11) {
428                         s[11] = '\0';
429                     }
430 #endif
431                     s = path + strlen(path);
432                     strcpy(s, ".in");
433                     invname = my_strdup(path);
434                     strcpy(s, ".po");
435                     invpost = my_strdup(path);
436                     break;
437                 case 'F':       /* symbol reference lines file */
438                     reflines = s;
439                     break;
440                 case 'i':       /* file containing file names */
441                     namefile = s;
442                     break;
443                 case 'I':       /* #include file directory */
444                     includedir(s);
445                     break;
446                 case 'p':       /* file path components to display */
447                     if (*s < '0' || *s > '9' ) {
448                         fprintf(stderr, "\
449 %s: -p option: missing or invalid numeric value\n", 
450                                 argv0);
451                         goto usage;
452                     }
453                     dispcomponents = atoi(s);
454                     break;
455                 case 'P':       /* prepend path to file names */
456                     prependpath = s;
457                     break;
458                 case 's':       /* additional source directory */
459                 case 'S':
460                     sourcedir(s);
461                     break;
462                 }
463                 goto nextarg;
464             default:
465                 fprintf(stderr, "%s: unknown option: -%c\n", argv0, 
466                         *s);
467             usage:
468                 usage();
469                 fprintf(stderr, "Try the -h option for more information.\n");
470                 myexit(1);
471             } /* switch(option letter) */
472         } /* for(option) */
473     nextarg:    
474         ;
475     } /* while(argv) */
476
477  lastarg:
478 #endif
479     /* read the environment */
480     editor = mygetenv("EDITOR", EDITOR);
481     editor = mygetenv("VIEWER", editor); /* use viewer if set */
482     editor = mygetenv("CSCOPE_EDITOR", editor); /* has last word */
483     home = mygetenv("HOME", HOME);
484     shell = mygetenv("SHELL", SHELL);
485     lineflag = mygetenv("CSCOPE_LINEFLAG", LINEFLAG);
486     lineflagafterfile = getenv("CSCOPE_LINEFLAG_AFTER_FILE") ? 1 : 0;
487     tmpdir = mygetenv("TMPDIR", TMPDIR);
488
489     /* XXX remove if/when clearerr() in dir.c does the right thing. */
490     if (namefile && strcmp(namefile, "-") == 0 && !buildonly) {
491         postfatal("cscope: Must use -b if file list comes from stdin\n");
492         /* NOTREACHED */
493     }
494
495     /* make sure that tmpdir exists */
496     if (lstat (tmpdir, &stat_buf)) {
497         fprintf (stderr, "\
498 cscope: Temporary directory %s does not exist or cannot be accessed\n", 
499                  tmpdir);
500         fprintf (stderr, "\
501 cscope: Please create the directory or set the environment variable\n\
502 cscope: TMPDIR to a valid directory\n");
503         myexit(1);
504     }
505
506     /* create the temporary file names */
507     orig_umask = umask(S_IRWXG|S_IRWXO);
508     pid = getpid();
509     snprintf(tempdirpv, sizeof(tempdirpv), "%s/cscope.%d", tmpdir, pid);
510     if(mkdir(tempdirpv,S_IRWXU)) {
511         fprintf(stderr, "\
512 cscope: Could not create private temp dir %s\n",
513                 tempdirpv);
514         myexit(1);
515     }
516     umask(orig_umask);
517
518     snprintf(temp1, sizeof(temp1), "%s/cscope.1", tempdirpv);
519     snprintf(temp2, sizeof(temp2), "%s/cscope.2", tempdirpv);
520
521     /* if running in the foreground */
522     if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
523         /* cleanup on the interrupt and quit signals */
524         signal(SIGINT, myexit);
525         signal(SIGQUIT, myexit);
526     }
527     /* cleanup on the hangup signal */
528     signal(SIGHUP, myexit);
529
530     /* ditto the TERM signal */
531     signal(SIGTERM, myexit);
532
533     /* ignore PIPE signal, so myexit() will have a chance to clean up in
534      * linemode, while in curses mode the "|" command can cause a pipe signal
535      * too
536      */
537     signal(SIGPIPE, SIG_IGN);
538
539     /* if the database path is relative and it can't be created */
540     if (reffile[0] != '/' && access(".", WRITE) != 0) {
541
542         /* put it in the home directory if the database may not be
543          * up-to-date or doesn't exist in the relative directory,
544          * so a database in the current directory will be
545          * used instead of failing to open a non-existant database in
546          * the home directory
547          */
548         snprintf(path, sizeof(path), "%s/%s", home, reffile);
549         if (isuptodate == NO || access(path, READ) == 0) {
550             reffile = my_strdup(path);
551             snprintf(path, sizeof(path), "%s/%s", home, invname);
552             invname = my_strdup(path);
553             snprintf(path, sizeof(path), "%s/%s", home, invpost);
554             invpost = my_strdup(path);
555         }
556     }
557
558     if (linemode == NO) {
559         signal(SIGINT, SIG_IGN);        /* ignore interrupts */
560
561 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
562         winch_action.sa_sigaction = sigwinch_handler;
563         sigemptyset(&winch_action.sa_mask);
564         winch_action.sa_flags = SA_SIGINFO;
565         sigaction(SIGWINCH,&winch_action,NULL);
566 #endif
567
568         /* initialize the curses display package */
569         initscr();      /* initialize the screen */
570         entercurses();
571 #if TERMINFO
572         keypad(stdscr, TRUE);   /* enable the keypad */
573 # ifdef HAVE_FIXKEYPAD
574         fixkeypad();    /* fix for getch() intermittently returning garbage */
575 # endif
576 #endif /* TERMINFO */
577 #if UNIXPC
578         standend();     /* turn off reverse video */
579 #endif
580         dispinit();     /* initialize display parameters */
581         setfield();     /* set the initial cursor position */
582         clearmsg();     /* clear any build progress message */
583         display();      /* display the version number and input fields */
584     }
585
586
587     /* if the cross-reference is to be considered up-to-date */
588     if (isuptodate == YES) {
589         if ((oldrefs = vpfopen(reffile, "rb")) == NULL) {
590             postfatal("cscope: cannot open file %s\n", reffile);
591             /* NOTREACHED */
592         }
593         /* get the crossref file version but skip the current directory */
594         if (fscanf(oldrefs, "cscope %d %*s", &fileversion) != 1) {
595             postfatal("cscope: cannot read file version from file %s\n", 
596                       reffile);
597             /* NOTREACHED */
598         }
599         if (fileversion >= 8) {
600
601             /* override these command line options */
602             compress = YES;
603             invertedindex = NO;
604
605             /* see if there are options in the database */
606             for (;;) {
607                 getc(oldrefs);  /* skip the blank */
608                 if ((c = getc(oldrefs)) != '-') {
609                     ungetc(c, oldrefs);
610                     break;
611                 }
612                 switch (getc(oldrefs)) {
613                 case 'c':       /* ASCII characters only */
614                     compress = NO;
615                     break;
616                 case 'q':       /* quick search */
617                     invertedindex = YES;
618                     fscanf(oldrefs, "%ld", &totalterms);
619                     break;
620                 case 'T':       /* truncate symbols to 8 characters */
621                     dbtruncated = YES;
622                     trun_syms = YES;
623                     break;
624                 }
625             }
626             initcompress();
627             seek_to_trailer(oldrefs);
628         }
629         /* skip the source and include directory lists */
630         skiplist(oldrefs);
631         skiplist(oldrefs);
632
633         /* get the number of source files */
634         if (fscanf(oldrefs, "%lu", &nsrcfiles) != 1) {
635             postfatal("\
636 cscope: cannot read source file size from file %s\n", reffile);
637             /* NOTREACHED */
638         }
639         /* get the source file list */
640         srcfiles = mymalloc(nsrcfiles * sizeof(char *));
641         if (fileversion >= 9) {
642
643             /* allocate the string space */
644             if (fscanf(oldrefs, "%d", &oldnum) != 1) {
645                 postfatal("\
646 cscope: cannot read string space size from file %s\n", reffile);
647                 /* NOTREACHED */
648             }
649             s = mymalloc(oldnum);
650             getc(oldrefs);      /* skip the newline */
651                         
652             /* read the strings */
653             if (fread(s, oldnum, 1, oldrefs) != 1) {
654                 postfatal("\
655 cscope: cannot read source file names from file %s\n", reffile);
656                 /* NOTREACHED */
657             }
658             /* change newlines to nulls */
659             for (i = 0; i < nsrcfiles; ++i) {
660                 srcfiles[i] = s;
661                 for (++s; *s != '\n'; ++s) {
662                     ;
663                 }
664                 *s = '\0';
665                 ++s;
666             }
667             /* if there is a file of source file names */
668             if ((namefile != NULL && (names = vpfopen(namefile, "r")) != NULL)
669                 || (names = vpfopen(NAMEFILE, "r")) != NULL) {
670         
671                 /* read any -p option from it */
672                 while (fgets(path, sizeof(path), names) != NULL && *path == '-') {
673                     i = path[1];
674                     s = path + 2;               /* for "-Ipath" */
675                     if (*s == '\0') {   /* if "-I path" */
676                         fgets(path, sizeof(path), names);
677                         s = path;
678                     }
679                     switch (i) {
680                     case 'p':   /* file path components to display */
681                         if (*s < '0' || *s > '9') {
682                             posterr("cscope: -p option in file %s: missing or invalid numeric value\n",                                                                 namefile);
683
684                         }
685                         dispcomponents = atoi(s);
686                     }
687                 }
688                 fclose(names);
689             }
690         } else {
691             for (i = 0; i < nsrcfiles; ++i) {
692                 if (!fgets(path, sizeof(path), oldrefs) ) {
693                     postfatal("\
694 cscope: cannot read source file name from file %s\n", 
695                               reffile);
696                     /* NOTREACHED */
697                 }
698                 srcfiles[i] = my_strdup(path);
699             }
700         }
701         fclose(oldrefs);
702     } else {
703         /* save the file arguments */
704         fileargc = argc;
705         fileargv = argv;
706         
707         /* get source directories from the environment */
708         if ((s = getenv("SOURCEDIRS")) != NULL) {
709             sourcedir(s);
710         }
711         /* make the source file list */
712         srcfiles = mymalloc(msrcfiles * sizeof(char *));
713         makefilelist();
714         if (nsrcfiles == 0) {
715             postfatal("cscope: no source files found\n");
716             /* NOTREACHED */
717         }
718         /* get include directories from the environment */
719         if ((s = getenv("INCLUDEDIRS")) != NULL) {
720             includedir(s);
721         }
722         /* add /usr/include to the #include directory list,
723            but not in kernelmode... kernels tend not to use it. */
724         if (kernelmode == NO) {
725             if (NULL != (s = getenv("INCDIR"))) {
726                 includedir(s);
727             } else {
728                 includedir(DFLT_INCDIR);
729             }
730         }
731
732         /* initialize the C keyword table */
733         initsymtab();
734
735         /* Tell build.c about the filenames to create: */
736         setup_build_filenames(reffile);
737
738         /* build the cross-reference */
739         initcompress();
740         if (linemode == NO || verbosemode == YES)    /* display if verbose as well */
741             postmsg("Building cross-reference...");                 
742         build();
743         if (linemode == NO )
744             clearmsg(); /* clear any build progress message */
745         if (buildonly == YES) {
746             myexit(0);
747         }
748     }
749     opendatabase();
750
751     /* if using the line oriented user interface so cscope can be a 
752        subprocess to emacs or samuel */
753     if (linemode == YES) {
754         if (*Pattern != '\0') {         /* do any optional search */
755             if (search() == YES) {
756                 /* print the total number of lines in
757                  * verbose mode */
758                 if (verbosemode == YES)
759                     printf("cscope: %d lines\n",
760                            totallines);
761
762                 while ((c = getc(refsfound)) != EOF)
763                     putchar(c);
764             }
765         }
766         if (onesearch == YES)
767             myexit(0);
768                 
769         for (;;) {
770             char buf[PATLEN + 2];
771                         
772             printf(">> ");
773             fflush(stdout);
774             if (fgets(buf, sizeof(buf), stdin) == NULL) {
775                 myexit(0);
776             }
777             /* remove any trailing newline character */
778             if (*(s = buf + strlen(buf) - 1) == '\n') {
779                 *s = '\0';
780             }
781             switch (*buf) {
782             case '0':
783             case '1':
784             case '2':
785             case '3':
786             case '4':
787             case '5':
788             case '6':
789             case '7':
790             case '8':
791             case '9':   /* samuel only */
792                 field = *buf - '0';
793                 strcpy(Pattern, buf + 1);
794                 search();
795                 printf("cscope: %d lines\n", totallines);
796                 while ((c = getc(refsfound)) != EOF) {
797                     putchar(c);
798                 }
799                 break;
800
801             case 'c':   /* toggle caseless mode */
802             case ctrl('C'):
803                 if (caseless == NO) {
804                     caseless = YES;
805                 } else {
806                     caseless = NO;
807                 }
808                 egrepcaseless(caseless);
809                 break;
810
811             case 'r':   /* rebuild database cscope style */
812             case ctrl('R'):
813                 freefilelist();
814                 makefilelist();
815                 /* FALLTHROUGH */
816
817             case 'R':   /* rebuild database samuel style */
818                 rebuild();
819                 putchar('\n');
820                 break;
821
822             case 'C':   /* clear file names */
823                 freefilelist();
824                 putchar('\n');
825                 break;
826
827             case 'F':   /* add a file name */
828                 strcpy(path, buf + 1);
829                 if (infilelist(path) == NO &&
830                     (s = inviewpath(path)) != NULL) {
831                     addsrcfile(s);
832                 }
833                 putchar('\n');
834                 break;
835
836             case 'q':   /* quit */
837             case ctrl('D'):
838             case ctrl('Z'):
839                 myexit(0);
840
841             default:
842                 fprintf(stderr, "cscope: unknown command '%s'\n", buf);
843                 break;
844             }
845         }
846         /* NOTREACHED */
847     }
848     /* pause before clearing the screen if there have been error messages */
849     if (errorsfound == YES) {
850         errorsfound = NO;
851         askforreturn();
852     }
853     /* do any optional search */
854     if (*Pattern != '\0') {
855         atfield();              /* move to the input field */
856         command(ctrl('Y'));     /* search */
857     } else if (reflines != NULL) {
858         /* read any symbol reference lines file */
859         readrefs(reflines);
860     }
861     display();          /* update the display */
862
863     for (;;) {
864         if (!selecting)
865             atfield();  /* move to the input field */
866
867         /* exit if the quit command is entered */
868         if ((c = mygetch()) == EOF || c == ctrl('D')) {
869             break;
870         }
871         if (c == ctrl('Z')) {
872 #ifdef SIGTSTP
873             kill(0, SIGTSTP);
874             continue;
875 #else
876             break;
877 #endif
878         }
879         /* execute the commmand, updating the display if necessary */
880         if (command(c) == YES) {
881             display();
882         }
883
884         if (selecting) {
885             move(displine[curdispline], 0);
886             refresh();
887         }
888     }
889     /* cleanup and exit */
890     myexit(0);
891     /* NOTREACHED */
892     return 0;           /* avoid warning... */
893 }
894
895 void
896 cannotopen(char *file)
897 {
898     posterr("Cannot open file %s", file);
899 }
900
901 /* FIXME MTE - should use postfatal here */
902 void
903 cannotwrite(char *file)
904 {
905     char        msg[MSGLEN + 1];
906
907     snprintf(msg, sizeof(msg), "Removed file %s because write failed", file);
908
909     myperror(msg);      /* display the reason */
910
911     unlink(file);
912     myexit(1);  /* calls exit(2), which closes files */
913 }
914
915
916 /* set up the digraph character tables for text compression */
917 static void
918 initcompress(void)
919 {
920     int i;
921         
922     if (compress == YES) {
923         for (i = 0; i < 16; ++i) {
924             dicode1[(unsigned char) (dichar1[i])] = i * 8 + 1;
925         }
926         for (i = 0; i < 8; ++i) {
927             dicode2[(unsigned char) (dichar2[i])] = i + 1;
928         }
929     }
930 }
931
932 /* skip the list in the cross-reference file */
933
934 static void
935 skiplist(FILE *oldrefs)
936 {
937     int i;
938         
939     if (fscanf(oldrefs, "%d", &i) != 1) {
940         postfatal("cscope: cannot read list size from file %s\n", reffile);
941         /* NOTREACHED */
942     }
943     while (--i >= 0) {
944         if (fscanf(oldrefs, "%*s") != 0) {
945             postfatal("cscope: cannot read list name from file %s\n", reffile);
946             /* NOTREACHED */
947         }
948     }
949 }
950
951
952 /* enter curses mode */
953 void
954 entercurses(void)
955 {
956     incurses = YES;
957 #ifndef __MSDOS__ /* HBB 20010313 */
958     nonl();                 /* don't translate an output \n to \n\r */
959 #endif
960     raw();                      /* single character input */
961     noecho();                   /* don't echo input characters */
962     clear();                    /* clear the screen */
963     mouseinit();                /* initialize any mouse interface */
964     drawscrollbar(topline, nextline);
965 }
966
967
968 /* exit curses mode */
969 void
970 exitcurses(void)
971 {
972         /* clear the bottom line */
973         move(LINES - 1, 0);
974         clrtoeol();
975         refresh();
976
977         /* exit curses and restore the terminal modes */
978         endwin();
979         incurses = NO;
980
981         /* restore the mouse */
982         mousecleanup();
983         fflush(stdout);
984 }
985
986
987 /* normal usage message */
988 static void
989 usage(void)
990 {
991         fprintf(stderr, "Usage: cscope [-bcCdehklLqRTuUvV] [-f file] [-F file] [-i file] [-I dir] [-s dir]\n");
992         fprintf(stderr, "              [-p number] [-P path] [-[0-8] pattern] [source files]\n");
993 }
994
995
996 /* long usage message */
997 static void
998 longusage(void)
999 {
1000         usage();
1001         fprintf(stderr, "\
1002 \n\
1003 -b            Build the cross-reference only.\n\
1004 -C            Ignore letter case when searching.\n\
1005 -c            Use only ASCII characters in the cross-ref file (don't compress).\n\
1006 -d            Do not update the cross-reference.\n\
1007 -e            Suppress the <Ctrl>-e command prompt between files.\n\
1008 -F symfile    Read symbol reference lines from symfile.\n\
1009 -f reffile    Use reffile as cross-ref file name instead of %s.\n",
1010                 REFFILE);
1011         fprintf(stderr, "\
1012 -h            This help screen.\n\
1013 -I incdir     Look in incdir for any #include files.\n\
1014 -i namefile   Browse through files listed in namefile, instead of %s\n",
1015                 NAMEFILE);
1016         fprintf(stderr, "\
1017 -k            Kernel Mode - don't use %s for #include files.\n",
1018                 DFLT_INCDIR);
1019         fputs("\
1020 -L            Do a single search with line-oriented output.\n\
1021 -l            Line-oriented interface.\n\
1022 -num pattern  Go to input field num (counting from 0) and find pattern.\n\
1023 -P path       Prepend path to relative file names in pre-built cross-ref file.\n\
1024 -p n          Display the last n file path components.\n\
1025 -q            Build an inverted index for quick symbol searching.\n\
1026 -R            Recurse directories for files.\n\
1027 -s dir        Look in dir for additional source  files.\n\
1028 -T            Use only the first eight characters to match against C symbols.\n\
1029 -U            Check file time stamps.\n\
1030 -u            Unconditionally build the cross-reference file.\n\
1031 -v            Be more verbose in line mode.\n\
1032 -V            Print the version number.\n\
1033 \n\
1034 Please see the manpage for more information.\n",
1035               stderr);
1036 }
1037
1038 /* cleanup and exit */
1039
1040 void
1041 myexit(int sig)
1042 {
1043         /* HBB 20010313; close file before unlinking it. Unix may not care
1044          * about that, but DOS absolutely needs it */
1045         if (refsfound != NULL)
1046                 fclose(refsfound);
1047         
1048         /* remove any temporary files */
1049         if (temp1[0] != '\0') {
1050                 unlink(temp1);
1051                 unlink(temp2);
1052                 rmdir(tempdirpv);               
1053         }
1054         /* restore the terminal to its original mode */
1055         if (incurses == YES) {
1056                 exitcurses();
1057         }
1058         /* dump core for debugging on the quit signal */
1059         if (sig == SIGQUIT) {
1060                 abort();
1061         }
1062         /* HBB 20000421: be nice: free allocated data */
1063         freefilelist();
1064         freeinclist();
1065         freesrclist();
1066         freecrossref();
1067         free_newbuildfiles();
1068
1069         exit(sig);
1070 }