/*
 *			    ---   SHAR_CMD.C   ---
 *			---   Command Portion of   ---
 *			     ---   UNSHAR.C   ---
 *
 *		  ---   Copywrite 24 September, 1986   ---
 *		      ---    John Birchfield      ---
 *		      ---    411 Crane Ave.       ---
 *		      ---    Turlock, CA  95380   ---
 *		      ---      (209) 634-6243     ---
 *
 *	Program to decode files created by the shell archive { shar }
 *	utility on Un*x machines or pc's.  Current capabilities include
 *
 *	1.	Able to unshar into a user specified directory or subdirectory
 *		as specified by a command line option { -Ddirectory_name }
 *
 *	2.	Recognizes the following commands
 *			cat, sed, uudecode, mkdir, chdir, 
 *			{test -f, test -d, test <number -ne wc ... }
 *
 *	3.	Can handle shar scripts created with the
 *			Options { -a -v -p -b -c -d }
 *
 *	4.	Successfully traverses directories and sub-directories
 *		creating the necessary subdirectories as necessary.
 *
 *	CAVEATS:
 *		Word Counting between Un*x machines and pc's just isn't
 *		gonna work out too well.  The \r\n - \n thing is not
 *		an easy thing to work around - and to be quite frank
 *		I just ain't up to it.
 *
 *	Program written for the Desmet (C-Ware) C Compiler Version 2.61
 *	not all of the routines in DOS_C.C and DOS_A.A are used by this
 *	program.  The program consists of the following modules:
 *		UNSHAR.C
 *		SHAR_CMD.C
 *		DOS_C.C
 *		DOS_A.A
 *
 *	This program is hereby placed in the public domain for 
 *	non-commersial use.
 *
 */

# include <stdio.h>
# include "unshar.h"
# include "dos.h"




/*
 *	P_ERROR ()	-	p_error () is a simple routine for
 *		displaying a message consisting of a sequence of
 *		strings thru stdout.  The first parameter is an
 *		integer { if non-zero an exit occurs } the last
 *		parameter is an empty string.
 */
 
void p_error (no, args)
int no;
char *args;
{
	char **tmp;
	tmp = &args;
	while (**tmp)
		fputs (*tmp++, stdout);
	if (no)
		_exit (no);
}




/*
 *	DO_CD ()	-	change directories
 */
 
int do_cd (fp)
FILE *fp;
{
	char buf [20];
	get_tok (buf, fp);
	return (chdir (buf));
}




/*
 *	DO_MKDIR ()	-	make a directory
 */
 
int do_mkdir (fp)
FILE *fp;
{
	char buf [20];
	get_tok (buf, fp);
	return (mkdir (buf));
}




/*
 *	DO_EXIT ()	-	Bye Bye
 */
 
void do_exit (fp)
FILE *fp;
{
	char buf [20];
	get_tok (buf, fp);
	exit (atoi (buf));
}




/*
 *	DO_CAT ()	-	get the destination filename, get the
 *		terminating string open the file and pass the contents
 *		of the input stream to it until the termination flag
 *		is recognized.
 */

void do_cat (fp)
FILE *fp;
{
	FILE *f_out;
	char delimiter [80], fname [65], *dp,
	     buf [256],
	     tmp [15];
	int len;
	
	fname [0] = delimiter [0] = '\0';
	do {
		get_tok (tmp, fp);
		if (strncmp (tmp, "<<", 2)==0)
			get_tok (delimiter, fp);
		else if (strncmp (tmp, ">", 1)==0)
			get_tok (fname, fp);
	} while (fname [0]=='\0' || delimiter [0]=='\0');

	fix_file_name (fname);
	if ((f_out=creat (fname))==-1)
		p_error (1, "Cat Can't Create '", fname, "'\n", "");
	dp = &delimiter [1];
	len = strlen (dp);
	while (fgets (buf, 256, fp)) {
		if (strncmp (dp, buf, len)==0)
			break;
		fputs (buf, f_out);
	}
	fclose (f_out);
}




/*
 *	DO_SED ()	-	do_sed () works just like do_cat ()
 *		except that the command line has the sed s/ //
 *		translation parameter in it { one more token }
 *		Command lines are of the form
 *			sed 's/^	X//' << \SHAR_EOF > '.cshrc'
 */
 
void do_sed (fp)
FILE *fp;
{
	FILE *f_out;
	char  replace [40], delimiter [80], fname [65], *dp, *rp,
	      buf [256], *nbuf,
	      tmp [15];
	int len, rlen;

	get_tok (replace, fp);
	fname [0] = delimiter [0] = '\0';
	do {
		get_tok (tmp, fp);
		if (strncmp (tmp, "<<", 2)==0)
			get_tok (delimiter, fp);
		else if (strncmp (tmp, ">", 1)==0)
			get_tok (fname, fp);
	} while (fname [0]=='\0' || delimiter [0]=='\0');

	fix_file_name (fname);
	rp = &replace [3];
	if (index (rp, '/'))
		*index (rp, '/') = '\0';
	rlen = strlen (rp);
	nbuf = &buf [0] + rlen;
	if ((f_out=creat (fname))==-1)
		p_error (1, "Cat Can't Create '", fname, "'\n", "");
	dp = &delimiter [1];
	len = strlen (dp);
	while (fgets (buf, 256, fp)) {
		if (strncmp (dp, buf, len)==0)
			break;
		if (strncmp (rp, buf, rlen)==0)
			fputs (nbuf, f_out);
		else	
			fputs (buf, f_out);
	}
	fclose (f_out);
	;
}



/*
 *	DO_UUDECODE ()	-	do_uudecode () handles uudecoding
 *		chores in the same way that do_cat () and do_sed ()
 *		process their input.  There are differnces in that
 *		the uuencoded stream has it's filename as the
 *		beginning of the file and not imbedded in
 *		the uudecode command line.
 */

