Hi, On Fri, Jun 26, 2009 at 05:51:53PM -0700, Leo L. Schwab wrote: > A colleague had trouble running 'gtags' in a source tree with > recursive symlinks. Next thing I knew, I was bashing this out. > > This patch adds a new argument to gtags: --follow-links. Note that > I completely wrote this before I found last month's email thread on the > subject, so my embodiment may not be precisely what everyone had in mind. > > I hacked this together in a few hours, and it has received minimal > testing. At the very least it should be good as a basis for discussion. > All feedback welcome.
I did a simple test with a quilt usecase (as described in [1]) and 'gtags -l files' works OK. Many thanks for the patch! [1] http://www.mail-archive.com/[email protected]/msg00523.html > > Thanks, > Schwab > ---- > diff -ur --exclude='*.o' global-5.7.5/doc/gtags.ref > global-5.7.5-hacking/doc/gtags.ref > --- global-5.7.5/doc/gtags.ref 2009-03-13 17:30:38.000000000 -0700 > +++ global-5.7.5-hacking/doc/gtags.ref 2009-06-26 15:56:43.000000000 > -0700 > @@ -4,7 +4,7 @@ > @unnumberedsubsec SYNOPSIS > @noindent > @quotation > -gtags [-ciIOqvw][-f file][-n number][dbpa...@* > +gtags [-ciIOqvw][-f file][-n number][-l arg][dbpa...@* > @end quotation > @unnumberedsubsec DESCRIPTION > Gtags recursively collect the source files under > @@ -52,6 +52,14 @@ > @item @samp{-i}, @samp{--incremental} > Update tag files incrementally. You had better use > global(1) with the -u option. > +...@item @samp{-l}, @samp{--follow-links} dirs|files|both|none > +Control how symbolic links are followed. > +By default, gtags will follow all symbolic links. Depending on > +how your source tree is arranged, this may not be desirable. > +The argument dirs will cause gtags to only follow symbolic > +links that point to directories, files will only follow > +links to files, both will follow all links, and none > +will ignore all symbolic links. > @item @samp{-n}, @samp{--max-args} number > Maximum number of arguments for gtags-parser(1). > By default, gtags invokes gtags-parser with arguments > diff -ur --exclude='*.o' global-5.7.5/gtags/const.h > global-5.7.5-hacking/gtags/const.h > --- global-5.7.5/gtags/const.h 2009-03-13 17:30:38.000000000 -0700 > +++ global-5.7.5-hacking/gtags/const.h 2009-06-26 15:56:43.000000000 > -0700 > @@ -1,6 +1,6 @@ > /* This file is generated automatically by convert.pl from gtags/manual.in. > */ > const char *progname = "gtags"; > -const char *usage_const = "Usage: gtags [-ciIOqvw][-f file][-n > number][dbpath]\n"; > +const char *usage_const = "Usage: gtags [-ciIOqvw][-f file][-n number][-l > arg][dbpath]\n"; > const char *help_const = "Options:\n\ > -c, --compact\n\ > Make GTAGS in compact format.\n\ > @@ -26,6 +26,14 @@ > -i, --incremental\n\ > Update tag files incrementally. You had better use\n\ > global(1) with the -u option.\n\ > +-l, --follow-links dirs|files|both|none\n\ > + Control how symbolic links are followed.\n\ > + By default, gtags will follow all symbolic links. Depending on\n\ > + how your source tree is arranged, this may not be desirable.\n\ > + The argument dirs will cause gtags to only follow symbolic\n\ > + links that point to directories, files will only follow\n\ > + links to files, both will follow all links, and none\n\ > + will ignore all symbolic links.\n\ > -n, --max-args number\n\ > Maximum number of arguments for gtags-parser(1).\n\ > By default, gtags invokes gtags-parser with arguments\n\ > diff -ur --exclude='*.o' global-5.7.5/gtags/gtags.1 > global-5.7.5-hacking/gtags/gtags.1 > --- global-5.7.5/gtags/gtags.1 2009-03-13 17:30:38.000000000 -0700 > +++ global-5.7.5-hacking/gtags/gtags.1 2009-06-26 15:56:43.000000000 > -0700 > @@ -3,7 +3,7 @@ > .SH NAME > gtags \- create tag files for global. > .SH SYNOPSIS > -\fBgtags\fP [-ciIOqvw][-f \fIfile\fP][-n \fInumber\fP][\fIdbpath\fP] > +\fBgtags\fP [-ciIOqvw][-f \fIfile\fP][-n \fInumber\fP][-l > \fIarg\fP][\fIdbpath\fP] > .br > .SH DESCRIPTION > \fBGtags\fP recursively collect the source files under > @@ -58,6 +58,15 @@ > Update tag files incrementally. You had better use > \fBglobal\fP(1) with the -u option. > .TP > +\fB-l\fP, \fB--follow-links\fP \fIdirs|files|both|none\fP > +Control how symbolic links are followed. > +By default, gtags will follow all symbolic links. Depending on > +how your source tree is arranged, this may not be desirable. > +The argument \fIdirs\fP will cause gtags to only follow symbolic > +links that point to directories, \fIfiles\fP will only follow > +links to files, \fIboth\fP will follow all links, and \fInone\fP > +will ignore all symbolic links. > +.TP > \fB-n\fP, \fB--max-args\fP \fInumber\fP > Maximum number of arguments for \fBgtags-parser\fP(1). > By default, gtags invokes \fBgtags-parser\fP with arguments > diff -ur --exclude='*.o' global-5.7.5/gtags/gtags.c > global-5.7.5-hacking/gtags/gtags.c > --- global-5.7.5/gtags/gtags.c 2009-03-13 17:30:37.000000000 -0700 > +++ global-5.7.5-hacking/gtags/gtags.c 2009-06-26 16:09:59.000000000 > -0700 > @@ -85,6 +85,7 @@ > */ > int do_path; > int convert_type = PATH_RELATIVE; > +int followflags = FOLLOWF_BOTH; > > int extractmethod; > int total; > @@ -122,6 +123,7 @@ > {"quiet", no_argument, NULL, 'q'}, > {"verbose", no_argument, NULL, 'v'}, > {"warning", no_argument, NULL, 'w'}, > + {"follow-links", required_argument, NULL, 'l'}, > > /* > * The following are long name only. > @@ -162,7 +164,7 @@ > int optchar; > int option_index = 0; > > - while ((optchar = getopt_long(argc, argv, "cf:iIn:oOqvwse", > long_options, &option_index)) != EOF) { > + while ((optchar = getopt_long(argc, argv, "cf:iIl:n:oOqvwse", > long_options, &option_index)) != EOF) { > switch (optchar) { > case 0: > /* already flags set */ > @@ -201,6 +203,18 @@ > case 'I': > Iflag++; > break; > + case 'l': > + if (!strncmp (optarg, "files", 5)) > + followflags = FOLLOWF_FILE; > + else if (!strncmp (optarg, "dirs", 4)) > + followflags = FOLLOWF_DIR; > + else if (!strncmp (optarg, "both", 4)) > + followflags = FOLLOWF_BOTH; > + else if (!strncmp (optarg, "none", 4)) > + followflags = FOLLOWF_NONE; > + else > + die ("Unknown --follow-flags argument: %s", > optarg); > + break; > case 'n': > max_args = atoi(optarg); > if (max_args <= 0) > @@ -502,7 +516,7 @@ > if (file_list) > find_open_filelist(file_list, root); > else > - find_open(NULL); > + find_open(NULL, followflags); > total = 0; > while ((path = find_read()) != NULL) { > const char *fid; > @@ -806,7 +820,7 @@ > if (file_list) > find_open_filelist(file_list, root); > else > - find_open(NULL); > + find_open(NULL, followflags); > /* > * Add tags. > */ > diff -ur --exclude='*.o' global-5.7.5/gtags/manual.in > global-5.7.5-hacking/gtags/manual.in > --- global-5.7.5/gtags/manual.in 2009-03-13 17:30:37.000000000 -0700 > +++ global-5.7.5-hacking/gtags/manual.in 2009-06-26 15:35:25.000000000 > -0700 > @@ -25,7 +25,7 @@ > @HEADER GTAGS,1,January 2009,GNU Project > @NAME gtags - create tag files for global. > @SYNOPSIS > - @name{gtags} [-ciIOqvw][-f @arg{file}][-n @arg{number...@arg{dbpath}] > + @name{gtags} [-ciIOqvw][-f @arg{file}][-n @arg{number}][-l > @arg{arg...@arg{dbpath}] > @DESCRIPTION > @name{Gtags} recursively collect the source files under > the current directory, > @@ -72,6 +72,14 @@ > @it...@option{-i}, @option{--incremental}} > Update tag files incrementally. You had better use > @xref{global,1} with the -u option. > + @it...@option{-l}, @option{--follow-links} @arg{dirs|files|both|none}} > + Control how symbolic links are followed. > + By default, gtags will follow all symbolic links. Depending on > + how your source tree is arranged, this may not be desirable. > + The argument @arg{dirs} will cause gtags to only follow symbolic > + links that point to directories, @arg{files} will only follow > + links to files, @arg{both} will follow all links, and @arg{none} > + will ignore all symbolic links. > @it...@option{-n}, @option{--max-args} @arg{number}} > Maximum number of arguments for @xref{gtags-parser,1}. > By default, gtags invokes @name{gtags-parser} with arguments > diff -ur --exclude='*.o' global-5.7.5/libutil/find.c > global-5.7.5-hacking/libutil/find.c > --- global-5.7.5/libutil/find.c 2009-03-13 17:30:37.000000000 -0700 > +++ global-5.7.5-hacking/libutil/find.c 2009-06-26 16:11:32.000000000 > -0700 > @@ -78,6 +78,7 @@ > static regex_t *suff = &suff_area; /* regex for suffixes */ > static STRBUF *list; > static int list_count; > +static int follow_flags; > static char **listarray; /* list for skipping full path */ > static FILE *ip; > static FILE *temp; > @@ -372,8 +373,9 @@ > * r) -1: error, 0: normal > * > * format of directory list: > - * |ddir1\0ffile1\0llink\0| > - * means directory 'dir1', file 'file1' and symbolic link 'link'. > + * |ddir1\0ffile1\0Dlinkeddir\0Flinkedfile\0llink\0| > + * means directory 'dir1', file 'file1', symbolic link to 'linkeddir', > + * symbolic link to 'linkedfile', and symbolic link 'link'. > */ > static int > getdirs(const char *dir, STRBUF *sb) > @@ -381,6 +383,7 @@ > DIR *dirp; > struct dirent *dp; > struct stat st; > + char islink; > > if ((dirp = opendir(dir)) == NULL) > return -1; > @@ -389,14 +392,24 @@ > continue; > if (!strcmp(dp->d_name, "..")) > continue; > - if (stat(makepath(dir, dp->d_name, NULL), &st) < 0) { > - warning("cannot stat '%s'. (Ignored)", dp->d_name); > + if (lstat(makepath(dir, dp->d_name, NULL), &st) < 0) { > + warning("cannot lstat '%s'. (Ignored)", dp->d_name); > continue; > } > + > + if (S_ISLNK(st.st_mode)) { > + if (stat(makepath(dir, dp->d_name, NULL), &st) < 0) { > + warning("cannot stat '%s'. (Ignored)", > dp->d_name); > + continue; > + } > + islink = 1; > + } else > + islink = 0; > + > if (S_ISDIR(st.st_mode)) > - strbuf_putc(sb, 'd'); > + strbuf_putc(sb, islink ? 'D' : 'd'); > else if (S_ISREG(st.st_mode)) > - strbuf_putc(sb, 'f'); > + strbuf_putc(sb, islink ? 'F' : 'f'); > else > strbuf_putc(sb, ' '); > strbuf_puts(sb, dp->d_name); > @@ -412,14 +425,18 @@ > * If NULL, assumed '.' directory. > */ > void > -find_open(const char *start) > +find_open(const char *start, int cl_follow_flags) > { > struct stack_entry *curp; > + > assert(find_mode == 0); > find_mode = FIND_OPEN; > > if (!start) > start = "./"; > + > + follow_flags = cl_follow_flags; > + > /* > * setup stack. > */ > @@ -531,6 +548,17 @@ > */ > /* makepath() returns unsafe module local area. > */ > strlimcpy(path, makepath(dir, unit, NULL), > sizeof(path)); > + if (type == 'D') { > + /* Softlink to dir. */ > + if (!(follow_flags & FOLLOWF_DIR)) > + continue; > + type = 'd'; > + } else if (type == 'F') { > + /* Softlink to file. */ > + if (!(follow_flags & FOLLOWF_FILE)) > + continue; > + type = 'f'; > + } > if (type == 'd') > strcat(path, "/"); > if (skipthisfile(path)) > @@ -541,6 +569,7 @@ > * o directory > * o file which does not exist > * o dead symbolic link > + * XXX ewhac: Is this test necessary? Is > stat(2) known to lie? > */ > if (!test("f", path)) { > if (test("d", path)) > diff -ur --exclude='*.o' global-5.7.5/libutil/find.h > global-5.7.5-hacking/libutil/find.h > --- global-5.7.5/libutil/find.h 2009-03-13 17:30:37.000000000 -0700 > +++ global-5.7.5-hacking/libutil/find.h 2009-06-26 14:58:31.000000000 > -0700 > @@ -21,9 +21,15 @@ > #ifndef _FIND_H_ > #define _FIND_H_ > > -void find_open(const char *); > +void find_open(const char *, int follow_flags); > void find_open_filelist(const char *, const char *); > char *find_read(void); > void find_close(void); > > +/* follow_flags */ > +#define FOLLOWF_NONE 0 > +#define FOLLOWF_FILE 1 > +#define FOLLOWF_DIR 2 > +#define FOLLOWF_BOTH (FOLLOWF_FILE | FOLLOWF_DIR) > + > #endif /* ! _FIND_H_ */ > > > _______________________________________________ > Bug-global mailing list > [email protected] > http://lists.gnu.org/mailman/listinfo/bug-global -- Best regards / s pozdravem Petr Uzel, Packages maintainer --------------------------------------------------------------------- SUSE LINUX, s.r.o. e-mail: [email protected] Lihovarská 1060/12 http://www.suse.cz 190 00 Prague 9 Czech Republic _______________________________________________ Bug-global mailing list [email protected] http://lists.gnu.org/mailman/listinfo/bug-global
