[comp.sources.unix] v17i093: Create tags file for Prolog

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.