/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- help
- check_dbpath
- get_global_path
- main
- get_line
- update
- print_case_distinction
- include_pattern
- command_help
- command_loop
- execute_command
- search
/*
* Copyright (c) 2006, 2008
* Tama Communications Corporation
*
* This file is part of GNU GLOBAL.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "getopt.h"
#include "global.h"
#include "const.h"
/* static void usage(void); */
static void help(void);
static void check_dbpath(void);
static void get_global_path(void);
int main(int, char **);
static char *get_line(void);
static void update(void);
static void print_case_distinction(void);
static char *include_pattern(const char *);
static void command_help(void);
static void command_loop(void);
static int execute_command(STRBUF *, const int, const int, const char *);
static void search(int, const char *);
int show_version;
int show_help;
int qflag;
int vflag;
#define NA -1
/*
static void
usage(void)
{
fputs(usage_const, stderr);
exit(2);
}
*/
static void
help(void)
{
fputs(usage_const, stdout);
fputs(help_const, stdout);
exit(0);
}
static struct option const long_options[] = {
{"ignore-case", no_argument, NULL, 'C'},
{"quiet", no_argument, NULL, 'q'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, &show_version, 1},
{"help", no_argument, &show_help, 1},
{ 0 }
};
char global_path[MAXFILLEN];
int ignore_case;
/*
* Check whether or not GTAGS exist.
*
* If GTAGS not found, this function abort with a message.
*/
static void
check_dbpath(void)
{
char cwd[MAXPATHLEN+1];
char root[MAXPATHLEN+1];
char dbpath[MAXPATHLEN+1];
getdbpath(cwd, root, dbpath, vflag);
}
/*
* Get global(1)'s path.
*/
static void
get_global_path(void)
{
const char *p;
if (!(p = usable("global")))
die("global command required but not found.");
strlimcpy(global_path, p, sizeof(global_path));
}
int
main(int argc, char **argv)
{
const char *av = NULL;
int optchar;
int option_index = 0;
while ((optchar = getopt_long(argc, argv, "bCcdeF:f:hI:i:kLlp:qRs:TUuVv0123456789", long_options, &option_index)) != EOF) {
switch (optchar) {
case 0:
break;
case 'C':
ignore_case = 1;
break;
case 'q':
qflag++;
break;
case 'v':
vflag++;
break;
default:
break;
}
}
if (show_help)
help();
argc -= optind;
argv += optind;
if (!av)
av = (argc > 0) ? *argv : NULL;
if (show_version)
version(av, vflag);
/*
* Get global(1)'s path. If not found, abort with a message.
*/
get_global_path();
/*
* Check dbpath. If dbpath not found, abort with a message.
*/
check_dbpath();
/*
* Command loop
*/
command_loop();
return 0;
}
/*
* Read line with a prompt.
*
* This is part of cscope protocol.
*/
static char *
get_line(void)
{
STATIC_STRBUF(sb);
/* Prompt */
fputs(">> ", stdout);
fflush(stdout);
if (strbuf_fgets(sb, stdin, STRBUF_NOCRLF) == NULL)
return NULL;
return strbuf_value(sb);
}
/*
* Update tag files.
*/
static void
update(void)
{
STATIC_STRBUF(command);
strbuf_clear(command);
strbuf_sprintf(command, "%s -u", global_path);
if (vflag) {
strbuf_putc(command, 'v');
fprintf(stderr, "gscope: %s\n", strbuf_value(command));
}
system(strbuf_value(command));
}
/*
* print verbose message about case distinction.
*/
static void
print_case_distinction(void)
{
if (vflag) {
const char *msg = ignore_case ?
"ignore letter case when searching" :
"use letter case when searching";
fprintf(stderr, "gscope: %s\n", msg);
}
}
/*
* get pattern which match to #include lines.
*
* i) arg include file name
* r) pattern which match to #include lines
*/
static char *
include_pattern(const char *arg)
{
#if defined(_WIN32)
#define INCLUDE "^[ \t]*#[ \t]*include[ \t].*[\\\"\"</\\]%s[\\\"\">]"
#elif defined(__DJGPP__)
#define INCLUDE "^[ \t]*#[ \t]*include[ \t].*[\"</\\]%s[\">]"
#else
#define INCLUDE "^[ \t]*#[ \t]*include[ \t].*[\"</]%s[\">]"
#endif
STATIC_STRBUF(pat);
strbuf_clear(pat);
strbuf_sprintf(pat, INCLUDE, quote_string(arg));
return strbuf_value(pat);
}
/*
* show help message.
*/
static void
command_help(void)
{
fprintf(stdout, "0<arg>: Find this C symbol\n");
fprintf(stdout, "1<arg>: Find this definition\n");
fprintf(stdout, "2<arg>: Find functions called by this function\n");
fprintf(stdout, " (Not implemented yet.)\n");
fprintf(stdout, "3<arg>: Find functions calling this function\n");
fprintf(stdout, "4<arg>: Find this text string\n");
fprintf(stdout, "6<arg>: Find this egrep pattern\n");
fprintf(stdout, "7<arg>: Find this file\n");
fprintf(stdout, "8<arg>: Find files #including this file\n");
fprintf(stdout, "c: Toggle ignore/use letter case\n");
fprintf(stdout, "r: Rebuild the database\n");
fprintf(stdout, "q: Quit the session\n");
fprintf(stdout, "h: Show help\n");
}
/*
* command loop.
*
* This is the main body of gtags-cscope.
*/
void
command_loop(void)
{
int com = 0;
char *line;
print_case_distinction();
while ((line = get_line()) != NULL) {
switch (com = *line++) {
/*
* Control command
*/
case 'c': /* c: ignore case or not */
ignore_case ^= 1;
print_case_distinction();
break;
case 'q': /* q: quit the program */
return;
case 'r': /* r: update tag file */
update();
break;
case 'h': /* h: show help */
command_help();
break;
/*
* Search command
*/
case '0':
case '1':
case '2':
case '3':
case '4':
case '6':
case '7':
case '8':
search(com, line);
break;
default:
fputs("cscope: 0 lines\n", stdout);
fflush(stdout);
break;
}
}
}
/*
* Execute global(1) and write the output to the 'sb' string buffer.
*
* o) sb output
* i) com cscope command (0-8)
* i) opt option for global(1)
* i) arg argument for global(1)
* r) number of output
*/
static int
execute_command(STRBUF *sb, const int com, const int opt, const char *arg)
{
#ifdef _WIN32
#define QUOTE '"'
#else
#define QUOTE '\''
#endif
STATIC_STRBUF(command);
STATIC_STRBUF(ib);
FILE *ip;
int count = 0;
strbuf_clear(command);
strbuf_puts(command, global_path);
strbuf_puts(command, " --result=cscope");
if (opt || ignore_case) {
strbuf_putc(command, ' ');
strbuf_putc(command, '-');
if (opt)
strbuf_putc(command, opt);
if (ignore_case)
strbuf_putc(command, 'i');
}
strbuf_putc(command, ' ');
strbuf_putc(command, QUOTE);
strbuf_puts(command, arg);
strbuf_putc(command, QUOTE);
if (!(ip = popen(strbuf_value(command), "r")))
die("cannot execute '%s'.", strbuf_value(command));
if (vflag)
fprintf(stderr, "gscope: %s\n", strbuf_value(command));
/*
* Copy records with little modification.
*/
strbuf_clear(ib);
while (strbuf_fgets(ib, ip, 0)) {
count++;
if (opt == 0) {
strbuf_puts(sb, strbuf_value(ib));
} else {
char *p = strbuf_value(ib);
/* path name */
while (*p && *p != ' ')
strbuf_putc(sb, *p++);
if (*p != ' ')
die("illegal cscope format. (%s)", strbuf_value(ib));
strbuf_putc(sb, *p++);
/* replace pattern with "<unknown>" or "<global>" */
while (*p && *p != ' ')
p++;
if (*p != ' ')
die("illegal cscope format. (%s)", strbuf_value(ib));
if (com == '8')
strbuf_puts(sb, "<global>");
else
strbuf_puts(sb, "<unknown>");
strbuf_putc(sb, *p++);
/* line number */
while (*p && *p != ' ')
strbuf_putc(sb, *p++);
if (*p != ' ')
die("illegal cscope format. (%s)", strbuf_value(ib));
strbuf_putc(sb, *p++);
/* line image */
if (*p == '\n')
strbuf_puts(sb, "<unknown>\n");
else
strbuf_puts(sb, p);
}
}
if (pclose(ip) < 0)
die("terminated abnormally.");
return count;
}
/*
* Execute retrieval command.
*
* i) command 0: Find this C symbol
* 1: Find this definition
* 2: Find functions called by this function (not supported)
* 3: Find functions calling this function
* 4: Find this text string
* 6: Find this egrep pattern
* 7: Find this file
* 8: Find files #including this file
* i) arg argument
*
* Unsupported command prints "cscope: 0 lines\n".
*/
static void
search(int com, const char *arg)
{
static STRBUF *sb;
char buf[1024];
int count = 0, opt = 0;
if (sb == NULL)
sb = strbuf_open(1024 * 1024);
else
strbuf_reset(sb);
/*
* Convert from cscope command to global command.
*/
switch (com) {
case '0': /* Find this C symbol */
case '1': /* Find this definition */
break;
case '2': /* Find functions called by this function */
opt = NA;
break;
case '3': /* Find functions calling this function */
opt = 'r';
break;
case '4': /* Find this text string */
opt = 'g';
strlimcpy(buf, quote_string(arg), sizeof(buf));
arg = buf;
break;
case '5': /* Change this text string */
opt = NA;
break;
case '6': /* Find this egrep pattern */
opt = 'g';
break;
case '7': /* Find this file */
opt = 'P';
break;
case '8': /* Find files #including this file */
opt = 'g';
arg = include_pattern(arg);
break;
}
/*
* Execute global(1).
*/
if (opt == NA) {
fprintf(stdout, "cscope: 0 lines\n");
return;
}
if (com == '0') {
count += execute_command(sb, com, 0, arg);
count += execute_command(sb, com, (count > 0) ? 'r' : 's', arg);
} else {
count += execute_command(sb, com, opt, arg);
}
/*
* Output format:
* cscope: <n> lines
* ****************** ... 1
* ****************** ... 2
* ...
* ****************** ... n
*
* Example:
* cscope: 3 lines
* global/global.c main 158 main(int argc, char **argv)
* gozilla/gozilla.c main 155 main(int argc, char **argv)
* gscope/gscope.c main 108 main(int argc, char **argv)
*/
fprintf(stdout, "cscope: %d lines\n", count);
if (count > 0)
fwrite(strbuf_value(sb), 1, strbuf_getlen(sb), stdout);
fflush(stdout);
}