"Fossies" - the Fresh Open Source Software archive

Member "leap-1.2.6.1/src/util.c" of archive leap-1.2.6.1.tar.gz:


/*
 * Name: util.c
 * Description: Utility Module - Contains all of the useful
 *		routines, and OS dependent stuff (where possible)
 * Version: util.c,v 1.208.2.1 2004/02/09 20:05:20 rleyton Exp
 *
 *   LEAP RDBMS - An extensible and free RDBMS
 *   Copyright (C) 1995-2004 Richard Leyton
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   See the file COPYING for more information.
 *
 *   Richard Leyton
 *   leap@leyton.org
 *   http://leap.sourceforge.net
 *   http://www.leyton.org
 *
 */


#ifdef HAVE_CONFIG_H
# include "defines.h"
#endif
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>

/* Taken from autoconf.info */
#ifndef __MSDOS__
#if HAVE_DIRENT_H
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#define dirent direct
#define NAMLEN(dirent) (dirent)->d_namlen
#if HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#if HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#if HAVE_NDIR_H
#include <ndir.h>
#endif
#endif
#else
#include <dirent.h>
#endif



#include "consts.h"
#include "globals.h"
#include "dtypes.h"
#include "util.h"
#include "dbase.h"
#include "vars.h"
#include "leapio.h"
#include "errors.h"
#include "parser.h"
#include "relation.h"

/* The definitions that were declared in the header file */
char tempdir[FILE_PATH_SIZE+1];
char scratchfile[FILE_PATH_SIZE+1];

/* Status flag */
boolean status=FALSE;

/* The error file */
char ERROR_FILE[FILE_PATH_SIZE+1];

/* The report file - for tracing */
FILE *REPORT_FILE=NULL;

/* Global var. for EOF at strange places... */
/* Could occur in a "press any key" type prompt...*/
boolean global_eof=FALSE;

char LEAP_BASE_DIR[FILE_PATH_SIZE+1];

/* This is just a holdall for impromptu sprintf calls. */
char temp_80_chars[80];

/*
 * Commands available - THEY MUST BE IN ALPHABETICAL ORDER
 * IN ORDER TO MAKE SEARCHING MORE EFFICENT (Binary chop
 * is used) (K&R again!)
 */
struct commands_struct commands[] = {
	{"!", C_DOS},
	{"#", C_COMMENT},
	{"##", C_COMMENT},
	{"###", C_COMMENT},
	{";", C_COMMENT}, /* Not a comment, but treat it as such if the parser 
					   * should get hold of it somehow. */
	{">", C_FILE_COMMENT},
	{"?", C_HELP},
	{"@", C_SRCFILE},
	{"add", C_ADD},
	{"addresses", C_ADDRESSES},
	{"average", C_SUMMARISE_AVG},
	{"avg", C_SUMMARISE_AVG},
	{"break", C_BREAK},
	{"cache", C_CACHE},
	{"case", C_CASE},
	{"change", C_CHANGE},
	{"clear", C_CLEAR},
	{"compact", C_COMPACT},
	{"copying", C_INFO},
	{"create", C_CREATE},
	{"debug", C_DEBUG},	
	{"delete", C_DELETE},
	{"delrel", C_DELREL},
	{"describe", C_DESCRIBE},
	{"difference", C_DIFFERENCE},
	{"dir", C_DIR},
	{"display", C_PRINT},
	{"dispose", C_DISPOSE},
	{"dump", C_DUMP},
	{"duplicate", C_DUPLICATE},
	{"erase", C_DELETE},
	{"exec", C_SRCFILE},
	{"exit", C_EXIT},
	{"flush", C_FLUSH},
	{"help", C_HELP},
	{"high", C_HIGH},
	{"idxprint", C_PRINT_IDX},
	{"idxstore", C_IDX_STORE},
	{"indexes", C_DISPLAY_INDEX},
	{"indices", C_DISPLAY_INDEX},
	{"infix", C_INFIX},
	{"internal", C_INTERNAL},
	{"intersect", C_INTERSECT},
	{"ioon", C_IOON},
	{"iterative", C_ITERATIVE},
	{"join", C_JOIN},
	{"l", C_LISTSRC},
	{"list", C_DISPLAY_REL},
	{"load", C_LOAD},
	{"mem", C_MEM},
	{"minus", C_DIFFERENCE},
	{"natjoin", C_NATURAL_JOIN},
	{"normal", C_NORMAL},
	{"panic", C_PANIC},
	{"parse", C_PARSE},
	{"print", C_PRINT},
	{"product", C_PRODUCT},
	{"project", C_PROJECT},
	{"prompt", C_PROMPT},
	{"quit", C_EXIT},
	{"record", C_RECORD},
	{"relation", C_CREATE_RELATION},
	{"relations", C_DISPLAY_REL},
	{"rename", C_RENAME},
	{"report", C_REPORT},
	{"restrict", C_SELECT},
	{"reverse", C_REVERSE_ENG},
	{"rmvtmp", C_RMVTMP},
	{"select", C_SELECT},
	{"set", C_SET},
	{"smjoin", C_SMJOIN},
	{"source", C_SRCFILE},
	{"sources", C_LIST},
	{"sp_help", C_DISPLAY_REL},
	{"specidx", C_IDX},
	{"status", C_STATUS},
	{"stop", C_EXIT},
	{"sum", C_SUMMARISE_SUM},
	{"summarise", C_SUMMARISE},
	{"summarize", C_SUMMARISE},
	{"terminatenow", C_TERMINATENOW},
	{"timing", C_TIMING},
	{"union", C_UNION},
	{"update", C_UPDATE},
	{"use", C_USE},
	{"ustime", C_US},
	{"variables", C_SHOWVARS},
	{"vars", C_SHOWVARS},
	{"ver", C_VERSION},
	{"version", C_VERSION},
	{"warranty", C_WARRANTY},
	{"what", C_WHAT}
};



