rsalz@uunet.uu.net (Rich Salz) (02/09/89)
Submitted-by: Chris Tweed <caad.ed.ac.uk!chris> Posting-number: Volume 17, Issue 93 Archive-name: prologtags [ I haven't tried this. --r$ ] Ptags creates tags for Prolog predicates as defined in the source files supplied as input. Its use is analogous to that of ctags(1). ------------------------------ CUT HERE ------------------------------ # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Contents: TestFiles/ READ_ME ptags.L Makefile ptags.h config.h main.c # process.c symbol.c tags TestFiles/comments.pl TestFiles/crazy.pl # TestFiles/difficult.pl TestFiles/morecomments.pl TestFiles/tags # TestFiles/tricky.pl echo x - READ_ME sed 's/^@//' > "READ_ME" <<'@//E*O*F READ_ME//' Making and installing ptags should be straightforward. Two things need to be done before attempting to compile and link the program on your system. First edit Makefile and set DFLAGS to be either -DBSD4_2 (the default) or -DSYSV, depending on which system you are using. If you're not using either of these you may have to make some changes to the code - sorry. You may also want to change the assignments to FINALVN such that ptags is installed somewhere else. Second make sure you know which characters your Prolog treats as symbols. Edit config.h and make any necessary changes to my definitions. After you have gone through these two stages you should be ready to type 'make ptags' or 'make install'. Happy tagging! Chris Tweed chris@caad.ed.ac.uk @...!mcvax!ukc!edcaad!chris @//E*O*F READ_ME// chmod u=rw,g=rw,o=r READ_ME echo x - ptags.L sed 's/^@//' > "ptags.L" <<'@//E*O*F ptags.L//' @.TH PTAGS L "February 11, 1988" "" "Local UNIX Programmer's Manual" @.UC 4 @.SH NAME ptags \- creates a tags file from Prolog sources @.SH SYNOPSIS @.B ptags [\-w] [\-l] [\-a] [\-p] files @.br @.SH DESCRIPTION @.I Ptags creates a tags file from one or more Prolog source files. It will do for Prolog predicates what @.IR ctags (1) does for C functions. Like @.I ctags, output is sent to a file called @.I tags in the current directory. Each line in the tags file contains three tab-separated fields: the first is the name of each predicate defined in the source files; the second is the name of the file where the predicate is defined; and the third is an instruction to be executed by @.IR vi (1) or @.IR ex (1) to find the definition within the source file. As with @.I ctags, the tags file is sorted alphabetically by predicate name. @.PP @.I Ptags accepts four flags to control its operation and output: @.TP @.B \-w suppresses warnings about multiple definitions of the same predicate in different files. Only the first occurrence of a definition is entered in the tags file. @.TP @.B \-l use line numbers, rather than regular expressions, to locate predicate definitions in source files. @.TP @.B \-a append the output to an existing tags file in the current directory. This allows you to combine output from @.I ptags with output from @.I ctags. A single tags file can be used to access Prolog predicates and C functions \(em useful if you are mixing Prolog and C in a single program. @.TP @.B \-p sends the output to @.I stdout instead of to tags file. @.PP @.SH "FILES" @.PP \&./tags tags file created or appended to @.SH "SEE ALSO" ctags(1), ex(1), vi(1) @.SH "DIAGNOSTICS" @.I Ptags complains about a variety of things, mostly related to reaching limits of static arrays, e.g. predicate name too long, too many symbols (predicates), too many source files, etc. @.sp @.I Ptags can optionally produce warnings about predicates defined across different files - see \-w above. Warnings are only issued once for each different file. Note that the arity of these predicates may be different - @.I ptags doesn't care. @.SH "BUGS" @.I Ptags is NOT a syntax checker for Prolog code, and will quite happily swallow syntax errors without complaint, probably filling the tags file with garbage at the same time. @.sp The parser is very basic and may not tolerate your style of layout for Prolog code. For example, if regular expressions are used to locate definitions (rather than line numbers), they will expect all definitions to begin at the leftmost side of the screen, because the regular expressions always begin with a leading `^' character, followed immediately by the predicate name, and some trailing context characters. @.sp Most Prologs will allow "strange" characters in predicate names, provided the names are enclosed in single quotes. These names will often not be acceptable as tags, though they seem to be accepted on the command line with the \-t option in ex or vi. Nothing we can do about this. Anybody care to modify ex? @.SH "AUTHORS" @.PP Chris Tweed @.br Bob Kemp @//E*O*F ptags.L// chmod u=rw,g=r,o=r ptags.L echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' INCDIR=. INC=-I$(INCDIR) # change to -DSYSV for System V DFLAGS=-DBSD4_2 CFLAGS=-g $(INC) $(DFLAGS) HDRS=ptags.h config.h OBJS=main.o process.o symbol.o BIN=ptags FINAL=/usr/local/bin/ptags MAN=/usr/man/manl/ptags.l SRCS=READ_ME ptags.L Makefile $(HDRS) $(OBJS:.o=.c) tags TestFiles TestFiles/* $(BIN): $(OBJS) cc $(CFLAGS) -o $(BIN) $(OBJS) install: $(BIN) $(MAN) install -s $(BIN) $(FINAL) $(MAN): ptags.L cp ptags.L $(MAN) shar: $(SRCS) shar -c -v $(SRCS) > ptags.shar lint: lint -h $(OBJS:.o=.c) tags: $(OBJS:.o=.c) $(HDRS) ctags -t $(HDRS) $(OBJS:.o=.c) clean: rm -f core makerr $(OBJS) p.c veryclean: clean rm -f $(BIN) # dependencies $(OBJS): $(HDRS) @//E*O*F Makefile// chmod u=rw,g=rw,o=r Makefile echo x - ptags.h sed 's/^@//' > "ptags.h" <<'@//E*O*F ptags.h//' /* * ptags - creates entries in a tags file for Prolog predicates * * Usage: ptags [-w] [-l] [-a] [-p] file1 ... filen * * This program code may be freely distributed provided * * a) it, or any part of it, is not sold for profit; and * * b) this entire comment remains intact. * * Copyright (c) 1988, Chris Tweed & Bob Kemp, EdCAAD, * University of Edinburgh * * Please mail us any changes, enhancements, or bug fixes. * * Chris Tweed * chris@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!chris * * or * * Bob Kemp * bob@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!bob * */ #define local static #define global #define VOID (void) #define REG register #define BOOL int #define MAXFILE 128 #define MAXSYM 1024 #define MAXSTR 256 #define MAXBUF MAXSTR #define TRUE 1 #define FALSE 0 #define FSEP '\t' typedef struct symbol { char name[MAXSTR]; char *file; } SYMBOL; /* NULLS etc. */ #define NOSYM (SYMBOL *)0 #define NOSTR (char *)0 #define NOOP 0 /* MISC */ #define EOSTR '\0' /* MACROS */ #define STREQ(s1, s2) (strcmp((s1), (s2))==0) /* string compare */ @//E*O*F ptags.h// chmod u=rw,g=rw,o=r ptags.h echo x - config.h sed 's/^@//' > "config.h" <<'@//E*O*F config.h//' /* header file to define system dependent things */ /* * ptags - creates entries in a tags file for Prolog predicates * * Usage: ptags [-w] [-l] [-a] [-p] file1 ... filen * * This program code may be freely distributed provided * * a) it, or any part of it, is not sold for profit; and * * b) this entire comment remains intact. * * Copyright (c) 1988, Chris Tweed & Bob Kemp, EdCAAD, * University of Edinburgh * * Please mail us any changes, enhancements, or bug fixes. * * Chris Tweed * chris@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!chris * * or * * Bob Kemp * bob@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!bob * */ #if SYSV # define INDEX strchr #else BSD4_2 # define INDEX index #endif /* * According to "Programming in Prolog", Clocksin and Mellish, * Springer-Verlag, 1981, Prolog has two types of atom: one * composed of letters and digits, and one composed of symbols. * In ptags we need to recognise both and distinguish between them * because they determine the valid characters in predicate names. * * The definitions used below are those used in C-Prolog 1.5+, but * your Prolog may be different. C-Prolog differs from Clocksin * and Mellish in that '$' has the same status as alphanumeric * characters and is not, therefore, a symbol. Check your Prolog * and edit these definitions if necessary. * * Also defined is a line comment character which may be different * in your Prolog. * */ /* valid symbol characters - NOTE '$' is not one of these */ #define SYM_CHRS "+-*/\\^<>=`~:.?@#&" /* test for valid symbol characters */ #define IS_SYM(c) (INDEX(SYM_CHRS, c) != NULL) /* test for valid character at start of predicate name */ #define BEGIN_NAME(c) (islower(c) || c == '\'' || c == '$') /* test for valid characters within predicate name */ #define IN_NAME(c) (isalnum(c) || c == '_' || c == '$') #define L_COMMENT_CHR '%' @//E*O*F config.h// chmod u=rw,g=rw,o=r config.h echo x - main.c sed 's/^@//' > "main.c" <<'@//E*O*F main.c//' #include <stdio.h> #include <sys/param.h> #include <sys/stat.h> #include "ptags.h" /* * ptags - creates entries in a tags file for Prolog predicates * * Usage: ptags [-w] [-l] [-a] [-p] file1 ... filen * * This program code may be freely distributed provided * * a) it, or any part of it, is not sold for profit; and * * b) this entire comment remains intact. * * Copyright (c) 1988, Chris Tweed & Bob Kemp, EdCAAD, * University of Edinburgh * * Please mail us any changes, enhancements, or bug fixes. * * Chris Tweed * chris@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!chris * * or * * Bob Kemp * bob@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!bob * */ #define TEMPLATE "/tmp/ptXXXXXXXX" /* shorthand for usage messages */ #define USE(mesg) VOID fprintf(stderr, mesg) global char *progname; /* name of this program */ global BOOL warnings = TRUE; /* warns of defs across files if TRUE */ global BOOL lines = FALSE; /* TRUE for line numbers in tags file */ global BOOL appending = FALSE; /* append to existing tags if TRUE */ global BOOL piping = FALSE; /* send output to stdout if TRUE */ global char *cfile; /* current input filename */ global char *filename[MAXFILE]; /* list of filenames to be processed */ global int nf = 0; /* number of files to be processed */ global FILE *tags; /* pointer to the tags file */ local void usage(); /* displays usage and EXITS */ main(argc, argv) int argc; char *argv[]; { extern char *strcpy(); extern char *mktemp(); extern FILE *fopen(); extern BOOL process_file(); int i; char *p; FILE *ifp; char tagfile[MAXPATHLEN]; char cmd[MAXPATHLEN]; struct stat stbuf; #if BSD4_2 setlinebuf(stderr); #endif progname = argv[0]; /* process arguments */ while (--argc) { if (argv[1][0] == '-') { p = argv[1] + 1; if (p == NOSTR) usage(progname); /* EXITS */ while(*p) { switch(*p) { case 'w': warnings = FALSE; break; case 'l': lines = TRUE; break; case 'a': appending = TRUE; break; case 'p': /* send output to stdout */ piping = TRUE; break; default: VOID usage(progname); /* EXITS */ break; } p++; } } else if (nf < MAXFILE) filename[nf++] = argv[1]; else { VOID fprintf(stderr, "%s: too many files\n", progname); exit(1); } argv++; } if (nf == 0) usage(progname); /* EXITS */ /* open temporary tags file */ VOID strcpy(tagfile, TEMPLATE); VOID mktemp(tagfile); /* if appending, copy existing tags file */ if (appending && stat("./tags", &stbuf) == 0) { VOID sprintf(cmd, "cp tags %s", tagfile); if (system(cmd) != 0) { VOID fprintf(stderr, "%s: error copying existing tags file\n", tagfile); } tags = fopen(tagfile, "a"); } else tags = fopen(tagfile, "w"); if (tags == NULL) { VOID fprintf(stderr, "%s: can't open tags file\n", progname); exit(1); } for(i=0; i<nf; i++) { cfile = filename[i]; /* open input file */ ifp = fopen(cfile, "r"); if (ifp == NULL) { VOID fprintf(stderr, "%s: can't open %s\n", progname, cfile); continue; } /* process the file */ VOID process_file(ifp); VOID fclose(ifp); } VOID fclose(tags); if (piping == TRUE) VOID sprintf(cmd, "sort %s", tagfile); else VOID sprintf(cmd, "sort %s > tags", tagfile); if (system(cmd) == 0) VOID unlink(tagfile); else { VOID fprintf(stderr, "%s: error copying temp file (%s)\n", progname, tagfile); exit(1); } } /* * local void * usage(s) * * Prints usage and EXITS. * */ local void usage(s) char *s; { VOID fprintf(stderr, "usage: %s\n", s); USE("\t\t[-w]\t/* suppress warnings about multiple defs */\n"); USE("\t\t[-l]\t/* use line numbers instead of search strings */\n"); USE("\t\t[-a]\t/* append to tags file */\n"); USE("\t\t[-p]\t/* send output to stdout */\n"); USE("\t\tfile1 ... filen\n"); exit(1); } @//E*O*F main.c// chmod u=rw,g=rw,o=r main.c echo x - process.c sed 's/^@//' > "process.c" <<'@//E*O*F process.c//' #include <stdio.h> #include <ctype.h> #include "config.h" #include "ptags.h" /* * ptags - creates entries in a tags file for Prolog predicates * * Usage: ptags [-w] [-l] [-a] [-p] file1 ... filen * * This program code may be freely distributed provided * * a) it, or any part of it, is not sold for profit; and * * b) this entire comment remains intact. * * Copyright (c) 1988, Chris Tweed & Bob Kemp, EdCAAD, * University of Edinburgh * * Please mail us any changes, enhancements, or bug fixes. * * Chris Tweed * chris@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!chris * * or * * Bob Kemp * bob@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!bob * */ /* lexical states */ #define S_SPACE 1 #define S_NAME 2 #define S_SYM 3 #define S_CLAUSE 4 #define S_DOT 5 #define S_STR 6 #define S_QUOTE 7 /* define characters that must be escaped in regular expressions */ #define ESC_CHRS ".\\$*%&^/[]" /* print character - with escape if necessary */ #define PRINTC(ch, fp) if (INDEX(ESC_CHRS, ch) != NULL) { \ VOID fprintf(fp, "\\%c", ch); \ } else VOID putc(ch, fp) extern char *INDEX(); extern SYMBOL *lookup(); local void skip_comment(); /* skips comments in source */ local int skip_to_chr(); /* move read pointer to chr */ local BOOL print_tag(); /* print the tag */ local void print_search(); /* print search string */ local int lineno = 1; /* line number in input file */ /* * global BOOL * process_file(fp) * * Main processing routine. * */ global BOOL process_file(fp) FILE *fp; /* pointer to current file */ { extern char *strcpy(); extern char *progname; /* name of this program */ extern char *cfile; /* name of current file */ char buff[MAXSTR]; /* buffer */ REG int chno = 0; /* current position in buff */ REG int state = S_SPACE; /* state of the parser */ int pstate = S_SPACE; /* previous state */ REG int ch; /* current character */ REG int lastch = EOSTR; /* previous character */ int c; /* temporary character store */ int startno; BOOL inquote = FALSE; lineno = 1; for (chno = 0; chno < MAXSTR; chno++) buff[chno] = EOSTR; while ((ch = getc(fp)) != EOF ) { if (ch == '\n') lineno++; switch (state) { case S_SPACE: /* in whitespace preceding clause */ if (!isspace(ch)) { /* usually will be space */ if (ch == L_COMMENT_CHR) skip_comment(ch, fp); else if (ch == '\'') { state = S_QUOTE; } else if (ch == '"' && ! inquote) { pstate = state; state = S_STR; } else if (BEGIN_NAME(ch)) { state = S_NAME; buff[0] = ch; chno = 1; /* look for comments */ } else if (ch == '/') { if ((c=getc(fp)) == '*') { skip_comment(c, fp); } else { ungetc(c, fp); state = S_SYM; buff[0] = ch; chno = 1; } } else if (IS_SYM(ch)) { state = S_SYM; buff[0] = ch; chno = 1; } else if (ch != '/') /* skip "rubbish" */ state = S_CLAUSE; } break; case S_NAME: /* in predicate name */ /* FALLS THROUGH */ case S_SYM: /* weed out directives and queries */ if (chno == 1 && ch == '-' && (lastch == ':' || lastch == '?')) { state = S_CLAUSE; break; } if (chno == MAXSTR) { VOID fprintf(stderr, "%s: predicate name too long at line %d\n", progname, lineno); return FALSE; } buff[chno++] = ch; if (ch == '\'') { state = S_QUOTE; } else if (inquote) { break; } else if (state == S_NAME && ch == '.') { state = S_DOT; } else if (ch == '/') { /* comment in symbol or name */ /* weed comments out */ if ((c=getc(fp)) == '*') { skip_comment(c, fp); chno--; break; } else { ungetc(c, fp); if (state == S_NAME) state = S_CLAUSE; } } else if ((state == S_NAME && ! IN_NAME(ch)) || (state == S_SYM && !IS_SYM(ch))) state = S_CLAUSE; if (state != S_NAME && state != S_SYM) { buff[--chno] = EOSTR; if (print_tag(buff, ch, lineno) == FALSE) exit(1); } if (ch == L_COMMENT_CHR) skip_comment(ch, fp); break; case S_CLAUSE: /* in the clause text */ if (ch == '\'') { inquote = (! inquote); } else if (inquote) break; else if (ch == L_COMMENT_CHR) { skip_comment(ch, fp); } else if (ch == '"') { pstate = state; state = S_STR; } else if (ch == '.') state = S_DOT; else if (ch == '*' && lastch == '/') skip_comment(ch, fp); break; case S_DOT: if (isspace(ch)) state = S_SPACE; else state = S_CLAUSE; break; case S_STR: /* we're not really interested in strings, but we want to * get them out of the way as soon as possible because they * may contain nasty things like a dot (.) followed by * white space which would otherwise throw the states off. * Note that we must also allow for "" within a string as * this is used to put a single " into the string. */ startno = lineno; do { if (skip_to_chr(fp, ch, '"') == EOF) { fprintf(stderr, "%s: can't find end of string on line %d in %s\n", progname, startno, cfile); exit(1); } } while((ch = getc(fp)) == '"'); state = pstate; break; case S_QUOTE: inquote = ( ! inquote); if (inquote) { state = S_NAME; buff[0] = '\''; buff[1] = ch; chno = 2; } else state = S_CLAUSE; break; } lastch = ch; } return TRUE; } /* * local void * skip_comment(ch, fp) * * Move reading position to beyond end of comment. * */ local void skip_comment(ch, fp) FILE *fp; /* pointer to current file */ REG int ch; /* current input character */ { extern char *progname; extern char *cfile; int startno = lineno; if (ch == L_COMMENT_CHR) { /* rest-of-line comment */ while ((ch = getc(fp)) != '\n') if (ch == EOF) return; lineno++; } else { /* this style of comment (slash-star star-slash) */ do { if (skip_to_chr(fp, ch, '*') == EOF) { fprintf(stderr, "%s: can't find end of comment, from line %d in %s\n", progname, startno, cfile); exit(1); } while ((ch = getc(fp)) == '*') ; } while (ch != '/'); } } /* * local BOOL * print_tag(fn_name, followch, lno) * * Process the predicate name, removing initial quotes if necessary. * Quotes must be retained in the search string. * */ local BOOL print_tag(fn_name, followch, lno) char *fn_name; /* predicate name */ char followch; /* char immediately after it */ int lno; /* current line number */ { extern SYMBOL *install(); extern char *progname; /* program name */ extern char *cfile; /* current source filename */ extern FILE *tags; /* pointer to tags file */ extern BOOL lines; /* TRUE if line numbers are requested */ extern BOOL warnings; /* warn about multiple defs? */ REG SYMBOL *sym; /* symbol table entry */ REG char *p; /* skip initial quote if necessary */ p = (*fn_name == '\'') ? fn_name+1 : fn_name; if ((sym = lookup(p)) == NOSYM) { #if DEBUG VOID printf("process_head: installing %s\n", s); #endif if (install(p, cfile) == NOSYM) return FALSE; } else if (warnings && sym->file != cfile) { VOID fprintf(stderr, "%s: warning - '%s' is defined in more than one file\n", progname, p); sym->file = cfile; /* no more warnings for this file */ } else /* already recorded for this file */ return TRUE; if (lines == TRUE) /* use line numbers */ VOID fprintf(tags, "%s\t%s\t%d\n", p, cfile, lno); else { /* use search string */ VOID fprintf(tags, "%s\t%s\t", p, cfile); VOID print_search(tags, fn_name, followch); } return TRUE; } /* * local void * print_search(fp, s, c) * * Construct a vi/ex regular expression as a search string, escaping * special characters if necessary. * */ local void print_search(fp, s, c) FILE *fp; REG char *s; /* string to search for */ char c; /* trailing context character */ { /* The definition is assumed to start on a line of its own * flush with the left margin. */ VOID fprintf(fp, "/^"); for ( ; *s != EOSTR; s++) PRINTC(*s, fp); /* Can't put these in search string */ if (c != '\n' && c != '\r' && c != '\f') PRINTC(c, fp); /* print char following name */ VOID fprintf(fp, "/\n"); } /* * local int * skip_to_chr(fp, ch, match) * * Skips input up to match character. Leaves ch at match. * Returns match or EOF on end-of-file. * */ local int skip_to_chr(fp, ch, match) FILE *fp; /* pointer to current input file */ int ch; /* current character */ char match; /* character to match */ { extern int lineno; extern char *progname; /* program name */ extern char *cfile; /* current input filename */ while (ch != match) { if (ch == '\n') lineno++; else if (ch == EOF) { fprintf(stderr, "%s: character (%c) not matched in %s\n", progname, match, cfile); return EOF; } ch = getc(fp); } return ch; } @//E*O*F process.c// chmod u=rw,g=rw,o=r process.c echo x - symbol.c sed 's/^@//' > "symbol.c" <<'@//E*O*F symbol.c//' #include <stdio.h> #include "ptags.h" /* * ptags - creates entries in a tags file for Prolog predicates * * Usage: ptags [-w] [-l] [-a] [-p] file1 ... filen * * This program code may be freely distributed provided * * a) it, or any part of it, is not sold for profit; and * * b) this entire comment remains intact. * * Copyright (c) 1988, Chris Tweed & Bob Kemp, EdCAAD, * University of Edinburgh * * Please mail us any changes, enhancements, or bug fixes. * * Chris Tweed * chris@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!chris * * or * * Bob Kemp * bob@caad.ed.ac.uk * ...!mcvax!ukc!edcaad!bob * */ extern BOOL warnings; local int nsym = 0; local SYMBOL symbol[MAXSYM]; /* * global SYMBOL * * install(name, file) * * Installs a symbol in the symbol table. NOTE: it doesn't check * if the symbol has already been entered. * */ global SYMBOL * install(name, file) char *name; /* predicate name */ char *file; /* name of file where pred is defined */ { extern SYMBOL *new_sym(); extern char *strcpy(); SYMBOL *sym; if ((sym = new_sym()) == NOSYM) return NOSYM; VOID strcpy(sym->name, name); sym->file = file; return sym; } /* * local SYMBOL * * new_sym() * * Returns a pointer to a new symbol, or NOSYM (NULL) if no * space is available. * */ local SYMBOL * new_sym() { extern char *progname; if (nsym < MAXSYM) return &symbol[nsym++]; else { fprintf(stderr, "%s: too many symbols\n", progname); return NOSYM; } } /* * global SYMBOL * * lookup(name) * * Returns pointer to symbol if it is in the symbol table; otherwise * it returns NOSYM (NULL). * */ global SYMBOL * lookup(name) REG char *name; { extern int nsym; REG int i; for (i = 0; i < nsym; i++) if (STREQ(name, symbol[i].name)) return &symbol[i]; return NOSYM; } @//E*O*F symbol.c// chmod u=rw,g=rw,o=r symbol.c echo x - tags sed 's/^@//' > "tags" <<'@//E*O*F tags//' BEGIN_NAME config.h /^#define BEGIN_NAME(c) (islower(c) || c == '\\'' || / IN_NAME config.h /^#define IN_NAME(c) (isalnum(c) || c == '_' || c ==/ IS_SYM config.h /^#define IS_SYM(c) (INDEX(SYM_CHRS, c) != NULL)$/ Mmain main.c /^main(argc, argv)$/ PRINTC process.c /^#define PRINTC(ch, fp) if (INDEX(ESC_CHRS, ch) != / STREQ ptags.h /^#define STREQ(s1, s2) (strcmp((s1), (s2))==0) \/* / USE main.c /^#define USE(mesg) VOID fprintf(stderr, mesg)$/ install symbol.c /^install(name, file)$/ lookup symbol.c /^lookup(name)$/ new_sym symbol.c /^new_sym()$/ print_search process.c /^print_search(fp, s, c)$/ print_tag process.c /^print_tag(fn_name, followch, lno)$/ process_file process.c /^process_file(fp)$/ skip_comment process.c /^skip_comment(ch, fp)$/ skip_to_chr process.c /^skip_to_chr(fp, ch, match)$/ usage main.c /^usage(s)$/ @//E*O*F tags// chmod u=rw,g=rw,o=r tags echo mkdir - TestFiles mkdir TestFiles chmod u=rwx,g=rwx,o=rx TestFiles echo x - TestFiles/comments.pl sed 's/^@//' > "TestFiles/comments.pl" <<'@//E*O*F TestFiles/comments.pl//' :- b, c, d. a. /* hello . x. */ /** m. **/ /*** n. **/ b(x, y) :- c(x), d(y,z). c (x, a) :- zxc(a). d :- true. % hi! e% . zzz. f/* . */ x . @//E*O*F TestFiles/comments.pl// chmod u=rw,g=rw,o=r TestFiles/comments.pl echo x - TestFiles/crazy.pl sed 's/^@//' > "TestFiles/crazy.pl" <<'@//E*O*F TestFiles/crazy.pl//' /* crazy program layout */ c(A, B) :- a(A), b(B). d(a(_),D) :- b(D). @//E*O*F TestFiles/crazy.pl// chmod u=rw,g=rw,o=r TestFiles/crazy.pl echo x - TestFiles/difficult.pl sed 's/^@//' > "TestFiles/difficult.pl" <<'@//E*O*F TestFiles/difficult.pl//' :- b, c, d. a. /* hello . x. */ :- op(500, xfx, '.'). b(n.n, y) :- c(x), d(y,z). c( x, a) :- zxc(a). 'a b c'(A,1.2) :- a(A), b(B). d("a dot. followed by white space", "", "and a quote in a string""") :- it_works(ok). e :- true. % hi! :- op(300, fx, f). /* below should be interpretable as 'f yyy.' */ f% . yyy. 'z & () . zz'. :- op(300, fx, g). g/* . */ x . h(a). /* symbolic atoms as predicate names */ ++(X). +.+(X). &+`(X). @~*\/#?```=(X). /* some really tricky ones ;-) */ x/*******/y(X). */**/*(X). /*******/y(X). /**/*(X). x/***//****/y(X). @//E*O*F TestFiles/difficult.pl// chmod u=rw,g=rw,o=r TestFiles/difficult.pl echo x - TestFiles/morecomments.pl sed 's/^@//' > "TestFiles/morecomments.pl" <<'@//E*O*F TestFiles/morecomments.pl//' a(b) :- true. /**/ c(b) :- true. d(b). @//E*O*F TestFiles/morecomments.pl// chmod u=rw,g=rw,o=r TestFiles/morecomments.pl echo x - TestFiles/tags sed 's/^@//' > "TestFiles/tags" <<'@//E*O*F TestFiles/tags//' &+` difficult.pl /^\&+`(/ * difficult.pl /^\*(/ ** difficult.pl /^\*\*(/ ++ difficult.pl /^++(/ +.+ difficult.pl /^+\.+(/ a difficult.pl /^a\./ a b c difficult.pl /^'a b c'/ b difficult.pl /^b(/ c difficult.pl /^c(/ d difficult.pl /^d(/ e difficult.pl /^e / f difficult.pl /^f\%/ g difficult.pl /^g / h difficult.pl /^h(/ xy difficult.pl /^xy(/ y difficult.pl /^y(/ z & () . zz difficult.pl /^'z \& () \. zz'/ @~*\/#?```= difficult.pl /^~\*\\\/#?```=(/ @//E*O*F TestFiles/tags// chmod u=rw,g=rw,o=r TestFiles/tags echo x - TestFiles/tricky.pl sed 's/^@//' > "TestFiles/tricky.pl" <<'@//E*O*F TestFiles/tricky.pl//' x/*******/y(X). */**/*(X). /*******/y(X). /**/*(X). x/***//****/y(X). @//E*O*F TestFiles/tricky.pl// chmod u=rw,g=rw,o=r TestFiles/tricky.pl echo Inspecting for damage in transit... temp=/tmp/shar$$; dtemp=/tmp/.shar$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 22 128 788 READ_ME 104 533 3067 ptags.L 38 83 677 Makefile 61 182 1128 ptags.h 68 325 1908 config.h 174 590 3995 main.c 374 1484 8989 process.c 105 309 1843 symbol.c 16 96 831 tags 32 39 155 comments.pl 13 13 82 crazy.pl 53 104 576 difficult.pl 4 8 39 morecomments.pl 18 68 452 tags 5 5 70 tricky.pl 1087 3967 24600 total !!! wc READ_ME ptags.L Makefile ptags.h config.h main.c process.c symbol.c tags TestFiles/comments.pl TestFiles/crazy.pl TestFiles/difficult.pl TestFiles/morecomments.pl TestFiles/tags TestFiles/tricky.pl | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if [ -s $dtemp ] then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.