X-Git-Url: http://source.jalview.org/gitweb/?p=jpred.git;a=blobdiff_plain;f=sources%2Foc%2Fgjutil.c;fp=sources%2Foc%2Fgjutil.c;h=95f8af54a90aaadd747cc6368e77024b9aa05e56;hp=0000000000000000000000000000000000000000;hb=9aa768067094f24f46f273077f867348e6143711;hpb=eb3001dc41bf6cd46e20fd13fe3efbe9dedf6013 diff --git a/sources/oc/gjutil.c b/sources/oc/gjutil.c new file mode 100644 index 0000000..95f8af5 --- /dev/null +++ b/sources/oc/gjutil.c @@ -0,0 +1,1203 @@ + +/**************************************************************************** + +gjutil.c: Various utility routines - error checking malloc and +free, string functions etc... + +Copyright: Geoffrey J. Barton (1992, 1993, 1995, 1997) +email: geoff@ebi.ac.uk + +This software is made available for educational and non-commercial research +purposes. + +For commercial use, a commercial licence is required - contact the author +at the above address for details. + + +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "gjtimes.h" +#include "gjutil.h" + +/* define pointers for standard streams to allow redefinition to files */ + +FILE *std_err; +FILE *std_in; +FILE *std_out; + +/* clock externs */ +clock_t start_time, end_time,initial_time,final_time; + +void *GJmalloc(size_t size) +/* malloc with simple error check */ +/* G. J. Barton 1992 */ +{ + void *ptr; + ptr = (void *) malloc(size); + if(ptr == NULL){ + GJerror("malloc error"); + exit(0); + } + return ptr; +} + +void *GJrealloc(void *ptr,size_t size) +/* realloc with error check */ +/* G. J. Barton 1992 */ +{ + ptr = (void *) realloc(ptr,size); + if(ptr == NULL){ + GJerror("realloc error"); + exit(0); + } + return ptr; +} +void *GJmallocNQ(size_t size) +/* as for GJmalloc, but returns NULL on error*/ +/* G. J. Barton 1992 */ +{ + void *ptr; + ptr = (void *) malloc(size); + if(ptr == NULL){ + GJerror("malloc error"); + return NULL; + } + return ptr; +} + +void *GJreallocNQ(void *ptr,size_t size) +/* as for GJrealloc with error check but returns NULL on error*/ +/* G. J. Barton 1992 */ +{ + ptr = (void *) realloc(ptr,size); + if(ptr == NULL){ + GJerror("realloc error"); + return NULL; + } + return ptr; +} +void GJfree(void *ptr) +/* free with error check */ +/* G. J. Barton 1992 */ +{ + if(ptr == NULL){ + GJerror("Attempt to free NULL pointer"); + exit(0); + } + free(ptr); +} + +void GJerror(const char *prefix) +/* writes error message contained in prefix and contents of errno + to std_err. +*/ +/* G. J. Barton 1992 */ +{ + if(prefix != NULL){ + if(*prefix != '\0'){ + fprintf(std_err,"%s: ",prefix); + } + } + fprintf(std_err,"%s\n",strerror(errno)); +} + +/* +error: calls GJerror +*/ +void error(const char *str,int flag) +{ + GJerror(str); + if(flag)exit(0); +} + + +char *GJstoupper(const char *s) +/* return a copy of s in upper case */ +/* G. J. Barton 1992 */ +{ + char *temp; + int i; + temp = GJstrdup(s); + i=0; + while(temp[i] != '\0'){ + temp[i] = toupper(temp[i]); + ++i; + } + return temp; +} +char *GJstolower(const char *s) +/* return a copy of s in lower case */ +/* G. J. Barton 1992 */ +{ + char *temp; + int i; + temp = GJstrdup(s); + i=0; + while(temp[i] != '\0'){ + temp[i] = tolower(temp[i]); + ++i; + } + return temp; +} +char *GJstoup(char *s) +/* return s in upper case */ +/* G. J. Barton 1992 */ +{ + int i; + i=0; + while(s[i] != '\0'){ + s[i] = toupper(s[i]); + ++i; + } + return s; +} +char *GJstolo(char *s) +/* return s in lower case */ +/* G. J. Barton 1992 */ +{ + int i; + i=0; + while(s[i] != '\0'){ + s[i] = tolower(s[i]); + ++i; + } + return s; +} + +char *GJstrdup(const char *s) +/* returns a pointer to a copy of string s */ +/* G. J. Barton 1992 */ + +{ + char *temp; + temp = (char *) GJmalloc(sizeof(char) * (strlen(s)+1)); + temp = strcpy(temp,s); + return temp; +} + +char *GJstrrename(char *old,const char *new) +/* takes old which is a pointer to a string, then replaces the contents + of the string with new, reallocating to the correct size +*/ +{ + int nlen; + nlen = strlen(new); + old = (char *) GJrealloc(old,sizeof(char) * (nlen + 1)); + old = strcpy(old,new); + return old; +} + + + +FILE *GJfopen(const char *fname,const char *type,int action) +/* a file open function with error checking. The third argument +is set to 0 if we want a failed open to return, or 1 if we +want a failed open to exit the program. +*/ +/* G. J. Barton 1992 */ +/* modified July 1995 - error message only printed if action is 1 */ +{ + FILE *ret_val; + ret_val = fopen(fname,type); + if(ret_val == NULL){ + /* GJerror(strcat("Cannot Open File: ",fname));*/ + if(action == 1){ + GJerror(strcat("Cannot Open File: ",fname)); + exit(1); + } + } + return ret_val; +} + +int GJfclose(FILE *fp,int action) +/* a file close function with error checking. The second argument +is set to 0 if we want a failed close to return, or 1 if we +want a failed close to exit the program. +*/ +/* G. J. Barton 1992 */ +{ + int ret_val; + ret_val = fclose(fp); + if(ret_val != 0){ + if(action == 1){ + GJerror("Error closing File"); + exit(1); + } + } + return ret_val; +} + + +GJFILE *GJfilemake(const char *name,const char *type,int action) +/* If action = 1 then +Tries to open the file with the given name. If successful returns +a pointer to a struct file structure with the name and filehandle. If +the open fails, or action= 0 then returns a struct file structure +with the name and a NULL filehandle */ +/* G. J. Barton 1995 */ +{ + GJFILE *ret_val; + ret_val = (GJFILE *) GJmalloc(sizeof(GJFILE)); + ret_val->name = GJstrdup(name); + if(action == 1) { + ret_val->handle = GJfopen(ret_val->name,type,0); + }else if(action == 0){ + ret_val->handle = NULL; + } + return ret_val; +} + +GJFILE *GJfilerename(GJFILE *ret_val, const char *name) +/* When passed the fval structure - renames the name part of the +file structure to name, if the handle is non null it tries to close +the file, then sets the file handle to NULL. */ +/* G. J. Barton 1995 */ +{ + if(ret_val->name != NULL) { + GJfree(ret_val->name); + ret_val->name = GJstrdup(name); + } + if(ret_val->handle != NULL) { + GJfclose(ret_val->handle,0); + ret_val->handle = NULL; + } + return ret_val; +} + +GJFILE *GJfileclose(GJFILE *ret_val,int action) +/* Closes a file named in the struct file structure returns the struct + file structure */ + +/* G. J. Barton July 1995 */ +{ + STD_FILES; + + if(GJfclose(ret_val->handle,0) == 0){ + return ret_val; + }else { + if(action == 1){ + GJerror("Error closing File"); + fprintf(std_err,"%s\n",ret_val->name); + exit(-1); + } + } + return ret_val; +} + +GJFILE *GJfileopen(GJFILE *ret_val,const char *type,int action) +/* Opens a file named in the struct file structure */ + +/* G. J. Barton October 1995 */ +{ + STD_FILES; + + ret_val->handle = GJfopen(ret_val->name,type,0); + if(ret_val->handle == NULL){ + if(action == 1){ + GJerror("Error opening File"); + fprintf(std_err,"%s\n",ret_val->name); + exit(-1); + } + } + return ret_val; +} + +GJFILE *GJfileclean(GJFILE *ret_val,int action) +/* Closes the file then sets the file pointer to NULL, then + frees the filename string */ + +/* G. J. Barton July 1995 */ +{ + if(GJfclose(ret_val->handle,0) == 0) { + ret_val->handle = NULL; + GJfree(ret_val->name); + return ret_val; + }else { + if(action == 1){ + GJerror("Error closing File"); + fprintf(std_err,"%s\n",ret_val->name); + exit(-1); + } + } + return ret_val; +} + +void GJinitfile(void) +/* just set the standard streams */ +{ + std_err = stderr; + std_in = stdin; + std_out = stdout; +} + +char *GJfnonnull(char *string) +/* return pointer to first non null character in the string */ +/* this could cause problems if the string is not null terminated */ +{ + while(*string != '\0'){ + ++string; + } + return ++string; +} + +char *GJstrappend(char *string1, char *string2) +/* appends string2 to the end of string2. Any newline characters are removed +from string1, then the first character of string2 overwrites the null at the +end of string1. +string1 and string2 must have been allocated with malloc. +*/ +/* G. J. Barton July 1992 */ +{ + char *ret_val; + ret_val = GJremovechar(string1,'\n'); + ret_val = (char *) GJrealloc(ret_val, + sizeof(char) * (strlen(ret_val) + strlen(string2) + 1)); + ret_val = strcat(ret_val,string2); + return ret_val; +} + +char *GJremovechar(char *string,char c) +/* removes all instances of character c from string + returns a pointer to the reduced, null terminated string + 11/8/1996: couple of bugs found in this routine. + the length of the string returned was too short by 2 bytes. + This is a dodgy routine since string is freed. +*/ +/* G. J. Barton (July 1992) */ +{ + char *temp; + int j,i,nchar; + nchar = 0; + i=0; + while(string[i] != '\0'){ + if(string[i] == c){ + ++nchar; + } + ++i; + } + if(nchar == 0){ + return string; + }else{ + temp = (char *) GJmalloc(sizeof(char) * (strlen(string)-nchar + 1)); + j=0; + i=0; + while(string[i] != '\0'){ + if(string[i] != c){ + temp[j] = string[i]; + ++j; + } + ++i; + } + temp[j] = '\0'; + GJfree(string); + return temp; + } +} + +char *GJremovechar2(char *string,char c) +/* removes all instances of character c from string + returns a pointer to the reduced, null terminated string +*/ +/* G. J. Barton (July 1992) */ +{ + char *temp; + int i,k,len; + k=0; + len=strlen(string); + temp = (char *) GJmalloc(sizeof(char) * (len+1)); + for(i=0;i<(len+1);++i){ + if(string[i] != c){ + temp[k] = string[i]; + ++k; + } + } + for(i=0;i<(strlen(temp)+1);++i){ + string[i] = temp[i]; + } + GJfree(temp); + return string; +} + + +char *GJsubchar(char *string,char c2,char c1) +/* substitutes c1 for c2 in string +*/ +/* G. J. Barton (July 1992) */ +{ + int i; + + i=0; + while(string[i] != '\0'){ + if(string[i] == c1){ + string[i] = c2; + } + ++i; + } + return string; +} + +/* create a string and if fchar != NULL fill with characters */ +/* always set the len-1 character to '\0' */ + +char *GJstrcreate(size_t len,char *fchar) +{ + char *ret_val; + ret_val = (char *) GJmalloc(sizeof(char) * len); + --len; + ret_val[len] = '\0'; + if(fchar != NULL){ + while(len > -1){ + ret_val[len] = *fchar; + --len; + } + } + return ret_val; +} + +/* searches for string s2 in string s1 and returns pointer to first instance +of s2 in s1 or NULL if no instance found. s1 and s2 must be null terminated +*/ +char *GJstrlocate(char *s1, char *s2) +{ + int i=0; + int j=0; + int k; + if(strlen(s1) == 0 || strlen(s2) == 0) return NULL; + while(i < strlen(s1)){ + j=0; + k=i; + while(j < strlen(s2) && s1[k] == s2[j]){ + ++k; + ++j; + } + if(j == strlen(s2)) return &s1[i]; + ++i; + } + return NULL; +} +#include +#include + + +/* GJstrtok() + +This version of strtok places the work pointer at the location of the first +character in the next token, rather than just after the last character of the +current token. This is useful for extracting quoted strings +*/ + +char *GJstrtok(char *input_string,const char *token_list) +{ + static char *work; + char *return_ptr; + + if(input_string != NULL){ + /* first call */ + work = input_string; + } + + /* search for next non-token character */ + while(strchr(token_list,*work)!=NULL){ + ++work; + } + + if(*work == '\0'){ + /* if we've reached the end of string, then return NULL */ + return NULL; + }else{ + return_ptr = (char *) work; + while(strchr(token_list,*work) == NULL){ + if(*work == '\0'){ + /* end of the string */ + return return_ptr; + }else{ + ++work; + } + } + *work = '\0'; + ++work; + /* now increment work until we find the next non-delimiter character */ + while(strchr(token_list,*work) != NULL){ + if(*work == '\0'){ + break; + }else{ + ++work; + } + } + return return_ptr; + } +} +/************************************************************************** +return a pointer to space for a rectangular unsigned character array +Version 2.0 ANSI and uses GJmallocNQ +--------------------------------------------------------------------------*/ + +unsigned char **uchararr(int i,int j) +{ + unsigned char **temp; + int k, rowsiz; + + temp = (unsigned char **) GJmallocNQ(sizeof(unsigned char *) * i); + if(temp == NULL) return NULL; + + rowsiz = sizeof(unsigned char) * j; + + for (k = 0; k < i; ++k){ + temp[k] = (unsigned char *) GJmallocNQ(rowsiz); + if(temp[k] == NULL) return NULL; + } + return temp; +} + +/************************************************************************** +free up space pointed to by rectangular unsigned character array +-------------------------------------------------------------------------*/ +void ucharfree(unsigned char **array,int i) + +{ + int k; + + for (k = 0; k < i; ++k){ + GJfree((char *) array[k]); + } + GJfree((char *) array); + +} + +/************************************************************************** +return a pointer to space for a rectangular signed character array +Version 2.0 ANSI +--------------------------------------------------------------------------*/ +signed char **chararr(int i,int j) + +{ + signed char **temp; + int k, rowsiz; + + temp = (signed char **) GJmallocNQ(sizeof(char *) * i); + + if(temp == NULL) return NULL; + + rowsiz = sizeof(char) * j; + + for (k = 0; k < i; ++k){ + temp[k] = (signed char *) GJmallocNQ(rowsiz); + if(temp[k] == NULL) return NULL; + } + return temp; +} + + +/* mcheck - check a call to malloc - if the call has failed, print the +error message and exit the program */ +/* ANSI Version - also uses GJerror routine and ptr is declared void*/ + +void mcheck(void *ptr,char *msg) + +{ + if(ptr == NULL){ + GJerror("malloc/realloc error"); + exit(0); + } +} + +/* set a string to blanks and add terminating nul */ +char *GJstrblank(char *string,int len) + +{ + --len; + string[len] = '\0'; + --len; + while(len > -1){ + string[len] = ' '; + --len; + } + return string; +} + +/* Initialise an unsigned char array */ +void GJUCinit(unsigned char **array,int i,int j,unsigned char val) +{ + int k,l; + + for(k=0;k0){ + token[i] = '\0'; + return token; + } + } +/* GJerror("End of File Encountered");*/ + GJfree(token); + return NULL; +} + +struct tokens *GJgettokens(const char *delims, char *buff) +/* This splits a buffer into tokens at each position defined by delims.*/ +/* The structure returned contains the number of tokens and the */ +/* tokens themselves as a char ** array */ +{ + char *token; + struct tokens *tok; + + token = strtok(buff,delims); + if(token == NULL) return NULL; + + tok = (struct tokens *) GJmalloc(sizeof(struct tokens)); + tok->ntok = 0; + tok->tok = (char **) GJmalloc(sizeof(char *)); + tok->tok[0] = GJstrdup(token); + ++tok->ntok; + while((token = strtok(NULL,delims)) != NULL) { + tok->tok = (char **) GJrealloc(tok->tok,sizeof(char *) * (tok->ntok+1)); + tok->tok[tok->ntok] = GJstrdup(token); + ++tok->ntok; + } + + return tok; +} + +void GJfreetokens(struct tokens *tok) +/* frees a tokens structure */ + +{ + int i; + for(i=0;intok;++i) { + GJfree(tok->tok[i]); + } + GJfree(tok->tok); + GJfree(tok); + tok = NULL; /* add this to avoid odd pointer 27/6/1997*/ +} + +char * GJtoktostr(struct tokens *tok,char delim,int s, int e) + +/* + returns a string with the tokens between s and e inclusive written to + it separated by delim + the tok structure is unmodified. +*/ + +{ + int n, i, j,k; + char *ret_val; + + n = 0; + + + if(s < 0 || s >= tok->ntok) s = 0; + if(e < 0 || e >= tok->ntok) e = tok->ntok - 1; + + for(i=s;i<=e;++i){ + n += strlen(tok->tok[i]); + ++n; + } + + ret_val = (char *) GJmalloc(sizeof(char) * n); + j = 0; + for(i=s;i<=e;++i){ + for(k=0;ktok[i]);++k){ + ret_val[j] = tok->tok[i][k]; + ++j; + } + ret_val[j++] = delim; + } + ret_val[n-1] = '\0'; + return ret_val; +} + +/* +void GJ_start_clock() +{ + extern clock_t start_time, end_time,initial_time,final_time; + start_time = clock(); + initial_time = start_time; +} + +void GJ_stop_clock(FILE *fp) +{ + extern clock_t start_time, end_time,initial_time,final_time; + end_time = clock(); + fprintf(fp,"CPU time: %f seconds\n", + ((float) (end_time - start_time))/CLOCKS_PER_SEC); +} +*/ + + +void GJ_start_clock(void) +{ + GJ_init_times(); +} + +void GJ_stop_clock(FILE *fp) +{ + GJ_show_times(fp,3); + GJ_reset_times(); +} + + +void GJindexx(int *arrin,int n,int *indx) + /* indexed heap sort - adapted from the NR routine indexx. + inarr is an integer array to sort, + indx is the returned index array + */ +{ + int l,j,ir,indxt,i; +/* SMJS + float q; +*/ + int q; + + for (j=1;j<=n;j++) indx[j]=j; + l=(n >> 1) + 1; + ir=n; + for (;;) { + if (l > 1) + q=arrin[(indxt=indx[--l])]; + else { + q=arrin[(indxt=indx[ir])]; + indx[ir]=indx[1]; + if (--ir == 1) { + indx[1]=indxt; + return; + } + } + i=l; + j=l << 1; + while (j <= ir) { + if (j < ir && arrin[indx[j]] < arrin[indx[j+1]]) j++; + if (q < arrin[indx[j]]) { + indx[i]=indx[j]; + j += (i=j); + } + else j=ir+1; + } + indx[i]=indxt; + } +} + +void GJindexxD(double *arrin,int n,int *indx) + /* indexed heap sort - adapted from the NR routine indexx. + arrin is a double array to sort, + indx is the returned index array + */ +{ + int l,j,ir,indxt,i; +/* + float q; +*/ + double q; + + for (j=1;j<=n;j++) indx[j]=j; + l=(n >> 1) + 1; + ir=n; + for (;;) { + if (l > 1) + q=arrin[(indxt=indx[--l])]; + else { + q=arrin[(indxt=indx[ir])]; + indx[ir]=indx[1]; + if (--ir == 1) { + indx[1]=indxt; + return; + } + } + i=l; + j=l << 1; + while (j <= ir) { + if (j < ir && arrin[indx[j]] < arrin[indx[j+1]]) j++; + if (q < arrin[indx[j]]) { + indx[i]=indx[j]; + j += (i=j); + } + else j=ir+1; + } + indx[i]=indxt; + } +} + +void GJindexxS1(char **arrin,int n,int *indx) + /*indexed sort of a character array - this uses qsort rather than the + heapsort in GJindexxS. indx runs from 0..(n-1) rather than 1..n as in + GJindexxS. + */ +{ + int i; + CWORK *ret; + + ret = (CWORK *) GJmalloc(sizeof(CWORK) * n); + + for(i=0;ival,b->val); +} + + +void GJindexxS(char **arrin,int n,int *indx) + /* indexed heap sort - adapted from the NR routine indexx. + arrin is a character string array to sort + indx is the returned index array + */ +{ + int l,j,ir,indxt,i; +/* + float q; +*/ + char *q; + + for (j=1;j<=n;j++) indx[j]=j; + l=(n >> 1) + 1; + ir=n; + for (;;) { + if (l > 1) + q=arrin[(indxt=indx[--l])]; + else { + q=arrin[(indxt=indx[ir])]; + indx[ir]=indx[1]; + if (--ir == 1) { + indx[1]=indxt; + return; + } + } + i=l; + j=l << 1; + while (j <= ir) { + if (j < ir && (strcmp(arrin[indx[j]],arrin[indx[j+1]]) < 0) ) j++; + if (strcmp(q,arrin[indx[j]])<0) { + indx[i]=indx[j]; + j += (i=j); + } + else j=ir+1; + } + indx[i]=indxt; + } +} + + + +void GJpline(FILE *fp,char c,int n) +/* print n copies of c to fp terminated by newline */ +{ + int i; + for(i=0;i n){ + for(i=0;i imax) imax = ivec[i]; + if(ivec[i] < imin) imin = ivec[i]; + } + + ret_val = (IRANGE *) GJmalloc(sizeof(IRANGE)); + ret_val->min = imin; + ret_val->max = imax; + + return ret_val; +} + +#define BLIM 5 +int GJbsearchINXS(char **cod, int n, char *query) + +/* binary search for query in table cod. If query is found, return index of query in cod.*/ +/* if it is not found, return -1 */ + +{ + int r; /* right limit */ + int l; /* left limit */ + int cv; /* centre value */ + + r = n-1; + l = 0; + + for(;;){ + if((r-l) > BLIM){ + cv = (r+l)/2; + if(strcmp(query,cod[cv]) == 0){ + return cv; + }else if(strcmp(query,cod[cv]) > 0){ + l = cv; + }else if(strcmp(query,cod[cv]) < 0){ + r = cv; + } + }else{ + for(cv=l;cv<(r+1);++cv){ + if(strcmp(query,cod[cv]) == 0){ + return cv; + } + } + return (int) -1; + } + } +} + +int GJbsearchINX_IS(char **cod, int *inx, int n, char *query) + +/* binary search for query in table cod. inx is the index into the table that specifies + the sorted order of the table cod. + If query is found, return index of query in inx that can be used to recover the value of + cod .*/ +/* if it is not found, return -1 */ + +{ + int r; /* right limit */ + int l; /* left limit */ + int cv; /* centre value */ + + r = n-1; + l = 0; + + for(;;){ + if((r-l) > BLIM){ + cv = (r+l)/2; + if(strcmp(query,cod[inx[cv]]) == 0){ + return cv; + }else if(strcmp(query,cod[inx[cv]]) > 0){ + l = cv; + }else if(strcmp(query,cod[inx[cv]]) < 0){ + r = cv; + } + }else{ + for(cv=l;cv<(r+1);++cv){ + if(strcmp(query,cod[inx[cv]]) == 0){ + return cv; + } + } + return (int) -1; + } + } +} + +/* + +$Id: gjutil.c,v 1.2 2003/05/13 09:42:18 jon Exp $ + +$Log: gjutil.c,v $ +Revision 1.2 2003/05/13 09:42:18 jon +Bug found in OC file input. The problem occurs when the first line giving +the number of entries to be clustered is missing, and the first ID has +a number at the beginning. This number is then taken to be the number +of entries in the file and much hilarious dumping of core results. + +To fix this requires a replacement of the file reading routines without +fscanf. This will be done... later. + +Revision 1.1 2002/08/04 19:06:18 geoff +Add the gjutil programs to oc archive + +Revision 1.1.1.1 2002/06/25 22:30:32 geoff +Import of gjnoc routines + + +Revision 1.12 2000/12/21 17:25:44 geoff +Add the option to output the sequence fragments from the multiple alignment +output option in fasta or pir format. Unlike the block file output, these +fragments contain the complete sequence between the start and end points, including +any parts deleted in the alignment process. +Add appropriate commands to scanps_alias.dat, getpars and getcmd. + +Revision 1.11 2000/07/04 11:01:37 searle +Changes for MMX + +Revision 1.10 1999/11/17 21:06:47 geoff +Add setup_caleb and other changes to swpp2 and so on. + +Revision 1.9 1999/07/09 13:34:10 geoff +modified these as a test + +Revision 1.8 1998/08/11 15:38:50 geoff +Modified the copyright notice to reflect the new +ownership of this software. + +Revision 1.7 1997/06/29 00:43:57 gjb +Changes to add sysinfo calls and test of license reading routines + +Revision 1.6 1997/06/27 16:42:41 gjb +Add trlic.c test.lic and clean up gjutil.c + +Revision 1.5 1997/06/27 07:17:31 gjb +Added rlic.c linfo.h and +changes to gjutil.c to give better support for +token manipulation + +Revision 1.4 1997/05/12 11:10:53 gjb +Re-added gjutil.c and gjutil.h to repository +after deleting them + +Revision 1.2 1997/05/12 10:47:52 gjb +Modified CVS header and log position + +*/ + + + +