diff -pur global-6.2.11/global/global.c global-6.2-11/global/global.c --- global-6.2.11/global/global.c 2014-03-12 12:49:34 +1000 +++ global-6.2-11/global/global.c 2014-03-12 23:45:32 +1000 @@ -38,6 +38,11 @@ #ifdef HAVE_UNISTD_H #include #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#define WIN32_LEAN_AND_MEAN +#include +#undef SLIST_ENTRY +#endif #include "getopt.h" #include "global.h" @@ -54,6 +59,7 @@ static void usage(void); static void help(void); static void setcom(int); int decide_tag_by_context(const char *, const char *, int); +int prepend_args(const char *, int *, char ***); int main(int, char **); int completion_tags(const char *, const char *, const char *, int); void completion(const char *, const char *, const char *, int); @@ -73,6 +79,7 @@ void encode(char *, int, const char *); const char *localprefix; /**< local prefix */ int aflag; /* [option] */ int cflag; /* command */ +int Cflag; /* [option] */ int dflag; /* command */ int fflag; /* command */ int gflag; /* command */ @@ -148,16 +155,21 @@ help(void) static struct option const long_options[] = { {"absolute", no_argument, NULL, 'a'}, {"completion", no_argument, NULL, 'c'}, + {"color", optional_argument, NULL, 'C'}, + {"colour", optional_argument, NULL, 'C'}, {"definition", no_argument, NULL, 'd'}, {"regexp", required_argument, NULL, 'e'}, + {"extended-regexp", no_argument, NULL, 'E'}, {"file", no_argument, NULL, 'f'}, {"local", no_argument, NULL, 'l'}, {"file-list", required_argument, NULL, 'L'}, {"nofilter", optional_argument, NULL, 'n'}, + {"project", no_argument, NULL, 'N'}, {"grep", no_argument, NULL, 'g'}, {"basic-regexp", no_argument, NULL, 'G'}, {"ignore-case", no_argument, NULL, 'i'}, {"idutils", no_argument, NULL, 'I'}, + {"match-case", no_argument, NULL, 'm'}, {"other", no_argument, NULL, 'o'}, {"only-other", no_argument, NULL, 'O'}, {"print-dbpath", no_argument, NULL, 'p'}, @@ -332,6 +344,62 @@ finish: } return db; } +/** + * prepend_args: insert arguments from a string into an array + * + * @param[in] var string containing arguments to be inserted + * @param argc current and new array size + * @param argv current and new array arguments + * @return number of arguments prepended + * + * @note preserves first element and assumes NULL exists after last. + * @note backslash is used to escape the next character. + * @note in DOS and Windows, @CODE{~} is translated to @CODE{=} (@FILE{COMMAND.COM} compatibility). + */ +int +prepend_args(const char *var, int *argc, char ***argv) +{ + int cnt = 0; + + if (var && *var) { + char *buf = check_strdup(var); + char *s = buf; + char *d = buf; + char **a, **args, **arg = *argv; + int i; + for (;;) { + while (*s && isspace(*s)) + ++s; + if (!*s) + break; + ++cnt; + do { + *d = *s++; +#if defined(__DJGPP__) || defined(_WIN32) + if (*d == '~') + *d = '='; + else +#endif + if (*d == '\\' && *s) + *d = *s++; + ++d; + } while (*s && !isspace(*s)); + *d++ = '\0'; + } + a = args = check_malloc((*argc + cnt + 1) * sizeof(char*)); + *a++ = *arg++; + s = buf; + for (i = 0; i < cnt; ++i) { + *a++ = s; + s = strchr(s, '\0') + 1; + } + memcpy(a, arg, *argc * sizeof(char*)); + *argc += cnt; + *argv = args; + } + + return cnt; +} int main(int argc, char **argv) { @@ -340,8 +408,11 @@ main(int argc, char **argv) int optchar; int option_index = 0; + if (getenv("GTAGSTHROUGH")) + Tflag++; + prepend_args(getenv("GLOBAL_OPTIONS"), &argc, &argv); logging_arguments(argc, argv); - while ((optchar = getopt_long(argc, argv, "acde:ifgGIlL:noOpPqrsS:tTuvVx", long_options, &option_index)) != EOF) { + while ((optchar = getopt_long(argc, argv, "acCde:EifgGIlL:mnNoOpPqrsS:tTuvVx", long_options, &option_index)) != EOF) { switch (optchar) { case 0: break; @@ -352,12 +423,28 @@ main(int argc, char **argv) cflag++; setcom(optchar); break; + case 'C': + if (optarg) { + if (!strcmp(optarg, "never")) + Cflag = 0; + else if (!strcmp(optarg, "always")) + Cflag = 1; + else if (!strcmp(optarg, "auto")) + Cflag = 2; + else + die_with_code(2, "unknown color type for the --color option."); + } else + Cflag = 2; + break; case 'd': dflag++; break; case 'e': av = optarg; break; + case 'E': + Gflag = 0; + break; case 'f': fflag++; xflag++; @@ -384,6 +471,9 @@ main(int argc, char **argv) case 'L': file_list = optarg; break; + case 'm': + iflag = 0; + break; case 'n': nflag++; if (optarg) { @@ -395,6 +485,9 @@ main(int argc, char **argv) nofilter = BOTH_FILTER; } break; + case 'N': + Tflag = 0; + break; case 'o': oflag++; break; @@ -531,12 +624,23 @@ main(int argc, char **argv) warning("cannot encode '/' and '.' in the path. Ignored."); set_encode_chars((unsigned char *)encode_chars); } - if (getenv("GTAGSTHROUGH")) - Tflag++; if (qflag) vflag = 0; if (show_help) help(); + if (Cflag) { + if (Vflag) + Cflag = 0; +#if defined(_WIN32) && !defined(__CYGWIN__) + else if (!(getenv("ANSICON") || LoadLibrary("ANSI32.dll")) + && Cflag == 2) + Cflag = 0; +#endif + if (Cflag == 2 && !isatty(1)) + Cflag = 0; + if (Cflag) + highlight_init(); + } argc -= optind; argv += optind; @@ -1067,6 +1171,7 @@ idutils(const char *pattern, const char const char *lid; int linenum, count; char *p, *q, *grep; + regex_t preg; lid = usable("lid"); if (!lid) @@ -1101,6 +1206,38 @@ idutils(const char *pattern, const char cv = convert_open(type, format, root, cwd, dbpath, stdout, NOTAGS); count = 0; strcpy(path, "./"); + if (Cflag) { + int flags = REG_EXTENDED; + STRBUF *pb = strbuf_open(0); + if (isregex(pattern)) { + FILE *tp; + STRBUF *tb = strbuf_open(0); + strbuf_sprintf(tb, "%s --file=%s/ID --result=none --regexp", lid, quote_shell(dbpath)); + if (iflag) + strbuf_puts(tb, " --ignore-case"); + strbuf_putc(tb, ' '); + strbuf_puts(tb, quote_shell(pattern)); + tp = popen(strbuf_value(tb), "r"); + strbuf_puts(pb, "\\b("); + while ((grep = strbuf_fgets(tb, tp, STRBUF_NOCRLF)) != NULL) { + strbuf_puts(pb, grep); + strbuf_putc(pb, '|'); + } + strbuf_unputc(pb, '|'); + strbuf_puts(pb, ")\\b"); + pclose(tp); + strbuf_close(tb); + } else { + strbuf_puts(pb, "\\b"); + strbuf_puts(pb, pattern); + strbuf_puts(pb, "\\b"); + if (iflag) + flags |= REG_ICASE; + } + if (regcomp(&preg, strbuf_value(pb), flags) != 0) + die("invalid regular expression."); + strbuf_close(pb); + } while ((grep = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) { q = path + 2; /* extract path name */ @@ -1137,6 +1274,11 @@ idutils(const char *pattern, const char /* * print out. */ + if (Cflag) { + regmatch_t m; + if (regexec(&preg, p, 1, &m, 0) == 0) + p = highlight(p, m.rm_so, m.rm_eo - m.rm_so); + } convert_put_using(cv, encoded_pattern, path, linenum, p, NULL); break; } @@ -1149,6 +1291,8 @@ idutils(const char *pattern, const char print_count(count); fprintf(stderr, " (using idutils index in '%s').\n", dbpath); } + if (Cflag) + regfree(&preg); } /** * grep: @NAME{grep} pattern @@ -1249,7 +1393,8 @@ grep(const char *pattern, char *const *a die("cannot open file '%s'.", path); linenum = 0; while ((buffer = strbuf_fgets(ib, fp, STRBUF_NOCRLF)) != NULL) { - int result = regexec(&preg, buffer, 0, 0, 0); + regmatch_t m; + int result = regexec(&preg, buffer, Cflag ? 1 : 0, &m, 0); linenum++; if ((!Vflag && result == 0) || (Vflag && result != 0)) { count++; @@ -1257,6 +1402,8 @@ grep(const char *pattern, char *const *a convert_put_path(cv, path); break; } else { + if (Cflag) + buffer = highlight(buffer, m.rm_so, m.rm_eo - m.rm_so); convert_put_using(cv, encoded_pattern, path, linenum, buffer, (user_specified) ? NULL : gp->dbop->lastdat); } @@ -1328,10 +1475,13 @@ pathlist(const char *pattern, const char */ p = path + strlen(localprefix) - 1; if (pattern) { - int result = regexec(&preg, p, 0, 0, 0); + regmatch_t m; + int result = regexec(&preg, p, Cflag ? 1 : 0, &m, 0); if ((!Vflag && result != 0) || (Vflag && result == 0)) continue; + if (Cflag && result == 0) + path = highlight(path, m.rm_so + p - path, m.rm_eo - m.rm_so); } else if (Vflag) continue; if (format == FORMAT_PATH) @@ -1428,6 +1578,8 @@ put_syms(int type, const char *tag, int default: return; } + if (Cflag) + line_image = highlight_tag(tag, line_image); convert_put_using(data->cv, tag, path, lno, line_image, data->fid); data->count++; } diff -pur global-6.2.11/global/literal.c global-6.2-11/global/literal.c --- global-6.2.11/global/literal.c 2014-03-12 12:49:34 +1000 +++ global-6.2-11/global/literal.c 2014-03-12 23:24:25 +1000 @@ -63,6 +63,7 @@ void cfail(void); extern int iflag; extern int Vflag; +extern int Cflag; extern void encode(char *, int, const char *); #define MAXSIZ 6000 @@ -76,6 +77,7 @@ static struct words { } w[MAXSIZ], *smax, *q; static char encoded_pattern[IDENTLEN]; +static int patlen; /** * literal_comple: compile literal for search. @@ -99,6 +101,10 @@ literal_comple(const char *pattern) * construct fail links. */ cfail(); + /* + * store length of pattern for possible highlight. + */ + patlen = strlen(pattern); } /** * literal_search: execute literal search @@ -120,6 +126,7 @@ literal_search(CONVERT *cv, const char * long lineno; int f; int count = 0; + int start; if ((f = open(file, O_BINARY)) < 0) { warning("cannot open '%s'.", file); @@ -157,6 +164,7 @@ literal_search(CONVERT *cv, const char * linep = p = buf; ccount = stb.st_size; lineno = 1; + start = 0; c = w; for (;;) { if (--ccount < 0) @@ -185,6 +193,7 @@ literal_search(CONVERT *cv, const char * else goto nstate; } if (c->out) { + start = p - linep - patlen + 1; while (*p++ != '\n') { if (--ccount < 0) break; @@ -197,12 +206,16 @@ literal_search(CONVERT *cv, const char * goto finish; } else { STATIC_STRBUF(sb); + char *s; strbuf_clear(sb); strbuf_nputs(sb, linep, p - linep); strbuf_unputc(sb, '\n'); strbuf_unputc(sb, '\r'); - convert_put_using(cv, encoded_pattern, file, lineno, strbuf_value(sb), NULL); + s = strbuf_value(sb); + if (Cflag) + s = highlight(s, start, patlen); + convert_put_using(cv, encoded_pattern, file, lineno, s, NULL); count++; } nomatch: lineno++; diff -pur global-6.2.11/global/manual.in global-6.2-11/global/manual.in --- global-6.2.11/global/manual.in 2014-03-12 12:49:34 +1000 +++ global-6.2-11/global/manual.in 2014-03-12 23:46:50 +1000 @@ -25,12 +25,12 @@ @HEADER GLOBAL,1,Jun 2013,GNU Project @NAME global - print locations of given symbols @SYNOPSIS - @name{global} [-adGilnqrsStTvx][-e] @arg{pattern} - @name{global} -c[diIoOPrsT] @arg{prefix} - @name{global} -f[adlnqrsStvx][-L file-list] @arg{files} - @name{global} -g[aGilnoOqStvVx][-L file-list][-e] @arg{pattern} address@hidden - @name{global} -I[ailnqStvx][-e] @arg{pattern} - @name{global} -P[aGilnoOqStvVx][-e] @arg{pattern} + @name{global} [-aCdEGilmnNqrsStTvx][-e] @arg{pattern} + @name{global} -c[diImNoOPrsT] @arg{prefix} + @name{global} -f[aCdlnqrsStvx][-L file-list] @arg{files} + @name{global} -g[aCEGilmnoOqStvVx][-L file-list][-e] @arg{pattern} address@hidden + @name{global} -I[aCilmnqStvx][-e] @arg{pattern} + @name{global} -P[aCEGilmnoOqStvVx][-e] @arg{pattern} @name{global} -p[qrv] @name{global} -u[qv] @DESCRIPTION @@ -94,10 +94,18 @@ @begin_itemize @address@hidden, @option{--absolute}} Print absolute path names. By default, print relative path names. + @address@hidden, @option{--color}, @option{--colour} @arg{when}} + Use color to highlight the pattern within the line; @arg{when} may be one + of: @arg{never}, @arg{always} or @arg{auto} (default). The default color is bold red + text on current background; the environment variable @var{GREP_COLORS} or + @var{GREP_COLOR} defines it. @address@hidden, @option{--definition}} Print locations of definitions. @address@hidden, @option{--regexp} @arg{pattern}} Use @arg{pattern} as the pattern; useful to protect patterns starting with @samp{-}. + @address@hidden, @option{--extended-regexp}} + Interpret @arg{pattern} as an extended regular expression. + This is provided to override @option{-G} in @var{GLOBAL_OPTIONS}. @address@hidden @arg{chars}} Convert path characters in @arg{chars} into a @samp{%} symbol, followed by the two-digit hexadecimal representation of the character. @@ -122,6 +130,9 @@ @address@hidden Execute literal search instead of regular expression search. This option is only valid when the @option{-g} command is specified. + @address@hidden, @option{--match-case}} + Consider case distinctions in the pattern. + This is provided to override @option{-i} in @var{GLOBAL_OPTIONS}. @address@hidden @arg{part}}} Specify how path name completion should match, where @arg{part} is one of: @val{first}, @val{last} or @val{all} (default). @@ -129,6 +140,9 @@ @address@hidden, @option{--nofilter}} Suppress sort filter and path conversion filter. + @address@hidden, @option{--project}} + Consider only the project's tag files. + This is provided to override @option{-T} in @var{GLOBAL_OPTIONS}. @address@hidden, @option{--only-other}} Treat only text files other than source code, like @file{README}. This option is valid only with the @option{-g} or @option{-P} command. @@ -234,6 +248,15 @@ The following environment variables affect the execution of @name{global}: @begin_itemize + @address@hidden + Use this variable to always add options to the beginning of the + command line (like 'global $GLOBAL_OPTIONS ...'). + Backslash is used to escape the next character. + @address@hidden + The color to use for @option{-C}; @var{GREP_COLORS} has precedence. + @address@hidden + The color (@arg{mt} or @arg{ms}) to use for @option{-C}; see @xref{grep,1}. + The default is @arg{1;31}, which is bold red text on current background. @address@hidden If this variable is set, the @option{--encode-path=" "} option is specified. @@ -262,6 +285,7 @@ The root directory of the project. @address@hidden If this variable is set, the @option{-T} option is specified. + (Deprecated, please use @var{GLOBAL_OPTIONS} instead.) @address@hidden If this variable is set, @file{$MAKEOBJDIR} is used as the name of BSD-style objdir. The default is @file{obj}. diff -pur global-6.2.11/global/output.c global-6.2-11/global/output.c --- global-6.2.11/global/output.c 2014-03-12 12:49:34 +1000 +++ global-6.2-11/global/output.c 2014-03-12 23:24:25 +1000 @@ -40,6 +40,7 @@ static void put_standard_format(CONVERT extern const char *root; extern int nosource; extern int format; +extern int Cflag; /** get next number and seek to the next character */ #define GET_NEXT_NUMBER(p) do { \ @@ -180,6 +181,8 @@ put_compact_format(CONVERT *cv, GTP *gtp } if (flags & GTAGS_COMPNAME) tagname = (char *)uncompress(tagname, gtp->tag); + if (Cflag) + src = highlight_tag(tagname, src); convert_put_using(cv, tagname, gtp->path, n, src, fid); count++; last_lineno = last = n; @@ -210,6 +213,8 @@ put_compact_format(CONVERT *cv, GTP *gtp } if (flags & GTAGS_COMPNAME) tagname = (char *)uncompress(tagname, gtp->tag); + if (Cflag) + src = highlight_tag(tagname, src); convert_put_using(cv, tagname, gtp->path, n, src, fid); count++; last_lineno = n; @@ -251,5 +256,7 @@ put_standard_format(CONVERT *cv, GTP *gt if (flags & GTAGS_COMPRESS) image = (char *)uncompress(image, gtp->tag); } + if (Cflag) + image = highlight_tag(tagname, image); convert_put_using(cv, tagname, gtp->path, gtp->lineno, image, fid); } diff -pur global-6.2.11/libutil/pathconvert.c global-6.2-11/libutil/pathconvert.c --- global-6.2.11/libutil/pathconvert.c 2014-03-12 12:49:34 +1000 +++ global-6.2-11/libutil/pathconvert.c 2014-03-12 23:24:25 +1000 @@ -44,6 +44,7 @@ static unsigned char encode[256]; static int encoding; static int newline = '\n'; +static const char *match_sgr = "1;31"; /* bold red text */ #define required_encode(c) encode[(unsigned char)c] /** @@ -443,3 +444,82 @@ convert_close(CONVERT *cv) gpath_close(); free(cv); } +void +highlight_init(void) +{ + STATIC_STRBUF(sb); + const char *sgr; + + sgr = getenv("GREP_COLOR"); + if (sgr) + match_sgr = sgr; + sgr = getenv("GREP_COLORS"); + if (sgr) { + const char *part = strstr(sgr, "mt="); + if (!part) + part = strstr(sgr, "ms="); + if (part) { + const char *sep = strchr(part, ':'); + if (!sep) + sep = strchr(part, '\0'); + part += 3; + strbuf_nputs(sb, part, sep - part); + match_sgr = strbuf_value(sb); + } + } +} +/** + * highlight: wrap an SGR sequence around characters in @a s. + * + * @param[in] s line of text + * @param[in] pos position to start highlight + * @param[in] len length of highlight + * @return highlighted text + */ +char * +highlight(const char *s, int pos, int len) +{ + STATIC_STRBUF(sb); + + strbuf_clear(sb); + if (len <= 0) + strbuf_puts(sb, s); + else { + strbuf_nputs(sb, s, pos); + strbuf_sprintf(sb, "\33[%sm", match_sgr); + strbuf_nputs(sb, s + pos, len); + strbuf_puts(sb, "\33[m"); + strbuf_puts(sb, s + pos + len); + } + return strbuf_value(sb); +} +/** + * highlight_tag: wrap an SGR sequence around @a tag in @a s. + * + * @param[in] tag tag + * @param[in] s line of text + * @return highlighted text (s if not found) + * + * @note Finds the first word matching tag, not necessarily the tag itself. + */ +const char * +highlight_tag(const char *tag, const char *s) +{ + static regex_t preg; + regmatch_t m; + STATIC_STRBUF(tb); + int len = strlen(tag); + + if (strbuf_getlen(tb) != len + 2 + || strncmp(tag, strbuf_value(tb) + 1, len) != 0) { + strbuf_clear(tb); + strbuf_puts(tb, "\\b"); + strbuf_puts(tb, tag); + strbuf_puts(tb, "\\b"); + regfree(&preg); + regcomp(&preg, strbuf_value(tb), 0); + } + if (regexec(&preg, s, 1, &m, 0) == 0) + s = highlight(s, m.rm_so, m.rm_eo - m.rm_so); + return s; +} diff -pur global-6.2.11/libutil/pathconvert.h global-6.2-11/libutil/pathconvert.h --- global-6.2.11/libutil/pathconvert.h 2014-03-12 12:49:34 +1000 +++ global-6.2-11/libutil/pathconvert.h 2014-03-12 23:24:25 +1000 @@ -42,5 +42,8 @@ void convert_put(CONVERT *, const char * void convert_put_path(CONVERT *, const char *); void convert_put_using(CONVERT *, const char *, const char *, int, const char *, const char *); void convert_close(CONVERT *cv); +void highlight_init(void); +char *highlight(const char *, int, int); +const char *highlight_tag(const char *, const char *); #endif /* ! _PATHCONVERT_H_ */