void do_uudecode (fp)
FILE *fp;
{
	FILE *f_out;
	char delimiter [80], fname [65], *dp,
	     buf [256];
	int len;
	
	do {
		get_tok (delimiter, fp);
	} while (strncmp (delimiter, "<<", 2));

	get_tok (delimiter, fp);
	dp = &delimiter [1];
	len = strlen (dp);
	fname [0] = '\0';
	do {
		if (get_tok (fname, fp)==0)
			p_error (2, "Uudecode: No begin statement\n", "");
	} while (strcmp (fname, "begin"));
	get_tok (fname, fp);
	get_tok (fname, fp);
	fix_file_name (fname);
	if ((f_out=creat (fname))==-1)
		p_error (1, "Uudecode Can't Create '", fname, "'\n", "");

	decode(fp, f_out);

	if (fgets(buf, sizeof buf, fp) == NULL || strcmp(buf, "end\n"))
		p_error (5, "Uudecode No end line\n", "");
	while (fgets (buf, sizeof (buf), fp))
		if (strncmp (buf, dp, len)==0)
			break;
	fclose (f_out);
}




/*
 * copy from in to out, decoding as you go along.
 */

#define DEC(c)	(((c) - ' ') & 077)

decode(in, out)
FILE *in;
FILE *out;
{
	char buf[80];
	char *bp;
	int n;

	for (;;) {
		/* for each input line */
		if (fgets(buf, sizeof buf, in) == NULL)
			p_error (10, "Uudecode Short file\n", "");

		n = DEC(buf[0]);
		if (n <= 0)
			break;
		bp = &buf[1];
		while (n > 0) {
			outdec(bp, out, n);
			bp += 4;
			n -= 3;
		}
	}
}




/*
 * output a group of 3 bytes (4 input characters).
 * the input chars are pointed to by p, they are to
 * be output to file f.  n is used to tell us not to
 * output all of them at the end of the file.
 */

outdec(p, f, n)
char *p;
FILE *f;
{
	int c1, c2, c3;

	c1 = DEC(*p) << 2 | DEC(p[1]) >> 4;
	c2 = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
	c3 = DEC(p[2]) << 6 | DEC(p[3]);
	if (n >= 1)
		fputc(c1, f);
	if (n >= 2)
		fputc(c2, f);
	if (n >= 3)
		fputc(c3, f);
}




/*
 *	DO_TEST ()	-	do_test () tries to perform the test
 *		requested by the shar script.  At the moment the 
 *		following tests are recognized
 *
 *		test ! -d directoryname
 *		test -f filename
 *		test <number> -ne wc ...  do a word count of
 *		                          the file
 */
 
int do_test (fp)
FILE *fp;
{
	FILE *tfp;
	
	char flag [10], fname [100];
	int	invert = FALSE;
	long	wc_arg;
	get_tok (flag, fp);
	if (strcmp (flag, "!")==0) {
		invert = TRUE;
		get_tok (flag, fp);
	}
	if (strcmp (flag, "-f")==0) {
		get_tok (fname, fp);
		fix_file_name (fname);
		if (tfp=fopen (fname, "r"))
			fclose (tfp);
	}
	else if (strcmp (flag, "-d")==0) {
		get_tok (fname, fp);
		fix_file_name (fname);
		if (tfp=(FILE *) opendir (fname))
			closedir (tfp);
	}
	else if (isdigit(flag [0])) {
		wc_arg = atol (flag);
		get_tok (flag, fp);
		if (strcmp (flag, "-ne")==0) {
			get_tok (fname, fp);
			return ((w_cnt (fname, wc_arg)));
		}
		else {
			cmd_init ();
			return ((int) test_flag);
		}
	}
	else {		
		cmd_init ();
		return ((int) test_flag);
	}
	return ((invert)?!(tfp):tfp);
}




/*
 *	W_CNT ()	-	This part of test is a separate function
 *		'cause I don't like to nest that deep.  by the time
 *		w_cnt () has been passed control it's broken down the
 *		shar script command of the form
 *			if test 488 -ne "`wc -c < 'FileName'`"
 *		to
 *			`wc -c < 'FileName'`
 *		which is not too hard to extract the filename from
 */

int w_cnt (name, arg)
char *name;
long  arg;
{
	char filename [65];
	struct stat statbuf;
	char *np, *index (), *rindex ();
	np = index (name, '\'') +1;
	*index (np, '\'') = '\0';
	strcpy (filename, np);
	fix_file_name (filename);
	stat (filename, &statbuf);
	if (statbuf.st_size != arg)
		return (1);
	return (0);
}




/*
 *	DO_SKIP ()	-	do_skip () passes thru the input until
 *		he has determined that the command that would have
 *		been processed at this point in the stream has been
 *		flushed.
 */
 
void do_skip (fp)
FILE *fp;
{
	char buf [256], delimiter [80], *dp;
	int len;

	do {
		get_tok (delimiter, fp);
	} while (strncmp (delimiter, "<<", 2));
	get_tok (delimiter, fp);
	dp = &delimiter [1];
	len = strlen (dp);
	while (fgets (buf, sizeof (buf), fp))
		if (strncmp (dp, buf, len)==0)
			break;
	cmd_init ();
}




/*
 *	FIX_FILE_NAME ()	-	 does two things
 *		(1)  strips off any leading path identifiers
 *		(2)  appends the leading path header to the
 *		     filename presented to it.
 */
 
void fix_file_name (s)
char *s;
{
	char buf [65], *tp, *rindex ();
	strcpy (buf, root_dir);
	if (tp=rindex (s, '/'))
		strcat (buf, tp+1);
	else
		strcat (buf, s);
	strcpy (s, buf);
}