/* Define the size of the above structure 
 * (See K&R 2nd Ed. pg 135 
 */
#define NKEYS (sizeof commands / sizeof commands[0])


/* Define various status flags */
boolean status_trace=DEFAULT_STATUS_TRACE;
boolean status_debug=DEFAULT_STATUS_DEBUG;
int status_debuglevel=DEFAULT_STATUS_DEBUG_LEVEL;
int status_mindebuglevel=DEFAULT_STATUS_MINDEBUG_LEVEL;
boolean status_timing=DEFAULT_STATUS_TIMING;
boolean status_case=DEFAULT_STATUS_CASE;
boolean status_quiet=DEFAULT_STATUS_QUIET;
boolean status_regression=DEFAULT_STATUS_REGRESSION;
boolean status_temporary_relations=DEFAULT_STATUS_TRELS;
boolean status_timelog=DEFAULT_STATUS_TIMELOG;
boolean status_longline=DEFAULT_STATUS_LONGLINE;
boolean status_padding=DEFAULT_STATUS_PADDING;
boolean status_tempdb=DEFAULT_STATUS_TEMPDB;
boolean status_productjoin=DEFAULT_STATUS_PRODUCTJOIN;
boolean status_daemon=DEFAULT_STATUS_DAEMON;
boolean status_merge_stderr=DEFAULT_STATUS_DAEMON;

char *generate_random_string(word size,
                 char *pstring) {
/* generate_random_string
 * Generates a string of 'size' random(ish) characters
 */
    char *string;
    int counter;

    /* Allocate the memory - +1 for null. */
#ifdef FULL_DEBUG
    leap_fprintf(stderr,"size: %d\n",size);
#endif
    if (pstring==NULL) {
        /* If the string is not allocated, allocate some
         * memory! */
        string=malloc(size+1);
    } else {
        string=pstring;
    }


    for(counter=0;( (unsigned int) counter)<size;counter++) {
        string[counter]=( (rand() % 26)+97);
#ifdef FULL_DEBUG
    leap_fprintf(stderr,"counter: %d\nstring: %s\n",counter,string);
#endif
    }

    string[size]='\0';

    /* Add an 'zz' at the beginning to make it a little
     * easier to find the LEAP temporary files...
     */
    string[0]='z';
    string[1]='z';

    return(string);
}

void util_close(void) {
/* util_close
 * Close files and stuff
 */
	int error;
	time_t tp;

	if (ACTIVITY_FILE!=NULL) {
		tp=time(NULL);
		fprintf(ACTIVITY_FILE,"###\n# Activity file STOPPED at: %s###\n",ctime(&tp));

		error=fclose(ACTIVITY_FILE);
		if (error==EOF) {
			raise_error(ERROR_CLOSING_FILE,NONFATAL,"Activity File");
		}
	}
}


