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