# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	4tags.1
#	4tags.c
#	Makefile
#	README
#
echo x - 4tags.1
sed 's/^X//' >4tags.1 << 'END-of-4tags.1'
X.TH 4gltags 1 
X.SH NAME
X4gltags \- Generates tags for Informix 4GL
X.SH SYNOPSIS
X.B 4gltags
X.RB [ "-vqbo" ]
X.I "file(s)"
X.br
X.SH OPTIONS
X.TP 1.5i
X.BR -v erbose
XDisplay information about which file and line that are being processed.
X.TP 1.5i
X.BR -q uite
XMake 4gltags not complaining about duplicated tags.
X.TP 1.5i
X.BR -b ackward
XMake tags search backward (default=forward)
X.TP 1.5i
X.BI -o filename
XOutput tags to 
X.I filename.
XDefault is tags
X.SH DESCRIPTION
X.I 4gltags
Xreads through the files given, checks for FUNCTION and REPORT definitions
Xin the file, and creates a tag for each occurence.
X.br
XTags are automatically appended to the tagfile if it already
Xexists. And the tagfile is automatically sorted.
X.br
XCurrent Version is 3.7 
X
X.SH FILES
Xtags, *.4gl
X
X.SH PROGRAMMER
X4gltags was programmed By :
X.br 
X  Lars Johansson     (lajo@dsr.dk)
X.br
X  Dansk Software R}dgivning Aps
X.br
X  Current location: lajo@q8.dk
END-of-4tags.1
echo x - 4tags.c
sed 's/^X//' >4tags.c << 'END-of-4tags.c'
X/* ------------------------------------------------------------------------- 
X * File       : 4tags.c
X * Author     : Lars Johansson (lajo@q8.dk)
X * Date       : 16/12/94
X * Purpose    : Creates tags for Informix 4gl file(s)
X *
X * Notice:
X * Permission to use, copy, modify, and distribute this software and
X * its documentation for any purpose is hereby granted without fee, provided
X * that the above copyright notices and this permission notice appear in
X * all copies of the software and related documentation.
X * 
X * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
X * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
X * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
X *
X * IN NO EVENT SHALL LARS JOHANSSON BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
X * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
X * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
X * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR
X * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X * 
X * ------------------------------------------------------------------------- */
Xstatic char Copyright[] = "Copyright (c) 1994,1995 Lars Johansson";
Xstatic char sccs[] = "@(#)4tags.c	3.7 96/10/08 11:59:36";
X#include <stdio.h>
X#include <signal.h>
X#include <search.h>
X
Xchar *mem_error = "Sorry, no more memory";
X
Xint 		verbose;
Xint 		append;
Xint 		update;
X
X
Xint SaveTag1(void *Save );
Xint WriteTags(char *tmpname, char *mode);
Xint TagSort(const void *p1, const void *p2);
Xvoid ToUpperStr( char * InBuffer);
Xvoid rm_trail( char *str);
X
Xstatic		int 	Count=0;		
Xstatic		int 	Line=0;
Xstatic		long 	TotalLines=0;
Xstatic		int 	Debug=0;
Xstatic		long 	Compares=0;
Xstatic 		int 	Complain=1;
Xstatic		int		backward=0;
Xstatic		FILE	*OutFile;
Xstatic		void	*Tree_Head;
X
Xmain( int argc, char **argv)
X{
X
X	FILE 		*fp, *outfp;
X	char		tmpname[1024];
X	char		omode[10];
X	int			files=0;
X	extern char	*optarg;
X	extern int	optind;
X	int			c;
X
X	if ( argc < 2)
X	{
X		printhelp();
X		exit(1);
X	}
X	append=1;
X	strcpy(tmpname, "tags");
X	strcpy(omode, "w");
X
X	/* Get some options */
X	while ((c =  getopt (argc, argv, "bo:qv?")) != -1 )
X	{
X		switch (c)
X		{
X			case 'o':
X				strcpy(tmpname, optarg);
X				break;
X			case 'q': Complain=0; break;
X			case 'v': verbose=1; break;
X			case 'b': backward=1; break;
X			case '?':
X			default:
X				printhelp();
X				exit(1);
X
X		}
X	}
X
X	/* 
X	 * Rest of argv must be files - loop thru 'em 
X	 */
X	argc--;	
X	for(; optind <= argc; optind++)
X	{
X		if ( (fp = fopen(argv[optind], "r")) != NULL)
X		{
X			MakeTag(fp, argv[optind]);
X			fclose(fp);
X			files++;
X		}
X		TotalLines+=Line;
X		Line=0;
X	}
X	if (files)
X	{
X		WriteTags(tmpname, omode);
X	}
X	else
X		if ( verbose )
X			printf("No files processed\n");
X	
X	if ( verbose )
X	{
X		printf("%d Files Processed\n", files);
X		printf("%d Lines Process\n", TotalLines);
X		printf("%d Compares\n", Compares);
X	}
X}
X
X/*
X * Make tags from the file 
X */
XMakeTag(FILE *infile, char *filename)
X{
X	register char	*ip;	/* pointer to InBuffer */
X	register char	*op;	/* Pointer to OutBuffer */
X	register char	*sp;	/* Point to first char after 'FUNCTION'/'REPORT'*/
X	register char	*tp;	/* pointer to Tag */
X	char			InBuffer[2048];
X	char			OutBuffer[2048];
X	char			WorkBuffer[2024];
X	int				Comment=0;
X	char			Tag[100];
X	int				rc;
X
X
X
X	if ( verbose )
X	{
X		printf("%s %d\r", filename, 0);
X		fflush(stdout);
X	}
X
X	while ( fgets(InBuffer, 2048, infile) && !feof(infile) )
X	{
X		ip=InBuffer;
X		op=OutBuffer;
X		Line++;
X
X		if ( verbose  && !(Line % 100 ) )
X		{
X				printf("%s %d\r", filename, Line);
X				fflush(stdout);
X		}
X
X		if ( Comment==3)
X		{
X			/* We have seen an unclosed '{'
X			 * now search for '}'
X			 */
X			while (*ip )
X			{
X				/* We can skip white spaces */
X				if ( *ip == ' ' || *ip == '\t')
X				{
X					ip++;
X					continue;
X				}
X				if ( *ip == '}' )
X				{
X					/* Multi line comment ends here */
X					Comment=0;
X					break;
X				}
X				*ip++;
X
X			}
X			if ( Comment == 3 )
X				continue;
X		}
X		else
X			Comment=0;
X
X		/* 
X		 * If line does not start with [whitespace][fFtT]
X		 * Then we don't make any further process on it
X		 */
X		while ( *ip )
X		{
X			if ( *ip == 'r' || *ip == 'R' || *ip == 'f' || *ip == 'F' )
X				break;
X			if ( (*ip >= 'A' && *ip <= 'z') || 
X					*ip == '#' || 
X					*ip == '\n' || 
X					*ip == '"'  ||
X					*ip == '-'  
X				)
X			{
X				Comment++;
X				break;
X			}
X			if ( *ip == '{' )
X			{
X				/* Lets check if this comment ends on this line */
X				while (*ip )
X				{
X					if ( *ip == '}' )
X					{
X						/* Comments ends here */
X						/* We need to fall through to the actual
X						 * processing, because the might be a
X						 * Function declaration after this comment */
X						Comment=4;
X						break;
X					}
X					*ip++;
X				}
X				if ( !Comment )
X				{
X					/* Comment span more lines */
X					Comment = 3;
X					break;
X				}
X			}
X			*ip++;
X		}
X		if ( Comment == 4 || Comment == 0 )
X			Comment=0;
X		else
X			continue;
X
X		strcpy(WorkBuffer, InBuffer);
X		ToUpperStr(WorkBuffer);
X
X		ip = (WorkBuffer + (ip - InBuffer));
X
X		if (strncmp(ip, "FUNCTION", 8)==0 || strncmp(ip, "REPORT", 6) ==0 )
X		{
X			/* 
X			 * It's a FUNCTION or a REPORT
X			 */
X			if ( *ip == 'F')
X			{
X				ip+=8;
X				/* Check if this really looks like a function/report decl. */
X				if ( *ip != ' ' && *ip != '\t')
X					continue;
X			}
X			else
X			{
X				ip+=6;
X				tp=ip;
X				/* Check if this really looks like a function/report decl. */
X				if ( *ip != ' ' && *ip != '\t')
X					continue;
X				/* Check if not "REPORT TO" */
X				op=Tag;
X				while(*ip==' ' || *ip =='\t')
X					++ip;
X				while(*ip!=' ' && *ip !='\t')
X				{
X					/* If end of line breakout */
X					if (*ip == '\n')
X					{
X						op=Tag;
X						*op=0;
X						break;
X					}
X					*op++ = *ip++;
X				}
X				*op=0;
X				ToUpperStr(Tag);
X				if (strcmp(Tag, "TO") == 0)
X					continue;
X				ip=tp;
X			}
X
X
X
X			sp = (InBuffer + (ip - WorkBuffer));
X			ip=InBuffer;
X
X			op=OutBuffer;
X			tp = Tag;
X			/* Skip white spaces */
X			while (*sp == ' ' || *sp == '\t')
X				sp++;
X
X			while (*ip)
X			{
X				if ( *ip == '(')
X				{
X					*op++=*ip++;
X					break;
X				}
X				else if (*sp == '(')
X					*tp++=0;
X				
X				*op++=*ip++;
X				*tp++=*sp++;
X			}
X			*op=0;
X			*tp=0;
X			rm_trail(Tag);
X			sprintf(WorkBuffer, "%s\t%s %c^%s%c\n" 
X					, Tag
X					, filename
X					, backward ? '?' : '/'
X					, OutBuffer
X					, backward ? '?' : '/'
X					);
X					
X			rc= SaveTag1(WorkBuffer);
X			if (rc && Complain )
X			{
X				printf("Duplicate tag: %s In file %s line %d\n",
X					Tag, filename, Line);
X			}
X			if ( rc == 0 )
X				Count++;
X
X		}
X	}
X	if ( verbose )
X	{
X		printf("%s %d\n", filename, Line);
X		fflush(stdout);
X	}
X}
X
X
X/* 
X * toupper a string as the functionname says
X */
Xvoid ToUpperStr( char * InBuffer)
X{
X	register char *p = InBuffer;
X
X	while (*p)
X		*p++ = toupper(*p);
X}
X
X/*
X * Save A char strings in the array ONLY IF NOT ALREADY THERE !!
X * 
X */
Xint SaveTag1(void *Save )
X{
X	void	*rc;
X	char	*p = (char*) malloc(strlen(Save)+2);
X	char	*q;
X	if ( p== NULL)
X	{
X		perror(mem_error);
X		exit(99);
X	}
X	strcpy(p, Save);
X	q = (char*)tfind(p, &Tree_Head, TagSort);
X	if ( q == NULL )
X	{
X		strcpy(p, Save);
X		rc = (void *)tsearch(p, &Tree_Head, TagSort);
X		return(0);
X	}
X	else
X		free(p);
X	return(1);
X}
X
X/*
X * Sort function for qsort
X */
Xint TagSort(const void *p1, const void *p2)
X{
X	register  const char *c1, *c2;
X	c1=p1;
X	c2=p2;
X
X	Compares++;
X	while ( *c1 == *c2 && *c1 != '\t' && *c2 != '\t')
X	{
X		*c1++;
X		*c2++;
X	}
X	if ( *c1 == '\t' && *c2 != '\t')
X		return(-1);
X	else if (*c2 == '\t' && *c1 != '\t')
X		return(1);
X	else if (*c1 != *c2)
X	{
X		if ( *c1 > *c2)
X			return(1);
X		else
X			return(-1);
X	}
X	else
X		return(0);
X}
X
Xvoid TreeWalk( void *node, VISIT order, int level) 
X{
X	if ( order == postorder || order == leaf )
X		fprintf(OutFile, "%s", *((char **)node));
X}
X
X/*
X * Writes all record saved in the tree
X */
Xint WriteTags(char *tmpname, char *mode)
X{
X	char				Buffer[2048];
X
X	if (append )
X	{
X		if ((OutFile = fopen(tmpname, "r")) != NULL)
X		{
X			if (verbose) printf("Appending\n");
X			while(fgets(Buffer, sizeof(Buffer)-1, OutFile))
X			{
X				SaveTag1(Buffer);
X			}
X			fclose(OutFile);
X		}
X		else
X			if ( verbose )
X				fprintf(stderr,
X					"Cannot open %s for reading - continuing anyway\n"
X					,tmpname);
X	}
X	unlink(tmpname);
X	if ((OutFile = fopen(tmpname, mode)) == NULL)
X	{
X		perror("Cannot open tagfile");
X		exit(1);
X	}
X
X	twalk(Tree_Head, TreeWalk);
X
X	if (verbose )
X		printf("%d Tags created\n", Count);
X	fflush(OutFile);
X	fclose(OutFile);
X}
X
Xint printhelp()
X{
X
X	printf("4gltags - (Ver. 3.7) %s\n", Copyright);
X	printf("Usage :4gltags [options] file(s)\n");
X	printf("\toptions:\n");
X	printf("\t -v verbose mode (show what's going on)\n");
X	printf("\t -o FILE output to file instead of tags\n");
X	printf("\t -b use backward search (default=forward)\n");
X	printf("\t -q be quiet about duplicated tags\n");
X}
X
Xvoid rm_trail( char *str)
X{
X	register char *p1=str;
X
X	while(*p1 != '\0') p1++;
X	p1--;
X	while(*p1 == ' ')
X		*p1-- = '\0';
X}
X
END-of-4tags.c
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XCC=gcc
XCCOPT=-O -s
XCFLAGS=$(CCOPT)
XCHMOD=/bin/chmod
XCP=/bin/cp
XRM=/bin/rm
X
XOBJS = 4tags.o
XSRCS = 4tags.c
X
Xall: 4gltags
Xexport : install clean
X4gltags: $(OBJS)
X	$(CC) $(CCOPT) -o 4gltags $(OBJS)
X	
X#install: 4gltags
X#	-$(CHMOD) 666 /Sys/Bin/4gltags
X#	$(CP) 4gltags /Sys/Bin/4gltags
X#	-$(CHMOD) 511 /Sys/Bin/4gltags
X
Xclean:
X	$(RM) -f $(OBJS)
X	$(RM) -f 4gltags
END-of-Makefile
echo x - README
sed 's/^X//' >README << 'END-of-README'
X
X                         4gltags - A 'C' Version 
X                                   By 
X                             Lars Johansson
X
X ------------------------------------------------------------------------------ 
X
X This 'C' version of 4gltags was programmed because I was beginning to get
X a bit tired of the poor speed of the original 4gltags, which was programmed
X in shell. 
X
X This version highlights :
X
X   - Very fast.
X   - Generates tags for both REPORT and FUNCTION.
X   - Automatically appends to and sorts the tagfile.
X   - knows about '--' '#' and '{}' type comments.
X
X 4gltags has been tested in-house and by Kerry Sainsbury (thanks Kerry).
X It has been used in-house for several months with no complains.
X
X 4gltags can be found at: http://q8t.q8.dk/~lajo
X Current Version is 3.7
X
X 4gltags Syntax.
X
X	 4gltags [options] 4gl-file(s)
X
X	 Three options are available for your use:
X
X	   -o outputfile     use 'outputfile' as tagfile
X	   -v                verbose run.
X	   -q                be quiet, don't complain about duplicated tags.
X	   -b                Make backward searching tags
X
X
X	All this can be read in the man-page.
X
X
X The included makefile is ready for GNU-C compiler, and should work
X with most ANSI-C compilers. Users of NON ANSI-C compilers needs to
X fiddle a little with the prototypes and functions declarations for
X a clean compile.
X
X All comments,suggestions and bugs are welcomed at lajo@dsr.dk.
X
X Thanks to :
X  Kerry Sainsbury(kerry@kcbbs.gen.nz) for testing and the Informix FAQ.
X
X Regards,
X Lars Johansson.
X
X
X Lars Johansson
X Dansk Software R}dgivning Aps.
X Bydammen 5
X DK-2750 Ballerup
X Denmark
X lajo@dsr.dk
X
X Currently located (as consultant) at:
X
X Kuwait Petroleum (Danmark) A/S
X Hummeltoftevej 49
X DK-2830 Virum
X Denmark
X lajo@q8.dk
END-of-README
exit