void build_base_dir( char *directory ) {
/* build_base_dir
 * Build the global variables for the base directory. Putting this
 * in a routine makes sense, so we can try various options if a 
 * file is not found.
 */

	/* Do the initial configuration that is necessary */
	strncpy(LEAP_BASE_DIR,directory,FILE_PATH_SIZE);

	/* Check that the directory specified contains a seperator
	 * at the end, because we mindlessly append directories to
	 * this, and that causes icky problems...
	 */
	if (LEAP_BASE_DIR[strlen(LEAP_BASE_DIR)-1]!=DIR_SEPERATOR) {
		strcat(LEAP_BASE_DIR,DIR_SEPERATOR_STRING);
	}

	/* Build the error file file name */
	sprintf(ERROR_FILE,"%s%s%s",LEAP_BASE_DIR,LEAP_ERROR_DIR,LEAP_ERROR_FILE);

}

void util_init() {
/* util_init
 * This routine sets up any global variables for the utilities
 * unit, for example, the full path to the errors file, to
 * save work later.
 */
	char fname[FILE_PATH_SIZE+1];
	char randomstr[7];

	/* Setup temporary directory - it may be specified on the command line (-x) */
	if (strlen(tempdir)==0) {
		strcpy(tempdir,LEAP_TEMP_DIR);
	}

	if (tempdir[strlen(tempdir)-1]!=DIR_SEPERATOR) {
		strcat(tempdir,DIR_SEPERATOR_STRING);
	}

	sprintf(scratchfile,"%s%s",tempdir,generate_random_string(6,randomstr));

	do_debug(DEBUG_INFO,"Temporary directory: [%s]\nScratch file: [%s]\n",tempdir,scratchfile);

	do_debug(DEBUG_INFO,"Error file: %s\t Report file: %s\n",ERROR_FILE,fname);
}

void check_assign(void *ptr, char *string) {
/* check_assign
 * Checks whether a pointer is assigned or not. If not, then
 * memory has probably run out.
 */
	if (ptr==NULL) {
		raise_error(ERROR_INSUFFICENT_MEMORY,FATAL,string);
	} 
}


void do_trace(char *trace_string) {
/* do_trace
 * Reports something to the screen, and if set, to the report
 * file as well 
 */
	if ((status_trace) || (status_debuglevel>=DEBUG_ENTER)) raise_event(MESSAGE,trace_string);
}


