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 /*
35 * gtags-cscope - interactive C symbol cross-reference (cscope)
36 *
37 * main functions
38 */
39
40 #include "global-cscope.h"
41 #include "char.h"
42 #include "strbuf.h"
43 #include "build.h"
44 #include "version-cscope.h" /* FILEVERSION and FIXVERSION */
45
46 /* for libutil */
47 #include "env.h"
48 #include "gparam.h"
49 #include "path.h"
50 #include "test.h"
51 #include "version.h"
52 /* usage */
53 #include "const.h"
54
55 #include <stdlib.h> /* atoi */
56 #include <unistd.h>
57 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
58 #include <ncurses.h>
59 #else
60 #include <curses.h>
61 #endif
62 #include <sys/types.h> /* needed by stat.h */
63 #include <sys/stat.h> /* stat */
64 #include <signal.h>
65
66 #if defined(_WIN32) && !defined(__CYGWIN__)
67 #define S_IRWXG 0070
68 #define S_IRWXO 0007
69 #define mkdir(path,mode) mkdir(path)
70 #endif
71
72 /* defaults for unset environment variables */
73 /** text editor */
74 #if defined(__DJGPP__) || (defined(_WIN32) && !defined(__CYGWIN__))
75 #define EDITOR "tde"
76 #else
77 #define EDITOR "vi"
78 #endif
79
80 /** no $HOME --> use root directory */
81 #define HOME "/"
82 /** shell executable */
83 #define SHELL "sh"
84
85 /** default: used by vi and emacs */
86 #define LINEFLAG "+%s"
87 /** temp dir */
88 #define TMPDIR "/tmp"
89
90 char *editor, *shell, *lineflag; /**< environment variables */
91 char *global_command; /**< "global" by default */
92 char *gtags_command; /**< "gtags" by default */
93 char *home; /**< Home directory */
94 BOOL lineflagafterfile;
95 char *argv0; /**< command name */
96 int dispcomponents = 1; /**< file path components to display */
97 #if CCS
98 BOOL displayversion; /**< display the C Compilation System version */
99 #endif
100 BOOL editallprompt = YES; /**< prompt between editing files */
101 BOOL incurses = NO; /**< in curses */
102 BOOL isuptodate; /**< consider the crossref up-to-date */
103 BOOL linemode = NO; /**< use line oriented user interface */
104 BOOL verbosemode = NO; /**< print extra information on line mode */
105 BOOL absolutepath = NO; /**< print absolute path name */
106 BOOL ignoresigint = NO; /**< ignore SIGINT signal */
107 BOOL ogs; /**< display OGS book and subsystem names */
108 char *prependpath; /**< prepend path to file names */
109 FILE *refsfound; /**< references found file */
110 char temp1[PATHLEN + 1]; /**< temporary file name */
111 char temp2[PATHLEN + 1]; /**< temporary file name */
112 char tempdirpv[PATHLEN + 1]; /**< private temp directory */
113 char tempstring[TEMPSTRING_LEN + 1]; /**< use this as a buffer, instead of yytext,
114 * which had better be left alone */
115 char *tmpdir; /**< temporary directory */
116
117 static BOOL onesearch; /**< one search only in line mode */
118 static char *reflines; /**< symbol reference lines file */
119
120 /* Internal prototypes: */
121 static void longusage(void);
122 static void usage(void);
123 int qflag;
124
125 #ifdef HAVE_FIXKEYPAD
126 void fixkeypad();
127 #endif
128
129 #if defined(KEY_RESIZE) && defined(SIGWINCH)
130 void
sigwinch_handler(int sig,siginfo_t * info,void * unused)131 sigwinch_handler(int sig, siginfo_t *info, void *unused)
132 {
133 (void) sig;
134 (void) info;
135 (void) unused;
136 if(incurses == YES)
137 ungetch(KEY_RESIZE);
138 }
139 #endif
140
141 int
main(int argc,char ** argv)142 main(int argc, char **argv)
143 {
144 char *s;
145 int c;
146 pid_t pid;
147 struct stat stat_buf;
148 mode_t orig_umask;
149 #if defined(KEY_RESIZE) && defined(SIGWINCH)
150 struct sigaction winch_action;
151 #endif
152
153 /* save the command name for messages */
154 argv0 = argv[0];
155
156 /* set the options */
157 while (--argc > 0 && (*++argv)[0] == '-') {
158 /* HBB 20030814: add GNU-style --help and --version options */
159 if (strequal(argv[0], "--help")
160 || strequal(argv[0], "-h")) {
161 longusage();
162 myexit(0);
163 }
164 if (strequal(argv[0], "--version")
165 || strequal(argv[0], "-V")) {
166 #if CCS
167 displayversion = YES;
168 #else
169 fprintf(stderr, "%s: %s (based on cscope version %d%s)\n", argv0, get_version(),
170 FILEVERSION, FIXVERSION);
171 myexit(0);
172 #endif
173 }
174
175 for (s = argv[0] + 1; *s != '\0'; s++) {
176
177 /* look for an input field number */
178 if (isdigit((unsigned char) *s)) {
179 field = *s - '0';
180 if (field > 8) {
181 field = 8;
182 }
183 if (*++s == '\0' && --argc > 0) {
184 s = *++argv;
185 }
186 if (strlen(s) > PATLEN) {
187 postfatal("\
188 gtags-cscope: pattern too long, cannot be > %d characters\n", PATLEN);
189 /* NOTREACHED */
190 }
191 strcpy(Pattern, s);
192 goto nextarg;
193 }
194 switch (*s) {
195 case '-': /* end of options */
196 --argc;
197 ++argv;
198 goto lastarg;
199 case 'a': /* absolute path name */
200 absolutepath = YES;
201 break;
202 case 'b': /* only build the cross-reference */
203 buildonly = YES;
204 linemode = YES;
205 break;
206 case 'c': /* ASCII characters only in crossref */
207 /* N/A */
208 break;
209 case 'C': /* turn on caseless mode for symbol searches */
210 caseless = YES;
211 break;
212 case 'd': /* consider crossref up-to-date */
213 isuptodate = YES;
214 break;
215 case 'e': /* suppress ^E prompt between files */
216 editallprompt = NO;
217 break;
218 case 'i': /* ignore SIGINT signal */
219 ignoresigint = YES;
220 break;
221 case 'k': /* ignore DFLT_INCDIR */
222 /* N/A */
223 break;
224 case 'L':
225 onesearch = YES;
226 /* FALLTHROUGH */
227 case 'l':
228 linemode = YES;
229 break;
230 case 'v':
231 verbosemode = YES;
232 break;
233 case 'o': /* display OGS book and subsystem names */
234 ogs = YES;
235 break;
236 case 'q': /* quick search */
237 /* N/A */
238 break;
239 case 'T': /* truncate symbols to 8 characters */
240 /* N/A */
241 break;
242 case 'u': /* unconditionally build the cross-reference */
243 /* N/A */
244 break;
245 case 'U': /* assume some files have changed */
246 /* N/A */
247 break;
248 case 'R':
249 usage();
250 break;
251 case 'f': /* alternate cross-reference file */
252 case 'F': /* symbol reference lines file */
253 /* case 'i': file containing file names */
254 case 'I': /* #include file directory */
255 case 'p': /* file path components to display */
256 case 'P': /* prepend path to file names */
257 case 's': /* additional source file directory */
258 case 'S':
259 c = *s;
260 if (*++s == '\0' && --argc > 0) {
261 s = *++argv;
262 }
263 if (*s == '\0') {
264 fprintf(stderr, "%s: -%c option: missing or empty value\n",
265 argv0, c);
266 goto usage;
267 }
268 switch (c) {
269 case 'f': /* alternate cross-reference file (default: cscope.out) */
270 /* N/A */
271 break;
272 case 'F': /* symbol reference lines file */
273 reflines = s;
274 break;
275 case 'i': /* file containing file names (default: cscope.files) */
276 /* N/A */
277 break;
278 case 'I': /* #include file directory */
279 /* N/A */
280 break;
281 case 'p': /* file path components to display */
282 if (*s < '0' || *s > '9' ) {
283 fprintf(stderr, "\
284 %s: -p option: missing or invalid numeric value\n",
285 argv0);
286 goto usage;
287 }
288 dispcomponents = atoi(s);
289 break;
290 case 'P': /* prepend path to file names */
291 /* N/A */
292 break;
293 case 's': /* additional source directory */
294 case 'S':
295 /* N/A */
296 break;
297 }
298 goto nextarg;
299 default:
300 fprintf(stderr, "%s: unknown option: -%c\n", argv0,
301 *s);
302 usage:
303 usage();
304 fprintf(stderr, "Try the -h option for more information.\n");
305 myexit(1);
306 } /* switch(option letter) */
307 } /* for(option) */
308 nextarg:
309 ;
310 } /* while(argv) */
311
312 lastarg:
313 /* read the environment */
314 editor = mygetenv("EDITOR", EDITOR);
315 editor = mygetenv("VIEWER", editor); /* use viewer if set */
316 editor = mygetenv("CSCOPE_EDITOR", editor); /* has last word */
317 home = mygetenv("HOME", HOME);
318 global_command = mygetenv("GTAGSGLOBAL", "global");
319 gtags_command = mygetenv("GTAGSGTAGS", "gtags");
320 #if defined(_WIN32) || defined(__DJGPP__)
321 shell = mygetenv("COMSPEC", SHELL);
322 shell = mygetenv("SHELL", shell);
323 tmpdir = mygetenv("TMP", TMPDIR);
324 tmpdir = mygetenv("TMPDIR", tmpdir);
325 #else
326 shell = mygetenv("SHELL", SHELL);
327 tmpdir = mygetenv("TMPDIR", TMPDIR);
328 #endif
329 lineflag = mygetenv("CSCOPE_LINEFLAG", LINEFLAG);
330 lineflagafterfile = getenv("CSCOPE_LINEFLAG_AFTER_FILE") ? 1 : 0;
331
332 /* make sure that tmpdir exists */
333 if (lstat (tmpdir, &stat_buf)) {
334 fprintf (stderr, "\
335 cscope: Temporary directory %s does not exist or cannot be accessed\n",
336 tmpdir);
337 fprintf (stderr, "\
338 cscope: Please create the directory or set the environment variable\n\
339 cscope: TMPDIR to a valid directory\n");
340 myexit(1);
341 }
342
343 /* create the temporary file names */
344 orig_umask = umask(S_IRWXG|S_IRWXO);
345 pid = getpid();
346 snprintf(tempdirpv, sizeof(tempdirpv), "%s/cscope.%d", tmpdir, pid);
347 if(mkdir(tempdirpv,S_IRWXU)) {
348 fprintf(stderr, "\
349 cscope: Could not create private temp dir %s\n",
350 tempdirpv);
351 myexit(1);
352 }
353 umask(orig_umask);
354
355 snprintf(temp1, sizeof(temp1), "%s/cscope.1", tempdirpv);
356 snprintf(temp2, sizeof(temp2), "%s/cscope.2", tempdirpv);
357
358 /* if running in the foreground */
359 if (signal(SIGINT, SIG_IGN) != SIG_IGN && ignoresigint == NO) {
360 /* cleanup on the interrupt and quit signals */
361 signal(SIGINT, myexit);
362 #ifdef SIGQUIT
363 signal(SIGQUIT, myexit);
364 #endif
365 }
366 /* cleanup on the hangup signal */
367 #ifdef SIGHUP
368 signal(SIGHUP, myexit);
369 #endif
370
371 /* ditto the TERM signal */
372 signal(SIGTERM, myexit);
373
374 if (linemode == NO) {
375 signal(SIGINT, SIG_IGN); /* ignore interrupts */
376 #ifdef SIGPIPE
377 signal(SIGPIPE, SIG_IGN);/* | command can cause pipe signal */
378 #endif
379
380 #if defined(KEY_RESIZE) && defined(SIGWINCH)
381 winch_action.sa_sigaction = sigwinch_handler;
382 sigemptyset(&winch_action.sa_mask);
383 winch_action.sa_flags = SA_SIGINFO;
384 sigaction(SIGWINCH,&winch_action,NULL);
385 #endif
386
387 /* initialize the curses display package */
388 initscr(); /* initialize the screen */
389 entercurses();
390 #if TERMINFO
391 keypad(stdscr, TRUE); /* enable the keypad */
392 # ifdef HAVE_FIXKEYPAD
393 fixkeypad(); /* fix for getch() intermittently returning garbage */
394 # endif
395 #endif /* TERMINFO */
396 #if UNIXPC
397 standend(); /* turn off reverse video */
398 #endif
399 dispinit(); /* initialize display parameters */
400 setfield(); /* set the initial cursor position */
401 clearmsg(); /* clear any build progress message */
402 display(); /* display the version number and input fields */
403 }
404
405 /* if the cross-reference is to be considered up-to-date */
406 if (isuptodate == YES) {
407 STRBUF *sb = strbuf_open(0);
408 strbuf_sprintf(sb, "%s -p >" NULL_DEVICE, quote_shell(global_command));
409 if (system(strbuf_value(sb)) != 0) {
410 postfatal("gtags-cscope: GTAGS not found. Please invoke again without -d option.\n");
411 /* NOTREACHED */
412 }
413 strbuf_close(sb);
414 } else {
415 if (linemode == NO || verbosemode == YES) /* display if verbose as well */
416 postmsg("Building cross-reference...");
417 rebuild();
418 if (linemode == NO )
419 clearmsg(); /* clear any build progress message */
420 if (buildonly == YES) {
421 myexit(0);
422 }
423 }
424
425 /* opendatabase(); */
426
427 /* if using the line oriented user interface so cscope can be a
428 subprocess to emacs or samuel */
429 if (linemode == YES) {
430 if (*Pattern != '\0') { /* do any optional search */
431 if (search() == YES) {
432 /* print the total number of lines in
433 * verbose mode */
434 if (verbosemode == YES)
435 printf("cscope: %d lines\n",
436 totallines);
437
438 while ((c = getc(refsfound)) != EOF)
439 putchar(c);
440 }
441 }
442 if (onesearch == YES)
443 myexit(0);
444
445 for (;;) {
446 char buf[PATLEN + 2];
447
448 printf(">> ");
449 fflush(stdout);
450 if (fgets(buf, sizeof(buf), stdin) == NULL) {
451 myexit(0);
452 }
453 /* remove any trailing newline character */
454 if (*(s = buf + strlen(buf) - 1) == '\n') {
455 *s = '\0';
456 }
457 switch (*buf) {
458 case '0':
459 case '1':
460 case '2':
461 case '3':
462 case '4':
463 case '5':
464 case '6':
465 case '7':
466 case '8':
467 case '9': /* Vim 8.0 calls this function. */
468 field = *buf - '0';
469 strcpy(Pattern, buf + 1);
470 search();
471 printf("cscope: %d lines\n", totallines);
472 while ((c = getc(refsfound)) != EOF) {
473 putchar(c);
474 }
475 break;
476
477 case 'c': /* toggle caseless mode */
478 case ctrl('C'):
479 if (caseless == NO) {
480 caseless = YES;
481 } else {
482 caseless = NO;
483 }
484 break;
485
486 case 'r': /* rebuild database cscope style */
487 case ctrl('R'):
488 rebuild();
489 putchar('\n');
490 break;
491
492 case 'C': /* clear file names */
493 /* N/A */
494 putchar('\n');
495 break;
496
497 case 'F': /* add a file name */
498 /* N/A */
499 putchar('\n');
500 break;
501
502 case 'q': /* quit */
503 case ctrl('D'):
504 case ctrl('Z'):
505 myexit(0);
506
507 default:
508 fprintf(stderr, "gtags-cscope: unknown command '%s'\n", buf);
509 break;
510 }
511 }
512 /* NOTREACHED */
513 }
514 /* do any optional search */
515 if (*Pattern != '\0') {
516 atfield(); /* move to the input field */
517 command(ctrl('Y')); /* search */
518 } else if (reflines != NULL) {
519 /* read any symbol reference lines file */
520 readrefs(reflines);
521 }
522 display(); /* update the display */
523
524 for (;;) {
525 if (!selecting)
526 atfield(); /* move to the input field */
527
528 /* exit if the quit command is entered */
529 if ((c = mygetch()) == EOF || c == ctrl('D') || c == ctrl('Z')) {
530 break;
531 }
532 /* execute the commmand, updating the display if necessary */
533 if (command(c) == YES) {
534 display();
535 }
536
537 if (selecting) {
538 move(displine[curdispline], 0);
539 refresh();
540 }
541 }
542 /* cleanup and exit */
543 myexit(0);
544 /* NOTREACHED */
545 return 0; /* avoid warning... */
546 }
547
548 void
cannotopen(char * file)549 cannotopen(char *file)
550 {
551 posterr("Cannot open file %s", file);
552 }
553
554 /* FIXME MTE - should use postfatal here */
555 void
cannotwrite(char * file)556 cannotwrite(char *file)
557 {
558 char msg[MSGLEN + 1];
559
560 snprintf(msg, sizeof(msg), "Removed file %s because write failed", file);
561 myperror(msg); /* display the reason */
562
563 unlink(file);
564 myexit(1); /* calls exit(2), which closes files */
565 }
566
567 /** enter curses mode */
568 void
entercurses(void)569 entercurses(void)
570 {
571 incurses = YES;
572 #ifndef __MSDOS__ /* HBB 20010313 */
573 nonl(); /* don't translate an output \n to \n\r */
574 #endif
575 raw(); /* single character input */
576 noecho(); /* don't echo input characters */
577 clear(); /* clear the screen */
578 mouseinit(); /* initialize any mouse interface */
579 drawscrollbar(topline, nextline);
580 }
581
582
583 /** exit curses mode */
584 void
exitcurses(void)585 exitcurses(void)
586 {
587 /* clear the bottom line */
588 move(LINES - 1, 0);
589 clrtoeol();
590 refresh();
591
592 /* exit curses and restore the terminal modes */
593 endwin();
594 incurses = NO;
595
596 /* restore the mouse */
597 mousecleanup();
598 fflush(stdout);
599 }
600
601
602 /** normal usage message */
603 static void
usage(void)604 usage(void)
605 {
606 fputs(usage_const, stderr);
607 }
608
609
610 /** long usage message */
611 static void
longusage(void)612 longusage(void)
613 {
614 fputs(usage_const, stdout);
615 fputs(help_const, stdout);
616 }
617
618 /** cleanup and exit */
619
620 void
myexit(int sig)621 myexit(int sig)
622 {
623 /* HBB 20010313; close file before unlinking it. Unix may not care
624 * about that, but DOS absolutely needs it */
625 if (refsfound != NULL)
626 fclose(refsfound);
627
628 /* remove any temporary files */
629 if (temp1[0] != '\0') {
630 unlink(temp1);
631 unlink(temp2);
632 rmdir(tempdirpv);
633 }
634 /* restore the terminal to its original mode */
635 if (incurses == YES) {
636 exitcurses();
637 }
638 /* dump core for debugging on the quit signal */
639 #ifdef SIGQUIT
640 if (sig == SIGQUIT) {
641 abort();
642 }
643 #endif
644 /* HBB 20000421: be nice: free allocated data */
645 exit(sig);
646 }
647