Subject: Simple Editor, utils
From: [email-masked] (Andrew Burt)
Date: Tue, 14 Dec 93 16:29:03 GMT

After various delays and fits of code cleanup, here is the simple editor
I mentioned and had various requests to post.  Alas, wordwrap was not as
trivial to add as I expected; it *could* be done, but I don't have the
time to work on it (our users aren't computer literate enough to even know
they're missing wordwrap).  Someday when I have nothing better to do I'll
tinker with it again (i.e., don't hold your breath).

Anyway, here it is.  I've tossed in a bunch of utility functions mainly
because it was easier to document them briefly than to weed out which ones
aren't used, etc. :-)

I highly suggest you test it a bit before placing into production.  I may
well have broken something as I cleaned up.  (Not to destroy all your
confidence, it did build & run from this shar on our system.)

---- Cut Here and unpack ----
#!/bin/sh
# shar:	Shell Archiver  (v1.29)
#
#	Run the following text with /bin/sh to create:
#	  Makefile
#	  README
#	  common.h
#	  dbname.h
#	  dl.c
#	  dl.h
#	  dl.man
#	  dltst.c
#	  fgiusr.c
#	  fglutil.c
#	  globals.4
#	  globals.4gl
#	  iedit
#	  iedit.4
#	  iedit.4gl
#	  iedit.per
#	  informix.h
#	  q2list.ec
#	  qlist.c
#	  qlist.h
#	  regexif.c
#	  stdio.c
#	  tssbug.c
#	  util.4
#	  util.4gl
#	  re/COPYRIGHT
#	  re/Makefile
#	  re/README
#	  re/regerror.c
#	  re/regexp.3
#	  re/regexp.c
#	  re/regexp.h
#	  re/regmagic.h
#	  re/regsub.c
#
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
XSHELL=/bin/sh
XRUNNERCSRC = fgiusr.c fglutil.c dl.c regexif.c stdio.c qlist.c q2list.ec \
X	tssbug.c
XREGEXOBJ = re/regerror.o re/regexp.o re/regsub.o
X
XEDIT4SRC = globals.4gl iedit.4gl
XEDIT4OBJ = globals.4go iedit.4go
XEDITFORMS = iedit.frm
X
XCPP = /usr/lib/gcc/gcc-cpp
X
X.SUFFIXES: .4 .4go .4gl .4gi .frm .per
X.4gl.4go:
X	fglpc $<
X
X.per.frm:
X	form4gl $<
X
X.4.4gl:
X	sed -e 's/\([^\]\)#.*/\1/' -e 's/^#.*//' -e 's/\\#/#/g' -e 's/^@/#/' -e 's/~/#/g' $< | $(CPP) > $@
X
X.4go.4gi:
X	cat $< > $@
X
X.4.4go:
X	sed -e 's/\([^\]\)#.*/\1/' -e 's/^#.*//' -e 's/\\#/#/g' -e 's/^@/#/' -e 's/~/#/g' $< | $(CPP) > $<gl
X	fglpc $<gl
X
Xall: iedit.4gi ngo
X
Xiedit.4gi: globals.4go iedit.4go iedit.frm
X	cat iedit.4go globals.4go > iedit.4gi
X
X# This may fail!  If so, you may need to hack cfglgo/cfgldb to work
X# with the .ec file.
X# You can just delete it, and remove the functions from fgiusr.c, no loss.
Xngo: $(RUNNERCSRC) regex
X	cfglgo $(RUNNERCSRC) $(REGEXOBJ) -o ngo
X
Xndb: $(RUNNERCSRC) regex
X	cfgldb $(RUNNERCSRC) $(REGEXOBJ) -o ndb
X
Xfglutil.o dl.o : dl.h
X
Xregex:
X	cd re && make
X
Xclean:
X	rm -f *.o *.4gl *.4go re/*.o *.frm q2list.c
SHAR_EOF
chmod 0644 Makefile || echo "restore of Makefile fails"
sed 's/^X//' << 'SHAR_EOF' > README &&
XThis is, ostensibly, the 'iedit' editor, a simple informix-based editor
X(though it could be called from anything, it uses Informix's bizarre keys
Xlike ^D for delete-to-end-of-line, etc. -- see your 4gl docs under "input array"
Xfor what keys it uses).  However, after I made the mistake of saying it
Xdid wordwrap (which I thought I could hack into it in 30 seconds), I found
Xthat 4gl's input-array + wordwrap didn't work as I was anticipating -- so,
Xah, it doesn't do wordwrap.  For my penance I've included various util
Xfunctions I've written that maybe someone will find useful.
X
XTo build, check "CPP" in the Makefile and do "make".  (I hope.  Works for me.
XIt wasn't really written to be portable to any environment except ours, which is
Xkludged up to work around informix's limitations.)  I've included the .4gl
Xfile built from the .4 just in case.
X
XTo use:  iedit filename
X
XQuestions to:  [email-masked]
X
XAll work herein without other ownership information is hereby placed into
Xthe public domain.  As is, no warranty, use as you want, etc.
X
X
XAs I have it set, all of the C functions get compiled into the new runner.
XMost aren't needed, but I don't have time to weed out which, sorry.
X
XAlso, I use a hacked cfglgo and cfgldb script because of some silly
Xproblems using esql code in a runner; you shouldn't need it if you avoid
Xthe .ec files (which aren't used in the editor); and if you do want
Xto use the .ec, you'll probably have to hack these scripts to work locally. :-(
XI would put a copy in, but, er, the ugly "trade secret" stuff at the top
Xmakes me think better of it.  This isn't as clean as I'd like, but time
Xprecludes a cleanup.
X
XAnother possibly useful goodie is the Makefile and its use of cpp to
Xallow @define, @include in 4gl (which I call ".4" files, that get cpp'd to
Xproduce .4gl files).  '#' as comment makes it a bit awkward, but workable.
X(Watch out for literal "#" in your strings -- "\#" does the trick.)
X
X
X
XSome very half-hearted five minute documentation:
X
XC functions usable in 4gl:
X
Xisnumeric
X	true if string passed only contains numerics
X
Xstrchar
X	strchar(string, "!@$") true if string contains any of !@$, etc.
X
Xpreclip
X	Removes leading blanks
X
Xre_start
X	Compiles a pattern, returns an int to pass to re_match.
Xre_match
X	Takes int from re_start and string, true of matches that compiled pat.
Xre_sub
X	Takes int from re_start and substitution request, returns sub'd value
Xre_end
X	Cleans up from re_start.
XSee the substitute() and matchre() functions in util.4 for an example.
X
Xmakelist
Xunmakelist
Xadd2list
Xpush2list
Xisonlist
Xdel1stonlist
Xstartlist
Xnextlist
Xlistsize
X	creates a linked list, adds to it at end or front, tests if value is
X	on the list, removes from list, walks through list, destroys list.
X
Xqlnode2rec
Xmkqlist
Xdel1stonqlist
Xstartqlist
Xnextqlist
Xunmakeqlist
X	Turns a query into a linked list of values
X
Xfopen_if
Xfclose_if
Xfgetline
Xfputline
X	Standard I/O access (4gl name is w/o "_if")
X
Xmktmpf
X	Unix make-temp-file function
Xfdelete
X	Remove file
Xfaccess
X	Unix access() function
Xfilesize
X	Size of file in bytes
Xsystem_if
X	System() function
Xgetpid_if
X	Unix getpid()
X
Xdelzero
X	Deletes leading 0's
X
X/* misc application specific */
Xchk_allocarea
Xget_allocarea
Xset_allocarea
Xlog_allocarea
Xclr_allocarea
X	The infamous -4518 bug routines.  Mainly in here for calling
X	clr_allocarea.  We ended up replacing the fgl_alloc functions
X	after having irreconcilable differences with Informix about
X	solving the -4518, and have a non-supported replacement for it,
X	but that we can't give out.
X
Xdl.c has a set of generic doubly-linked list functions for C.  I've tossed
Xin dl.man and dltst.c for your enjoyment.  They aren't necessary for iedit.
X
XThe stuff in the 're' subdir are off the net, Henry Spencer's regexp code for C.
X
X
X4gl functions from util.4:
X
X	Some of these are used in iedit (and are incorporated into iedit.4)
Xfunction oopse(s)
X	Display s as "error".
X
Xfunction oopsw(s)
X	Display s as "warning".
X
Xfunction oops(msg, s)
X	Display s as msg.
X
Xfunction start_wait(msg)
X	Start a box saying "wait" (flashing) msg.
X
Xfunction end_wait()
X	Clean up the wait box.
X
Xfunction min(a, b)
X	min of a,b
X
Xfunction ok(msg1, msg2, msg3)
X	prints msg1/2/3 with yes/no choice after msg3
X
Xfunction substitute(pat, repl, text)
X	returns 'text' replaced per 'repl' based on 'pat'.
Xfunction matchre(text, pat)
X	true/false whether text matches pat.
X
Xfunction more(fname, title)
X	Displays fname in a box with title at top.  F10/escape to quit.
X
Xfunction smallmore(fname, title)
X	Small box, uses return to quit.
X
Xfunction print_file(f)
X	prints file via whatever program you tell it to use.
X
Xfunction preview(fname)
X	cleans up fname for screen display, based on your idea of 'clean'.
X
Xfunction init_acckey(key)
Xfunction push_acckey(key)
Xfunction pop_acckey()
Xfunction set_acckey(key)
X
X	These set up a stack for the accept key.  See iedit.4 for example
X	of use.  I.e., you can do "set accept key" in 4gl, but you can't
X	then go *back* to what it was before you set it; with this you
X	can.  (You push a new one, then pop back to the old key.)
SHAR_EOF
chmod 0644 README || echo "restore of README fails"
sed 's/^X//' << 'SHAR_EOF' > common.h &&
X/* must use C style #define and comments in here since not seen by 4gl */
X#define TRUE	1
X#define FALSE	0
X
X#define DT_canon	100
X#define DT_aggregate	101
X#define PT_canon	0
X#define PT_aggregate	1
X#define PT_sub_prop	2
X#define PT_sub		3
X#define PT_sec_prop	4
X#define PT_claim_prop	5
X#define NT_canon	200	/* 200 so mk_id can handle these too */
X#define NT_aggregate	201	/* w/o conflicting with DT, PT, etc. */
X#define AT_canon	300
X#define AT_aggregate	301
X#define CT_canon	400
X#define CT_aggregate	401
X#define ACT_none	0
X#define ACT_insert	1000000
X#define ACT_update	1
X#define ACT_error	-1000000
X#define COMMENTLEN	80
X#define NAMELEN		36
X#define DATELEN		10
X#define QUERYLEN	160
X#define PATTERNLEN	40
X#define RECDATALEN	50
X#define PROPDATALEN	80
X
X#define OOPSLEN 50
X#define OOPSWID 60
X#define WAITLEN 12
X#define WAITWID 16
X
X#define DISPATTR green
X#define INPATTR white
X#define POPUPATTR yellow
X#define ARROWATTR yellow, reverse
X
X#define FIXPAT(var)	if length(var) = 0 then let var = "*" end if
X#define NULLFIX(var)	if length(var) = 0 then let var = " " end if
X
X#define PE_ERROR	0
X#define PE_OK		1
X#define PE_NOEDIT	2
X#define PE_VACATED	3
X#define PE_NONPOST	4
X#define PE_SOFTERR	5
X
X#define ERR_NONE	0
X#define ERR_SOFT	1
X#define ERR_HARD	2
SHAR_EOF
chmod 0644 common.h || echo "restore of common.h fails"
sed 's/^X//' << 'SHAR_EOF' > dbname.h &&
Xdatabase skldprop
SHAR_EOF
chmod 0644 dbname.h || echo "restore of dbname.h fails"
sed 's/^X//' << 'SHAR_EOF' > dl.c &&
X/* ============================== dl.c ============================== */
X/*
X * Copyright (C) 1991 - Andrew Burt
X *	 [email-masked] - University of Denver, Math/CS dept.
X */
X#include "dl.h"
X
Xvoid *malloc();
X
Xvoid *getnode(size)
X{
X	DL_NODE *n;
X
X	if (n = (DL_NODE *) malloc(size)) {
X		memset(n, '\0', size);
X		n->size = size;
X	}
X	return(n);
X}
X
Xfreenode(n)
XDL_NODE *n;
X{
X	free(n);
X}
X
XDL dl_create(flags)
Xint flags;
X{
X	DL l;
X
X	if ((l = (DL) malloc(sizeof(struct dl))) == NULL)
X		return NULL;
X
X	l->head = l->tail = l->curr = NULL;
X	l->size = 0;
X	l->flags = flags;
X	return(l);
X}
X
X/*
X * Free entire list
X */
Xdl_destroy(l)
XDL l;
X{
X	while (dl_shead(l))
X		dl_delete(l);
X	/*
X	 * it is assumed that the list head structure itself was
X	 * from dl_create, thus will always be free'd.
X	 */
X	free(l);
X}
X
X/*
X * Delete specific node
X */
Xdl_delete_node(l, n)
XDL l;
XDL_NODE *n;
X{
X	if (n) {
X		dl_detach_node(l, n);
X
X		if ((l->flags & DL_FREE) == DL_FREE)
X			freenode(n);
X	}
X}
X
X/*
X * Delete node, but leave memory alone
X */
Xdl_detach_node(l, n)
XDL l;
XDL_NODE *n;
X{
X	l->size--;
X
X	l->curr = n->next;
X	if (n->prev)
X		n->prev->next = n->next;
X	else
X		l->head = n->next;
X	if (n->next)
X		n->next->prev = n->prev;
X	else
X		l->tail = n->prev;
X}
X
X/*
X * Insert node n before the current location in list l
X */
Xdl_ins_before_node(l, n_on_list, new_n)
XDL l;
XDL_NODE *n_on_list, *new_n;
X{
X	l->size++;
X
X	if (l->head == NULL) {
X		l->head = l->tail = l->curr = new_n;
X		new_n->next = new_n->prev = NULL;
X		return;
X	}
X
X	dl_set(l, n_on_list);
X	if (l->curr == NULL)
X		l->curr = l->head;
X
X	new_n->prev = l->curr->prev;
X	new_n->next = l->curr;
X	if (l->curr->prev)
X		l->curr->prev->next = new_n;
X	else
X		l->head = new_n;		/* else l->curr == l->head */
X	l->curr->prev = new_n;
X
X	l->curr = new_n;
X}
X
Xdl_ins_after_node(l, n_on_list, new_n)
XDL l;
XDL_NODE *n_on_list, *new_n;
X{
X	l->size++;
X
X	if (l->head == NULL) {
X		l->head = l->tail = l->curr = new_n;
X		new_n->next = new_n->prev = NULL;
X		return;
X	}
X
X	dl_set(l, n_on_list);
X	if (l->curr == NULL)
X		l->curr = l->tail;
X
X	new_n->prev = l->curr;
X	new_n->next = l->curr->next;
X	if (l->curr->next)
X		l->curr->next->prev = new_n;
X	else
X		l->tail = new_n;		/* else l->curr == l->tail */
X	l->curr->next = new_n;
X
X	l->curr = new_n;
X}
X
X/* future enhancement: dl_ins_sorted(l, n, func) */
X
X/*
X * Join l2 to the end of l1; l2 is no longer usable
X */
Xdl_cat(l1, l2)
XDL l1, l2;
X{
X	l1->size += l2->size;
X	l1->tail->next = l2->head;
X	l2->head->prev = l1->tail;
X	l1->tail = l2->tail;
X
X	free(l2);
X}
X
X#if assignment
X	/* you write this function */
XDL dl_split_at_node(l, n)
XDL l;
XDL_NODE *n;
X{
X	DL newl;
X
X	if ((newl = dl_create(dl_flags(l))) == NULL)
X		return(NULL);
X
X	newl->size = count to current pos
X}
X#endif
X
XDL dl_copy(l)
XDL l;
X{
X	DL newl;
X	DL_NODE *n, *newn;
X
X	if ((newl = dl_create(dl_flags(l))) == NULL)
X		return(NULL);
X
X	foreachnode(l, n) {
X		newn = getnode(n->size);
X		if (newn == NULL) {
X			dl_destroy(newl);
X			return(NULL);
X		}
X		memcpy(newn, n, n->size);	/* copies ptrs, but is ok */
X		dl_append(newl, newn);
X	}
X
X	return(newl);
X}
X
Xdl_compare(l1, l2, func)
XDL l1, l2;
Xint (*func)();
X{
X	int comp;
X
X	for (dl_shead(l1), dl_shead(l2);
X	     dl_curr(l1) && dl_curr(l2);
X	     dl_snext(l1), dl_snext(l2))
X		if ((comp = (*func)(dl_curr(l1), dl_curr(l2))) != 0)
X			return(comp);
X
X	return(dl_size(l1)-dl_size(l2));	/* decide based on #nodes */
X}
X
Xdl_apply(l, func, arg)
XDL l;
Xint (*func)();
Xchar *arg;
X{
X	foreach(l)
X		(*func)(dl_curr(l), arg);
X}
X
X/*
X * Do a linear search on the list, given a start/end point
X */
Xdl_lsearch(l, begin, end, key, func)
XDL l;
XDL_NODE *begin, *end;
Xvoid *key;
Xint (*func)();
X{
X	DL_NODE *n;
X	int pos = 1;
X
X	for (n = begin; n != end; n = n->next)
X		if ((*func)(n, key)) {
X			l->curr = n;
X			return(pos);
X		}
X		else
X			pos++;
X
X	return(FALSE);
X}
X
X/*
X * dl_sort - take a list and a 'qsort' type cmp func & sort
X *
X * since we make an array of ptrs, qsort really needs a cmp func
X * that follows a pointer to pointer to user struct; but this is
X * ugly for users, so dl_sort_cmp_fun does one indirection then
X * calls the users cmp func.
X */
X
Xstatic int (*dl_sort_user_cmp_fun)();
X
Xdl_sort_cmp_fun(p1, p2)
XDL_NODE **p1, **p2;
X{
X	return((*dl_sort_user_cmp_fun)(*p1, *p2));
X}
X
Xdl_sort(l, func)
XDL l;
Xint (*func)();
X{
X	DL_NODE **array;
X	int i, last;
X
X	if (l->size <= 1)
X		return(0);
X
X	if ((array = dl_l2a(l)) == NULL)
X		return(-1);
X
X	dl_sort_user_cmp_fun = func;
X	qsort(array, l->size, sizeof(DL_NODE *), dl_sort_cmp_fun);
X
X	dl_a2l(l, array);
X
X	free(array);
X
X	return(0);
X}
X
X/* from a list, make an array of pointers to the list items */
XDL_NODE **dl_l2a(l)
XDL l;
X{
X	DL_NODE *n, **array, **a;
X
X	array = (DL_NODE **) malloc(l->size*sizeof(DL_NODE *));
X	if (array == NULL)
X		return(NULL);
X	
X	for (n = l->head, a = array; n != NULL; n = n->next, a++)
X		*a = n;
X
X	return(array);
X}
X
X/* turn an array of pointers to the list items into a list */
Xdl_a2l(l, array)
XDL l;
XDL_NODE **array;
X{
X	int i, last;
X
X	l->head = array[0];
X	l->head->prev = NULL;
X	l->head->next = array[1];
X
X	last = l->size - 1;
X	for (i = 1; i < last; i++) {
X		array[i]->next = array[i+1];
X		array[i]->prev = array[i-1];
X	}
X
X	l->tail = array[last];
X	l->tail->next = NULL;
X	l->tail->prev = array[last-1];
X}
SHAR_EOF
chmod 0644 dl.c || echo "restore of dl.c fails"
sed 's/^X//' << 'SHAR_EOF' > dl.h &&
X/* ============================== dl.h ============================== */
X/*
X * Copyright (C) 1991 - Andrew Burt
X *	 [email-masked] - University of Denver, Math/CS dept.
X */
X#ifndef DL_H
X#define DL_H    /* so it isn't included more than once */
X
X#ifndef TRUE
X#define TRUE	1
X#define FALSE	0
X#endif
X
X/*
X * Template for other "derived" node types.  We assume a struct dl_node is at
X * the front of the derived type.  Thus dl.c knows nothing about user data.
X */
Xtypedef struct dl_node {
X        struct dl_node *next;
X        struct dl_node *prev;
X        int size;                       /* number of bytes of data following */
X} DL_NODE;
X
X/*
X * Structure to refer to a doubly-linked list.
X */
Xtypedef struct dl {
X        DL_NODE *head;
X        DL_NODE *tail;
X        DL_NODE *curr;
X        int size;                       /* number of nodes in list */
X        int flags;
X} *DL;
X
Xvoid *getnode();
XDL dl_create(), dl_copy();
XDL_NODE **dl_l2a();
X
X#define dl_head(dl)		((void *) ((dl)->head))
X#define dl_tail(dl)		((void *) ((dl)->tail))
X#define dl_curr(dl)		((void *) ((dl)->curr))
X#define dl_size(dl)		((dl)->size)
X#define dl_flags(dl)		((dl)->flags)
X#define dl_prev(dl)		((void *) ((dl)->curr->prev))
X#define dl_next(dl)		((void *) ((dl)->curr->next))
X#define dl_set(dl, n)		((void *) ((dl)->curr = (DL_NODE *) (n)))
X#define dl_shead(dl)		((void *) (dl_set(dl, dl_head(dl))))
X#define dl_stail(dl)		((void *) (dl_set(dl, dl_tail(dl))))
X#define dl_snext(dl)		((void *) (dl_set(dl, dl_next(dl))))
X#define dl_sprev(dl)		((void *) (dl_set(dl, dl_prev(dl))))
X#define dl_nextof(n)		((void *) (((DL_NODE *)(n))->next))
X#define dl_prevof(n)		((void *) (((DL_NODE *)(n))->prev))
X#define dl_sflags(dl,f)		((dl)->flags = (f))
X#define dl_searchlist(l,key,func)  (dl_lsearch(l, dl_head(l), NULL, key, func))
X#define dl_delete(l)		(dl_delete_node(l, dl_curr(l)))
X#define dl_detach(l)		(dl_detach_node(l, dl_curr(l)))
X#define dl_ins_after(l, n)	(dl_ins_after_node(l, dl_curr(l), n))
X#define dl_ins_before(l, n)	(dl_ins_before_node(l, dl_curr(l), n))
X#define dl_append(l, n)		(dl_ins_after_node(l, dl_tail(l), n))
X#define dl_prepend(l, n)	(dl_ins_before_node(l, dl_head(l), n))
X#define dl_split(l, n)		(dl_split_at_node(l, dl_curr(l)))
X
X/* Whether to free() nodes upon deletion or not. */
X#define DL_FREE         1
X#define DL_NOFREE       0
X
X#ifndef NULL
X#define NULL	(0)
X#endif
X
X#define NIL     ((void *) 0)            /* for passing as param */
X
X#define foreach(l)		for (dl_shead(l); dl_curr(l); dl_snext(l))
X#define foreachnode(l,p)	for ((p)=dl_shead(l); (p); (p)=dl_snext(l))
X#endif
SHAR_EOF
chmod 0644 dl.h || echo "restore of dl.h fails"
sed 's/^X//' << 'SHAR_EOF' > dl.man &&
X/*
X * Copyright (C) 1991 - Andrew Burt
X *	 [email-masked] - University of Denver, Math/CS dept.
X */
X========== Include file:
X
X	#include "dl.h"
X
X========== Data types:
X
X	DL l;		gives a "list" variable.
X			Think of it as "the whole list".
X
X	DL_NODE	n;	should be the first field of any struct type
X			that will use the dl package.  Hence,
X
X				struct foo {
X					DL_NODE n;
X					...user data here...
X				};
X
X			Note that the field may be named anything; 'n'
X			is not mandatory.  The DL_NODE type is never
X			used by the user except for this one usage.
X
X	Unless otherwise noted below, assume 'l' is of type DL and 'n' of
X	type DL_NODE *n.
X
X========== Creation of list and nodes:
X
XDL dl_create(int flags)
X
X	Create a new list (actually list header for an empty list).
X	Current flags are:
X		DL_FREE		= free memory for nodes on dl_delete, destroy.
X		DL_NOFREE	= don't free up the memory for the nodes.
X
Xvoid *getnode(unsigned size)
Xfreenode(void *n)
X
X	getnode allocates memory for a node of 'size' bytes.  Freenode frees
X	the same.  'size' should be the size of the user's struct type.
X	These functions most likely call malloc() and free(), if this is
X	of concern to you.
X
X========== Adding nodes to the list:
X
Xdl_append(l, n)
Xdl_prepend(l, n)
X
X	dl_append adds new node, n, to end of list l.
X	dl_prepend adds new node, n, at the front of the list.
X
Xdl_ins_after(l, n)
Xdl_ins_before(l, n)
X
X	dl_ins_after adds a new node, n, after the current pointer on the list.
X	If the current pointer is NULL, then n is added to the end of the list.
X	dl_ins_before adds a new node, n, before the current pointer on the
X	list.  If the current pointer is NULL, then n is added to the front of
X	the list.
X
Xdl_ins_after_node(l, n_on_list, new_n)
Xdl_ins_before_node(l, n_on_list, new_n)
X
X	dl_ins_after_node inserts new node (new_n) after a specific node on
X	the list.  If n_on_list is NULL, add new_n at the end of the list.
X	dl_ins_before_node inserts new node before a specific node on the
X	list.  If n_on_list is NULL, add new_n at the front of the list.
X
X
X========== Accessing lists:
X
Xdl_head(l)
Xdl_shead(l)
Xdl_tail(l)
Xdl_stail(l)
X
X	dl_head returns a pointer to the first element of the list without
X	setting the current pointer there.  dl_shead does the same, but
X	_sets_ the implicit current pointer to the head as well.
X
X	Similarly for dl_tail and dl_stail.
X
Xdl_next(l)
Xdl_snext(l)
Xdl_prev(l)
Xdl_sprev(l)
X
X	dl_next returns the address of the DL_NODE after the current one
X	(using the implicit current pointer).  NULL is returned if
X	there is no next node.
X
X	dl_snext _sets_ the current pointer to the next element, and
X	returns this address.
X
X	dl_prev and dl_sprev act likewise for the previous pointer.
X
Xdl_nextof(n)
Xdl_prevof(n)
X
X	dl_nextof returns the address of the DL_NODE after the node 'n',
X	or NULL if n is the last.  dl_prevof returns the address of the
X	DL_NODE before the node 'n', or NULL if n is the first.
X	The implicit current pointer is neither looked at nor changed.
X
Xdl_curr(l)
X
X	Returns a pointer to the implicit 'current' node on the list.
X	Current is changed during each modification or reference to
X	the list (e.g., on insertion, current is set to the added node).
X
Xdl_set(l, n)
X
X	Set the implicit current pointer to node 'n'.  No checking is
X	done to verify that 'n' is a node of list l.
X
Xdl_size(l)
X
X	Returns the number of nodes on list l.  An empty list is size 0.
X
Xdl_flags(l)
X
X	Return the settings for the flags (e.g., DL_FREE) for the list.
X
Xforeach(l)
Xforeachnode(l, p)
X
X	Utility control structures for walking through each element
X	of a list.  With foreach, use dl_curr(l) to access the element within
X	the loop.  With foreachnode, user supplies a valid pointer variable
X	which is assigned each node address in turn (dl_curr is also usable).
X
Xdl_apply(l, func, arg)
X
X	Call function 'func' for each node on list l; supplies arg as
X	2nd parameter to func, i.e., function is
X		func(DL_NODE *node, void *arg).
X
Xdl_searchlist(l,key,func)
X
X	Search the entire list (see dl_search).  Calls dl_search
X	with dl_head(l) and NULL.
X
Xint dl_lsearch(l, begin, end, key, func)
X
X	Return TRUE/FALSE whether a given value is on list l.
X	Search begins with the node 'begin', and ends BEFORE the
X	node 'end'; use NULL for 'end' to search to end of list.
X	Each node is passed to func,
X		func(DL_NODE *n, void *key),
X	which should return TRUE or FALSE (1 or 0) whether it is
X	the desired node.
X
X	The current pointer of the list is set to this node, or NULL
X	if none is found.
X
X========== Modifying lists:
X
Xdl_destroy(l)
X
X	Delete every node from the list and free the list header.
X	The l variable is now useless and should not be used until a
X	subsequent dl_create is done.
X
Xdl_delete(l)
Xdl_delete_node(l, n)
X
X	dl_delete removes the current node from the list, freeing memory
X	if DL_FREE is set for the list.
X
X	dl_delete_node deletes a _specific_ node from the list.
X
Xdl_detach(l)
Xdl_detach_node(l, n)
X
X	dl_detach removes the current node from the list; no memory is freed,
X	even if DL_FREE is set for the list.
X
X	dl_detach_node detaches a specific node from the list.
X
XDL dl_copy(l)
X
X	Return a copy of list l.
X
Xdl_compare(l1, l2, func)
X
X	Compare list l1 and l2 using func as a comparison routine.  Func
X	should be:
X		int func(struct real_type *l1, struct real_type *l2)
X	and return <0, 0, or >0 just as for dl_sort (can be same func).
X
Xdl_cat(l1, l2)
X
X	Concatenate list l2 to the end of list l1.  List l2 is
X	destroyed.
X
Xdl_sort(l, func)
X
X	Given a comparison function sufficient for 'qsort', dl_sort
X	sorts the list l (rearranges pointers).
X
X========== Misc.:
X
Xdl_a2l(l, array)
X
X	Convert an array of pointers to DL_NODEs to a list.
X	This modifies all previous/next pointers of the list elements,
X	but leaves the nodes in place (doesn't move any nodes, just
X	updates pointers).
X
XDL_NODE **dl_l2a(l)
X
X	Make and return an array of pointers to DL_NODEs such that
X	array[i] is a pointer to the ith element of the list.  The
X	array is obtained via malloc, and should be free()d when done.
SHAR_EOF
chmod 0644 dl.man || echo "restore of dl.man fails"
sed 's/^X//' << 'SHAR_EOF' > dltst.c &&
X#include <stdio.h>
X#include "dl.h"
X
Xstruct datum {
X	DL_NODE n;		/* for linking */
X	int item;		/* data fields start here */
X};
X
Xstruct datum *fillnode();
Xint printnode(), datumcomp(), datumfind();
X
Xmain()
X{
X	DL l;			/* a list variable */
X	DL lcopy;		/* a list to test copying */
X	struct datum *x;	/* a pointer to one node */
X	int value;		/* for input from user */
X	int sum;		/* sum of values on list */
X
X	if ((l = dl_create(DL_FREE)) == NULL) {
X		printf("Not enough memory.\n");
X		exit(1);
X	}
X
X	while ((value = getvalue("Enter value, 0 to end")) != 0)
X		dl_append(l, fillnode(value));
X
X	/* List out elements */
X	print_list("Elements are: ", l);
X
X	if (dl_searchlist(l, getvalue("Enter a value to delete"), datumfind))
X		dl_delete(l);
X
X	print_list("List after delete is: ", l);
X
X	x = fillnode(getvalue("Enter a value to add"));
X
X	if (dl_searchlist(l, getvalue("value to insert before"), datumfind))
X		dl_ins_before(l, x);
X
X	print_list("List after insert is: ", l);
X
X	dl_sort(l, datumcomp);
X
X	print_list("Sorted elements are: ", l);
X
X	sum = 0;
X	foreachnode(l, x)
X		sum += x->item;
X	/*
X	 * Alternatively,
X	 *	foreach(l)
X	 *		sum += ((struct datum *) dl_curr(l))->item)
X	 * or
X	 *	dl_apply(l, sum_func, &sum)
X	 *	...
X	 *	sum_func(x, sump)
X	 *	struct datum *x;
X	 *	int *sump;
X	 *	{
X	 *		*sump += x->item;
X	 *	}
X	 * or
X	 *	for (x = dl_head(l); x; x = dl_nextof(x))
X	 *		sum += x->item;
X	 */
X	printf("Sum of the %d list values = %d\n", dl_size(l), sum);
X
X	if (lcopy = dl_copy(l)) {
X		printf("after copy, compare -> %d\n",
X				dl_compare(l, lcopy, datumcomp));
X		x = dl_stail(lcopy);
X		dl_detach(lcopy);
X		printf("after change, compare -> %d\n",
X				dl_compare(l, lcopy, datumcomp));
X		dl_append(lcopy, x);
X		printf("after put back, compare -> %d\n",
X				dl_compare(l, lcopy, datumcomp));
X	}
X	else
X		printf("copy failed\n");
X}
X
Xgetvalue(prompt)
Xchar *prompt;
X{
X	int i;
X	printf("%s: ", prompt);
X	scanf("%d", &i);
X	return(i);
X}
X
Xprintnode(x)
Xstruct datum *x;
X{
X	printf("%d ", x->item);
X}
X
Xprint_list(label, l)
Xchar *label;
XDL l;
X{
X	printf("%s", label);
X	dl_apply(l, printnode);
X	printf("\n");
X}
X
Xdatumcomp(p1, p2)
Xstruct datum *p1, *p2;
X{
X	return(p1->item - p2->item);
X}
X
Xdatumfind(node, key)
Xstruct datum *node;
Xint key;
X{
X	return(node->item == key);
X}
X
Xstruct datum *fillnode(value)
Xint value;
X{
X	struct datum *x;
X
X	x = getnode(sizeof(struct datum));
X	if (x == NULL) {
X		printf("Not enough memory.\n");
X		exit(1);
X	}
X	x->item = value;
X	return(x);
X}
SHAR_EOF
chmod 0644 dltst.c || echo "restore of dltst.c fails"
sed 's/^X//' << 'SHAR_EOF' > fgiusr.c &&
X/***************************************************************************
X *
X *			   INFORMIX SOFTWARE, INC.
X *
X *			      PROPRIETARY DATA
X *
X *	THIS DOCUMENT CONTAINS TRADE SECRET DATA WHICH IS THE PROPERTY OF 
X *	INFORMIX SOFTWARE, INC.  THIS DOCUMENT IS SUBMITTED TO RECIPIENT IN
X *	CONFIDENCE.  INFORMATION CONTAINED HEREIN MAY NOT BE USED, COPIED OR 
X *	DISCLOSED IN WHOLE OR IN PART EXCEPT AS PERMITTED BY WRITTEN AGREEMENT 
X *	SIGNED BY AN OFFICER OF INFORMIX SOFTWARE, INC.
X *
X *	THIS MATERIAL IS ALSO COPYRIGHTED AS AN UNPUBLISHED WORK UNDER
X *	SECTIONS 104 AND 408 OF TITLE 17 OF THE UNITED STATES CODE. 
X *	UNAUTHORIZED USE, COPYING OR OTHER REPRODUCTION IS PROHIBITED BY LAW.
X *
X *
X *  Title:	fgiusr.c
X *  Sccsid:	@(#)fgiusr.c	7.2	7/8/90  13:50:19
X *  Description:
X *		definition of user C functions
X *
X ***************************************************************************
X */
X
X/***************************************************************************
X *
X * This table is for user-defined C functions.
X *
X * Each initializer has the form:
X *
X *	"name", name, nargs,
X *
X * Variable # of arguments:
X *
X *	set nargs to -(maximum # args)
X *
X * Be sure to declare name before the table and to leave the
X * line of 0's at the end of the table.
X *
X * Example:
X *
X *	You want to call your C function named "mycfunc" and it expects
X *	2 arguments.  You must declare it:
X *
X *		int mycfunc();
X *
X *	and then insert an initializer for it in the table:
X *
X *		"mycfunc", mycfunc, 2,
X *
X ***************************************************************************
X */
X
X#include "fgicfunc.h"
X
Xint isnumeric();
Xint strchar();
Xint preclip();
Xint re_start(), re_match(), re_sub(), re_end();
Xint makelist();
Xint add2list();
Xint push2list();
Xint isonlist();
Xint del1stonlist();
Xint startlist();
Xint nextlist();
Xint listsize();
Xint unmakelist();
Xint qlnode2rec(), mkqlist(), del1stonqlist(), startqlist(), nextqlist();
Xint unmakeqlist();
Xint fopen_if(), fclose_if(), fgetline(), mktmpf(), fdelete(), faccess();
Xint fputline();
Xint filesize();
Xint system_if();
Xint getpid_if();
Xint delzero();
Xint chk_allocarea(), get_allocarea(), set_allocarea(), log_allocarea();
Xint clr_allocarea();
X
Xcfunc_t usrcfuncs[] = 
X    {
X    /* utility */
X    "isnumeric", isnumeric, 1,
X    "strchar", strchar, 2,
X    "preclip", preclip, 1,
X
X    "re_start", re_start, 1,
X    "re_match", re_match, 2,
X    "re_sub", re_sub, 2,
X    "re_end", re_end, 1,
X
X    "makelist", makelist, 0,
X    "unmakelist", unmakelist, 1,
X    "add2list", add2list, 2,
X    "push2list", push2list, 2,
X    "isonlist", isonlist, 2,
X    "del1stonlist", del1stonlist, 1,
X    "startlist", startlist, 1,
X    "nextlist", nextlist, 1,
X    "listsize", listsize, 1,
X
X    "qlnode2rec", qlnode2rec, 1,
X    "mkqlist", mkqlist, 1,
X    "del1stonqlist", del1stonqlist, 1,
X    "startqlist", startqlist, 1,
X    "nextqlist", nextqlist, 1,
X    "unmakeqlist", unmakeqlist, 1,
X
X    "fopen", fopen_if, 2,
X    "fclose", fclose_if, 1,
X    "fgetline", fgetline, 2,
X    "fputline", fputline, 2,
X    "mktmpf", mktmpf, 0,
X    "fdelete", fdelete, 1,
X    "faccess", faccess, 1,
X    "filesize", filesize, 1,
X    "system", system_if, 1,
X    "getpid", getpid_if, 0,
X
X    "delzero", delzero, 1,
X
X    /* misc application specific */
X    "chk_allocarea", chk_allocarea, 0,
X    "get_allocarea", get_allocarea, 0,
X    "set_allocarea", set_allocarea, 1,
X    "log_allocarea", log_allocarea, 1,
X    "clr_allocarea", clr_allocarea, 0,
X    0, 0, 0
X    };
SHAR_EOF
chmod 0644 fgiusr.c || echo "restore of fgiusr.c fails"
sed 's/^X//' << 'SHAR_EOF' > fglutil.c &&
X#include "informix.h"
X#include "dl.h"
X
Xstruct listnode {
X	DL_NODE n;
X	char data[1];
X};
X
XPUBLIC int isnumeric(int nargs)
X{
X	int t = TRUE;
X	char s[STRSIZE], *p;
X
X	CHECKNARGS(isnumeric, 1)
X
X	popvchar(s, STRSIZE);
X
X	for (p = s; *p; p++)
X		if (!isdigit(*p)) {
X			t = FALSE;
X			break;
X		}
X
X	retint(t);
X	return(1);
X}
X
XPUBLIC int strchar(int nargs)
X{
X	char s[STRSIZE], charlist[STRSIZE], *strpbrk();
X
X	CHECKNARGS(strchar, 2)
X
X	popvchar(charlist, STRSIZE);
X	popvchar(s, STRSIZE);
X
X	retint(strpbrk(s, charlist) != NULL);
X	return(1);
X}
X
XPUBLIC int makelist(int nargs)
X{
X	DL l;
X
X	CHECKNARGS(makelist, 0)
X
X	l = dl_create(DL_FREE);
X
X	retlong((long) l);
X	return(1);
X}
X
XPUBLIC int add2list(int nargs)
X{
X	DL l;
X	char buf[1024];
X	struct listnode *n;
X	void *p;
X
X	CHECKNARGS(add2list, 2)
X
X	popvchar(buf, 1024);
X	poplong(&l);
X
X	/* listnode contains has 1 char, count it for the \0, so +strlen */
X	if (n = getnode(sizeof(struct listnode)+strlen(buf))) {
X		strcpy(n->data, buf);
X		dl_append(l, n);
X	}
X
X	return(0);
X}
X
XPUBLIC int push2list(int nargs)
X{
X	DL l;
X	char buf[1024];
X	struct listnode *n;
X	void *p;
X
X	CHECKNARGS(push2list, 2)
X
X	popvchar(buf, 1024);
X	poplong(&l);
X
X	/* listnode has 1 char, count it for the \0, so +strlen */
X	if (n = getnode(sizeof(struct listnode)+strlen(buf))) {
X		strcpy(n->data, buf);
X		dl_prepend(l, n);
X	}
X
X	return(0);
X}
X
Xint listcmp(struct listnode *n, char *k)
X{
X	return(strcmp(n->data, k) == 0);
X}
X
XPUBLIC int isonlist(int nargs)
X{
X	DL l;
X	char buf[1024];
X	struct listnode *n;
X
X	CHECKNARGS(isonlist, 2)
X
X	popvchar(buf, 1024);
X	poplong(&l);
X
X	retint(dl_searchlist(l, buf, listcmp));
X	return(1);
X}
X
XPUBLIC int del1stonlist(int nargs)
X{
X	DL l;
X
X	CHECKNARGS(del1stonlist, 1)
X
X	poplong(&l);
X
X	dl_shead(l);
X	dl_delete(l);
X
X	return(0);
X}
X
XPUBLIC int startlist(int nargs)
X{
X	DL l;
X	struct listnode *n;
X
X	CHECKNARGS(startlist, 1)
X
X	poplong(&l);
X
X	if (n = dl_shead(l))
X		retvchar(n->data);
X	else
X		retvchar("");
X	return(1);
X}
X
XPUBLIC int nextlist(int nargs)
X{
X	DL l;
X	struct listnode *n;
X
X	CHECKNARGS(nextlist, 1)
X
X	poplong(&l);
X
X	if (n = dl_snext(l))
X		retvchar(n->data);
X	else
X		retvchar("");
X	return(1);
X}
X
XPUBLIC int listsize(int nargs)
X{
X	DL l;
X
X	CHECKNARGS(listsize, 1)
X
X	poplong(&l);
X
X	retint(dl_size(l));
X	return(1);
X}
X
XPUBLIC int unmakelist(int nargs)
X{
X	DL l;
X
X	CHECKNARGS(unmakelist, 1)
X
X	poplong(&l);
X
X	dl_destroy(l);
X	return(0);
X}
X
XPUBLIC int delzero(int nargs)
X{
X	char s[STRSIZE], *p;
X
X	CHECKNARGS(delzero, 1)
X
X	popvchar(s, STRSIZE);
X
X	for (p = s; *p && *p == ' ' || *p == '0'; p++)
X		;
X
X	retvchar(p);
X	return(1);
X}
X
XPUBLIC int system_if(int nargs)
X{
X	char s[STRSIZE], *p;
X
X	CHECKNARGS(system_if, 1)
X
X	popvchar(s, STRSIZE);
X
X	system(s);
X
X	return(0);
X}
X
XPUBLIC int preclip(int nargs)
X{
X	char s[STRSIZE], *p;
X
X	CHECKNARGS(preclip, 1)
X
X	popvchar(s, STRSIZE);
X
X	for (p = s; *p && *p == ' '; p++)
X		;
X
X	retvchar(p);
X	return(1);
X}
X
XPUBLIC int getpid_if(int nargs)
X{
X	CHECKNARGS(getpid, 0)
X
X	retint(getpid());
X	return(1);
X}
X
Xvoid clip(char *s)
X{
X	char *p = s + strlen(s)-1;
X
X	while (p >= s && *p == ' ')
X		p--;
X	p[1] = '\0';
X}
SHAR_EOF
chmod 0644 fglutil.c || echo "restore of fglutil.c fails"
sed 's/^X//' << 'SHAR_EOF' > globals.4 &&
X@include "dbname.h"
Xglobals
X
X	define acckeystk integer,	# accept key stack
X		acckey char(20)		# current accept key
Xend globals
SHAR_EOF
chmod 0644 globals.4 || echo "restore of globals.4 fails"
sed 's/^X//' << 'SHAR_EOF' > globals.4gl &&
X# 1 ""
X# 1 "dbname.h" 1
Xdatabase skldprop
X# 1 "" 2
X
Xglobals
X
X	define acckeystk integer,	
X		acckey char(20)		
Xend globals
SHAR_EOF
chmod 0644 globals.4gl || echo "restore of globals.4gl fails"
sed 's/^X//' << 'SHAR_EOF' > iedit &&
X#!/bin/sh
Xngo iedit $*
SHAR_EOF
chmod 0755 iedit || echo "restore of iedit fails"
sed 's/^X//' << 'SHAR_EOF' > iedit.4 &&
X@include "dbname.h"
X
Xglobals "globals.4gl"
X
X@define TRUE	1
X@define FALSE	0
X
X@define OOPSLEN 50
X@define OOPSWID 60
X@define WAITLEN 12
X@define WAITWID 16
X
X@define DISPATTR green
X@define INPATTR white
X@define POPUPATTR yellow
X@define ARROWATTR yellow, reverse
X
Xmain
X	define fname char(100)
X	defer interrupt
X	options insert key F6
X	options delete key F7
X	options message line last
X	options prompt line last
X	options menu line last
X	call init_acckey("F10")
X	call arg_val(1) returning fname
X	message "                  Enter text, press F10 when finished."
X	call edit(fname)
Xend main
X
Xfunction oopse(s)
X	define s char(OOPSLEN)
X
X	call oops("Error!", s)
Xend function
X
Xfunction oopsw(s)
X	define s char(OOPSLEN)
X
X	call oops("Warning!", s)
Xend function
X
Xfunction oops(msg, s)
X	define msg char(OOPSLEN), s char(OOPSLEN)
X	define x char,
X		col integer
X
X	open window woops at 11, 12 with 3 rows, OOPSWID columns
X		attribute(border, red, prompt line last)
X
X	let col = (OOPSWID-length(msg))/2
X	display msg at 1,col attribute(red)
X
X	let col = (OOPSWID-length(s))/2
X	display s clipped at 2,col attribute(red, reverse)
X
X	call push_acckey("CONTROL-A")
X	prompt "                  Press RETURN to continue." for x
X		on key (ESCAPE)
X			let x = x
X	end prompt
X	call pop_acckey()
X
X	close window woops
Xend function
X
Xfunction start_wait(msg)
X	define msg char(WAITLEN)
X	define col integer
X
X	open window wwait at 12,32 with 2 rows, WAITWID columns
X		attribute(border, blue)
X	let col = (WAITWID+2-length("WAIT"))/2
X	display "WAIT" at 1,col attribute(blue, reverse, blink)
X
X	let col = (WAITWID+2-length(msg))/2
X	display msg clipped at 2,col attribute(blue)
X
Xend function
X
Xfunction end_wait()
X	close window wwait
Xend function
X
Xfunction ok(msg1, msg2, msg3)
X	define msg1 char(OOPSLEN), msg2 char(OOPSLEN), msg3 char(OOPSLEN)
X	define ans char,
X		col integer,
X		r integer,
X		ret integer
X
X	let r = 3;
X	if length(msg1) > 0 then
X		let r = r + 1
X	end if
X	if length(msg2) > 0 then
X		let r = r + 1
X	end if
X
X	open window wok at 7, 12 with r rows, OOPSWID columns
X		attribute(border, magenta, prompt line last)
X
X	let col = (OOPSWID-length(msg1))/2
X	display msg1 at 1,col attribute(magenta)
X
X	let col = (OOPSWID-length(msg2))/2
X	display msg2 at 2,col attribute(magenta)
X
X	menu msg3
X		command "Yes"
X			let ret = true
X			exit menu
X		command "No"
X			let ret = false
X			exit menu
X	end menu
X
X	close window wok
X
X	return ret
Xend function
X
X@define MORELINELEN	70
X@define MORELINES 100
X
Xfunction edit(fname)
X	define fname char(100), title char(40)
X	define cmd char(120), tfname char(100)
X	define fp integer, i integer, abort integer, nlines integer,
X		line char(MORELINELEN),
X		lines array[MORELINES] of record
X			l char(MORELINELEN)
X		end record
X
X	let title = "         Text Editor"
X	let fp = fopen(fname clipped, "r")
X	let i = 0
X	if fp != 0 then
X		call fgetline(MORELINELEN, fp) returning line
X		while line != "\200"	# assumes cpp will cvt the \stuff
X			let i = i + 1
X			if i > MORELINES then
X				call oopse("File too large to edit")
X				exit while
X			end if
X			let lines[i].l = line
X			call fgetline(MORELINELEN, fp) returning line
Xcall clr_allocarea();#XXX
X		end while
X		call fclose(fp);
X	end if
X
X	open window wedit at 4,4
X		with form "iedit"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("F10")
X	let abort = false
X	call set_count(i)
X	input array lines without defaults from s_editlines.*
X		on key (CONTROL-C)
X			if ok("", "", "Ok to lose your changes?") then
X				let abort = true
X				exit input
X			end if
X		on key (CONTROL-T, F11)
X			call arr_curr() returning i
X			call infield(line) returning lines[i].l
X			let tfname = mktmpf();
X			let fp = fopen(tfname clipped, "w")
X			if fp = 0 then
X				call oops("Error - can't write temp file", fname);
X				continue input
X			end if
X			let i = 1
X			while i <= nlines
X				call fputline(lines[i].l clipped, fp)
X				let i = i + 1
X			end while
X			call fclose(fp)
X			call print_file(tfname);
X			call fdelete(tfname);
X	end input
X	call pop_acckey()
X
X	call arr_count() returning nlines
X	close window wedit
X
X	if abort then
X		return
X	end if
X
X	# else save file
X	let fp = fopen(fname clipped, "w")
X	if fp = 0 then
X		call oops("Error - can't write file", fname);
X		return
X	end if
X	let i = 1
X	while i <= nlines
X		call fputline(lines[i].l clipped, fp)
X		let i = i + 1
X	end while
X	call fclose(fp)
Xend function
X
Xfunction init_acckey(key)
X	define key char(20)
X	call makelist() returning acckeystk
X	call set_acckey(key)
Xend function
X
Xfunction push_acckey(key)
X	define key char(20)
X	# push current one on stack, set to requested one
X	call push2list(acckeystk, acckey clipped)
X	call set_acckey(key)
Xend function
X
Xfunction pop_acckey()
X	define key char(20)
X	if listsize(acckeystk) > 0 then
X		call startlist(acckeystk) returning key
X		call del1stonlist(acckeystk)
X	else
X		let key = "ESCAPE"
X	end if
X	call set_acckey(key)
Xend function
X
Xfunction set_acckey(key)
X	define key char(20)
X
X	let acckey = key	# set global (for next push)
X
X	case when key = "ESCAPE" or key = "ESC"
X		options accept key ESCAPE
X
X	when key = "RETURN"
X		options accept key RETURN
X
X	when key = "CONTROL-A"
X		options accept key CONTROL-A
X
X	when key = "F10"
X		options accept key F10
X
X	#add others here as desired
X	#could call efacckey(n), though not as portable I suppose
X	end case
Xend function
X
Xfunction print_file(f)
X	define f char(100)
X	define cmd char(120)
X
X	call start_wait("Printing")
X	let cmd = "local-print ", f
X	call system(cmd)
X	call end_wait()
Xend function
SHAR_EOF
chmod 0644 iedit.4 || echo "restore of iedit.4 fails"
sed 's/^X//' << 'SHAR_EOF' > iedit.4gl &&
X# 1 ""
X# 1 "dbname.h" 1
Xdatabase skldprop
X# 1 "" 2
X
X
Xglobals "globals.4gl"
X
X
X
X
X
X
X
X
X
X
X
X
X
X
Xmain
X	define fname char(100)
X	defer interrupt
X	options insert key F6
X	options delete key F7
X	options message line last
X	options prompt line last
X	options menu line last
X	call init_acckey("F10")
X	call arg_val(1) returning fname
X	message "                  Enter text, press F10 when finished."
X	call edit(fname)
Xend main
X
Xfunction oopse(s)
X	define s char(50 )
X
X	call oops("Error!", s)
Xend function
X
Xfunction oopsw(s)
X	define s char(50 )
X
X	call oops("Warning!", s)
Xend function
X
Xfunction oops(msg, s)
X	define msg char(50 ), s char(50 )
X	define x char,
X		col integer
X
X	open window woops at 11, 12 with 3 rows, 60  columns
X		attribute(border, red, prompt line last)
X
X	let col = (60 -length(msg))/2
X	display msg at 1,col attribute(red)
X
X	let col = (60 -length(s))/2
X	display s clipped at 2,col attribute(red, reverse)
X
X	call push_acckey("CONTROL-A")
X	prompt "                  Press RETURN to continue." for x
X		on key (ESCAPE)
X			let x = x
X	end prompt
X	call pop_acckey()
X
X	close window woops
Xend function
X
Xfunction start_wait(msg)
X	define msg char(12 )
X	define col integer
X
X	open window wwait at 12,32 with 2 rows, 16  columns
X		attribute(border, blue)
X	let col = (16 +2-length("WAIT"))/2
X	display "WAIT" at 1,col attribute(blue, reverse, blink)
X
X	let col = (16 +2-length(msg))/2
X	display msg clipped at 2,col attribute(blue)
X
Xend function
X
Xfunction end_wait()
X	close window wwait
Xend function
X
Xfunction ok(msg1, msg2, msg3)
X	define msg1 char(50 ), msg2 char(50 ), msg3 char(50 )
X	define ans char,
X		col integer,
X		r integer,
X		ret integer
X
X	let r = 3;
X	if length(msg1) > 0 then
X		let r = r + 1
X	end if
X	if length(msg2) > 0 then
X		let r = r + 1
X	end if
X
X	open window wok at 7, 12 with r rows, 60  columns
X		attribute(border, magenta, prompt line last)
X
X	let col = (60 -length(msg1))/2
X	display msg1 at 1,col attribute(magenta)
X
X	let col = (60 -length(msg2))/2
X	display msg2 at 2,col attribute(magenta)
X
X	menu msg3
X		command "Yes"
X			let ret = true
X			exit menu
X		command "No"
X			let ret = false
X			exit menu
X	end menu
X
X	close window wok
X
X	return ret
Xend function
X
X
X
X
Xfunction edit(fname)
X	define fname char(100), title char(40)
X	define cmd char(120), tfname char(100)
X	define fp integer, i integer, abort integer, nlines integer,
X		line char(70 ),
X		lines array[100 ] of record
X			l char(70 )
X		end record
X
X	let title = "         Text Editor"
X	let fp = fopen(fname clipped, "r")
X	let i = 0
X	if fp != 0 then
X		call fgetline(70 , fp) returning line
X		while line != "\200"	
X			let i = i + 1
X			if i > 100  then
X				call oopse("File too large to edit")
X				exit while
X			end if
X			let lines[i].l = line
X			call fgetline(70 , fp) returning line
Xcall clr_allocarea();
X		end while
X		call fclose(fp);
X	end if
X
X	open window wedit at 4,4
X		with form "iedit"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("F10")
X	let abort = false
X	call set_count(i)
X	input array lines without defaults from s_editlines.*
X		on key (CONTROL-C)
X			if ok("", "", "Ok to lose your changes?") then
X				let abort = true
X				exit input
X			end if
X		on key (CONTROL-T, F11)
X			call arr_curr() returning i
X			call infield(line) returning lines[i].l
X			let tfname = mktmpf();
X			let fp = fopen(tfname clipped, "w")
X			if fp = 0 then
X				call oops("Error - can't write temp file", fname);
X				continue input
X			end if
X			let i = 1
X			while i <= nlines
X				call fputline(lines[i].l clipped, fp)
X				let i = i + 1
X			end while
X			call fclose(fp)
X			call print_file(tfname);
X			call fdelete(tfname);
X	end input
X	call pop_acckey()
X
X	call arr_count() returning nlines
X	close window wedit
X
X	if abort then
X		return
X	end if
X
X	
X	let fp = fopen(fname clipped, "w")
X	if fp = 0 then
X		call oops("Error - can't write file", fname);
X		return
X	end if
X	let i = 1
X	while i <= nlines
X		call fputline(lines[i].l clipped, fp)
X		let i = i + 1
X	end while
X	call fclose(fp)
Xend function
X
Xfunction init_acckey(key)
X	define key char(20)
X	call makelist() returning acckeystk
X	call set_acckey(key)
Xend function
X
Xfunction push_acckey(key)
X	define key char(20)
X	
X	call push2list(acckeystk, acckey clipped)
X	call set_acckey(key)
Xend function
X
Xfunction pop_acckey()
X	define key char(20)
X	if listsize(acckeystk) > 0 then
X		call startlist(acckeystk) returning key
X		call del1stonlist(acckeystk)
X	else
X		let key = "ESCAPE"
X	end if
X	call set_acckey(key)
Xend function
X
Xfunction set_acckey(key)
X	define key char(20)
X
X	let acckey = key	
X
X	case when key = "ESCAPE" or key = "ESC"
X		options accept key ESCAPE
X
X	when key = "RETURN"
X		options accept key RETURN
X
X	when key = "CONTROL-A"
X		options accept key CONTROL-A
X
X	when key = "F10"
X		options accept key F10
X
X	
X	
X	end case
Xend function
X
Xfunction print_file(f)
X	define f char(100)
X	define cmd char(120)
X
X	call start_wait("Printing")
X	let cmd = "local-print ", f
X	call system(cmd)
X	call end_wait()
Xend function
SHAR_EOF
chmod 0644 iedit.4gl || echo "restore of iedit.4gl fails"
sed 's/^X//' << 'SHAR_EOF' > iedit.per &&
X
XDATABASE formonly
X
XSCREEN
X{
X                     [title                         ]
X
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X[line                                                                  ]
X}
X
X
XATTRIBUTES
Xtitle = formonly.title, color = WHITE;
Xline = formonly.line;
X
XINSTRUCTIONS
XDELIMITERS "  "
XSCREEN RECORD s_editlines[12](formonly.line)
SHAR_EOF
chmod 0644 iedit.per || echo "restore of iedit.per fails"
sed 's/^X//' << 'SHAR_EOF' > informix.h &&
X#include <stdio.h>
X
Xvoid popvchar(char *, int);
Xvoid retvchar(char *);
X
X#define PUBLIC	/* i.e., those informix uses */
X
X#define TRUE	1
X#define FALSE	0
X
X#define STRSIZE 100
X
X#define CHECKNARGS(name, num) \
X	if (nargs != num)	{ \
X		fprintf(stderr, "name: got %d args, want num\n", nargs); \
X		return(-1); \
X	}
SHAR_EOF
chmod 0644 informix.h || echo "restore of informix.h fails"
sed 's/^X//' << 'SHAR_EOF' > q2list.ec &&
X#undef TEST
X#include <stdio.h>
X#include "qlist.h"
X$include sqlca;
X$include sqlda;
X$include sqltypes;
X
XDL q2list(query)
X$char *query;
X{
X	DL l;
X	int ncol, i /*, type */;
X	char *p;
X	char buf[DATALEN];
X	struct listnode *n;
X	struct sqlda *qlistdesc;
X	struct sqlvar_struct *col;
X
X	if ((l = dl_create(DL_FREE)) == NULL)
X		return(NULL);
X
X#ifdef TEST
X	$database skldprop;
X#endif
X	$prepare qlistid from $query;
X	$declare qlistcurs cursor for qlistid;
X	$open qlistcurs;
X	$describe qlistid into qlistdesc;
X	ncol = qlistdesc->sqld;
X
X	for (;;) {
X		if ((n = getnode(sizeof(struct listnode))) == NULL ||
X		  (n->cols = malloc(ncol*sizeof(struct datanode))) == NULL) {
X			if (n) free(n);
X			dl_destroy(l);
X			return(NULL);
X		}
X		n->ncol = ncol;
X
X		for (i = 0; i < ncol; i++) {
X			qlistdesc->sqlvar[i].sqldata = n->cols[i].data;
X			qlistdesc->sqlvar[i].sqltype = CCHARTYPE;
X			qlistdesc->sqlvar[i].sqllen = DATALEN;
X		}
X
X		$fetch qlistcurs using descriptor qlistdesc;
X		if (sqlca.sqlcode) {
X			/*printf("sqlcode = %d\n", sqlca.sqlcode);*/
X			free(n->cols);
X			free(n);
X			break;
X		}
X
X		for (i = 0; i < ncol; i++) {
X			clip(n->cols[i].data);
X			strcpy(n->cols[i].name, qlistdesc->sqlvar[i].sqlname);
X/*printf("got %s = <%s>\n", n->cols[i].name, n->cols[i].data);*/
X		}
X		dl_append(l, n);
X	}
X
X	$close qlistcurs;
X	$free qlistid;
X
X	return(l);
X}
X
Xstatic int (*qlistsortuserfun)(), qlistsortcol;
X
Xint qlistsortfun(struct listnode *n1, struct listnode *n2)
X{
X	return((*qlistsortuserfun)(n1->cols[qlistsortcol].data,
X				   n2->cols[qlistsortcol].data));
X}
X
X/*
X * Sort based on one field only
X */
Xqlistsort(DL l, char *fieldname, int (*func)())
X{
X	int i;
X	struct listnode *n = dl_head(l);
X
X	if (l == NULL)
X		return(0);
X
X	qlistsortuserfun = func;
X
X	/* find which column to sort on */
X	for (i = 0; i < n->ncol; i++)
X		if (strcmp(n->cols[i].name, fieldname) == 0) {
X			qlistsortcol = i;
X			break;
X		}
X
X	if (i == n->ncol)	/* column not found */
X		return(0);
X
X	return(dl_sort(l, qlistsortfun));
X}
X#ifdef TEST
Xmain(int argc, char **argv)
X{
X	DL l;
X	struct listnode *n;
X
X	l = q2list(argv[1]);
X/*	qlistsort(l, "lot", bynum);*/
X	dl_sort(l, subdivlotcomp);
X	foreachnode(l, n)
X		printf("received: %s\n", n->cols[4].data);
X}
X#endif
SHAR_EOF
chmod 0644 q2list.ec || echo "restore of q2list.ec fails"
sed 's/^X//' << 'SHAR_EOF' > qlist.c &&
X#include "qlist.h"
X#include "informix.h"
X
XPUBLIC int qlnode2rec(int nargs)
X{
X	struct listnode *n;
X	int i;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "qlnode2rec: got %d args, want 1\n", nargs);
X		return(0);
X	}
X
X	poplong(&n);
X
X	for (i=0; i < n->ncol; i++)
X		retvchar(n->cols[i].data);
X
X	return(n->ncol);
X}
X
XPUBLIC int mkqlist(int nargs)
X{
X	DL l, q2list();
X	char q[1024];
X
X	if (nargs != 1)	{
X		fprintf(stderr, "mkqlist: got %d args, want 1\n", nargs);
X		return(0);
X	}
X
X	popvchar(q, sizeof(q));
X	l = q2list(q);
X
X	retlong((long) l);
X	return(1);
X}
X
XPUBLIC int del1stonqlist(int nargs)
X{
X	DL l;
X	struct listnode *n;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "del1stonqlist: got %d args, want 1\n", nargs);
X		return(-1);
X	}
X
X	poplong(&l);
X
X	n = dl_shead(l);
X	free(n->cols);
X	dl_delete(l);
X
X	return(0);
X}
X
XPUBLIC int startqlist(int nargs)
X{
X	DL l;
X	struct listnode *n;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "startqlist: got %d args, want 1\n", nargs);
X		return(0);
X	}
X
X	poplong(&l);
X
X	retlong((long) dl_shead(l));
X	return(1);
X}
X
XPUBLIC int nextqlist(int nargs)
X{
X	DL l;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "nextlist: got %d args, want 1\n", nargs);
X		return(0);
X	}
X
X	poplong(&l);
X
X	retlong((long) dl_snext(l));
X	return(1);
X}
X
XPUBLIC int unmakeqlist(int nargs)
X{
X	DL l;
X	struct listnode *n;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "listsize: got %d args, want 1\n", nargs);
X		return(-1);
X	}
X
X	poplong(&l);
X
X	foreachnode(l, n)
X		free(n->cols);
X
X	dl_destroy(l);
X	return(0);
X}
SHAR_EOF
chmod 0644 qlist.c || echo "restore of qlist.c fails"
sed 's/^X//' << 'SHAR_EOF' > qlist.h &&
X#include "dl.h"
X
X#define COLNAMELEN 19
X#define DATALEN	81
X
Xstruct datanode {
X/*	int type;*/
X	char name[COLNAMELEN];
X	char data[DATALEN];
X};
X
Xstruct listnode {
X	DL_NODE n;
X	int ncol;
X	struct datanode *cols;
X};
SHAR_EOF
chmod 0644 qlist.h || echo "restore of qlist.h fails"
sed 's/^X//' << 'SHAR_EOF' > regexif.c &&
X#include "informix.h"
X#include "re/regexp.h"
X#define STRSIZE 256
X
XPUBLIC int re_start(int nargs)
X{
X	long re = 0;
X	char s[STRSIZE];
X
X	if (nargs != 1)	{
X		fprintf(stderr, "re_start: got %d args, want 1\n", nargs);
X		return(0);
X	}
X
X	popvchar(s, STRSIZE);
X
X	re = regcomp(s);
X
X	retlong(re);
X	return(1);
X}
X
XPUBLIC int re_match(int nargs)
X{
X	long re = 0;
X	static char s[STRSIZE];
X
X	if (nargs != 2)	{
X		fprintf(stderr, "re_match: got %d args, want 2\n", nargs);
X		return(0);
X	}
X
X	popvchar(s, STRSIZE);
X	poplong(&re);
X
X	retint(regexec(re, s));
X	return(1);
X}
X
XPUBLIC int re_sub(int nargs)
X{
X	long re = 0;
X	char src[STRSIZE], dst[STRSIZE];
X
X	if (nargs != 2)	{
X		fprintf(stderr, "re_sub: got %d args, want 2\n", nargs);
X		return(0);
X	}
X
X	popvchar(src, STRSIZE);
X	poplong(&re);
X
X	regsub(re, src, dst);
X
X	retvchar(dst);
X	return(1);
X}
X
XPUBLIC int re_end(int nargs)
X{
X	long re = 0;
X
X	if (nargs != 1)	{
X		fprintf(stderr, "re_end: got %d args, want 3\n", nargs);
X		return(0);
X	}
X
X	poplong(&re);
X
X	free(re);
X
X	return(0);
X}
SHAR_EOF
chmod 0644 regexif.c || echo "restore of regexif.c fails"
sed 's/^X//' << 'SHAR_EOF' > stdio.c &&
X#include "informix.h"
X#include <string.h>
X#include <sys/stat.h>
X
XPUBLIC int fopen_if(int nargs)
X{
X	FILE *fp = 0;
X	char fname[STRSIZE], mode[STRSIZE];
X
X	CHECKNARGS(fopen, 2)
X
X	popvchar(mode, STRSIZE);
X	popvchar(fname, STRSIZE);
X
X	fp = fopen(fname, mode);
X
X	retlong(fp);
X	return(1);
X}
X
XPUBLIC int fgetline(int nargs)
X{
X	FILE *fp = 0;
X	int len;
X	char line[1024], *p;
X
X	CHECKNARGS(fgetline, 2)
X
X	poplong(&fp);
X	popint(&len);
X
X	if (fgets(line, len+1, fp) == NULL)
X		retvchar("\200");
X	else {
X		if (p = strchr(line, '\n'))
X			*p = '\0';
X		clip(line);
X		if (line[0] == '\0')
X			strcpy(line, " "); /*XXX could display graphically?*/
X		retvchar(line);
X	}
X
X	return(1);
X}
X
XPUBLIC int fputline(int nargs)
X{
X	FILE *fp = 0;
X	int len;
X	char line[1024], *p;
X
X	CHECKNARGS(fputline, 2)
X
X	poplong(&fp);
X	popvchar(line, STRSIZE);
X
X	fprintf(fp, "%s\n", line);
X
X	return(0);
X}
X
XPUBLIC int fclose_if(int nargs)
X{
X	FILE *fp = 0;
X	char fname[STRSIZE], mode[STRSIZE];
X
X	CHECKNARGS(fclose, 1)
X
X	poplong(&fp);
X
X	fclose(fp);
X
X	return(0);
X}
X
XPUBLIC int mktmpf(int nargs)
X{
X	char fname[STRSIZE];
X
X	CHECKNARGS(mktmpf, 0)
X
X	tmpnam(fname);
X
X	retvchar(fname);
X	return(1);
X}
X
XPUBLIC int fdelete(int nargs)
X{
X	char fname[STRSIZE];
X
X	CHECKNARGS(fdelete, 1)
X
X	popvchar(fname, STRSIZE);
X	clip(fname);
X	unlink(fname);
X
X	return(0);
X}
X
XPUBLIC int faccess(int nargs)
X{
X	char fname[STRSIZE];
X
X	CHECKNARGS(faccess, 1)
X
X	popvchar(fname, STRSIZE);
X	clip(fname);
X	retint(access(fname, 0) == 0);
X	return(1);
X}
X
XPUBLIC int filesize(int nargs)
X{
X	char fname[STRSIZE];
X	struct stat st;
X
X	CHECKNARGS(filesize, 1)
X
X	popvchar(fname, STRSIZE);
X	clip(fname);
X	if (stat(fname, &st) == -1)
X		retint(-1);
X	else
X		retint(st.st_size);
X	return(1);
X}
SHAR_EOF
chmod 0644 stdio.c || echo "restore of stdio.c fails"
sed 's/^X//' << 'SHAR_EOF' > tssbug.c &&
X/*
X**	@(#)e4518.c	1.2 90/11/12
X**	@(#)Module to help detect error -4518 errors before they occur
X**	@(#)Author: J Leffler, Informix Software
X**	@(#)Applicable to I4GL Version 4.00 (C4GL and RDS)
X**	@(#)(Probably applicable to earlier versions too).
X*/
X
X/*TABSTOP=4*/
X
X#ifndef lint
Xstatic char	sccs[] = "@(#)e4518.c	1.2 90/11/12";
X#endif
X
X#include <stdio.h>
X
X/*
X**	Definitions from source file fglalloc.c
X*/
X
X#define ALLOCAREASIZE	512
X#define ALLOCTABSIZE	10
Xtypedef struct elalloc
X{
X	char	*allocarea;
X	int		 iallocarea;
X}	Elalloc;
X#if STD_TSS
Xextern Elalloc	alloctab[ALLOCTABSIZE];
X#endif
X
X/*
X**	Local definitions
X*/
X
X#define I4GL_C	int
X#define ALLOCSTACKSIZE	256
X#define ALLOCLOGFILE	"allocarea.log"
X
Xtypedef struct Stack
X{
X	int		offset[ALLOCTABSIZE];
X}	Stack;
X
Xstatic	Stack	allocstack[ALLOCSTACKSIZE];
Xstatic	int		alloc_sp;
Xstatic	FILE	*stdlog = (FILE *)0;
X
X/*
X**	Find maximum string space and total string space left
X*/
X
Xvoid	_chk_allocarea(max, tot)
Xint		*max;
Xint		*tot;
X{
X#if STD_TSS
X	int		i;
X	int		m;
X	int		n;
X	int		t;
X
X	m = t = 0;
X	for (i = 0; i < ALLOCTABSIZE; i++)
X	{
X		n = ALLOCAREASIZE - alloctab[i].iallocarea;
X		if (n > m)
X			m = n;
X		t += n;
X	}
X	*tot = t;
X	*max = m;
X#endif
X}
X
X/*ARGSUSED*/
XI4GL_C	chk_allocarea(n)
Xint	n;	/* Should be 0 */
X{
X	int		max;
X	int		tot;
X
X	_chk_allocarea(&max, &tot);
X	retint(max);
X	retint(tot);
X	return(2);
X}
X
X/*
X**	Allocation control routines
X**	get_allocarea -- store   current state of alloctab
X**	set_allocarea -- restore current state of alloctab
X*/
X
X/*
X** Save the current string space state and return stack pointer plus one
X*/
Xint		_get_allocarea()
X{
X#if STD_TSS
X	Stack	*sp;
X	int		 i;
X	int		 retval;
X
X	if (alloc_sp >= ALLOCSTACKSIZE)
X		retval = 0;
X	else
X	{
X		sp = &allocstack[alloc_sp++];
X		for (i = 0; i < ALLOCTABSIZE; i++)
X			sp->offset[i] = alloctab[i].iallocarea;
X		retval = alloc_sp;
X	}
X	return(retval);
X#endif
X}
X
XI4GL_C get_allocarea(n)
Xint	n;	/* Should be zero */
X{
X	n = _get_allocarea();
X	retint(n);
X	return(1);
X}
X
X/*
X**	Restore a state from the stack, implicitly invalidating
X**	any later saved states.  This is OK provided that the rules below are
X**	adhered to.  The rules are:
X**	1.	Thou shalt call get_allocarea() once in a function, preferably near
X**		the beginning of the function.
X**	2.	Thou shalt save the returned integer.
X**	3.	Thou shalt only use that value as an argument to set_allocarea()
X**		within the function where get_allocarea() was called.
X**	4.	The system reserves the right to crash out of control if these rules
X**		are broken.  It probably won't happen, but the right is reserved.
X*/
Xvoid	_set_allocarea(n)
Xint		n;
X{
X#if STD_TSS
X	Stack	*sp;
X	int		 i;
X
X	if (n <= 0 || n > alloc_sp)
X		return;
X	alloc_sp = n - 1;
X	sp = &allocstack[alloc_sp];
X	for (i = 0; i < ALLOCTABSIZE; i++)
X		alloctab[i].iallocarea = sp->offset[i];
X#endif
X}
X
XI4GL_C set_allocarea(n)
Xint	n;	/* Should be 1 */
X{
X	popint(&n);
X	_set_allocarea(n);
X	return(0);
X}
X
X/*
X**	Log allocated state.
X*/
X
Xvoid	_log_allocarea(fn)
Xchar	*fn;
X{
X	int		mx;
X	int		tt;
X	_chk_allocarea(&mx, &tt);
X	if (stdlog == (FILE *)0)
X		stdlog = fopen(ALLOCLOGFILE, "w");
X	if (stdlog != (FILE *)0)
X		fprintf(stdlog, "%s: max %4d, total %4d\n", fn, mx, tt);
X}
X
XI4GL_C	log_allocarea(n)
Xint		n;		/* Should be 1 */
X{
X	char	fn[33];
X
X	popquote(fn, sizeof(fn));
X	_log_allocarea(fn);
X	return(0);
X}
X
XI4GL_C  clr_allocarea(n)
Xint     n; /*0*/
X{
X#if STD_TSS
X	int i;
X
X	for (i = 0; i < ALLOCTABSIZE; i++)
X		alloctab[i].iallocarea = 0;
X#else
X	acdealloc();
X#endif
X	return(0);
X}
SHAR_EOF
chmod 0644 tssbug.c || echo "restore of tssbug.c fails"
sed 's/^X//' << 'SHAR_EOF' > util.4 &&
X# These aren't used by the editor (those that are needed have been
X# copied into it), but are useful perhaps.
X
X@include "dbname.h"
X
Xglobals "globals.4gl"
X
X@define TRUE	1
X@define FALSE	0
X
X@define OOPSLEN 50
X@define OOPSWID 60
X@define WAITLEN 12
X@define WAITWID 16
X
X@define DISPATTR green
X@define INPATTR white
X@define POPUPATTR yellow
X@define ARROWATTR yellow, reverse
X
Xfunction oopse(s)
X	define s char(OOPSLEN)
X
X	call blankscr()
X	call oops("Error!", s)
X	call unblankscr()
Xend function
X
Xfunction oopsw(s)
X	define s char(OOPSLEN)
X
X	call blankscr()
X	call oops("Warning!", s)
X	call unblankscr()
Xend function
X
Xfunction oops(msg, s)
X	define msg char(OOPSLEN), s char(OOPSLEN)
X	define x char,
X		col integer
X
X	open window woops at 11, 12 with 3 rows, OOPSWID columns
X		attribute(border, red, prompt line last)
X
X	let col = (OOPSWID-length(msg))/2
X	display msg at 1,col attribute(red)
X
X	let col = (OOPSWID-length(s))/2
X	display s clipped at 2,col attribute(red, reverse)
X
X	call push_acckey("CONTROL-A")
X	prompt "                  Press RETURN to continue." for x
X		on key (ESCAPE)
X			let x = x
X	end prompt
X	call pop_acckey()
X
X	close window woops
Xend function
X
Xfunction start_wait(msg)
X	define msg char(WAITLEN)
X	define col integer
X
X	open window wwait at 12,32 with 2 rows, WAITWID columns
X		attribute(border, blue)
X	let col = (WAITWID+2-length("WAIT"))/2
X	display "WAIT" at 1,col attribute(blue, reverse, blink)
X
X	let col = (WAITWID+2-length(msg))/2
X	display msg clipped at 2,col attribute(blue)
Xend function
X
Xfunction end_wait()
X	close window wwait
Xend function
X
Xfunction min(a, b)
X	define a, b integer
X
X	if a < b then
X		return a
X	else
X		return b
X	end if
Xend function
X
Xfunction ok(msg1, msg2, msg3)
X	define msg1 char(OOPSLEN), msg2 char(OOPSLEN), msg3 char(OOPSLEN)
X	define ans char,
X		col integer,
X		r integer,
X		ret integer
X
X	let r = 3;
X	if length(msg1) > 0 then
X		let r = r + 1
X	end if
X	if length(msg2) > 0 then
X		let r = r + 1
X	end if
X
X	open window wok at 7, 12 with r rows, OOPSWID columns
X		attribute(border, magenta, prompt line last)
X
X	let col = (OOPSWID-length(msg1))/2
X	display msg1 at 1,col attribute(magenta)
X
X	let col = (OOPSWID-length(msg2))/2
X	display msg2 at 2,col attribute(magenta)
X
X	menu msg3
X		command "Yes"
X			let ret = true
X			exit menu
X		command "No"
X			let ret = false
X			exit menu
X	end menu
X
X	close window wok
X
X	return ret
Xend function
X
Xfunction substitute(pat, repl, text)
X	define pat, repl, text char(100)
X	define re int, new char(100)
X
X	let re = re_start(pat clipped)
X	if not re then
X		return ""
X	end if
X
X	if not re_match(re, text clipped) then
X		let new = text
X	else
X		call re_sub(re, repl clipped) returning new
X	end if
X
X	call re_end(re)
X
X	return new
Xend function
X
Xfunction matchre(text, pat)
X	define pat, text char(200)
X	define re int, result int
X
X	let re = re_start(pat clipped)
X	if not re then
X		return false
X	end if
X
X	call re_match(re, text clipped) returning result
X
X	call re_end(re)
X
X	return result
Xend function
X
X@define MORELINELEN	70
X@define MORELINES 1000
X
Xfunction more(fname, title)
X	define fname char(100), origfname char(100), title char(40)
X	define fp integer, i integer,
X		line char(MORELINELEN),
X		lines array[MORELINES] of record
X			l char(MORELINELEN)
X		end record
X
X	let origfname = fname
X	call preview(fname) returning fname
X	let fp = fopen(fname clipped, "r")
X	if fp = 0 then
X		call oops("Error - can't read file", fname);
X		return
X	end if
X
X	let i = 0
X	call fgetline(MORELINELEN, fp) returning line
X	while line != "\200"	# assumes cpp will cvt the \stuff
X		let i = i + 1
X		if i > MORELINES then
X			call oopse("Limiting preview to first 1000 lines")
X			exit while
X		end if
X		let lines[i].l = line
X		call fgetline(MORELINELEN, fp) returning line
Xcall clr_allocarea();#XXX
X	end while
X	call fclose(fp);
X	call fdelete(fname);	# the temp file from preview
X
X	if i = 0 then
X		let i = 1
X		let lines[1].l = "<No output>"
X	end if
X
X	open window wmore at 4,4
X		with form "morewin"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("ESCAPE")
X	call set_count(i)
X	display array lines to s_morelines.*
X		on key (F10, CONTROL-C)
X			exit display
X		on key (F11, CONTROL-T)#(F9,CONTROL-P)XXX can't use "!" :-(
X			call print_file(origfname)
X	end display
X	call pop_acckey()
X
X	close window wmore
Xend function
X
X@define SMALLMORELINELEN	50
X@define SMALLMORELINES 		4
X
Xfunction smallmore(fname, title)
X	define fname char(100), origfname char(100), title char(40)
X	define fp integer, i integer,
X		line char(SMALLMORELINELEN),
X		lines array[SMALLMORELINES] of record
X			l char(SMALLMORELINELEN)
X		end record
X
X	let origfname = fname
X	let fp = fopen(fname clipped, "r")
X	if fp = 0 then
X		call oops("Error - can't read file", fname);
X		return
X	end if
X
X	let i = 0
X	call fgetline(SMALLMORELINELEN, fp) returning line
X	while line != "\200"	# assumes cpp will cvt the \stuff
X		let i = i + 1
X		if i > SMALLMORELINES then
X			#call oopse("Limiting preview to first 10 lines")
X			exit while
X		end if
X		let lines[i].l = line
X		call fgetline(SMALLMORELINELEN, fp) returning line
Xcall clr_allocarea();#XXX
X	end while
X	call fclose(fp);
X
X	if i = 0 then
X		return
X	end if
X
X	open window wsmore at 16,15
X		with form "smorewin"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("RETURN")
X	call set_count(i)
X	display array lines to s_smorelines.*
X		on key (F10, CONTROL-C)
X			exit display
X		on key (F11, CONTROL-T)#(F9,CONTROL-P)XXX can't use "!" :-(
X			call print_file(origfname)
X	end display
X	call pop_acckey()
X
X	close window wsmore
Xend function
X
Xfunction init_acckey(key)
X	define key char(20)
X	call makelist() returning acckeystk
X	call set_acckey(key)
Xend function
X
Xfunction push_acckey(key)
X	define key char(20)
X	# push current one on stack, set to requested one
X	call push2list(acckeystk, acckey clipped)
X	call set_acckey(key)
Xend function
X
Xfunction pop_acckey()
X	define key char(20)
X	if listsize(acckeystk) > 0 then
X		call startlist(acckeystk) returning key
X		call del1stonlist(acckeystk)
X	else
X		let key = "ESCAPE"
X	end if
X	call set_acckey(key)
Xend function
X
Xfunction set_acckey(key)
X	define key char(20)
X
X	let acckey = key	# set global (for next push)
X
X	case when key = "ESCAPE" or key = "ESC"
X		options accept key ESCAPE
X
X	when key = "RETURN"
X		options accept key RETURN
X
X	when key = "CONTROL-A"
X		options accept key CONTROL-A
X
X	#add others here as desired
X	#could call efacckey(n), though not as portable I suppose
X	end case
Xend function
X
Xfunction print_file(f)
X	define f char(100)
X	define cmd char(120)
X
X	if length(options.printer) = 0 or options.printer = "none" then
X		call oopse("No printer has been configured.")
X		return
X	end if
X
X	#let cmd = "cat > /dev/pts001 < ", f
X	call start_wait("Printing")
X	case options.printer
X	when "local"
X		let cmd = "local-print ", f
X	otherwise
X		let cmd = "remote-print ", options.printer clipped, " ", f
X	end case
X
X	call system(cmd)
X	call end_wait()
Xend function
X
Xfunction preview(fname)
X	define fname char(100)
X	define cmd char(120), nfname char(100)
X
X	let nfname = mktmpf();
X	let cmd = "preview ", fname clipped, " > ", nfname
X	call system(cmd)
X
X	return nfname
X
X# note: the preview command does whatever cleanup you might want, e.g.,
X#	#!/bin/sh
X#	sed '/ Page /d' $* | uniq
Xend function
SHAR_EOF
chmod 0644 util.4 || echo "restore of util.4 fails"
sed 's/^X//' << 'SHAR_EOF' > util.4gl &&
X# 1 ""
X
X
X
X# 1 "dbname.h" 1
Xdatabase skldprop
X# 4 "" 2
X
X
Xglobals "globals.4gl"
X
X
X
X
X
X
X
X
X
X
X
X
X
X
Xfunction oopse(s)
X	define s char(50 )
X
X	call blankscr()
X	call oops("Error!", s)
X	call unblankscr()
Xend function
X
Xfunction oopsw(s)
X	define s char(50 )
X
X	call blankscr()
X	call oops("Warning!", s)
X	call unblankscr()
Xend function
X
Xfunction oops(msg, s)
X	define msg char(50 ), s char(50 )
X	define x char,
X		col integer
X
X	open window woops at 11, 12 with 3 rows, 60  columns
X		attribute(border, red, prompt line last)
X
X	let col = (60 -length(msg))/2
X	display msg at 1,col attribute(red)
X
X	let col = (60 -length(s))/2
X	display s clipped at 2,col attribute(red, reverse)
X
X	call push_acckey("CONTROL-A")
X	prompt "                  Press RETURN to continue." for x
X		on key (ESCAPE)
X			let x = x
X	end prompt
X	call pop_acckey()
X
X	close window woops
Xend function
X
Xfunction start_wait(msg)
X	define msg char(12 )
X	define col integer
X
X	open window wwait at 12,32 with 2 rows, 16  columns
X		attribute(border, blue)
X	let col = (16 +2-length("WAIT"))/2
X	display "WAIT" at 1,col attribute(blue, reverse, blink)
X
X	let col = (16 +2-length(msg))/2
X	display msg clipped at 2,col attribute(blue)
Xend function
X
Xfunction end_wait()
X	close window wwait
Xend function
X
Xfunction min(a, b)
X	define a, b integer
X
X	if a < b then
X		return a
X	else
X		return b
X	end if
Xend function
X
Xfunction ok(msg1, msg2, msg3)
X	define msg1 char(50 ), msg2 char(50 ), msg3 char(50 )
X	define ans char,
X		col integer,
X		r integer,
X		ret integer
X
X	let r = 3;
X	if length(msg1) > 0 then
X		let r = r + 1
X	end if
X	if length(msg2) > 0 then
X		let r = r + 1
X	end if
X
X	open window wok at 7, 12 with r rows, 60  columns
X		attribute(border, magenta, prompt line last)
X
X	let col = (60 -length(msg1))/2
X	display msg1 at 1,col attribute(magenta)
X
X	let col = (60 -length(msg2))/2
X	display msg2 at 2,col attribute(magenta)
X
X	menu msg3
X		command "Yes"
X			let ret = true
X			exit menu
X		command "No"
X			let ret = false
X			exit menu
X	end menu
X
X	close window wok
X
X	return ret
Xend function
X
Xfunction substitute(pat, repl, text)
X	define pat, repl, text char(100)
X	define re int, new char(100)
X
X	let re = re_start(pat clipped)
X	if not re then
X		return ""
X	end if
X
X	if not re_match(re, text clipped) then
X		let new = text
X	else
X		call re_sub(re, repl clipped) returning new
X	end if
X
X	call re_end(re)
X
X	return new
Xend function
X
Xfunction matchre(text, pat)
X	define pat, text char(200)
X	define re int, result int
X
X	let re = re_start(pat clipped)
X	if not re then
X		return false
X	end if
X
X	call re_match(re, text clipped) returning result
X
X	call re_end(re)
X
X	return result
Xend function
X
X
X
X
Xfunction more(fname, title)
X	define fname char(100), origfname char(100), title char(40)
X	define fp integer, i integer,
X		line char(70 ),
X		lines array[1000 ] of record
X			l char(70 )
X		end record
X
X	let origfname = fname
X	call preview(fname) returning fname
X	let fp = fopen(fname clipped, "r")
X	if fp = 0 then
X		call oops("Error - can't read file", fname);
X		return
X	end if
X
X	let i = 0
X	call fgetline(70 , fp) returning line
X	while line != "\200"	
X		let i = i + 1
X		if i > 1000  then
X			call oopse("Limiting preview to first 1000 lines")
X			exit while
X		end if
X		let lines[i].l = line
X		call fgetline(70 , fp) returning line
Xcall clr_allocarea();
X	end while
X	call fclose(fp);
X	call fdelete(fname);	
X
X	if i = 0 then
X		let i = 1
X		let lines[1].l = "<No output>"
X	end if
X
X	open window wmore at 4,4
X		with form "morewin"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("ESCAPE")
X	call set_count(i)
X	display array lines to s_morelines.*
X		on key (F10, CONTROL-C)
X			exit display
X		on key (F11, CONTROL-T)
X			call print_file(origfname)
X	end display
X	call pop_acckey()
X
X	close window wmore
Xend function
X
X
X
X
Xfunction smallmore(fname, title)
X	define fname char(100), origfname char(100), title char(40)
X	define fp integer, i integer,
X		line char(50 ),
X		lines array[		4 ] of record
X			l char(50 )
X		end record
X
X	let origfname = fname
X	let fp = fopen(fname clipped, "r")
X	if fp = 0 then
X		call oops("Error - can't read file", fname);
X		return
X	end if
X
X	let i = 0
X	call fgetline(50 , fp) returning line
X	while line != "\200"	
X		let i = i + 1
X		if i > 		4  then
X			
X			exit while
X		end if
X		let lines[i].l = line
X		call fgetline(50 , fp) returning line
Xcall clr_allocarea();
X	end while
X	call fclose(fp);
X
X	if i = 0 then
X		return
X	end if
X
X	open window wsmore at 16,15
X		with form "smorewin"
X		attribute(border, cyan)
X
X	display by name title
X	call push_acckey("RETURN")
X	call set_count(i)
X	display array lines to s_smorelines.*
X		on key (F10, CONTROL-C)
X			exit display
X		on key (F11, CONTROL-T)
X			call print_file(origfname)
X	end display
X	call pop_acckey()
X
X	close window wsmore
Xend function
X
Xfunction init_acckey(key)
X	define key char(20)
X	call makelist() returning acckeystk
X	call set_acckey(key)
Xend function
X
Xfunction push_acckey(key)
X	define key char(20)
X	
X	call push2list(acckeystk, acckey clipped)
X	call set_acckey(key)
Xend function
X
Xfunction pop_acckey()
X	define key char(20)
X	if listsize(acckeystk) > 0 then
X		call startlist(acckeystk) returning key
X		call del1stonlist(acckeystk)
X	else
X		let key = "ESCAPE"
X	end if
X	call set_acckey(key)
Xend function
X
Xfunction set_acckey(key)
X	define key char(20)
X
X	let acckey = key	
X
X	case when key = "ESCAPE" or key = "ESC"
X		options accept key ESCAPE
X
X	when key = "RETURN"
X		options accept key RETURN
X
X	when key = "CONTROL-A"
X		options accept key CONTROL-A
X
X	
X	
X	end case
Xend function
X
Xfunction print_file(f)
X	define f char(100)
X	define cmd char(120)
X
X	if length(options.printer) = 0 or options.printer = "none" then
X		call oopse("No printer has been configured.")
X		return
X	end if
X
X	
X	call start_wait("Printing")
X	case options.printer
X	when "local"
X		let cmd = "local-print ", f
X	otherwise
X		let cmd = "remote-print ", options.printer clipped, " ", f
X	end case
X
X	call system(cmd)
X	call end_wait()
Xend function
X
Xfunction preview(fname)
X	define fname char(100)
X	define cmd char(120), nfname char(100)
X
X	let nfname = mktmpf();
X	let cmd = "preview ", fname clipped, " > ", nfname
X	call system(cmd)
X
X	return nfname
X
X
X
X
Xend function
SHAR_EOF
chmod 0644 util.4gl || echo "restore of util.4gl fails"
if test ! -d re; then echo "Creating directory re"; mkdir re ||
  { echo "Can't create dir!"; exit 1; }