char *cut_to_right_bracket( char *s,
			   int bdepth, int force,
			   char *result) {
/* cut_to_right_bracket
 * This should return a ptr to an expression contained
 * within the brackets of depth bdepth in *result and as result
 * new for new parser.
 * if force==TRUE then force brackets onto the string, else
 * don't - and return nothing if no brackets exist.
 */

	struct bracket {
		int pos,depth;
	} bracket[MAXIMUM_BRACKETS];

	boolean bexist=FALSE;
	int start,finish,counter,c,no,depth;
	char rstr[MAXIMUM_EXPRESSION+1],tstr[MAXIMUM_EXPRESSION+1];
	char *tstrp,*endstart,*beginning,*ostart;
	char removed[MAXIMUM_EXPRESSION+1];
	int slen=0;

	/* Check that the parameters are ok */
	if ( (s==NULL) || (strlen(s)==0) ) {
		strcpy(result,"");
		return(result);
	}

	slen=strlen(s);

	/* Reset the data */		
	for (counter=0;counter<MAXIMUM_BRACKETS;counter++) bracket[counter].pos=bracket[counter].depth=0;
	
	bracket[0].pos=0;
	bracket[0].depth=1;
	bracket[1].pos=slen+1;
	bracket[1].depth=1;
	c=0;
	depth=1;
	no=0;

	while ( ((unsigned int) c) <=slen) {
		if (s[c]=='(') {
			bracket[no].pos=c;
			bracket[no].depth=depth;

			/* Increment the bracket 'depth' counter */
			depth++;

			/* Set the brackets-exist flag */
			bexist=TRUE;
			no++;
		}

		if (s[c]==')') {

			depth--;

			bracket[no].pos=c;
			bracket[no].depth=depth;
			no++;
		}

		c++;
	}

	/* If the specified depth is greater than the maximum depth
	 * of the brackets in the given string, then set the parameter
	 * to the maximum depth possible 
	 */
	if (bdepth>depth) {
		bdepth=depth;
	}

	/* If no brackets occur in the string */
	/* ...and they can be forced on */
	if ( (!bexist) && (force==TRUE) ) {
		/* Add 'em */
		/* sprintf(tstr,"(%s)",s); */

		/* Test... */
		strcpy(result,s);
		strcpy(s,"");

		/* And copy it back */
		/* strcpy(s,tstr); */

		return(result);

	/* If no brackets occur in the string */
	/* ...but they cannot be forced on */
	} else if ( (!bexist) && (force==FALSE) ) {

		/* Return an empty string */
		strcpy(result,"\0");
		return(result);

	/* ELSE  brackets occur in the string, so
	 * carry on - brackets are there 
   	 */
	} else {
		strncpy(tstr,s,MAXIMUM_EXPRESSION);
	}

	ostart=tstr;

	strncpy(rstr,"",MAXIMUM_EXPRESSION);

	c=0;
	while (bracket[c].depth<bdepth) c++;
	
	start=c;

	c++;

	while (bracket[c].depth>bdepth) c++;
	finish=c;


	/* tstr still contains s - so it can be played with */	
	tstr[(bracket[finish].pos)]='\0';
	tstrp=&(tstr[(bracket[start].pos)+1]);
	strcpy(result,tstrp);

	/* This puts a null at bracket point */
	tstrp=&tstr[(bracket[start].pos)];
	*tstrp='\0';

	/* This gets the start of the string after the bracket */
	endstart=&tstr[(bracket[finish].pos)+1];
	beginning=ostart;

	/* And this puts the beginning and end bits together, to
	 * give a string without the bracketed bit
	 */
	sprintf(removed,"%s%s",beginning,endstart);

	/* Bugger... */
	if (strlen(removed)==0) {
		*s=(int) NULL;
	} else {
		strncpy(s,removed,MAXIMUM_EXPRESSION);
	}
#ifdef DEBUG
	leap_fprintf(stderr,"Retrieved: >%s<\n",result);
#endif	
	return(result);	
}

char *get_token(char *string,
	        char seperator,
		char *result) {
/* get_token
 * Gets the first token from the string, and returns
 * it (in result, and as a result)
 */
	char seperator_list[MAX_SEPERATORS];
	char *rptr;

	/* If sepereator is NULL, then use the normal seperators */
	if (seperator=='\0') {
		strcpy(seperator_list,TOKEN_SEPERATORS);
	} else {
		/* Otherwise, use the character specified */
		sprintf(seperator_list,"%c",seperator);
	}
	/* Copy the original string into "result" */
	strcpy(result,string);

	/* Locate the first symbol - strtok puts a null at
 	 * the location of the seperator, so we can then return
	 * the string, with the first item located. strtok returns
	 * NULL if no seperator is found. 
	 */
	rptr=strtok(result,seperator_list);

	/* If there is no token, then make the string empty */
	/* (Can't return NULL, incase used in strcpy) */
	if (rptr==NULL) result[0]='\0';

	/* Return the result */
	return(result);

}	

char *allbut(char *string, char *chars) {
/* allbut
 * return string with contents of *string without
 * any chars specified in *chars
 */
	char *newstring;
	int ilength,length,count,icount,ocount;
	boolean found=FALSE;


	length=strlen(string);
	ilength=strlen(chars);

	newstring=malloc(length+1);

	ocount=0;

	/* Loop through the original string */
	for (count=0; count<length; count++) {
		
		/* Nothing found yet */
		found=FALSE;

		/* Loop throught the tokens, whilst nothing found... */
		for (icount=0; ((found==FALSE)&&(icount<ilength)); icount++) {

			/* If something is found */
			if (string[count]==chars[icount]) {
				found=TRUE;	
			}

		}

		/* Nothing was found... */
		if (found==FALSE) {
			newstring[ocount]=string[count];
			ocount++;
		}
	}	

	newstring[ocount]='\0';
	return(newstring);
}

