/*
 * 	go: a program to change current disk/drive and directory
 *		V1.30 9 June 86
 *
 *	by (c)1986 Chris DeVoney (Que Corp) Tim Leslie (Ecosoft, Inc.)
 *		   Alan Stegamoller (Carmel Valley Associates)
 */


/*
 *	some includes
 */

#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <fcntl.h>

/*
 * some defines
 * GOFILE for the default data file name
 * EOS for end-of-string
 */

#define	GOFILE	"go.dat"
#define EOS	'\0'

static	char	copyright[] =
 "Copyright (c) 1986 Rights reserved\nDeVoney, Leslie, Stegamoller";


/*
 * some function declarations
 */

int	find(char *where, char *file, char *env);
int	locate(char *where, char *file, char *p);
void	seldsk(int drive);
int	toupper(int);
int	uppercmp(char *a, char *b);
void 	writestr(int fd, char *s);


int main(int argc, char **argv)
{

	int 	i,		/* gp counter */
		c,		/* input character for MOREing */
		flag;		/* gp flag */
	char	*sp;		/* string pointer */
	FILE	*gfp;		/* go file pointer */
	char	buff[200], 	/* input file line buffer */
		path[200];	/* buffer for original path name */

	/*
	 * The first proceedure is to find the data file.
	 * If GO is in the environment, stuff the associated information
	 * into buff.
	 * Else use find() to search for the GOFILE down the PATH
	 * environment variable. Once you have it, open it. 
	 * Else squawk and quit.
	 */
	
	flag = 1;
	if ((sp = getenv("GO")) != (char *)NULL) 
		strcpy(buff, sp);
	else if (find(buff, GOFILE, "PATH")) 
		flag = 0;
	if (!flag || (gfp = fopen(buff, "r")) == (FILE *)NULL) {
		writestr(1, "go: cannot find ");
		writestr(1, GOFILE);
		writestr(1, "\n");
		exit(1);
	}

	/*
	 * No arguments? Produce a paginated listing of go.dat
	 * and quit.
	 */

	if (argc != 2) {
		writestr(1, "Usage:\r\n\n");
		i = 0;
		while (fgets(buff, sizeof(buff), gfp) != (FILE *)NULL) {
			if ((sp = strtok(buff, "\t, \n")) == (char *)NULL)
				break;
			writestr(1, sp);
			writestr(1, "\t");
			writestr(1, strtok((char *)NULL, "\t :,"));
			writestr(1, ":");
			writestr(1, strtok((char *)NULL, "\t :,\n"));
			writestr(1, "\r\n");
			if (++i > 22) {
				writestr(1, "-- More --");
				if ((c = getch()) == 0x1b || c == 0x03)
					exit(1);
				i = 0;
				writestr(1, "\r\n");
			}
		}
		exit(1);
	}

	/* 
	 * Start reading the data file, convert the keyword and argv[1]
	 * to uppercase, and compare. If a match, change disk drives
	 * and change to that directory. Then quit. If a problem, report
	 * it.
	 */

	while (fgets(buff, sizeof(buff), gfp) != (char *)NULL) {
		strcpy(path, strtok(buff, "\t, \n"));
		if (uppercmp(path, argv[1]) == (char *)NULL) {
			seldsk(*strtok((char *)0, "\t,: "));
			if (chdir((sp = strtok((char *)0, "\t:,\n "))) == EOF) {
				writestr(1, "go: cannot change to ");
				writestr(1, path);
				writestr(1, "!\r\n");
				exit(1);
			}
			exit(0);
		}
	}

	/*
	 * If here, no match. Try to change to that disk drive (if given)  
	 * and directory. If successful, quit.
	 */

	flag = 0;
	if ((sp = strchr(argv[1], ':')) != (char *)NULL) {
		seldsk(*argv[1]);
		flag = 1;
	}
	if (chdir(flag ? sp + 1 : argv[1]) != EOF) 
		exit(0);

	/*
	 * if here, squawk and quit
	 */
	writestr(1, "go: where is ");
	writestr(1, argv[1]);
	writestr(1, "?\r\n");
	exit(1);
}

/*
 * find a file
 *
 * where is the string to receive the full path name of the file
 * file holds the name of the file to search for
 * env is the environmental variable naming the directories to search
 *
 * returns 0 if the file is found, else nozero
 */

int	find(char *where, char *file, char *env)
{

	char	*p;

	if (p = getenv(env)) {
		if (locate(where, file, p)) {
			free(p);
			return(0);
		}
		where[0] = 1;
		free(p);
		return(1);
	}
	where[0] = EOS;
	return(1);
}

/*
 * locate a file
 *
 * where is string to hold full path name of the found file
 * file is the name of the file to search for
 * p is a list of paths to search
 *
 * returns zero if a file was found with the full path name in where
 * returns nonzero if the file was not found
 */



int	locate(char *where, char *file, char *p)
{

	int	i, j;

	/* Strip off the path names from p into where one at a time.
	 * Add the file name to where and attempt to open the
	 * file. If successful, close it and return nonzero leaving
	 * the full path name (drive/directory/file) in where.
	 * If unsuccessful, keep on going. If none work, return
	 * zero and place null in where.
	 */

	for (i = 0; p[i]; ) {

		for (j = 0; p[i] && p[i] != ';'; i++, j++) {
			where[j] = p[i];
		}
		if (p[i])
			i++;
		where[j] = EOS;
		if (where[0] && where[strlen(where) - 1] != '\\')
			strcat(where, "\\");
		strcat(where, file);
		if ((j = open(where, O_RDONLY)) != -1) {
			close(j);
			return(1);
		} else if (p[i] == EOS)	{
			break;
		}
	}
	where[0] = EOS;
	return(0);
}

/*
 * change the current disk drive
 * drive is the letter of the new current disk drive
 */

void	seldsk(int drive)
{
	union	 REGS	regs;

	regs.x.ax = 0x0e00;
	regs.x.dx = toupper(drive) - 'A';
	intdos(&regs, &regs);
}

/*
 * convert two strings to uppercase and compare
 *
 * returns 0 if strings match, >0 if a is lexically greater than b,
 * <0 if a is lexically less than b.
 */

int	uppercmp(char *a, char *b)
{

	while (*a && *b && toupper(*a) == toupper(*b)) {
		a++;
		b++;
	}
	return(*a - *b);
}

/*
 * write a string to a file descriptor
 *
 * fd is the file descriptor
 * s is the string to write
 *
 */

void writestr(int fd, char *s)
{

	write(fd, s, strlen(s));

}