fi
sed 's/^X//' << 'SHAR_EOF' > re/COPYRIGHT &&
XThis entire subtree is copyright the University of Toronto.
XThe following copyright notice applies to all files found here.  None of
Xthese files contain AT&T proprietary source code.
X_____________________________________________________________________________
X
X	Copyright (c) 1986 by University of Toronto.
X	Written by Henry Spencer.  Not derived from licensed software.
X
X	Permission is granted to anyone to use this software for any
X	purpose on any computer system, and to redistribute it freely,
X	subject to the following restrictions:
X
X	1. The author is not responsible for the consequences of use of
X		this software, no matter how awful, even if they arise
X		from defects in it.
X
X	2. The origin of this software must not be misrepresented, either
X		by explicit claim or by omission.
X
X	3. Altered versions must be plainly marked as such, and must not
X		be misrepresented as being the original software.
X
SHAR_EOF
chmod 0444 re/COPYRIGHT || echo "restore of re/COPYRIGHT fails"
sed 's/^X//' << 'SHAR_EOF' > re/Makefile &&
XCFLAGS = -I. -O
XCC=gcc
X
Xall: regexp.o regsub.o regerror.o
SHAR_EOF
chmod 0644 re/Makefile || echo "restore of re/Makefile fails"
sed 's/^X//' << 'SHAR_EOF' > re/README &&
XThis is a nearly-public-domain reimplementation of the V8 regexp(3) package.
XIt gives C programs the ability to use egrep-style regular expressions, and
Xdoes it in a much cleaner fashion than the analogous routines in SysV.
X
X	Copyright (c) 1986 by University of Toronto.
X	Written by Henry Spencer.  Not derived from licensed software.
X
X	Permission is granted to anyone to use this software for any
X	purpose on any computer system, and to redistribute it freely,
X	subject to the following restrictions:
X
X	1. The author is not responsible for the consequences of use of
X		this software, no matter how awful, even if they arise
X		from defects in it.
X
X	2. The origin of this software must not be misrepresented, either
X		by explicit claim or by omission.
X
X	3. Altered versions must be plainly marked as such, and must not
X		be misrepresented as being the original software.
X
XBarring a couple of small items in the BUGS list, this implementation is
Xbelieved 100% compatible with V8.  It should even be binary-compatible,
Xsort of, since the only fields in a "struct regexp" that other people have
Xany business touching are declared in exactly the same way at the same
Xlocation in the struct (the beginning).
X
XThis implementation is *NOT* AT&T/Bell code, and is not derived from licensed
Xsoftware.  Even though U of T is a V8 licensee.  This software is based on
Xa V8 manual page sent to me by Dennis Ritchie (the manual page enclosed
Xhere is a complete rewrite and hence is not covered by AT&T copyright).
XThe software was nearly complete at the time of arrival of our V8 tape.
XI haven't even looked at V8 yet, although a friend elsewhere at U of T has
Xbeen kind enough to run a few test programs using the V8 regexp(3) to resolve
Xa few fine points.  I admit to some familiarity with regular-expression
Ximplementations of the past, but the only one that this code traces any
Xancestry to is the one published in Kernighan & Plauger (from which this
Xone draws ideas but not code).
X
XSimplistically:  put this stuff into a source directory, copy regexp.h into
X/usr/include, inspect Makefile for compilation options that need changing
Xto suit your local environment, and then do "make r".  This compiles the
Xregexp(3) functions, compiles a test program, and runs a large set of
Xregression tests.  If there are no complaints, then put regexp.o, regsub.o,
Xand regerror.o into your C library, and regexp.3 into your manual-pages
Xdirectory.
X
XNote that if you don't put regexp.h into /usr/include *before* compiling,
Xyou'll have to add "-I." to CFLAGS before compiling.
X
XThe files are:
X
XMakefile	instructions to make everything
Xregexp.3	manual page
Xregexp.h	header file, for /usr/include
Xregexp.c	source for regcomp() and regexec()
Xregsub.c	source for regsub()
Xregerror.c	source for default regerror()
Xregmagic.h	internal header file
Xtry.c		source for test program
Xtimer.c		source for timing program
Xtests		test list for try and timer
X
XThis implementation uses nondeterministic automata rather than the
Xdeterministic ones found in some other implementations, which makes it
Xsimpler, smaller, and faster at compiling regular expressions, but slower
Xat executing them.  In theory, anyway.  This implementation does employ
Xsome special-case optimizations to make the simpler cases (which do make
Xup the bulk of regular expressions actually used) run quickly.  In general,
Xif you want blazing speed you're in the wrong place.  Replacing the insides
Xof egrep with this stuff is probably a mistake; if you want your own egrep
Xyou're going to have to do a lot more work.  But if you want to use regular
Xexpressions a little bit in something else, you're in luck.  Note that many
Xexisting text editors use nondeterministic regular-expression implementations,
Xso you're in good company.
X
XThis stuff should be pretty portable, given appropriate option settings.
XIf your chars have less than 8 bits, you're going to have to change the
Xinternal representation of the automaton, although knowledge of the details
Xof this is fairly localized.  There are no "reserved" char values except for
XNUL, and no special significance is attached to the top bit of chars.
XThe string(3) functions are used a fair bit, on the grounds that they are
Xprobably faster than coding the operations in line.  Some attempts at code
Xtuning have been made, but this is invariably a bit machine-specific.
SHAR_EOF
chmod 0444 re/README || echo "restore of re/README fails"
sed 's/^X//' << 'SHAR_EOF' > re/regerror.c &&
X#include <regexp.h>
X#include <stdio.h>
X
Xvoid
Xregerror(s)
Xconst char *s;
X{
X#ifdef ERRAVAIL
X	error("regexp: %s", s);
X#else
X/*
X	fprintf(stderr, "regexp(3): %s\n", s);
X	exit(1);
X*/
X	return;	  /* let std. egrep handle errors */
X#endif
X	/* NOTREACHED */
X}
SHAR_EOF
chmod 0444 re/regerror.c || echo "restore of re/regerror.c fails"
sed 's/^X//' << 'SHAR_EOF' > re/regexp.3 &&
X.\" Copyright 1991 The Regents of the University of California.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\" 3. All advertising materials mentioning features or use of this software
X.\"    must display the following acknowledgement:
X.\"	This product includes software developed by the University of
X.\"	California, Berkeley and its contributors.
X.\" 4. Neither the name of the University nor the names of its contributors
X.\"    may be used to endorse or promote products derived from this software
X.\"    without specific prior written permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\"     @(#)regexp.3	5.2 (Berkeley) 4/20/91
X.\"
X.Dd April 20, 1991
X.Dt REGEXP 3
X.Os
X.Sh NAME
X.Nm regcomp ,
X.Nm regexec ,
X.Nm regsub ,
X.Nm regerror
X.Nd regular expression handlers
X.Sh SYNOPSIS
X.Fd #include <regexp.h>
X.Ft regexp *
X.Fn regcomp "const char *exp"
X.Ft int
X.Fn regexec "const regexp *prog" "const char *string"
X.Ft void
X.Fn regsub "const regexp *prog" "const char *source" "char *dest"
X.Sh DESCRIPTION
XThe
X.Fn regcomp ,
X.Fn regexec ,
X.Fn regsub ,
Xand
X.Fn regerror
Xfunctions
Ximplement
X.Xr egrep 1 Ns -style
Xregular expressions and supporting facilities.
X.Pp
XThe
X.Fn regcomp
Xfunction
Xcompiles a regular expression into a structure of type
X.Xr regexp ,
Xand returns a pointer to it.
XThe space has been allocated using
X.Xr malloc 3
Xand may be released by
X.Xr free .
X.Pp
XThe
X.Fn regexec
Xfunction
Xmatches a
X.Dv NUL Ns -terminated
X.Fa string
Xagainst the compiled regular expression
Xin
X.Fa prog .
XIt returns 1 for success and 0 for failure, and adjusts the contents of
X.Fa prog Ns 's
X.Em startp
Xand
X.Em endp
X(see below) accordingly.
X.Pp
XThe members of a
X.Xr regexp
Xstructure include at least the following (not necessarily in order):
X.Bd -literal -offset indent
Xchar *startp[NSUBEXP];
Xchar *endp[NSUBEXP];
X.Ed
X.Pp
Xwhere
X.Dv NSUBEXP
Xis defined (as 10) in the header file.
XOnce a successful
X.Fn regexec
Xhas been done using the
X.Fn regexp ,
Xeach
X.Em startp Ns - Em endp
Xpair describes one substring
Xwithin the
X.Fa string ,
Xwith the
X.Em startp
Xpointing to the first character of the substring and
Xthe
X.Em endp
Xpointing to the first character following the substring.
XThe 0th substring is the substring of
X.Fa string
Xthat matched the whole
Xregular expression.
XThe others are those substrings that matched parenthesized expressions
Xwithin the regular expression, with parenthesized expressions numbered
Xin left-to-right order of their opening parentheses.
X.Pp
XThe
X.Fn regsub
Xfunction
Xcopies
X.Fa source
Xto
X.Fa dest ,
Xmaking substitutions according to the
Xmost recent
X.Fn regexec
Xperformed using
X.Fa prog .
XEach instance of `&' in
X.Fa source
Xis replaced by the substring
Xindicated by
X.Em startp Ns Bq
Xand
X.Em endp Ns Bq .
XEach instance of
X.Sq \e Ns Em n ,
Xwhere
X.Em n
Xis a digit, is replaced by
Xthe substring indicated by
X.Em startp Ns Bq Em n
Xand
X.Em endp Ns Bq Em n .
XTo get a literal `&' or
X.Sq \e Ns Em n
Xinto
X.Fa dest ,
Xprefix it with `\e';
Xto get a literal `\e' preceding `&' or
X.Sq \e Ns Em n ,
Xprefix it with
Xanother `\e'.
X.Pp
XThe
X.Fn regerror
Xfunction
Xis called whenever an error is detected in
X.Fn regcomp ,
X.Fn regexec ,
Xor
X.Fn regsub .
XThe default
X.Fn regerror
Xwrites the string
X.Fa msg ,
Xwith a suitable indicator of origin,
Xon the standard
Xerror output
Xand invokes
X.Xr exit 2 .
XThe
X.Fn regerror
Xfunction
Xcan be replaced by the user if other actions are desirable.
X.Sh REGULAR EXPRESSION SYNTAX
XA regular expression is zero or more
X.Em branches ,
Xseparated by `|'.
XIt matches anything that matches one of the branches.
X.Pp
XA branch is zero or more
X.Em pieces ,
Xconcatenated.
XIt matches a match for the first, followed by a match for the second, etc.
X.Pp
XA piece is an
X.Em atom
Xpossibly followed by `*', `+', or `?'.
XAn atom followed by `*' matches a sequence of 0 or more matches of the atom.
XAn atom followed by `+' matches a sequence of 1 or more matches of the atom.
XAn atom followed by `?' matches a match of the atom, or the null string.
X.Pp
XAn atom is a regular expression in parentheses (matching a match for the
Xregular expression), a
X.Em range
X(see below), `.'
X(matching any single character), `^' (matching the null string at the
Xbeginning of the input string), `$' (matching the null string at the
Xend of the input string), a `\e' followed by a single character (matching
Xthat character), or a single character with no other significance
X(matching that character).
X.Pp
XA
X.Em range
Xis a sequence of characters enclosed in `[]'.
XIt normally matches any single character from the sequence.
XIf the sequence begins with `^',
Xit matches any single character
X.Em not
Xfrom the rest of the sequence.
XIf two characters in the sequence are separated by `\-', this is shorthand
Xfor the full list of
X.Tn ASCII
Xcharacters between them
X(e.g. `[0-9]' matches any decimal digit).
XTo include a literal `]' in the sequence, make it the first character
X(following a possible `^').
XTo include a literal `\-', make it the first or last character.
X.Sh AMBIGUITY
XIf a regular expression could match two different parts of the input string,
Xit will match the one which begins earliest.
XIf both begin in the same place but match different lengths, or match
Xthe same length in different ways, life gets messier, as follows.
X.Pp
XIn general, the possibilities in a list of branches are considered in
Xleft-to-right order, the possibilities for `*', `+', and `?' are
Xconsidered longest-first, nested constructs are considered from the
Xoutermost in, and concatenated constructs are considered leftmost-first.
XThe match that will be chosen is the one that uses the earliest
Xpossibility in the first choice that has to be made.
XIf there is more than one choice, the next will be made in the same manner
X(earliest possibility) subject to the decision on the first choice.
XAnd so forth.
X.Pp
XFor example,
X.Sq Li (ab|a)b*c
Xcould match
X`abc' in one of two ways.
XThe first choice is between `ab' and `a'; since `ab' is earlier, and does
Xlead to a successful overall match, it is chosen.
XSince the `b' is already spoken for,
Xthe `b*' must match its last possibility\(emthe empty string\(emsince
Xit must respect the earlier choice.
X.Pp
XIn the particular case where no `|'s are present and there is only one
X`*', `+', or `?', the net effect is that the longest possible
Xmatch will be chosen.
XSo
X.Sq Li ab* ,
Xpresented with `xabbbby', will match `abbbb'.
XNote that if
X.Sq Li ab* ,
Xis tried against `xabyabbbz', it
Xwill match `ab' just after `x', due to the begins-earliest rule.
X(In effect, the decision on where to start the match is the first choice
Xto be made, hence subsequent choices must respect it even if this leads them
Xto less-preferred alternatives.)
X.Sh RETURN VALUES
XThe
X.Fn regcomp
Xfunction
Xreturns
X.Dv NULL
Xfor a failure
X.Pf ( Fn regerror
Xpermitting),
Xwhere failures are syntax errors, exceeding implementation limits,
Xor applying `+' or `*' to a possibly-null operand.
X.Sh SEE ALSO
X.Xr ed 1 ,
X.Xr ex 1 ,
X.Xr expr 1 ,
X.Xr egrep 1 ,
X.Xr fgrep 1 ,
X.Xr grep 1 ,
X.Xr regex 3
X.Sh HISTORY
XBoth code and manual page for
X.Fn regcomp ,
X.Fn regexec ,
X.Fn regsub ,
Xand
X.Fn regerror
Xwere written at the University of Toronto
Xand appeared in
X.Bx 4.3 tahoe .
XThey are intended to be compatible with the Bell V8
X.Xr regexp 3 ,
Xbut are not derived from Bell code.
X.Sh BUGS
XEmpty branches and empty regular expressions are not portable to V8.
X.Pp
XThe restriction against
Xapplying `*' or `+' to a possibly-null operand is an artifact of the
Xsimplistic implementation.
X.Pp
XDoes not support
X.Xr egrep Ns 's
Xnewline-separated branches;
Xneither does the V8
X.Xr regexp 3 ,
Xthough.
X.Pp
XDue to emphasis on
Xcompactness and simplicity,
Xit's not strikingly fast.
XIt does give special attention to handling simple cases quickly.
SHAR_EOF
chmod 0444 re/regexp.3 || echo "restore of re/regexp.3 fails"
sed 's/^X//' << 'SHAR_EOF' > re/regexp.c &&
X/*
X * regcomp and regexec -- regsub and regerror are elsewhere
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X *** THIS IS AN ALTERED VERSION.  It was altered by John Gilmore,
X *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to |
X *** to assist in implementing egrep.
X *** THIS IS AN ALTERED VERSION.  It was altered by John Gilmore,
X *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching
X *** as in BSD grep and ex.
X *** THIS IS AN ALTERED VERSION.  It was altered by John Gilmore,
X *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \.
X *** THIS IS AN ALTERED VERSION.  It was altered by James A. Woods,
X *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy.
X *
X * Beware that some of this code is subtly aware of the way operator
X * precedence is structured in regular expressions.  Serious changes in
X * regular-expression syntax might require a total rethink.
X */
X#include <regexp.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <stdlib.h>
X#include <string.h>
X#include "regmagic.h"
X
X/*
X * The "internal use only" fields in regexp.h are present to pass info from
X * compile to execute that permits the execute phase to run lots faster on
X * simple cases.  They are:
X *
X * regstart	char that must begin a match; '\0' if none obvious
X * reganch	is the match anchored (at beginning-of-line only)?
X * regmust	string (pointer into program) that match must include, or NULL
X * regmlen	length of regmust string
X *
X * Regstart and reganch permit very fast decisions on suitable starting points
X * for a match, cutting down the work a lot.  Regmust permits fast rejection
X * of lines that cannot possibly match.  The regmust tests are costly enough
X * that regcomp() supplies a regmust only if the r.e. contains something
X * potentially expensive (at present, the only such thing detected is * or +
X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
X * supplied because the test in regexec() needs it and regcomp() is computing
X * it anyway.
X */
X
X/*
X * Structure for regexp "program".  This is essentially a linear encoding
X * of a nondeterministic finite-state machine (aka syntax charts or
X * "railroad normal form" in parsing technology).  Each node is an opcode
X * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
X * all nodes except BRANCH implement concatenation; a "next" pointer with
X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
X * have one of the subtle syntax dependencies:  an individual BRANCH (as
X * opposed to a collection of them) is never concatenated with anything
X * because of operator precedence.)  The operand of some types of node is
X * a literal string; for others, it is a node leading into a sub-FSM.  In
X * particular, the operand of a BRANCH node is the first node of the branch.
X * (NB this is *not* a tree structure:  the tail of the branch connects
X * to the thing following the set of BRANCHes.)  The opcodes are:
X */
X
X/* definition	number	opnd?	meaning */
X#define	END	0	/* no	End of program. */
X#define	BOL	1	/* no	Match "" at beginning of line. */
X#define	EOL	2	/* no	Match "" at end of line. */
X#define	ANY	3	/* no	Match any one character. */
X#define	ANYOF	4	/* str	Match any character in this string. */
X#define	ANYBUT	5	/* str	Match any character not in this string. */
X#define	BRANCH	6	/* node	Match this alternative, or the next... */
X#define	BACK	7	/* no	Match "", "next" ptr points backward. */
X#define	EXACTLY	8	/* str	Match this string. */
X#define	NOTHING	9	/* no	Match empty string. */
X#define	STAR	10	/* node	Match this (simple) thing 0 or more times. */
X#define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */
X#define	WORDA	12	/* no	Match "" at wordchar, where prev is nonword */
X#define	WORDZ	13	/* no	Match "" at nonwordchar, where prev is word */
X#define	OPEN	20	/* no	Mark this point in input as start of #n. */
X			/*	OPEN+1 is number 1, etc. */
X#define	CLOSE	30	/* no	Analogous to OPEN. */
X
X/*
X * Opcode notes:
X *
X * BRANCH	The set of branches constituting a single choice are hooked
X *		together with their "next" pointers, since precedence prevents
X *		anything being concatenated to any individual branch.  The
X *		"next" pointer of the last BRANCH in a choice points to the
X *		thing following the whole choice.  This is also where the
X *		final "next" pointer of each individual branch points; each
X *		branch starts with the operand node of a BRANCH node.
X *
X * BACK		Normal "next" pointers all implicitly point forward; BACK
X *		exists to make loop structures possible.
X *
X * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular
X *		BRANCH structures using BACK.  Simple cases (one character
X *		per match) are implemented with STAR and PLUS for speed
X *		and to minimize recursive plunges.
X *
X * OPEN,CLOSE	...are numbered at compile time.
X */
X
X/*
X * A node is one char of opcode followed by two chars of "next" pointer.
X * "Next" pointers are stored as two 8-bit pieces, high order first.  The
X * value is a positive offset from the opcode of the node containing it.
X * An operand, if any, simply follows the node.  (Note that much of the
X * code generation knows about this implicit relationship.)
X *
X * Using two bytes for the "next" pointer is vast overkill for most things,
X * but allows patterns to get big without disasters.
X */
X#define	OP(p)	(*(p))
X#define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
X#define	OPERAND(p)	((p) + 3)
X
X/*
X * See regmagic.h for one further detail of program structure.
X */
X
X
X/*
X * Utility definitions.
X */
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X#define	FAIL(m)	{ regerror(m); return(NULL); }
X#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?')
X
X/*
X * Flags to be passed up and down.
X */
X#define	HASWIDTH	01	/* Known never to match null string. */
X#define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */
X#define	SPSTART		04	/* Starts with * or +. */
X#define	WORST		0	/* Worst case. */
X
X/*
X * Global work variables for regcomp().
X */
Xstatic char *regparse;		/* Input-scan pointer. */
Xstatic int regnpar;		/* () count. */
Xstatic char regdummy;
Xstatic char *regcode;		/* Code-emit pointer; &regdummy = don't. */
Xstatic long regsize;		/* Code size. */
X
X/*
X * Forward declarations for regcomp()'s friends.
X */
X#ifndef STATIC
X#define	STATIC	static
X#endif
XSTATIC char *reg();
XSTATIC char *regbranch();
XSTATIC char *regpiece();
XSTATIC char *regatom();
XSTATIC char *regnode();
XSTATIC char *regnext();
XSTATIC void regc();
XSTATIC void reginsert();
XSTATIC void regtail();
XSTATIC void regoptail();
X#ifdef STRCSPN
XSTATIC int strcspn();
X#endif
X
X/*
X - regcomp - compile a regular expression into internal code
X *
X * We can't allocate space until we know how big the compiled form will be,
X * but we can't compile it (and thus know how big it is) until we've got a
X * place to put the code.  So we cheat:  we compile it twice, once with code
X * generation turned off and size counting turned on, and once "for real".
X * This also means that we don't allocate space until we are sure that the
X * thing really will compile successfully, and we never have to move the
X * code and thus invalidate pointers into it.  (Note that it has to be in
X * one piece because free() must be able to free it all.)
X *
X * Beware that the optimization-preparation code in here knows about some
X * of the structure of the compiled regexp.
X */
Xregexp *
Xregcomp(exp)
Xconst char *exp;
X{
X	register regexp *r;
X	register char *scan;
X	register char *longest;
X	register int len;
X	int flags;
X
X	if (exp == NULL)
X		FAIL("NULL argument");
X
X	/* First pass: determine size, legality. */
X#ifdef notdef
X	if (exp[0] == '.' && exp[1] == '*') exp += 2;  /* aid grep */
X#endif
X	regparse = (char *)exp;
X	regnpar = 1;
X	regsize = 0L;
X	regcode = &regdummy;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Small enough for pointer-storage convention? */
X	if (regsize >= 32767L)		/* Probably could be 65535L. */
X		FAIL("regexp too big");
X
X	/* Allocate space. */
X	r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
X	if (r == NULL)
X		FAIL("out of space");
X
X	/* Second pass: emit code. */
X	regparse = (char *)exp;
X	regnpar = 1;
X	regcode = r->program;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Dig out information for optimizations. */
X	r->regstart = '\0';	/* Worst-case defaults. */
X	r->reganch = 0;
X	r->regmust = NULL;
X	r->regmlen = 0;
X	scan = r->program+1;			/* First BRANCH. */
X	if (OP(regnext(scan)) == END) {		/* Only one top-level choice. */
X		scan = OPERAND(scan);
X
X		/* Starting-point info. */
X		if (OP(scan) == EXACTLY)
X			r->regstart = *OPERAND(scan);
X		else if (OP(scan) == BOL)
X			r->reganch++;
X
X		/*
X		 * If there's something expensive in the r.e., find the
X		 * longest literal string that must appear and make it the
X		 * regmust.  Resolve ties in favor of later strings, since
X		 * the regstart check works with the beginning of the r.e.
X		 * and avoiding duplication strengthens checking.  Not a
X		 * strong reason, but sufficient in the absence of others.
X		 */
X		if (flags&SPSTART) {
X			longest = NULL;
X			len = 0;
X			for (; scan != NULL; scan = regnext(scan))
X				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
X					longest = OPERAND(scan);
X					len = strlen(OPERAND(scan));
X				}
X			r->regmust = longest;
X			r->regmlen = len;
X		}
X	}
X
X	return(r);
X}
X
X/*
X - reg - regular expression, i.e. main body or parenthesized thing
X *
X * Caller must absorb opening parenthesis.
X *
X * Combining parenthesis handling with the base level of regular expression
X * is a trifle forced, but the need to tie the tails of the branches to what
X * follows makes it hard to avoid.
X */
Xstatic char *
Xreg(paren, flagp)
Xint paren;			/* Parenthesized? */
Xint *flagp;
X{
X	register char *ret;
X	register char *br;
X	register char *ender;
X	register int parno;
X	int flags;
X
X	*flagp = HASWIDTH;	/* Tentatively. */
X
X	/* Make an OPEN node, if parenthesized. */
X	if (paren) {
X		if (regnpar >= NSUBEXP)
X			FAIL("too many ()");
X		parno = regnpar;
X		regnpar++;
X		ret = regnode(OPEN+parno);
X	} else
X		ret = NULL;
X
X	/* Pick up the branches, linking them together. */
X	br = regbranch(&flags);
X	if (br == NULL)
X		return(NULL);
X	if (ret != NULL)
X		regtail(ret, br);	/* OPEN -> first. */
X	else
X		ret = br;
X	if (!(flags&HASWIDTH))
X		*flagp &= ~HASWIDTH;
X	*flagp |= flags&SPSTART;
X	while (*regparse == '|' || *regparse == '\n') {
X		regparse++;
X		br = regbranch(&flags);
X		if (br == NULL)
X			return(NULL);
X		regtail(ret, br);	/* BRANCH -> BRANCH. */
X		if (!(flags&HASWIDTH))
X			*flagp &= ~HASWIDTH;
X		*flagp |= flags&SPSTART;
X	}
X
X	/* Make a closing node, and hook it on the end. */
X	ender = regnode((paren) ? CLOSE+parno : END);	
X	regtail(ret, ender);
X
X	/* Hook the tails of the branches to the closing node. */
X	for (br = ret; br != NULL; br = regnext(br))
X		regoptail(br, ender);
X
X	/* Check for proper termination. */
X	if (paren && *regparse++ != ')') {
X		FAIL("unmatched ()");
X	} else if (!paren && *regparse != '\0') {
X		if (*regparse == ')') {
X			FAIL("unmatched ()");
X		} else
X			FAIL("junk on end");	/* "Can't happen". */
X		/* NOTREACHED */
X	}
X
X	return(ret);
X}
X
X/*
X - regbranch - one alternative of an | operator
X *
X * Implements the concatenation operator.
X */
Xstatic char *
Xregbranch(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char *chain;
X	register char *latest;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	ret = regnode(BRANCH);
X	chain = NULL;
X	while (*regparse != '\0' && *regparse != ')' &&
X	       *regparse != '\n' && *regparse != '|') {
X		latest = regpiece(&flags);
X		if (latest == NULL)
X			return(NULL);
X		*flagp |= flags&HASWIDTH;
X		if (chain == NULL)	/* First piece. */
X			*flagp |= flags&SPSTART;
X		else
X			regtail(chain, latest);
X		chain = latest;
X	}
X	if (chain == NULL)	/* Loop ran zero times. */
X		(void) regnode(NOTHING);
X
X	return(ret);
X}
X
X/*
X - regpiece - something followed by possible [*+?]
X *
X * Note that the branching code sequences used for ? and the general cases
X * of * and + are somewhat optimized:  they use the same NOTHING node as
X * both the endmarker for their branch list and the body of the last branch.
X * It might seem that this node could be dispensed with entirely, but the
X * endmarker role is not redundant.
X */
Xstatic char *
Xregpiece(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char op;
X	register char *next;
X	int flags;
X
X	ret = regatom(&flags);
X	if (ret == NULL)
X		return(NULL);
X
X	op = *regparse;
X	if (!ISMULT(op)) {
X		*flagp = flags;
X		return(ret);
X	}
X
X	if (!(flags&HASWIDTH) && op != '?')
X		FAIL("*+ operand could be empty");
X	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
X
X	if (op == '*' && (flags&SIMPLE))
X		reginsert(STAR, ret);
X	else if (op == '*') {
X		/* Emit x* as (x&|), where & means "self". */
X		reginsert(BRANCH, ret);			/* Either x */
X		regoptail(ret, regnode(BACK));		/* and loop */
X		regoptail(ret, ret);			/* back */
X		regtail(ret, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '+' && (flags&SIMPLE))
X		reginsert(PLUS, ret);
X	else if (op == '+') {
X		/* Emit x+ as x(&|), where & means "self". */
X		next = regnode(BRANCH);			/* Either */
X		regtail(ret, next);
X		regtail(regnode(BACK), ret);		/* loop back */
X		regtail(next, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '?') {
X		/* Emit x? as (x|) */
X		reginsert(BRANCH, ret);			/* Either x */
X		regtail(ret, regnode(BRANCH));		/* or */
X		next = regnode(NOTHING);		/* null. */
X		regtail(ret, next);
X		regoptail(ret, next);
X	}
X	regparse++;
X	if (ISMULT(*regparse))
X		FAIL("nested *?+");
X
X	return(ret);
X}
X
X/*
X - regatom - the lowest level
X *
X * Optimization:  gobbles an entire sequence of ordinary characters so that
X * it can turn them into a single node, which is smaller to store and
X * faster to run.  Backslashed characters are exceptions, each becoming a
X * separate node; the code is simpler that way and it's not worth fixing.
X */
Xstatic char *
Xregatom(flagp)
Xint *flagp;
X{
X	register char *ret;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	switch (*regparse++) {
X	/* FIXME: these chars only have meaning at beg/end of pat? */
X	case '^':
X		ret = regnode(BOL);
X		break;
X	case '$':
X		ret = regnode(EOL);
X		break;
X	case '.':
X		ret = regnode(ANY);
X		*flagp |= HASWIDTH|SIMPLE;
X		break;
X	case '[': {
X			register int class;
X			register int classend;
X
X			if (*regparse == '^') {	/* Complement of range. */
X				ret = regnode(ANYBUT);
X				regparse++;
X			} else
X				ret = regnode(ANYOF);
X			if (*regparse == ']' || *regparse == '-')
X				regc(*regparse++);
X			while (*regparse != '\0' && *regparse != ']') {
X				if (*regparse == '-') {
X					regparse++;
X					if (*regparse == ']' || *regparse == '\0')
X						regc('-');
X					else {
X						class = UCHARAT(regparse-2)+1;
X						classend = UCHARAT(regparse);
X						if (class > classend+1)
X							FAIL("invalid [] range");
X						for (; class <= classend; class++)
X							regc(class);
X						regparse++;
X					}
X				} else
X					regc(*regparse++);
X			}
X			regc('\0');
X			if (*regparse != ']')
X				FAIL("unmatched []");
X			regparse++;
X			*flagp |= HASWIDTH|SIMPLE;
X		}
X		break;
X	case '(':
X		ret = reg(1, &flags);
X		if (ret == NULL)
X			return(NULL);
X		*flagp |= flags&(HASWIDTH|SPSTART);
X		break;
X	case '\0':
X	case '|':
X	case '\n':
X	case ')':
X		FAIL("internal urp");	/* Supposed to be caught earlier. */
X		break;
X	case '?':
X	case '+':
X	case '*':
X		FAIL("?+* follows nothing");
X		break;
X	case '\\':
X		switch (*regparse++) {
X		case '\0':
X			FAIL("trailing \\");
X			break;
X		case '<':
X			ret = regnode(WORDA);
X			break;
X		case '>':
X			ret = regnode(WORDZ);
X			break;
X		/* FIXME: Someday handle \1, \2, ... */
X		default:
X			/* Handle general quoted chars in exact-match routine */
X			goto de_fault;
X		}
X		break;
X	de_fault:
X	default:
X		/*
X		 * Encode a string of characters to be matched exactly.
X		 *
X		 * This is a bit tricky due to quoted chars and due to
X		 * '*', '+', and '?' taking the SINGLE char previous
X		 * as their operand.
X		 *
X		 * On entry, the char at regparse[-1] is going to go
X		 * into the string, no matter what it is.  (It could be
X		 * following a \ if we are entered from the '\' case.)
X		 * 
X		 * Basic idea is to pick up a good char in  ch  and
X		 * examine the next char.  If it's *+? then we twiddle.
X		 * If it's \ then we frozzle.  If it's other magic char
X		 * we push  ch  and terminate the string.  If none of the
X		 * above, we push  ch  on the string and go around again.
X		 *
X		 *  regprev  is used to remember where "the current char"
X		 * starts in the string, if due to a *+? we need to back
X		 * up and put the current char in a separate, 1-char, string.
X		 * When  regprev  is NULL,  ch  is the only char in the
X		 * string; this is used in *+? handling, and in setting
X		 * flags |= SIMPLE at the end.
X		 */
X		{
X			char *regprev;
X			register char ch;
X
X			regparse--;			/* Look at cur char */
X			ret = regnode(EXACTLY);
X			for ( regprev = 0 ; ; ) {
X				ch = *regparse++;	/* Get current char */
X				switch (*regparse) {	/* look at next one */
X
X				default:
X					regc(ch);	/* Add cur to string */
X					break;
X
X				case '.': case '[': case '(':
X				case ')': case '|': case '\n':
X				case '$': case '^':
X				case '\0':
X				/* FIXME, $ and ^ should not always be magic */
X				magic:
X					regc(ch);	/* dump cur char */
X					goto done;	/* and we are done */
X
X				case '?': case '+': case '*':
X					if (!regprev) 	/* If just ch in str, */
X						goto magic;	/* use it */
X					/* End mult-char string one early */
X					regparse = regprev; /* Back up parse */
X					goto done;
X
X				case '\\':
X					regc(ch);	/* Cur char OK */
X					switch (regparse[1]){ /* Look after \ */
X					case '\0':
X					case '<':
X					case '>':
X					/* FIXME: Someday handle \1, \2, ... */
X						goto done; /* Not quoted */
X					default:
X						/* Backup point is \, scan							 * point is after it. */
X						regprev = regparse;
X						regparse++; 
X						continue;	/* NOT break; */
X					}
X				}
X				regprev = regparse;	/* Set backup point */
X			}
X		done:
X			regc('\0');
X			*flagp |= HASWIDTH;
X			if (!regprev)		/* One char? */
X				*flagp |= SIMPLE;
X		}
X		break;
X	}
X
X	return(ret);
X}
X
X/*
X - regnode - emit a node
X */
Xstatic char *			/* Location. */
Xregnode(op)
Xchar op;
X{
X	register char *ret;
X	register char *ptr;
X
X	ret = regcode;
X	if (ret == &regdummy) {
X		regsize += 3;
X		return(ret);
X	}
X
X	ptr = ret;
X	*ptr++ = op;
X	*ptr++ = '\0';		/* Null "next" pointer. */
X	*ptr++ = '\0';
X	regcode = ptr;
X
X	return(ret);
X}
X
X/*
X - regc - emit (if appropriate) a byte of code
X */
Xstatic void
Xregc(b)
Xchar b;
X{
X	if (regcode != &regdummy)
X		*regcode++ = b;
X	else
X		regsize++;
X}
X
X/*
X - reginsert - insert an operator in front of already-emitted operand
X *
X * Means relocating the operand.
X */
Xstatic void
Xreginsert(op, opnd)
Xchar op;
Xchar *opnd;
X{
X	register char *src;
X	register char *dst;
X	register char *place;
X
X	if (regcode == &regdummy) {
X		regsize += 3;
X		return;
X	}
X
X	src = regcode;
X	regcode += 3;
X	dst = regcode;
X	while (src > opnd)
X		*--dst = *--src;
X
X	place = opnd;		/* Op node, where operand used to be. */
X	*place++ = op;
X	*place++ = '\0';
X	*place++ = '\0';
X}
X
X/*
X - regtail - set the next-pointer at the end of a node chain
X */
Xstatic void
Xregtail(p, val)
Xchar *p;
Xchar *val;
X{
X	register char *scan;
X	register char *temp;
X	register int offset;
X
X	if (p == &regdummy)
X		return;
X
X	/* Find last node. */
X	scan = p;
X	for (;;) {
X		temp = regnext(scan);
X		if (temp == NULL)
X			break;
X		scan = temp;
X	}
X
X	if (OP(scan) == BACK)
X		offset = scan - val;
X	else
X		offset = val - scan;
X	*(scan+1) = (offset>>8)&0377;
X	*(scan+2) = offset&0377;
X}
X
X/*
X - regoptail - regtail on operand of first argument; nop if operandless
X */
Xstatic void
Xregoptail(p, val)
Xchar *p;
Xchar *val;
X{
X	/* "Operandless" and "op != BRANCH" are synonymous in practice. */
X	if (p == NULL || p == &regdummy || OP(p) != BRANCH)
X		return;
X	regtail(OPERAND(p), val);
X}
X
X/*
X * regexec and friends
X */
X
X/*
X * Global work variables for regexec().
X */
Xstatic char *reginput;		/* String-input pointer. */
Xstatic char *regbol;		/* Beginning of input, for ^ check. */
Xstatic char **regstartp;	/* Pointer to startp array. */
Xstatic char **regendp;		/* Ditto for endp. */
X
X/*
X * Forwards.
X */
XSTATIC int regtry();
XSTATIC int regmatch();
XSTATIC int regrepeat();
X
X#ifdef DEBUG
Xint regnarrate = 0;
Xvoid regdump();
XSTATIC char *regprop();
X#endif
X
X/*
X - regexec - match a regexp against a string
X */
Xint
Xregexec(prog, string)
Xregister const regexp *prog;
Xregister const char *string;
X{
X	register char *s;
X	extern char *strchr();
X
X	/* Be paranoid... */
X	if (prog == NULL || string == NULL) {
X		regerror("NULL parameter");
X		return(0);
X	}
X
X	/* Check validity of program. */
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("corrupted program");
X		return(0);
X	}
X
X	/* If there is a "must appear" string, look for it. */
X	if (prog->regmust != NULL) {
X		s = (char *)string;
X		while ((s = strchr(s, prog->regmust[0])) != NULL) {
X			if (strncmp(s, prog->regmust, prog->regmlen) == 0)
X				break;	/* Found it. */
X			s++;
X		}
X		if (s == NULL)	/* Not present. */
X			return(0);
X	}
X
X	/* Mark beginning of line for ^ . */
X	regbol = (char *)string;
X
X	/* Simplest case:  anchored match need be tried only once. */
X	if (prog->reganch)
X		return(regtry(prog, string));
X
X	/* Messy cases:  unanchored match. */
X	s = (char *)string;
X	if (prog->regstart != '\0')
X		/* We know what char it must start with. */
X		while ((s = strchr(s, prog->regstart)) != NULL) {
X			if (regtry(prog, s))
X				return(1);
X			s++;
X		}
X	else
X		/* We don't -- general case. */
X		do {
X			if (regtry(prog, s))
X				return(1);
X		} while (*s++ != '\0');
X
X	/* Failure. */
X	return(0);
X}
X
X/*
X - regtry - try match at specific point
X */
Xstatic int			/* 0 failure, 1 success */
Xregtry(prog, string)
Xregexp *prog;
Xchar *string;
X{
X	register int i;
X	register char **sp;
X	register char **ep;
X
X	reginput = string;
X	regstartp = prog->startp;
X	regendp = prog->endp;
X
X	sp = prog->startp;
X	ep = prog->endp;
X	for (i = NSUBEXP; i > 0; i--) {
X		*sp++ = NULL;
X		*ep++ = NULL;
X	}
X	if (regmatch(prog->program + 1)) {
X		prog->startp[0] = string;
X		prog->endp[0] = reginput;
X		return(1);
X	} else
X		return(0);
X}
X
X/*
X - regmatch - main matching routine
X *
X * Conceptually the strategy is simple:  check to see whether the current
X * node matches, call self recursively to see whether the rest matches,
X * and then act accordingly.  In practice we make some effort to avoid
X * recursion, in particular by going through "ordinary" nodes (that don't
X * need to know whether the rest of the match failed) by a loop instead of
X * by recursion.
X */
Xstatic int			/* 0 failure, 1 success */
Xregmatch(prog)
Xchar *prog;
X{
X	register char *scan;	/* Current node. */
X	char *next;		/* Next node. */
X	extern char *strchr();
X
X	scan = prog;
X#ifdef DEBUG
X	if (scan != NULL && regnarrate)
X		fprintf(stderr, "%s(\n", regprop(scan));
X#endif
X	while (scan != NULL) {
X#ifdef DEBUG
X		if (regnarrate)
X			fprintf(stderr, "%s...\n", regprop(scan));
X#endif
X		next = regnext(scan);
X
X		switch (OP(scan)) {
X		case BOL:
X			if (reginput != regbol)
X				return(0);
X			break;
X		case EOL:
X			if (*reginput != '\0')
X				return(0);
X			break;
X		case WORDA:
X			/* Must be looking at a letter, digit, or _ */
X			if ((!isalnum(*reginput)) && *reginput != '_')
X				return(0);
X			/* Prev must be BOL or nonword */
X			if (reginput > regbol &&
X			    (isalnum(reginput[-1]) || reginput[-1] == '_'))
X				return(0);
X			break;
X		case WORDZ:
X			/* Must be looking at non letter, digit, or _ */
X			if (isalnum(*reginput) || *reginput == '_')
X				return(0);
X			/* We don't care what the previous char was */
X			break;
X		case ANY:
X			if (*reginput == '\0')
X				return(0);
X			reginput++;
X			break;
X		case EXACTLY: {
X				register int len;
X				register char *opnd;
X
X				opnd = OPERAND(scan);
X				/* Inline the first character, for speed. */
X				if (*opnd != *reginput)
X					return(0);
X				len = strlen(opnd);
X				if (len > 1 && strncmp(opnd, reginput, len) != 0)
X					return(0);
X				reginput += len;
X			}
X			break;
X		case ANYOF:
X 			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
X				return(0);
X			reginput++;
X			break;
X		case ANYBUT:
X 			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
X				return(0);
X			reginput++;
X			break;
X		case NOTHING:
X			break;
X		case BACK:
X			break;
X		case OPEN+1:
X		case OPEN+2:
X		case OPEN+3:
X		case OPEN+4:
X		case OPEN+5:
X		case OPEN+6:
X		case OPEN+7:
X		case OPEN+8:
X		case OPEN+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - OPEN;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set startp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regstartp[no] == NULL)
X						regstartp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case CLOSE+1:
X		case CLOSE+2:
X		case CLOSE+3:
X		case CLOSE+4:
X		case CLOSE+5:
X		case CLOSE+6:
X		case CLOSE+7:
X		case CLOSE+8:
X		case CLOSE+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - CLOSE;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set endp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regendp[no] == NULL)
X						regendp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case BRANCH: {
X				register char *save;
X
X				if (OP(next) != BRANCH)		/* No choice. */
X					next = OPERAND(scan);	/* Avoid recursion. */
X				else {
X					do {
X						save = reginput;
X						if (regmatch(OPERAND(scan)))
X							return(1);
X						reginput = save;
X						scan = regnext(scan);
X					} while (scan != NULL && OP(scan) == BRANCH);
X					return(0);
X					/* NOTREACHED */
X				}
X			}
X			break;
X		case STAR:
X		case PLUS: {
X				register char nextch;
X				register int no;
X				register char *save;
X				register int min;
X
X				/*
X				 * Lookahead to avoid useless match attempts
X				 * when we know what character comes next.
X				 */
X				nextch = '\0';
X				if (OP(next) == EXACTLY)
X					nextch = *OPERAND(next);
X				min = (OP(scan) == STAR) ? 0 : 1;
X				save = reginput;
X				no = regrepeat(OPERAND(scan));
X				while (no >= min) {
X					/* If it could work, try it. */
X					if (nextch == '\0' || *reginput == nextch)
X						if (regmatch(next))
X							return(1);
X					/* Couldn't or didn't -- back up. */
X					no--;
X					reginput = save + no;
X				}
X				return(0);
X			}
X			break;
X		case END:
X			return(1);	/* Success! */
X			break;
X		default:
X			regerror("memory corruption");
X			return(0);
X			break;
X		}
X
X		scan = next;
X	}
X
X	/*
X	 * We get here only if there's trouble -- normally "case END" is
X	 * the terminating point.
X	 */
X	regerror("corrupted pointers");
X	return(0);
X}
X
X/*
X - regrepeat - repeatedly match something simple, report how many
X */
Xstatic int
Xregrepeat(p)
Xchar *p;
X{
X	register int count = 0;
X	register char *scan;
X	register char *opnd;
X
X	scan = reginput;
X	opnd = OPERAND(p);
X	switch (OP(p)) {
X	case ANY:
X		count = strlen(scan);
X		scan += count;
X		break;
X	case EXACTLY:
X		while (*opnd == *scan) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYOF:
X		while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYBUT:
X		while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	default:		/* Oh dear.  Called inappropriately. */
X		regerror("internal foulup");
X		count = 0;	/* Best compromise. */
X		break;
X	}
X	reginput = scan;
X
X	return(count);
X}
X
X/*
X - regnext - dig the "next" pointer out of a node
X */
Xstatic char *
Xregnext(p)
Xregister char *p;
X{
X	register int offset;
X
X	if (p == &regdummy)
X		return(NULL);
X
X	offset = NEXT(p);
X	if (offset == 0)
X		return(NULL);
X
X	if (OP(p) == BACK)
X		return(p-offset);
X	else
X		return(p+offset);
X}
X
X#ifdef DEBUG
X
XSTATIC char *regprop();
X
X/*
X - regdump - dump a regexp onto stdout in vaguely comprehensible form
X */
Xvoid
Xregdump(r)
Xregexp *r;
X{
X	register char *s;
X	register char op = EXACTLY;	/* Arbitrary non-END op. */
X	register char *next;
X	extern char *strchr();
X
X
X	s = r->program + 1;
X	while (op != END) {	/* While that wasn't END last time... */
X		op = OP(s);
X		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */
X		next = regnext(s);
X		if (next == NULL)		/* Next ptr. */
X			printf("(0)");
X		else 
X			printf("(%d)", (s-r->program)+(next-s));
X		s += 3;
X		if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
X			/* Literal string, where present. */
X			while (*s != '\0') {
X				putchar(*s);
X				s++;
X			}
X			s++;
X		}
X		putchar('\n');
X	}
X
X	/* Header fields of interest. */
X	if (r->regstart != '\0')
X		printf("start `%c' ", r->regstart);
X	if (r->reganch)
X		printf("anchored ");
X	if (r->regmust != NULL)
X		printf("must have \"%s\"", r->regmust);
X	printf("\n");
X}
X
X/*
X - regprop - printable representation of opcode
X */
Xstatic char *
Xregprop(op)
Xchar *op;
X{
X	register char *p;
X	static char buf[50];
X
X	(void) strcpy(buf, ":");
X
X	switch (OP(op)) {
X	case BOL:
X		p = "BOL";
X		break;
X	case EOL:
X		p = "EOL";
X		break;
X	case ANY:
X		p = "ANY";
X		break;
X	case ANYOF:
X		p = "ANYOF";
X		break;
X	case ANYBUT:
X		p = "ANYBUT";
X		break;
X	case BRANCH:
X		p = "BRANCH";
X		break;
X	case EXACTLY:
X		p = "EXACTLY";
X		break;
X	case NOTHING:
X		p = "NOTHING";
X		break;
X	case BACK:
X		p = "BACK";
X		break;
X	case END:
X		p = "END";
X		break;
X	case OPEN+1:
X	case OPEN+2:
X	case OPEN+3:
X	case OPEN+4:
X	case OPEN+5:
X	case OPEN+6:
X	case OPEN+7:
X	case OPEN+8:
X	case OPEN+9:
X		sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
X		p = NULL;
X		break;
X	case CLOSE+1:
X	case CLOSE+2:
X	case CLOSE+3:
X	case CLOSE+4:
X	case CLOSE+5:
X	case CLOSE+6:
X	case CLOSE+7:
X	case CLOSE+8:
X	case CLOSE+9:
X		sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
X		p = NULL;
X		break;
X	case STAR:
X		p = "STAR";
X		break;
X	case PLUS:
X		p = "PLUS";
X		break;
X	case WORDA:
X		p = "WORDA";
X		break;
X	case WORDZ:
X		p = "WORDZ";
X		break;
X	default:
X		regerror("corrupted opcode");
X		break;
X	}
X	if (p != NULL)
X		(void) strcat(buf, p);
X	return(buf);
X}
X#endif
X
X/*
X * The following is provided for those people who do not have strcspn() in
X * their C libraries.  They should get off their butts and do something
X * about it; at least one public-domain implementation of those (highly
X * useful) string routines has been published on Usenet.
X */
X#ifdef STRCSPN
X/*
X * strcspn - find length of initial segment of s1 consisting entirely
X * of characters not from s2
X */
X
Xstatic int
Xstrcspn(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X	register char *scan1;
X	register char *scan2;
X	register int count;
X
X	count = 0;
X	for (scan1 = s1; *scan1 != '\0'; scan1++) {
X		for (scan2 = s2; *scan2 != '\0';)	/* ++ moved down. */
X			if (*scan1 == *scan2++)
X				return(count);
X		count++;
X	}
X	return(count);
X}
X#endif
SHAR_EOF
chmod 0444 re/regexp.c || echo "restore of re/regexp.c fails"
sed 's/^X//' << 'SHAR_EOF' > re/regexp.h &&
X/*
X * Definitions etc. for regexp(3) routines.
X *
X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
X * not the System V one.
X */
X#define NSUBEXP  10
Xtypedef struct regexp {
X	char *startp[NSUBEXP];
X	char *endp[NSUBEXP];
X	char regstart;		/* Internal use only. */
X	char reganch;		/* Internal use only. */
X	char *regmust;		/* Internal use only. */
X	int regmlen;		/* Internal use only. */
X	char program[1];	/* Unwarranted chumminess with compiler. */
X} regexp;
X
Xextern regexp *regcomp();
Xextern int regexec();
Xextern void regsub();
Xextern void regerror();
SHAR_EOF
chmod 0444 re/regexp.h || echo "restore of re/regexp.h fails"
sed 's/^X//' << 'SHAR_EOF' > re/regmagic.h &&
X/*
X * The first byte of the regexp internal "program" is actually this magic
X * number; the start node begins in the second byte.
X */
X#define	MAGIC	0234
SHAR_EOF
chmod 0444 re/regmagic.h || echo "restore of re/regmagic.h fails"
sed 's/^X//' << 'SHAR_EOF' > re/regsub.c &&
X/*
X * regsub
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X */
X#include <regexp.h>
X#include <stdio.h>
X#include <string.h>
X#include "regmagic.h"
X
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X/*
X - regsub - perform substitutions after a regexp match
X */
Xvoid
Xregsub(prog, source, dest)
Xconst regexp *prog;
Xconst char *source;
Xchar *dest;
X{
X	register char *src;
X	register char *dst;
X	register char c;
X	register int no;
X	register int len;
X	extern char *strncpy();
X
X	if (prog == NULL || source == NULL || dest == NULL) {
X		regerror("NULL parm to regsub");
X		return;
X	}
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("damaged regexp fed to regsub");
X		return;
X	}
X
X	src = (char *)source;
X	dst = dest;
X	while ((c = *src++) != '\0') {
X		if (c == '&')
X			no = 0;
X		else if (c == '\\' && '0' <= *src && *src <= '9')
X			no = *src++ - '0';
X		else
X			no = -1;
X 		if (no < 0) {	/* Ordinary character. */
X 			if (c == '\\' && (*src == '\\' || *src == '&'))
X 				c = *src++;
X 			*dst++ = c;
X 		} else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
X			len = prog->endp[no] - prog->startp[no];
X			(void) strncpy(dst, prog->startp[no], len);
X			dst += len;
X			if (len != 0 && *(dst-1) == '\0') {	/* strncpy hit NUL. */
X				regerror("damaged match string");
X				return;
X			}
X		}
X	}
X	*dst++ = '\0';
X}
SHAR_EOF
chmod 0444 re/regsub.c || echo "restore of re/regsub.c fails"
exit 0
-- 

Andrew Burt 		   					  [email-masked]

"But if he was dying he wouldn't bother to carve "Aaaaargh", he'd just say it."