char *cut_token(char *string,
	        char seperator,
   		    char *result) {
/* cut_token
 * Cuts the first token from the string, and returns
 * it (in result, and as a result)
 */
	char seperator_list[MAX_SEPERATORS];
	char *rptr;
	size_t length;

	/* If seperator is NULL, then use the normal seperators */
	if (seperator=='\0') {
		strcpy(seperator_list,TOKEN_SEPERATORS);
	} else {
		/* Otherwise, use the character specified */
		sprintf(seperator_list,"%c",seperator);
	}

	/* Copy the original string into "result" */
	strcpy(result,string);
	
	/* Locate the first symbol - strtok puts a null at
 	 * the location of the seperator, so we can then return
	 * the string, with the first item located. strtok returns
	 * NULL if no seperator is found. 
	 */
	rptr=strtok(result,seperator_list);

	/* If there is no token, then make the string empty */
	/* (Can't return NULL, incase used in strcpy) */
	if (rptr==NULL) {
		result[0]='\0';
	} else {
		/* Get the length of the first token, to 
		 * indicate where the seperator may be found 
		 */
		length=strlen(rptr);

		/* Copy into the source string the remainder,
		 * so that the string is "cut" out
		 */

		/* BUT first, skip over any spaces... They're never
		 * going to be any use */
		while ((string[length]==' ') || (string[length]==',')){
			length++;
		}

		strcpy(string,&string[length]);
	}

	/* Return the result */
	return(result);

}	

char *skip_to_alnum(char *string ) {

	int pos=0;

	while ((string[pos]!='\0')&&(!isalnum(string[pos]))) pos++;

	return(&string[pos]);

	
}

int binsearch(char *word, struct commands_struct commands[], int n) {
	int cond;
	int low, high, mid;

	low=0;
	high=n-1;

	while (low <= high) {
		mid=(low+high)/2;
		if ( (cond = strcmp(word, commands[mid].text)) < 0)
			high=mid-1;
		else if (cond>0)
			low=mid+1;
		else
			return mid;
	}
	return -1;
}

int get_command(char *word) {
	int result;
	int counter=0;

	/* skip over spaces */
	while (word[counter]==' ') counter++;
	word=&word[counter];

	result=binsearch(word,commands,NKEYS);

	if (result!=C_UNKNOWN) {
		return( commands[result].command );
	} else {
		return( C_UNKNOWN );
	}
}

void assign_input_stream( char *filen ) {
/* assign_input_stream
 * This is used to assign the input stream (input_stream)
 * declared as extern in dtypes, and in the main module, to
 * the specified file. If empty, then the file is reset to stdin.
 */
	char temp[100];
	FILE *ninputstream;
	
	do_debug(DEBUG_ENTER,"ENTERing assign_input_stream [%s].\n",filen);

	if (strlen(filen)==0) {
		do_trace("Setting source file to stdin.");
		if (input_stream!=NULL) fclose(input_stream);
		input_stream=stdin;
	} else {
		sprintf(temp,"Setting source file to %s.",filen);
		do_trace(temp);
		ninputstream=fopen(filen,"r");
		if (ninputstream==NULL) {
			input_stream=stdin;
			raise_error(ERROR_FILE_OPENING,NONFATAL,filen);
			leap_printf("Resetting input file\n");
		} else {
			input_stream=ninputstream;
		}
	}

	do_debug(DEBUG_ENTER,"EXITing assign_input_stream.\n");
}

char *find_start_of_data( char *string) {
/* Scans over a string, and locates the first text. */
	char *sptr;

	/* Start at the start! */
	sptr=string;

	/* Whilst the string is not null, and
	 * not equal to a character 
	 */
	while ( (*sptr!='\0') &&
		(    ( (*sptr<'A') || (*sptr>'Z') )
                  && ( (*sptr<'a') || (*sptr>'z') )
		) ) {

		/* Skip over to the next pointer */
		sptr++;

	}

	/* If we have found the end of the string... */
	if (*sptr=='\0') {
		
		/* Return a NULL string */
		return(NULL);
	} else {

		/* Otherwise, return the start of the data */
		return(sptr);
	}

}

void list_source_code( ) {
/* list_source_code
 * Prints out the source files in a database.
 */

	struct dirent *d;
	DIR *directory;
	char dirpath[FILE_PATH_SIZE+1],filename[FILE_PATH_SIZE+1],*cptr;

	/* Build the directory path */
	sprintf(dirpath,"%s%s",database_dir(current_db),LEAP_SOURCE_DIR);

	/* Open the directory */
	directory=opendir(dirpath);

	/* Read the first entry */
	d=readdir(directory);

	leap_printf("Source files:\n");
	leap_printf("-------------\n");

	/* Whilst there are directory entries to process */	
	while (d!=NULL) {

		/* Check if the entry contains the extension signifying
		 * it's a source file 
		 */

#ifdef __MSDOS__
		/* MSDOS returns the filename in uppercase, so
		 * we should convert it to lower case. The alternative
		 * is to do the comparison in a compiler directive, to
		 * save pre-converting items that don't contain the
		 * string.
		 */
	downcase(d->d_name);
#endif
		if (strstr(d->d_name,LEAP_SOURCE_EXT)!=NULL) {
			/* Make a copy of the filename, so we don't
			 * risk messing up things out of our control
			 */
			strcpy(filename,d->d_name);

			/* Terminate where the extension starts */
			cptr=strstr(filename,LEAP_SOURCE_EXT);
			*cptr='\0';

			leap_printf("%s\n",filename);
		}

		/* Read the next directory entry */
		d=readdir(directory);
	}

	closedir(directory);

}

void print_source_code( char *file_name ) {
/* print_source_code
 * Prints the specified source file name. Assumes the .src extension
 * is not specified.
 */
	char dirpath[FILE_PATH_SIZE+1],source_line[MAXIMUM_EXPRESSION];
	char *read_status;
	FILE *fptr;
	int x;

	/* Build the directory path */
	sprintf(dirpath,"%s%s%s%s",database_dir(current_db),LEAP_SOURCE_DIR,file_name,LEAP_SOURCE_EXT);

	/* Open the file */
	fptr=fopen(dirpath,"r");

	/* If the file could not be found */
	if (fptr==NULL) {
			/* Report an error */
			raise_error(ERROR_FILE_OPENING,NONFATAL,file_name);
	} else {

		/* No error occured - so read the first line */
		read_status=fgets(source_line,MAXIMUM_EXPRESSION,fptr);

		if (read_status!=NULL) {
			leap_fprintf(stdout,"Source File: %s\n",file_name);
			leap_fprintf(stdout,"-------------");
			for (x=0; x<strlen(file_name); x++) leap_fprintf(stdout,"-"); 		
			leap_fprintf(stdout,"\n");
		}

		/* Whilst no error occurs reading from the file */
		while( read_status!=NULL ) {

			/* Display the current line */
			/* Includes cr, so skip that and use write */
			leap_fprintf(stdout,source_line);

			/* Get the next line */
			read_status=fgets(source_line,MAXIMUM_EXPRESSION,fptr);
		}
		leap_printf("<EOF>\n");

		fclose(fptr);
	}
}


void reverse_source_code() {
/* reverse_source_code
 * Reverse out the source code in a directory.
 */
    char dirpath[FILE_PATH_SIZE+1],source_line[MAXIMUM_EXPRESSION];
    char *read_status, ofname[FILE_PATH_SIZE+1], filename[FILE_PATH_SIZE+1],*cptr;
    FILE *fptr;
	DIR *directory;
	struct dirent *d;

    /* Build the directory path */
    sprintf(dirpath,"%s%s",database_dir(current_db),LEAP_SOURCE_DIR);
	
    /* Open the directory */
    directory=opendir(dirpath);

    /* Read the first entry */
    d=readdir(directory);

    /* Whilst there are directory entries to process */
    while (d!=NULL) {

        /* Check if the entry contains the extension signifying
         * it's a source file 
         */

#ifdef __MSDOS__
        /* MSDOS returns the filename in uppercase, so
         * we should convert it to lower case. The alternative
         * is to do the comparison in a compiler directive, to
         * save pre-converting items that don't contain the
         * string.
         */
    downcase(d->d_name);
#endif
        if (strstr(d->d_name,LEAP_SOURCE_EXT)!=NULL) {
            /* Make a copy of the filename, so we don't
             * risk messing up things out of our control
             */
            strcpy(filename,d->d_name);

			sprintf(ofname,"%s%s",dirpath,filename);

            /* Terminate where the extension starts */
            cptr=strstr(filename,LEAP_SOURCE_EXT);
            *cptr='\0';

            leap_printf("record %s\n",filename);

    		/* Open the file */
   			fptr=fopen(ofname,"r");
            /* If the file could not be found */
            if (fptr==NULL) {
                /* Report an error */
                raise_error(ERROR_FILE_OPENING,NONFATAL,filename);
            } else {

                /* No error occured - so read the first line */
                read_status=fgets(source_line,MAXIMUM_EXPRESSION,fptr);

                /* Whilst no error occurs reading from the file */
                while( read_status!=NULL ) {

                    /* Display the current line */
                    /* Includes cr, so skip that and use write */
                    leap_printf(source_line);

                    /* Get the next line */
                    read_status=fgets(source_line,MAXIMUM_EXPRESSION,fptr);
                }

                leap_printf(".\n");

                fclose(fptr);

            }

		}

        /* Read the next directory entry */
        d=readdir(directory);
    }

    closedir(directory);

}
			
void upcase(char *string) {
/* upcase
 * Converts the specified string to upper case
 */

	int counter;
	char *s;

	for (counter=0; counter<strlen(string); counter++) {

		s=&string[counter];

		if ( (*s>='a') && (*s<='z') ) {
			*s-=32;
		}
	}
}

void downcase(char *string) {
/* down
 * Converts the specified string to lower case
 */

	int counter;
	char *s;

	for (counter=0; counter<strlen(string); counter++) {

		s=&string[counter];

		if ( (*s>='A') && (*s<='Z') ) {
			*s+=32;
		}
	}
}

void print_helppage( char *page ) {
/* print_helppage
 * Prints the help page specified, or the introduction/summary
 * page in the absence of a value
 */
	FILE *helpfile;
	char helpfile_name[FILE_PATH_SIZE+1],line[HELP_PAGE_LINE_SIZE+1],*lptr,*sptr;
	int counter,tabcounter=0,introcount=0;
	boolean finished=FALSE,printing=FALSE,summary=FALSE,intro=FALSE;

	leap_fprintf(stdout,"Help page: %s\n",page);

	/* Build the help file name */
	sprintf(helpfile_name,"%s%s%s",LEAP_BASE_DIR,LEAP_HELP_DIR,LEAP_HELP_FILE);

	/* Get a file ptr */
	helpfile=fopen(helpfile_name,"r");

	if (helpfile!=NULL) {
		/* File is ok */

		upcase(page);

		/* Is the user after a summary? */
		summary=(strcmp(page,HELP_SUMMARY)==0);

		/* If no help segment was specified */
		if (strlen(page)==0) {
			printing=TRUE;
			intro=TRUE;
		}

		do {
			/* Read the line in */
			lptr=fgets( line, HELP_PAGE_LINE_SIZE, helpfile );

			/* Whilst the page has not been located, and a line has been read */

			/* Change the newline to a null */
			line[strlen(line)-1]='\0';

			if (line[0]==HELP_PAGE_COMMENT) {

			} else if (line[0]==HELP_PAGE_START) {

				/* Get the length of the line */
				counter=strlen(line)-1;

				/* Scan back to the first non-space */
				while (line[counter]==' ') counter--;

				/* Mark the end of the DATA */
				line[counter+1]='\0';

				/* Get the start of the actual header... */
				sptr=&line[2];

				/* If this is the help page we are after */
				if (strcmp(sptr,page)==0) {
					/* Enable printing */

					/* because of nested if, this will
					 * take effect next iteration 
					 */

					printing=TRUE;
				} else if (summary==TRUE) {
					leap_fprintf(stdout,"%s",sptr);
					for (counter=strlen(sptr);counter<20;counter++) leap_fprintf(stdout," ");
					tabcounter++;
					if (tabcounter>=HELP_ITEMS) {
						tabcounter=0;
						leap_fprintf(stdout,"\n");
					}

					fflush(stdout);
				}
			} else if ( (printing==TRUE) && (line[0]==HELP_PAGE_END) ) { 

				/* If printing true to prevent reading of
				 * page seperators in first block 
				 */
				/* If this is the end of the help page
				 * Disable printing, and ensure the
				 * loop terminates neatly 
				 */

				if ( (intro==TRUE) && (introcount<3) ) {
					introcount++;
				} else {
					printing=FALSE;
					finished=TRUE;
				}

			} else if (printing==TRUE) {

				/* If printing is on, then print the current
				 * line
				 */
				leap_printf("%s\n",line);
			} 


		} while ( (finished!=TRUE) && (lptr!=NULL) );

		leap_printf("\n");

		/* Close the file */	
		fclose(helpfile);	

	} else {
		/* No file ptr */

		raise_error(ERROR_FILE_OPENING,NONFATAL,helpfile_name);

	}
}

int set_status( char *status_option, char *value ) {
/* set_status
 * Sets status flags on or off
 */
	char *rval;

		rval=set_variable(status_option,value);

		/* Success? */
		if (rval==NULL) {
			/* Create a new variable - error code/message
			 * if cannot be done 
			 */
			return(new_variable(status_option,value));
		} else {
			/* Success! */
			return(RETURN_SUCCESS);
		}
}

/* Macro to do all the hard work.
 * All this does is evaluate value. If true, then prints "on",
 * otherwise, prints off. Take a look at show_status calls
 * to the macro, and all should be clear...
 */

/* For util.c use only... */
static char backup_prompt[MAXIMUM_PROMPT+1];
static boolean prompt_change;

void set_prompt(char *prompt) {
/* set_prompt
 * Sets the prompt to that specified
 */
	strcpy(backup_prompt,current_prompt);

	strcpy(current_prompt,prompt);

	prompt_change=TRUE;
}

void unset_prompt() {
/* unset_prompt
 * Sets the prompt back to the default
 */
	if (prompt_change==TRUE) {
		strcpy(current_prompt,backup_prompt);
		prompt_change=FALSE;
	} 
}

char *copy_to_token( char *source,
					 char *delimiter,
					 char *destination ) {
/* copy_to_token
 * Tokenises source using strtok, and copies to destination
 */
	char *sptr;

	sptr=strtok(source,delimiter);
	if (sptr!=NULL) {
		strcpy(destination,sptr);
		return(destination);
	} else {
		return(NULL);
	}
}

void strip_leading_spaces(char *source) {
/* strip_leading_spaces
 * Strips the leading spaces from source
 */

	char *sptr;
	char temp[MAXIMUM_EXPRESSION+1];
	int counter=0;

	sptr=&source[counter];
	while (*sptr==' ') {
		counter++;
		sptr=&source[counter];
	}

	/* TODO - There is no doubt a better, more efficent way 
     * of doing this.
	 */
	strcpy(temp,sptr);
	strcpy(source,temp);

}

void strip_trailing_spaces(char *string) {
/* strip_trailing_spaces
 * strips out trailing spaces in string
 */
	char *sptr;

	sptr=&string[strlen(string)-1];

	/* Identify any white space at the end of the string.
	 * This has caused problems with Unix scripts not understanding
	 * non-parameterised operators (eg. "change <rel>")	
	 */
	while ( (*sptr==' ') || (*sptr=='\n') || (*sptr=='\r') ){ 
		sptr--;
	}
	*(sptr+1)='\0';
}

void start_record(char *fname) {
	char dirpath[FILE_PATH_SIZE+1];

    /* Build the directory path */
    sprintf(dirpath,"%s%s%s%s",database_dir(current_db),LEAP_SOURCE_DIR,fname,LEAP_SOURCE_EXT);

	vprocess_query(current_db,"add (leapscripts) (%s,%s)",fname,dirpath);

    /* Open the file */
    recordingfptr=fopen(dirpath,"w");

	recording=TRUE;

}

void stop_record() {

	fclose(recordingfptr);

	recording=FALSE;
}

void dumprelstruct(relation rel) {

	if (rel!=NULL) {
		leap_printf("Name:\t[%s]\n",rel->name);
		leap_printf("noattr:\t[%d]\n",rel->noattributes);
		leap_printf("curpos:\t[%d]\n",rel->current_pos);
		leap_printf("temp:\t[%d]\n",rel->temporary);
		leap_printf("syst:\t[%d]\n",rel->system);
		leap_printf("update:\t[%d]\n",rel->updated);
		leap_printf("hash:\t[%p]\n",rel->hash_table);
		leap_printf("fpath:\t[%s]\n",rel->filepath);
		leap_printf("fname:\t[%s]\n",rel->filename);
		leap_printf("next:\t[%p]\n",rel->next);
		leap_printf("prev:\t[%p]\n",rel->previous);
	} else {
		leap_printf("NULL ptr passed to dumprelstruct\n");
	}
	
}

void util_internal( char *desc ) {
/* util_internal
 * Display internal LEAP structures - this will be expanded to provide a 
 * mechanism to probe internal structures in due course. 
 *
 */

	relation rel;

	if (desc!=NULL) {
		if (strcmp(desc,"listrel")==0) {
			leap_printf("Internal db rel. structure\n");
			relation_display(current_db);
		} else  {
			rel=relation_find(current_db,desc);

			dumprelstruct(rel);
		}
	}

}