[comp.sources.misc] v10i055: MSDOS Shell

istewart@datlog.co.uk (Ian Stewartson) (02/14/90)

Posting-number: Volume 10, Issue 55
Submitted-by: istewart@datlog.co.uk (Ian Stewartson)
Archive-name: sh_dos/part03

#!/bin/sh
# this is part 2 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file lib/ms_dio.c continued
#
CurArch=2
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file lib/ms_dio.c"
sed 's/^X//' << 'SHAR_EOF' >> lib/ms_dio.c
X
X	if (FP->drive & HD_FLAG)
X	{
X	    struct partition	*pp, tp;
X
X/* System call failed - no device */
X
X	    if ((iregs.x.cflag) || (ndrive >= iregs.h.dl))
X	    {
X		free (FP);
X		errno = ENOENT;
X		return -1;
X	    }
X
X/* OK - save the parameters */
X
X	    FP->m_cyl    = (iregs.h.ch | ((iregs.h.cl & 0x0c0) << 2)) + 2;
X	    FP->m_head   = iregs.h.dh + 1;
X	    FP->m_sector = iregs.h.cl & 0x03f;
X	    FP->m_scount = FP->m_cyl * FP->m_head * FP->m_sector;
X
X/* If this is not partition 0 - read the partition table */
X
X	    if (FP->partition)
X	    {
X		if (dio_do (BIOS_READ, FP, buf, 0L, 1) == -1)
X		{
X		    free (FP);
X		    return -1;
X		}
X
X		if (*(int *)&buf[510] != 0xaa55)
X		{
X		    errno = ENOENT;
X		    return -1;
X		}
X
X/* Sort the partition table */
X
X		pp = (struct partition *)&buf[0x1be];
X
X		for (i = 0; i < 4; i++)
X		{
X		    for (j = 0; j < 3; j++)
X		    {
X			if (((!pp[j].offset) && pp[j + 1].offset) ||
X			    ((pp[j].offset > pp[j + 1].offset) &&
X			      pp[j + 1].offset))
X			{
X			    tp        = pp[j];
X			    pp[j]     = pp[j + 1];
X			    pp[j + 1] = tp;
X			}
X		    }
X		}
X
X		if (pp[FP->partition - 1].offset == 0L)
X		{
X		    errno = ENOENT;
X		    return -1;
X		}
X
X		FP->m_start = pp[FP->partition - 1].offset;
X		FP->m_scount = pp[FP->partition - 1].size;
X	    }
X	}
X
X/* Floppy disk - get parameters.  We try our best here, but DOS 3.3 allows
X * you to format any number of sectors per track and tracks per disk
X */
X
X	else
X	{
X
X/* System call failed - think this means we're on an XT.  So set up the
X * XT parameters
X */
X
X	    if ((iregs.x.cflag) && (ndrive < 2))
X	    {
X		iregs.h.bl   = 0x01;
X		FP->m_cyl    = 40;
X		FP->m_head   = 2;
X		FP->m_sector = 9;
X	    }
X
X/* No Drive */
X
X	    else if ((iregs.x.cflag) || (ndrive >= iregs.h.dl))
X	    {
X		free (FP);
X		errno = ENOENT;
X		return -1;
X	    }
X
X/* OK - save the parameters */
X
X	    else
X	    {
X		FP->m_cyl    = iregs.h.ch;
X		FP->m_head   = iregs.h.dh + 1;
X		FP->m_sector = iregs.h.cl;
X	    }
X
X	    FP->m_scount = FP->m_cyl * FP->m_head * FP->m_sector;
X
X/* High capacity drive ? */
X
X	    if ((iregs.h.bl == 0x02) || (iregs.h.bl == 0x03))
X	    {
X
X/* Try reading sector 0 */
X
X		FP->m_sector = (iregs.h.bl == 0x02) ? 9 : 15;
X
X/* If it failed - switch to the other type */
X
X		if (dio_do (BIOS_READ, FP, buf, 0L, 1) == -1)
X		{
X		    FP->m_sector = (iregs.h.bl == 0x02) ? 15 : 9;
X
X		    iregs.h.ah   = 0x17;
X		    iregs.h.dl   = (unsigned char)FP->drive;
X		    iregs.h.al   = (unsigned char)(5 - iregs.h.bl);
X		    int86 (0x13, &iregs, &iregs);
X		}
X	    }
X
X/* 8 or 9 Sectors, 1 or 2 heads */
X
X	    if (((iregs.h.bl > 0x00) || (iregs.h.bl < 0x04)) &&
X		(FP->m_sector == 9))
X	    {
X		FP->m_scount = FP->m_cyl * FP->m_head * FP->m_sector;
X
X/* Check to see if sector 8 exists */
X
X		if (dio_do (BIOS_READ, FP, buf, 8L, 1) == -1)
X		    FP->m_sector = 8;
X
X/* Check to see if sector 380 exists */
X
X		if (dio_do (BIOS_READ, FP, buf, 380L, 1) == -1)
X		    FP->m_head = 1;
X	    }
X
X/* 720K drive - read sector 15 to see if 1.4M */
X
X	    else if (iregs.h.bl == 0x04)
X	    {
X		FP->m_scount = FP->m_cyl * FP->m_head * FP->m_sector;
X
X		if (dio_do (BIOS_READ, FP, buf, 17L, 1) == -1)
X		    FP->m_sector = 9;
X	    }
X
X	    FP->m_scount = FP->m_cyl * FP->m_head * FP->m_sector;
X	}
X
X/* Set up the file descriptor entry and return the number */
X
X	MS_io_fs[fp] = FP;
X	return fp + MS_MODIFIER;
X    }
X
X    else
X	return open (name, mode, permissions);
X}
X
X/* fstat function */
X
Xint		dio_fstat (fp, St)
Xint		fp;
Xstruct stat	*St;
X{
X    struct fs		*FP;
X
X    if (fp < MS_MODIFIER)
X	return fstat (fp, St);
X
X    if ((FP = dio_fpcheck (fp)) == (struct fs *)NULL)
X	return -1;
X
X/* Dummy values */
X
X    memset (St, 0, sizeof (struct stat));
X    St->st_mode = 0x61b6;
X    St->st_nlink = 1;
X
X    if (FP->drive == DRIVE_RAM)
X    {
X	St->st_size = MEGABYTE;
X	St->st_rdev = 0x0300;
X    }
X
X    else
X    {
X	St->st_rdev = ((FP->drive & (~HD_FLAG)) * 10 + FP->partition) |
X		      ((FP->drive & HD_FLAG) ? 0x0200 : 0x0100);
X	St->st_dev = FP->drive;
X    }
X
X    St->st_atime = time ((time_t *)NULL);
X    St->st_ctime = St->st_ctime;
X    St->st_mtime = St->st_atime;
X    return 0;
X}
X
X/*
X * Close function
X */
X
Xint	dio_close (fp)
Xint	fp;
X{
X    struct fs		*FP;
X
X    if (fp < MS_MODIFIER)
X	return close (fp);
X
X    if ((FP = dio_fpcheck (fp)) == (struct fs *)NULL)
X	return -1;
X
X    free (FP);
X    MS_io_fs[fp - MS_MODIFIER] = (struct fs *)NULL;
X    return 0;
X}
X
X/*
X * Seek function
X */
X
Xlong	dio_lseek (fp, off, type)
Xint	fp;
Xoff_t	off;
Xint	type;
X{
X    off_t		check;
X    struct fs		*FP;
X
X    if (fp < MS_MODIFIER)
X	return lseek (fp, off, type);
X
X    if ((FP = dio_fpcheck (fp)) == (struct fs *)NULL)
X	return -1L;
X
X    switch (type)
X    {
X	case SEEK_SET:
X	    check = off;
X	    break;
X
X	case SEEK_CUR:
X	    check = off + FP->location;
X	    break;
X
X	case SEEK_END:
X	default:
X	    errno = EINVAL;
X	    return -1L;
X    }
X
X    if (check < 0L)
X    {
X	errno = EINVAL;
X	return -1L;
X    }
X
X    return (FP->location = check);
X}
X
X/* Check for a valid file pointer */
X
Xstatic struct fs	*dio_fpcheck (fp)
Xint			fp;
X{
X    struct fs	*FP;
X
X    if (((FP = MS_io_fs[fp - MS_MODIFIER]) == (struct fs *)NULL) ||
X	(fp - MS_MODIFIER >= _NFILE) || (fp - MS_MODIFIER < 0))
X    {
X	errno = EBADF;
X	return (struct fs *)NULL;
X    }
X
X    return FP;
X}
X
X/* Check for a valid file name */
X
Xstatic int	dio_fncheck (name)
Xchar		*name;
X{
X
X/* Check for hard disk */
X
X    if (isdigit(name[7]) && isdigit(name[8]) && (strlen (name) == 9) &&
X	(!strnicmp (name, "/dev/hd", 6)))
X    {
X	int	i = atoi (&name[7]);
X
X	return ((i % 10) > 4) ? -1 : i | HD_FLAG;
X    }
X
X/* Check for floppy disk */
X
X    else if (isdigit(name[7]) && (strlen (name) == 8) &&
X	(!strnicmp (name, "/dev/fd", 6)))
X	return name[7] - '0';
X
X    else if (!stricmp (name, "/dev/kmem"))
X	return DRIVE_RAM;
X
X    return -1;
X}
X
X/* Get file status */
X
Xint		dio_stat (name, St)
Xchar		*name;
Xstruct stat	*St;
X{
X    int		drive;
X
X    if ((drive = dio_fncheck (name)) == -1)
X	return stat (name, St);
X
X    memset (St, 0, sizeof (struct stat));
X    St->st_mode = 0x61b6;
X    St->st_nlink = 1;
X    St->st_atime = time ((time_t *)NULL);
X    St->st_ctime = St->st_ctime;
X    St->st_mtime = St->st_atime;
X
X    if (drive == DRIVE_RAM)
X    {
X	St->st_size = MEGABYTE;
X	St->st_rdev = 0x0300;
X    }
X
X    else
X    {
X	St->st_rdev = (drive & (~HD_FLAG)) | ((drive & HD_FLAG) ? 0x0200
X								: 0x0100);
X	St->st_dev = drive;
X    }
X
X    return 0;
X}
X
X/* Check file access */
X
Xint	dio_access (name, mode)
Xchar	*name;
Xint	mode;
X{
X    if (dio_fncheck (name) == -1)
X	return access (name, mode);
X
X    else if (mode & 1)
X    {
X	errno = EACCES;
X	return -1;
X    }
X
X    return 0;
X}
X
X/* Change file permissions */
X
Xint	dio_chmod (name, mode)
Xchar	*name;
Xint	mode;
X{
X    return (dio_fncheck (name) == -1) ? chmod (name, mode) : 0;
X}
X
X/* Create file */
X
Xint	dio_creat (name, mode)
Xchar	*name;
Xint	mode;
X{
X    return (dio_fncheck (name) == -1) ? creat (name, mode)
X				      : dio_open (name, O_WRONLY, mode);
X}
X
X/* Duplicate handler */
X
Xint	dio_dup (fp)
Xint	fp;
X{
X    struct fs	*FP;		/* New pointer			*/
X    struct fs	*FP1;		/* Old pointer			*/
X
X    if (fp < MS_MODIFIER)
X	return dup (fp);
X
X    if ((FP1 = dio_fpcheck (fp)) == (struct fs *)NULL)
X	return -1;
X
X    for (fp = 0; (fp < _NFILE) && (MS_io_fs[fp] != (struct fs *)NULL); fp++)
X	;
X
X    if ((fp == _NFILE) ||
X	((FP = (struct fs *)malloc (sizeof (struct fs))) == (struct fs *)NULL))
X    {
X	errno = EMFILE;
X	return -1;
X    }
X
X    MS_io_fs[fp] = FP;
X    *FP = *FP1;
X    return fp;
X}
X
X/* Check if tty */
X
Xint	dio_isatty (fp)
Xint	fp;
X{
X    if (fp < MS_MODIFIER)
X	return isatty (fp);
X
X    return 0;
X}
X
X/* Tell location */
X
Xlong	dio_tell (fp)
Xint	fp;
X{
X    struct fs	*FP;
X
X    if (fp < MS_MODIFIER)
X	return tell (fp);
X
X    return ((FP = dio_fpcheck (fp)) == (struct fs *)NULL) ? -1L : FP->location;
X}
SHAR_EOF
echo "File lib/ms_dio.c is complete"
chmod 0644 lib/ms_dio.c || echo "restore of lib/ms_dio.c fails"
set `wc -c lib/ms_dio.c`;Sum=$1
if test "$Sum" != "17329"
then echo original size 17329, current size $Sum;fi
echo "x - extracting lib/director.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/director.c &&
X/*
X * @(#)msd_dir.c 1.4 87/11/06	Public Domain.
X *
X *  A public domain implementation of BSD directory routines for
X *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
X *  August 1897
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <malloc.h>
X#include <string.h>
X#include <limits.h>
X#include <errno.h>
X#include <dirent.h>
X#include <dos.h>
X
X#define	ATTRIBUTES		(_A_SUBDIR | _A_HIDDEN | _A_SYSTEM | \
X				 _A_NORMAL | _A_RDONLY | _A_ARCH | _A_VOLID)
X
Xstatic void	free_dircontents (struct _dircontents *);
X
XDIR	*opendir(name)
Xchar	*name;
X{
X    struct stat		statb;
X    DIR			*dirp;
X    char		c;
X    struct _dircontents	*dp;
X    char		nbuf[PATH_MAX + NAME_MAX + 2];
X    struct find_t	dtabuf;
X    
X    if (stat (name, &statb) < 0)
X	return (DIR *) NULL;
X
X    if (!S_ISDIR(statb.st_mode))
X    {
X	errno = ENOTDIR;
X	return (DIR *)NULL;
X    }
X
X    if ((dirp = (DIR *) malloc (sizeof (DIR))) == (DIR *) NULL)
X	return (DIR *) NULL;
X
X    if (*name && (c = name[strlen (name) - 1]) != '\\' && c != '/')
X	(void) strcat (strcpy (nbuf, name), "\\*.*");
X
X    else
X	(void) strcat (strcpy (nbuf, name), "*.*");
X
X    dirp->dd_loc      = 0;
X    dirp->dd_cp       = (struct _dircontents *) NULL;
X    dirp->dd_contents = (struct _dircontents *) NULL;
X
X    if (_dos_findfirst (nbuf, ATTRIBUTES, &dtabuf) != 0)
X	return dirp;
X
X    do 
X    {
X	if (((dp = (struct _dircontents *) malloc(sizeof(struct _dircontents))) == (struct _dircontents *) NULL) ||
X	    ((dp->_d_entry = strdup (dtabuf.name)) == (char *) NULL))
X	{
X	    if (dp != (char *)NULL)
X		free ((char *) dp);
X
X	    free_dircontents (dirp->dd_contents);
X	    return (DIR *) NULL;
X	}
X
X	if (dirp->dd_contents)
X	    dirp->dd_cp = dirp->dd_cp->_d_next = dp;
X
X	else
X	    dirp->dd_contents = dirp->dd_cp = dp;
X
X	dp->_d_next = (struct _dircontents *) NULL;
X
X    } while (_dos_findnext (&dtabuf) == 0);
X
X    dirp->dd_cp = dirp->dd_contents;
X
X    return dirp;
X}
X
Xint	closedir(dirp)
XDIR	*dirp;
X{
X    free_dircontents (dirp->dd_contents);
X    free ((char *) dirp);
X    return 0;
X}
X
Xstruct dirent	*readdir(dirp)
XDIR		*dirp;
X{
X    static struct dirent	dp;
X    
X    if (dirp->dd_cp == (struct _dircontents *) NULL)
X	return (struct dirent *) NULL;
X
X    dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));
X    dp.d_off    = dirp->dd_loc * 32;
X    dp.d_ino    = (ino_t)++dirp->dd_loc;
X    dirp->dd_cp = dirp->dd_cp->_d_next;
X    strlwr (dp.d_name);
X
X    return &dp;
X}
X
Xvoid	rewinddir (dirp)
XDIR	*dirp;
X{
X    seekdir (dirp, (off_t)0);
X}
X
Xvoid	seekdir (dirp, off)
XDIR	*dirp;
Xoff_t	off;
X{
X    long		i = off;
X    struct _dircontents	*dp;
X
X    if (off < 0L)
X	return;
X
X    for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next)
X	;
X
X    dirp->dd_loc = off - (i + 1);
X    dirp->dd_cp = dp;
X}
X
Xoff_t	telldir(dirp)
XDIR	*dirp;
X{
X    return dirp->dd_loc;
X}
X
Xstatic void		free_dircontents(dp)
Xstruct _dircontents	*dp;
X{
X    struct _dircontents	*odp;
X
X    while (dp) 
X    {
X	if (dp->_d_entry)
X	    free(dp->_d_entry);
X
X	dp = (odp = dp)->_d_next;
X	free((char *) odp);
X    }
X}
SHAR_EOF
chmod 0644 lib/director.c || echo "restore of lib/director.c fails"
set `wc -c lib/director.c`;Sum=$1
if test "$Sum" != "3067"
then echo original size 3067, current size $Sum;fi
echo "x - extracting lib/popen.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/popen.c &&
X/*
X * popen/pclose: simple MS-DOS piping scheme to imitate UNIX pipes
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <string.h>
X#include <errno.h>
X#include <process.h>
X#include <limits.h>
X#include <stdlib.h>
X#include <unistd.h>
X
Xtypedef struct	pipes {
X    FILE	*p_fp;			/* File id			*/
X    char	*p_process;		/* Program name			*/
X    char	*p_file;		/* Pipe file name		*/
X    int		p_status;		/* Status for close to return	*/
X					/* Read pipes only		*/
X    bool	p_write;		/* Read or write		*/
X} PIPE;
X
Xstatic PIPE	P_list[_NFILE];		/* The pipe structures		*/
Xstatic int	Pipes_Inited = 0;	/* Initialised ?		*/
Xstatic int	Unique_Pipe  = 0;
X
Xstatic PIPE	*_p_save_entry (char *, bool);
Xstatic int	_p_run (char *);
Xstatic int	_p_reset_entry (PIPE *, int);
Xstatic PIPE	*_p_get_entry (FILE *);
X
X/* Set up a pipe structure */
X
Xstatic PIPE	*_p_save_entry (prog, mode)
Xchar		*prog;
Xbool		mode;
X{
X    FILE	*fp;		/* File handler				*/
X    PIPE	*pp;		/* Pipe handler structure		*/
X    char	tmpfile[NAME_MAX + PATH_MAX + 2];
X    char	*tmpdir;	/* Points to directory prefix of pipe	*/
X
X/* Find out where we should put temporary files */
X
X    if ((tmpdir = getenv ("TMPDIR")) == (char *) NULL) 
X	tmpdir = getenv ("TMP");
X
X/* Use temporary directory if available */
X
X    if (tmpdir == (char *)NULL) 
X	tmpdir = ".";
X
X/* Get a unique pipe file name */
X
X    sprintf (tmpfile, "%s/pipe%05d.tmp", tmpdir, Unique_Pipe++);
X    unlink (tmpfile);
X
X/* Create the pipe */
X
X    if ((fp = fopen (tmpfile, "w")) == (FILE *) NULL)
X	return (PIPE *)NULL;
X
X/* Create the PIPE entry */
X
X    if ((pp = _p_get_entry ((FILE *)NULL)) == (PIPE *)NULL)
X    {
X	fclose (fp);
X	unlink (tmpfile);
X	errno = EMFILE;
X	return (PIPE *)NULL;
X    }
X
X/* Set up the entry */
X
X    pp->p_fp      = fp;
X    pp->p_write   = mode;
X    pp->p_process = strdup (prog);
X    pp->p_file    = strdup (tmpfile);
X
X/* Check for errors */
X
X    if ((pp->p_process == (char *)NULL) || (pp->p_file == (char *)NULL))
X    {
X	_p_reset_entry (pp, 1);
X	errno = ENOMEM;
X	return (FILE *)NULL;
X    }
X
X    return pp;
X}
X
X/* Execute command via SHELL or COMSPEC */
X
Xstatic int	_p_run (command)
Xchar		*command;
X{
X    char	*shell;			/* Command processor		*/
X    char	*shellpath;		/* Full command processor path	*/
X    char	*bp;			/* Generic string pointer	*/
X    char	*dash = "/c";
X
X/* Determine the command processor */
X
X    if (((shell = getenv ("SHELL")) == (char *) NULL) &&
X	((shell = getenv ("COMSPEC")) == (char *) NULL))
X	shell = "command.com";
X
X    shellpath = strlwr (shell);
X
X/* Strip off any leading backslash directories */
X
X    if ((shell = strrchr (shellpath, '\\')) != (char *)NULL)
X	++shell;
X
X    else
X	shell = shellpath;
X
X/* Strip off any leading slash directories */
X
X    if ((bp = strrchr (shell, '/')) != (char *)NULL)
X	shell = ++bp;
X
X    if (strcmp (shell, "command.com"))
X	*dash = '-';
X
X/* Run the program */
X
X    return spawnl (P_WAIT, shellpath, shell, dash, command, (char *) NULL);
X}
X
X/* resetpipe: Private routine to cancel a pipe */
X
Xstatic int	_p_reset_entry (pp, mode)
XPIPE		*pp;
Xint		mode;
X{
X    int		result = (!mode) ? 0 : -1;
X    int		serrno = errno;
X
X/* Close the pipe */
X    
X    fclose (pp->p_fp);
X
X/* Free up memory */
X
X    if (pp->p_file != (char *)NULL)
X    {
X	result = unlink (pp->p_file);
X
X	if (!mode)
X	    serrno = errno;
X
X	else
X	    result = -1;
X
X	free (pp->p_file);
X    }
X
X    if (pp->p_process != (char *)NULL)
X	free (pp->p_process);
X
X    memset (pp, 0, sizeof (PIPE));
X
X/* Return error code */
X
X    errno = serrno;
X    return result;
X}
X
X/* Find a free entry */
X
Xstatic PIPE	*_p_get_entry (fp)
XFILE		*fp;
X{
X    int		i;
X
X    for (i = 0; i < _NFILE; i++)
X    {
X	if (P_list[i].p_fp == fp)
X	    return &P_list[i];
X    }
X    
X    return (PIPE *)NULL;
X}
X
X
X/* popen: open a pipe */
X
XFILE	*popen (command, type)
Xchar	*command;		/* The command to be run		*/
Xchar	*type;			/* "w" or "r"				*/
X{ 
X    int		old_stdout;
X    PIPE	*pp;
X
X/* Initialise the pipe structure */
X
X    if (!Pipes_Inited)
X    {
X	memset (&P_list[0], 0, sizeof (P_list));
X	Pipes_Inited = 1;
X    }
X
X/* For write style pipe, pclose handles program execution */
X
X    if (strcmp (type, "w") == 0)
X	return ((pp = _p_save_entry (command, TRUE)) == (PIPE *)NULL)
X	       ? (FILE *)NULL : pp->p_fp;
X    
X/* read pipe must create tmp file, set up stdout to point to the temp
X * file, and run the program.  note that if the pipe file cannot be
X * opened, it'll return a condition indicating pipe failure, which is
X * fine.
X */
X
X    else if (strcmp (type, "r") == 0)
X    {
X	if ((pp = _p_save_entry (command, FALSE)) == (PIPE *)NULL)
X	   return (FILE *)NULL;
X
X/* Save the stdout file descriptor, dup the pipe onto standard out,
X * execute the command, close the pipe and re-open it 
X */
X
X	if ((old_stdout = dup (fileno(stdout)) < 0)		||
X	    (dup2 (fileno (pp->p_fp), fileno(stdout)) < 0)	||
X	    ((pp->p_status = _p_run (command)) < 0)		||
X	    (fclose (pp->p_fp) < 0)				||
X	    (dup2 (old_stdout, fileno (stdout)) < 0)		||
X	    ((pp->p_fp = fopen (pp->p_file, "r")) == (FILE *)NULL))
X	{
X	    _p_reset_entry (pp, 1);
X	    return (FILE *)NULL;
X	}
X
X	else
X	    return pp->p_fp;
X    }
X    
X/* screwy call or unsupported type */
X
X    errno = EINVAL;
X    return (FILE *)NULL;
X}
X
X/* close a pipe */
X
Xint	pclose (fp)
XFILE	*fp;
X{
X    PIPE	*pp;			/* Current pipe structure	*/
X    int		old_stdin;		/* Where our stdin points now	*/
X
X    if ((pp = _p_get_entry (fp)) == (PIPE *)NULL)
X    {
X	errno = EBADF;
X	return -1;
X    }
X
X    if (fclose (pp->p_fp) < 0)
X	return _p_reset_entry (pp, 1);
X
X/* Open the pipe in read mode, Save stdin file descriptor, copy pipe file
X * descriptor to stdin, execute the command, and then restore stdin
X */
X
X    if (pp->p_write &&
X	(	((pp->p_fp = fopen (pp->p_file, "r")) == (FILE *)NULL)	||
X		((old_stdin = dup (fileno (stdin))) < 0)		||
X		(dup2 (fileno (pp->p_fp), fileno (stdin)) < 0)		||
X		((pp->p_status = _p_run (pp->p_process)) < 0)		||
X		(fclose (pp->p_fp) < 0)					||
X		(dup2 (old_stdin, fileno (stdin)) < 0)
X	))
X	return _p_reset_entry (pp, 1);
X
X/* Close the temp file and remove it */
X
X    return _p_reset_entry (pp, 0);
X}
SHAR_EOF
chmod 0644 lib/popen.c || echo "restore of lib/popen.c fails"
set `wc -c lib/popen.c`;Sum=$1
if test "$Sum" != "6060"
then echo original size 6060, current size $Sum;fi
echo "x - extracting lib/syserr.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/syserr.c &&
X/* perror(s) print the current error message. */
X
Xchar	*sys_errlist[] = {
X    "Error 0 ",
X    "Operation not permitted",
X    "No such file or directory",
X    "No such process",
X    "Interrupted system call",
X    "I/O error",
X    "No such device or address",
X    "Arg list too long",
X    "Exec format error",
X    "Bad file number",
X    "No children",
X    "No more processes",
X    "Not enough core",
X    "Permission denied",
X    "Bad address",
X    "Block device required",
X    "Mount device busy",
X    "File exists",
X    "Cross-device link",
X    "No such device",
X    "Not a directory",
X    "Is a directory",
X    "Invalid argument",
X    "File table overflow",
X    "Too many open files",
X    "Not a typewriter",
X    "Text file busy",
X    "File too large",
X    "No space left on device",
X#define  ESPIPE    29
X    "Illegal seek",
X    "Read-only file system",
X    "Too many links",
X
X    "Broken pipe",
X    "Math argument",
X    "Result too large",
X    "EUCLEAN",
X    "No message of desired type",
X    "Resource deadlock would occur"
X    "Unknown error"
X};
X
Xint	sys_nerr = sizeof(sys_errlist)/sizeof(char *) - 1;
SHAR_EOF
chmod 0644 lib/syserr.c || echo "restore of lib/syserr.c fails"
set `wc -c lib/syserr.c`;Sum=$1
if test "$Sum" != "1106"
then echo original size 1106, current size $Sum;fi
echo "x - extracting lib/stdargv.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/stdargv.c &&
X/*
X *  MODULE NAME:     expand.c  					  Revision: 1.0
X *
X *  AUTHOR:	     Ian Stewartson
X *
X *  LOCATION:	     Data Logic,
X *		     Greenford,
X *		     Middlesex,
X *		     England.
X *
X#include <logo.h>
X *  MODULE DEFINITION:	This function expandes the command line parameters
X *			in a UNIX like manner.	Wild character *?[] are
X *			allowed in file names. @filename causes command lines
X *			to be read from filename.  Strings between " or ' are
X *			not expanded.  All entries in the array are malloced.
X *
X *			This function replaces the standard MS-DOS command
X *			line processing function (_setargv in stdargv.obj).
X *
X *  CALLING SEQUENCE:	The following calling sequences are used:
X *
X *			void	_setargv ();
X *
X *  ERROR MESSAGES:	Out of memory
X *
X *  INCLUDE FILES:
X */
X
X#include <sys/types.h>			/* MS-DOS type definitions          */
X#include <sys/stat.h>			/* File status definitions	    */
X#include <stdio.h>			/* Standard I/O delarations         */
X#include <stdlib.h>			/* Standard library functions       */
X#include <errno.h>			/* Error number declarations        */
X#include <dos.h>			/* DOS functions declarations       */
X#include <bios.h>			/* BIOS functions declarations      */
X#include <ctype.h>			/* Character type declarations      */
X#include <string.h>			/* String library functions         */
X#include <limits.h>			/* String library functions         */
X#include <fcntl.h>			/* File Control Declarations        */
X#include <io.h>				/* Input/Output Declarations        */
X#include <dirent.h>			/* Direction I/O functions	    */
X
X/*
X *  DATA DEFINITIONS:
X */
X
X#define MAX_LINE	160		/* Max line length		*/
X#define S_ENTRY		sizeof (char *)
X
X/*
X *  DATA DECLARATIONS:
X */
X#ifdef MSDOS
X
Xextern void	_setargv (void);
Xstatic void	exp_line (char *);		/* Expand file		*/
Xstatic int	ex_pfield (char	*, char *);	/* Expand field		*/
Xstatic void	ex_pfile (char *);
Xstatic char	*ex_gspace (int, char *);	/* Get space		*/
Xstatic void	ex_add_arg (char *);	/* Add argument			*/
Xstatic char	*ex_skip_sp (char *);	/* Skip spaces			*/
Xstatic char	*ex_tounix (char *);	/* Convert name to Unix format	*/
Xstatic int	ex_find (char*, int);	/* Split file name		*/
Xstatic void	ex_fatal (int, char *, char *);	/* Fatal error processing*/
Xstatic char	*ex_environment (char *);	/* Process environment	*/
Xstatic char	*_ex_multi_drive (char *);	/* Check for multidrive	*/
Xstatic char	*ex_nomem = "%s: %s\n";
X
Xextern char far	*_pgmptr; 		/* Program name			*/
Xextern char	**__argv; 		/* Current argument address	*/
Xextern int	__argc; 		/* Current argument count	*/
X
X/*
X *  MODULE ABSTRACT: _setargv
X *
X *  UNIX like command line expansion
X */
X
Xvoid	_setargv ()
X{
X					/* Set up pointer to command line */
X    char far		*argvp = (char far *)((((long)_psp) << 16) + 0x081L);
X    unsigned int	envs = *(int far *)((((long)_psp) << 16) + 0x02cL);
X    char far		*s; 		/* Temporary string pointer    	*/
X#ifndef M_I86LM
X    char		buf[MAX_LINE];	/* Temporary space		*/
X    char		*cp;
X#endif
X
X/* Command line can be null or 0x0d terminated - convert to null */
X
X    s = argvp;
X
X    while (*s && (*s != 0x0d))
X	++s;
X    
X    if (*s == 0x0d)
X	*s = 0;
X
X/* Set up global parameters and expand */
X
X    __argc = 0;
X
X/* Get the program name */
X
X    if (_osmajor <= 2)
X	s = "unknown";
X
X/* In the case of DOS 3+, we look in the environment space */
X
X    else
X    {
X	s = (char far *)(((long)envs) << 16);
X
X	while (*s)
X	{
X	    while (*(s++) != 0);
X	}
X
X	s += 3;
X    }
X
X    _pgmptr = s;
X
X#ifndef M_I86LM
X    cp = buf;
X    while (*(cp++) = *(s++));
X
X    ex_add_arg (ex_tounix (buf));	/* Add the program name		*/
X
X    s  = argvp;
X    cp = buf;
X    while (*(cp++) = *(s++));
X
X    exp_line (buf);
X#else
X    ex_add_arg (ex_tounix (s));		/* Add the program name		*/
X    exp_line (argvp);
X#endif
X
X    ex_add_arg ((char *)NULL);
X    --__argc;
X}
X
X/*
X * Expand a line
X */
X
Xstatic void	exp_line (argvp)
Xchar		*argvp;			/* Line to expand    		*/
X{
X    char	*spos;			/* End of string pointer	*/
X    char	*cpos;			/* Start of string pointer	*/
X    char	*fn;			/* Extracted file name string	*/
X
X/* Search for next separator */
X
X    spos = argvp;
X
X    while (*(cpos = ex_skip_sp (spos)))
X    {
X
X/* Extract string argument */
X
X	if ((*cpos == '"') || (*cpos == '\''))
X	{
X	    spos = cpos + 1;
X
X	    do
X	    {
X		if ((spos = strchr (spos, *cpos)) != NULL)
X		{
X		    spos++;
X		    if (spos[-2] != '\\')
X			break;
X		}
X
X		else
X		    spos = &spos[strlen (cpos)];
X
X	    }
X	    while (*spos);
X
X	    fn	= ex_gspace (spos - cpos - 2, cpos + 1);
X	}
X
X/* Extract normal argument */
X
X	else
X	{
X	    spos = cpos;
X	    while (!isspace(*spos) && *spos)
X		spos++;
X	    
X	    fn = ex_gspace (spos - cpos, cpos);
X	}
X
X/* Process argument */
X
X	if (*cpos != '"')
X	    fn = ex_environment (fn);
X
X	switch (*cpos)
X	{
X	    case '@':		/* Expand file	    			*/
X		ex_pfile (fn);
X		break;
X
X	    case '"':		/* Expand string	    		*/
X	    case '\'':
X		ex_add_arg (fn);
X		break;
X
X	    default:		/* Expand field	    			*/
X		if (!ex_find (fn, 0))
X		    ex_add_arg (fn);
X	}
X
X	free (fn);
X    }
X}
X
X/* Expand a field if it has metacharacters in it */
X
Xstatic int	ex_pfield (prefix, postfix)
Xchar		*prefix;		/* Prefix field	    		*/
Xchar		*postfix;		/* Postfix field	    	*/
X{
X    int 	 	count;		/* File path length		*/
X    int 		f_count = 0;	/* Number of files generated    */
X    int			slash_flag = 0;	/* slash required		*/
X    char		fn[PATH_MAX + NAME_MAX + 2];/* Search file name */
X    char		*name;		/* Match string			*/
X    char		*p, *p1;
X    DIR			*dp;
X    struct dirent	*c_de;
X    unsigned int	c_drive;	/* Current drive		*/
X    unsigned int	m_drive;	/* Max drive			*/
X    unsigned int	s_drive;	/* Selected drive		*/
X    unsigned int	x_drive, y_drive;	/* Dummies		*/
X    char		*multi;		/* Multi-drive flag		*/
X    char		t_drive[2];
X
X/* Convert file name to lower case */
X
X    strlwr (prefix);
X
X/* Search all drives ? */
X
X    if ((multi = _ex_multi_drive (prefix)) != (char *)NULL)
X    {
X	_dos_getdrive (&c_drive);	/* Get number of drives		*/
X	_dos_setdrive (c_drive, &m_drive);
X	t_drive[1] = 0;
X
X	for (s_drive = 1; s_drive <= m_drive; ++s_drive)
X	{
X	    _dos_setdrive (s_drive, &x_drive);
X	    _dos_getdrive (&y_drive);
X	    _dos_setdrive (c_drive, &x_drive);
X
X/* Check to see if the second diskette drive is really there */
X
X	    if (((_bios_equiplist () & 0x00c0) == 0x0000) && (s_drive == 2))
X		continue;
X
X/* If the drive exists and is in our list - process it */
X
X	    *multi = 0;
X	    *t_drive = (char)(s_drive + 'a' - 1);
X
X	    if ((y_drive == s_drive) && pnmatch (t_drive, prefix, 0))
X	    {
X		*multi = ':';
X		*fn = *t_drive;
X		strcpy (fn + 1, multi);
X		f_count += ex_pfield (fn, postfix);
X	    }
X
X	    *multi = ':';
X	}
X
X	return f_count;
X    }
X
X/* Get the path length */
X
X    p = strrchr (prefix, '/');
X    p1 = strchr (prefix, ':');
X
X    if ((p1 == (char *)NULL) || (p1 < p))
X    {
X	if (p == (char *)NULL)
X	{
X	    count = 0;
X	    name = prefix;
X	}
X
X	else
X	{
X	    count = p - prefix;
X	    name = p + 1;
X	}
X    }
X	
X    else if ((p == (char *)NULL) || (p < p1))
X    {
X	count = p1 - prefix;
X	name = p1 + 1;
X    }
X    
X/* Set up file name for search */
X	
X    if (((count == 2) && (strncmp (prefix + 1, ":/", 2) == 0)) ||
X	((count == 0) && (*prefix == '/')))
X    {
X	strncpy (fn, prefix, ++count);
X	fn[count] = 0;
X	strcat (fn, ".");
X    }
X
X    else
X    {
X	if ((count == 1) && (*(prefix + 1) == ':'))
X	    count++;
X
X	strncpy (fn, prefix, count);
X	fn[count] = 0;
X
X	if (((count == 2) && (*(prefix + 1) == ':')) || (count == 0))
X	    strcat (fn, ".");
X	
X	else
X	    slash_flag = 1;
X    }
X	
X/* Search for file names */
X
X    if ((dp = opendir (fn)) == (DIR *)NULL)
X	return 0;
X
X/* Are there any matches */
X
X    while ((c_de = readdir (dp)) != (struct dirent *)NULL)
X    {
X	if ((*c_de->d_name == '.') && (*name != '.'))
X	    continue;
X
X/* Check for match */
X
X	if (pnmatch (c_de->d_name, name, 0))
X	{
X	    fn[count] = 0;
X
X	    if (slash_flag)
X		strcat (fn, "/");
X
X	    strcat (fn, c_de->d_name);
X
X/* If the postfix is not null, this must be a directory */
X
X	    if (postfix != (char *)NULL)
X	    {
X		struct stat		statb;
X
X		if (stat (fn, &statb) < 0 ||
X		    (statb.st_mode & S_IFMT) != S_IFDIR)
X		    continue;
X
X		strcat (fn, "/");
X		strcat (fn, postfix);
X	    }
X
X	    f_count += ex_find (fn, 1);
X	}
X    }
X
X    closedir (dp);
X    return f_count;
X}
X
X/* Expand file name */
X
Xstatic void	ex_pfile (file)
Xchar		*file;		/* Expand file name	    		*/
X{
X    FILE    	*fp;		/* File descriptor	    		*/
X    char	*p;		/* Pointer				*/
X    int		c_maxlen = MAX_LINE;
X    char	*line;		/* Line buffer		    		*/
X
X/* Grab some memory for the line */
X
X    if ((line = malloc (c_maxlen)) == (char *)NULL)
X	ex_fatal (ENOMEM, ex_nomem, (char *)NULL);
X
X/* If file open fails, expand as a field */
X
X    if ((fp = fopen (file + 1, "rt")) == NULL)
X    {
X	if (!ex_find (file, 0))
X	    ex_add_arg (file);
X
X	return;
X    }
X
X/* For each line in the file, remove EOF characters and add argument */
X
X    while (fgets (line, c_maxlen, fp) != (char *)NULL)
X    {
X	while ((p = strchr (line, '\n')) == (char *)NULL)
X	{
X	    if ((p = strchr (line, 0x1a)) != (char *)NULL)
X		break;
X
X	    if ((line = realloc (line, c_maxlen + MAX_LINE)) == (char *)NULL)
X		ex_fatal (ENOMEM, ex_nomem, (char *)NULL);
X
X	    if (fgets (&line[c_maxlen - 1], MAX_LINE, fp) == (char *)NULL)
X		break;
X
X	    c_maxlen += MAX_LINE - 1;
X	}
X
X	if (p != (char *)NULL)
X	    *p = 0;
X
X	ex_add_arg (line);
X    }
X
X    if (ferror(fp))
X	ex_fatal (errno, "%s: %s (%s)\n", file + 1);
X
X    free (line);
X    fclose (fp);
X}
X
X/* Get space for name */
X
Xstatic char	*ex_gspace (l, in_s)
Xint		l;			/* String length                */
Xchar		*in_s;                  /* String address		*/
X{
X    char	*out_s;			/* Malloced space address       */
X
X    if ((out_s = malloc (l + 1)) == (char *)NULL)
X	ex_fatal (ENOMEM, ex_nomem, (char *)NULL);
X
X/* Copy string for specified length */
X
X    strncpy (out_s, in_s, l);
X    out_s[l] = 0;
X
X    return (out_s);
X}
X
X/* Append an argument to the string */
X
Xstatic void	ex_add_arg (fn)
Xchar		*fn;			/* Argument to add		*/
X{
X    if (__argc == 0)
X	__argv = (char **)malloc (50 * S_ENTRY);
X    
X    else if ((__argc % 50) == 0)
X	__argv = (char **)realloc (__argv, (__argc + 50) * S_ENTRY);
X    
X    if (__argv == (char **)NULL)
X	ex_fatal (ENOMEM, ex_nomem, (char *)NULL);
X
X    __argv[__argc++] = (fn == (char *)NULL) ? fn : ex_gspace (strlen (fn), fn);
X}
X
X/*  Skip over spaces */
X
Xstatic char	*ex_skip_sp (a)
Xchar		*a;			/* String start address		*/
X{
X    while (isspace(*a))
X        a++;
X
X    return (a);
X}
X
X/* Convert name to Unix format */
X
Xstatic char	*ex_tounix (a)
Xchar		*a;
X{
X    char	*sp = a;
X
X    while ((a = strchr (a, '\\')) != (char *)NULL)
X	*(a++) = '/';
X
X    return strlwr (sp);
X}
X
X/* Find the location of meta-characters.  If no meta, add the argument and
X * return NULL.  If meta characters, return position of end of directory
X * name.  If not multiple directories, return -1
X */
X
Xstatic int	ex_find (file, must_exist)
Xchar		*file;
Xint		must_exist;		/* FIle must exist flag		*/
X{
X    char	*p;
X    int		i;
X    static char	ex_meta[] = "?*[]\\";	/* Metacharacters		*/
X
X    if ((p = strpbrk (file, ex_meta)) == (char *)NULL)
X    {
X	if (must_exist && (access (file, 0) < 0))
X	    return 0;
X
X	ex_add_arg (file);
X	return 1;
X    }
X    
X    else if ((p = strchr (p, '/')) != (char *)NULL)
X	*(p++) = 0;
X	
X    i = ex_pfield (file, p);
X
X    if (p != (char *)NULL)
X       *(--p) = '/';
X
X    return i;
X}
X
X/* Fatal errors */
X
Xstatic void	ex_fatal (ecode, format, para)
Xint		ecode;
Xchar		*format;
Xchar		*para;
X{
X    fprintf (stderr, format, "stdargv", strerror (ecode), para);
X    exit (1);
X}
X
X/* Process Environment - note that field is a malloc'ed field */
X
Xstatic char	*ex_environment (field)
Xchar		*field;
X{
X    char	*sp, *cp, *np, *ep;
X    char	save;
X    int		b_flag;
X
X    sp = field;
X
X/* Replace any $ strings */
X
X    while ((sp = strchr (sp, '$')) != (char *)NULL)
X    {
X	if (*(cp = ++sp) == '{')
X	{
X	    b_flag = 1;
X	    ++cp;
X
X	    while (*cp && (*cp != '}'))
X		cp++;
X	}
X
X	else
X	{
X	    b_flag;
X
X	    while (isalnum(*cp))
X		cp++;
X	}
X
X/* Grab the environment variable */
X
X	if (cp == sp)
X	    continue;
X
X	save = *cp;
X	*cp = 0;
X	ep = getenv (sp + b_flag);
X	*cp = save;
X
X	if (ep != (char *)NULL)
X	{
X	    np = ex_gspace (strlen(field) - (cp - sp) + strlen (ep) - 1, field);
X	    strcpy (&np[sp - field - 1], ep);
X	    ex_tounix (&np[sp - field - 1]);
X	    free (field);
X	    strcpy ((sp = &np[strlen(np)]), cp + b_flag);
X	    field = np;
X	}
X    }
X
X    return field;
X}
X
X/* Check for multi_drive prefix */
X
Xstatic char	*_ex_multi_drive (prefix)
Xchar		*prefix;
X{
X    if (strlen (prefix) < 2)
X	return (char *)NULL;
X    
X    if (((*prefix == '*') || (*prefix == '?')) && (prefix[1] == ':'))
X	return prefix + 1;
X    
X    if (*prefix != '[')
X	return (char *)NULL;
X
X    while (*prefix && (*prefix != ']'))
X    {
X	if ((*prefix == '\\') && (*(prefix + 1)))
X	    ++prefix;
X	
X	++prefix;
X    }
X
X    return (*prefix && (*(prefix + 1) == ':')) ? prefix + 1 : (char *)NULL;
X}
X#endif
SHAR_EOF
chmod 0644 lib/stdargv.c || echo "restore of lib/stdargv.c fails"
set `wc -c lib/stdargv.c`;Sum=$1
if test "$Sum" != "12922"
then echo original size 12922, current size $Sum;fi
echo "x - extracting lib/pnmatch.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/pnmatch.c &&
X#include <stdlib.h>
X
X/* File name pattern matching function */
X
Xint	pnmatch (string, pattern, flag)
Xchar	*string;			/* String to match                  */
Xchar	*pattern;			/* Pattern to match against         */
Xint	flag;				/* Match using '$' & '^'            */
X{
X    register int	cur_s;		/* Current string character         */
X    register int	cur_p;		/* Current pattern character        */
X
X/* Match $ and ^ ? */
X
X    if (flag == 1)
X    {
X	while (*string)
X	{
X	    if (pnmatch (string++, pattern, ++flag))
X		return 1;
X	}
X
X	return 0;
X    }
X
X/* Match string */
X
X    while (cur_p = *(pattern++))
X    {
X	cur_s = *(string++);		/* Load current string character    */
X
X        switch (cur_p)			/* Switch on pattern character      */
X        {
X            case '^':			/* Match start of string            */
X            {
X                if (flag == 2)
X                    string--;
X
X                else if ((flag) || (cur_p != cur_s))
X		    return 0;
X
X                break;
X            }
X
X            case '$':			/* Match end of string              */
X            {
X                if (!flag)
X                {
X                    if (cur_p != cur_s)
X			return 0;
X
X                    break;
X                }
X
X                else
X		    return ((cur_s) ? 0 : 1);
X            }
X
X            case '[':			/* Match class of characters        */
X            {
X                while(1)
X                {
X                    if (!(cur_p = *(pattern++)))
X			return 0;
X
X                    if (cur_p == ']')
X			return 0;
X
X                    if (cur_s != cur_p)
X                    {
X                        if (*pattern == '-')
X                        {
X                            if(cur_p > cur_s)
X                                continue;
X
X                            if (cur_s > *(++pattern))
X                                continue;
X                        }
X                        else
X                            continue;
X                    }
X
X                    break;
X                }
X
X                while (*pattern)
X                {
X                    if (*(pattern++) == ']')
X                        break;
X                }
X            }
X
X            case '?':			/* Match any character              */
X            {
X                if (!cur_s)
X		    return 0;
X
X                break;
X            }
X
X            case '*':			/* Match any number of any character*/
X            {
X                string--;
X
X                do
X                {
X                    if (pnmatch (string, pattern, 0))
X			return 1;
X                }
X                while (*(string++));
X
X		return 0;
X            }
X
X            case '\\':			/* Next character is non-meta       */
X            {
X                if (!(cur_p = *(pattern++)))
X		    return 0;
X            }
X
X            default:			/* Match against current pattern    */
X            {
X                if (cur_p != cur_s)
X		    return 0;
X
X                break;
X            }
X        }
X    }
X
X    return ((flag || (!(*string))) ? 1 : 0);
X}
SHAR_EOF
chmod 0644 lib/pnmatch.c || echo "restore of lib/pnmatch.c fails"
set `wc -c lib/pnmatch.c`;Sum=$1
if test "$Sum" != "2985"
then echo original size 2985, current size $Sum;fi
echo "x - extracting lib/getopt.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lib/getopt.c &&
X/*
X *  MODULE NAME:   getopt.c				Revision 1.0
X *
X *  AUTHOR:        I. Stewartson
X *                 Data Logic Ltd.,
X *                 Queens House,
X *                 Greenhill Way,
X *                 Harrow,
X *                 Middlesex HA1 1YR.
X *                 Telephone: London (01) 863 0383
X *
X#include <logo.h>
X *  MODULE DESCRIPTION: This function is based on the UNIX library function.
X *			getopt return the next option letter in argv that
X *			matches a letter in opstring.  optstring is a string
X *			of recognised option letters; if a letter is followed
X *			by a colon, the option is expected to have an argument
X *			that may or may not be separated from it by white
X *			space.  optarg is set to point to the start of the
X *			option argument on return from getopt.
X *
X *			getopt places in optind the argv index of the next
X *			argument to be processed.  Because optind is external,
X *			it is normally initialised to zero automatically before
X *			the first call to getopt.
X *
X *			When all options have been processed (i.e. up to the
X *			first non-option argument), getopt returns EOF.  The
X *			special option -- may be used to delimit the end of
X *			the options; EOF will be returned, and -- will be
X *			skipped.
X *
X *			getopt prints an error message on stderr and returns a
X *			question mark (?) when it encounters an option letter
X *			not included in optstring.  This error message may be
X *			disabled by setting opterr to a non-zero value.
X *
X *  CALLING SEQUENCE:	The following calling sequences are used:
X *
X *			int	getopt(argc, argv, optstring)
X *			int	argc;
X *			char	**argv;
X *			char	*optstring;
X *
X *  ERROR MESSAGES:
X *			%s: illegal option -- %c
X *			%s: option requires an argument -- %c
X *
X *  INCLUDE FILES:
X */
X
X#include <stdio.h>			/* Standard Input/Output	*/
X#include <string.h>			/* String function declarations	*/
X#include <stdlib.h>			/* Standard library declarations*/
X
X/*
X *  DATA DECLARATIONS:
X */
X
Xint		opterr = 0;
Xint		optind = 1;
Xint		optopt;
Xint		optvar = 0;
Xchar		*optarg;
X
Xstatic char	*errmes1 = "%s: illegal option -- %c\n";
Xstatic char	*errmes2 = "%s: option requires an argument -- %c\n";
X
X/*
X *  MODULE ABSTRACT:
X *
X *  EXECUTABLE CODE:
X */
X
Xint	getopt(argc, argv, optstring)
Xint	argc;				/* Argument count		*/
Xchar	**argv;				/* Argument string vector	*/
Xchar	*optstring;			/* Valid options		*/
X{
X    static int	string_off = 1;		/* Current position		*/
X    int		cur_option;		/* Current option		*/
X    char	*cp;			/* Character pointer		*/
X
X    if (string_off == 1)
X    {
X	if ((optind >= argc) || (argv[optind][0] != '-') || (!argv[optind][1]))
X	    return (EOF);
X	
X	else if (!strcmp(argv[optind], "--"))
X	{
X	    optind++;
X	    return (EOF);
X	}
X    }
X
X/* Get the current character from the current argument vector */
X
X    optopt = cur_option = argv[optind][string_off];
X
X/* Validate it */
X
X    if ((cur_option == ':') || ((cur_option == '*') && optvar) ||
X	((cp = strchr(optstring, cur_option)) == (char *)NULL))
X    {
X	if (opterr)
X	    fprintf(stderr, errmes1, cur_option, argv[0]);
X
X	if (!argv[optind][++string_off])
X	{
X	    optind++;
X	    string_off = 1;
X	}
X
X	return ('?');
X    }
X
X/* Parameters following ? */
X
X    if (*(++cp) == ':')
X    {
X	if (argv[optind][string_off + 1])
X	    optarg = &argv[optind++][string_off + 1];
X
X	else if (++optind >= argc)
X	{
X	    if (opterr)
X		fprintf(stderr, errmes2, cur_option, argv[0]);
X
X	    string_off = 1;
X	    return ('?');
X	}
X
X	else
X	    optarg = argv[optind++];
X	
X	string_off = 1;
X    }
X
X    else if ((*cp == '*') && optvar)
X    {
X	if (argv[optind][string_off + 1] != 0)
X	    optarg = &argv[optind++][string_off + 1];
X	else
X	{
X	    optarg = "";
X	    optind++;
X	    string_off = 1;
X	}
X    }
X
X    else
X    {
X	if (!argv[optind][++string_off])
X	{
X	    string_off = 1;
X	    optind++;
X	}
X
X	optarg = (char *)NULL;
X    }
X
X    return (cur_option);
X}
SHAR_EOF
chmod 0644 lib/getopt.c || echo "restore of lib/getopt.c fails"
set `wc -c lib/getopt.c`;Sum=$1
if test "$Sum" != "3853"
then echo original size 3853, current size $Sum;fi
echo "x - extracting scripts/l (Text)"
sed 's/^X//' << 'SHAR_EOF' > scripts/l &&
X#!sh
Xls -C $*
SHAR_EOF
chmod 0644 scripts/l || echo "restore of scripts/l fails"
set `wc -c scripts/l`;Sum=$1
if test "$Sum" != "14"
then echo original size 14, current size $Sum;fi
echo "x - extracting scripts/extend.lst (Text)"
sed 's/^X//' << 'SHAR_EOF' > scripts/extend.lst &&
XLIB.EXE
XLINK.EXE
Xsh.exe
SHAR_EOF
chmod 0644 scripts/extend.lst || echo "restore of scripts/extend.lst fails"
set `wc -c scripts/extend.lst`;Sum=$1
if test "$Sum" != "24"
then echo original size 24, current size $Sum;fi
echo "x - extracting scripts/profile.sh (Text)"
sed 's/^X//' << 'SHAR_EOF' > scripts/profile.sh &&
X#!sh
XPATH=".;..;c:/oracle5/bin;c:/bin;c:/dos;c:/scm;c:/windows"
XCDPATH=".;..;/;c:/;c:/windows;c:/scm;c:/ian"
XINCLUDE=c:/include
XLIB=c:/lib
XTMP=c:/tmp
XTZ=GMT0BST
XEDITOR=vi
XPS1="[%p] %d %t%n %e> "
XHOME=c:/
XTERM=ibmpc-mono
XCL="-AL -Zid -W3"
XINIT=c:/bin/me_ini
XEXTENDED_LINE=c:/bin/extend.lst
XEXINIT="set aw ai sm dm sw=4 wm=5"
X
Xmsdos LIB TMP
SHAR_EOF
chmod 0644 scripts/profile.sh || echo "restore of scripts/profile.sh fails"
set `wc -c scripts/profile.sh`;Sum=$1
if test "$Sum" != "366"
then echo original size 366, current size $Sum;fi
echo "x - extracting shell/Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/Makefile &&
X#
X# MS-DOS SHELL - Makefile
X#
X# MS-DOS SHELL - Copyright (c) 1989 Data Logic Limited.
X#
X# Redistribution and use in source and binary forms are permitted
X# provided that the above copyright notice is duplicated in the
X# source form.
X#
X#
X#    $Header$
X#
X#    $Log$
X#
X
XOBJS=sh0.obj sh1.obj sh2.obj sh3.obj sh4.obj sh5.obj sh6.obj	\
X     sh7.obj sh8.obj sh9.obj sh10.obj
XASFLAGS= /Ml /Zi /Zd
X
Xsh.exe:	$(OBJS)
X	link sh0+sh1+sh2+sh3+sh4+sh5+sh6+sh7+sh8+sh9+sh10/noi, sh.exe;
SHAR_EOF
chmod 0644 shell/Makefile || echo "restore of shell/Makefile fails"
set `wc -c shell/Makefile`;Sum=$1
if test "$Sum" != "470"
then echo original size 470, current size $Sum;fi
echo "x - extracting shell/sh1.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh1.c &&
X/* MS-DOS SHELL - Main program, memory and variable management
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
X *
X * This code is based on (in part) the shell program written by Charles
X * Forsyth and is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh1.c 1.1 90/01/25 13:40:39 MS_user Exp $
X *
X *    $Log:	sh1.c $
X * Revision 1.1  90/01/25  13:40:39  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <stdarg.h>
X#include <string.h>
X#include <unistd.h>
X#include <ctype.h>
X#include <fcntl.h>
X#include <limits.h>
X#include <dos.h>
X#include <time.h>
X#include "sh.h"
X
X/*
X * Structure of Malloced space to allow release of space nolonger required
X * without having to know about it.
X */
X
Xtypedef struct region {
X    struct region	*next;
X    int			area;
X} s_region;
X
Xstatic struct region	*areastart = (s_region *)NULL;
X
X/*
X * default shell, search rules
X */
X
Xstatic char	*shellname = "c:/bin/sh";
Xstatic char	*search    = ";c:/bin;c:/usr/bin";
Xstatic char	*ymail     = "You have mail\n";
Xstatic char	*Path	   = "PATH";
X#ifdef SIGQUIT
Xstatic void	(*qflag)() = SIG_IGN;
X#endif
X
X/* Functions */
X
Xstatic char	*cclass (char *, int, bool);
Xstatic char	*copy_to_equals (char *, char *);
Xstatic void	nameval (Var_List *, char *, char *, bool);
Xstatic void	patch_up (void);
Xstatic void	onecommand (void);
Xstatic void	Check_Mail (void);
Xstatic void	Pre_Process_Argv (char **);
Xstatic void	Load_G_VL (void);
X
X/*
X * The main program starts here
X */
X
Xvoid		main (argc, argv)
Xint		argc;
Xregister char	**argv;
X{
X    register int	f;
X    register char	*s, *s1;
X    int			cflag = 0;
X    int			sc;
X    char		*name, **ap;
X    int			(*iof)(IO_State *) = filechar;
X    Var_List		*lset;
X    bool		l_rflag = FALSE;
X
X/* Patch up various parts of the system */
X
X    patch_up ();
X
X/* Load the environment into our structures */
X
X    if ((ap = environ) != (char **)NULL)
X    {
X	while (*ap)
X	    assign (*ap++, !COPYV);
X
X	for (ap = environ; *ap;)
X	    s_vstatus (lookup (*ap++, TRUE), EXPORT);
X    }
X
X/* Zap all files */
X
X    closeall ();
X    areanum = 1;
X
X/* Get the current directory */
X
X    Getcwd ();
X
X/* Set up some stardard variables if their not set */
X
X    if ((lset = lookup (shell, TRUE))->value == null)
X	setval (lset, shellname);
X
X    s_vstatus (lset, EXPORT);
X
X/* Check for restricted shell */
X
X    if ((s = strrchr (lset->value, '/')) == (char *)NULL)
X	s = lset->value;
X
X    else
X	s++;
X
X    if (*s == 'r')
X	l_rflag = TRUE;
X
X/* Set up home directory */
X
X    if ((lset = lookup (home, TRUE))->value == null)
X	setval (lset, "c:/");
X
X    s_vstatus (lset, EXPORT);
X
X/* Set up history file location */
X
X    setval (lookup ("$", TRUE), putn (getpid ()));
X
X    Load_G_VL ();
X    path->status |= (EXPORT | PONLY);
X    ifs->status  |= (EXPORT | PONLY);
X    ps1->status  |= (EXPORT | PONLY);
X    ps2->status  |= (EXPORT | PONLY);
X
X    if (path->value == null)
X	setval (path, search);
X
X    if (ifs->value == null)
X	setval (ifs, " \t\n");
X
X    if (ps1->value == null)
X	setval (ps1, "$ ");
X
X    if (ps2->value == null)
X	setval (ps2, "> ");
X
X/* Check the restricted shell */
X
X    if ((s = strrchr ((name = *argv), '/')) == (char *)NULL)
X	s = name;
X
X    if ((s1 = strchr (s, '.')) != (char *)NULL)
X	*s1 = 0;
X
X    if (strcmp (s, "rsh") == 0)
X	l_rflag = TRUE;
X
X    if (s1 != (char *)NULL)
X	*s1 = '.';
X
X/* Preprocess options to convert two character options of the form /x to
X * -x.  Some programs!!
X */
X
X     Pre_Process_Argv (argv);
X
X/* Process the options */
X
X    while ((sc = getopt (argc, argv, "abc:defghijklmnopqrtsuvwxyz0")) != EOF)
X    {
X	switch (sc)
X	{
X	    case '0':				/* Level 0 flag for DOS	*/
X		level0 = TRUE;
X		break;
X
X	    case 'r':				/* Restricted		*/
X		l_rflag = TRUE;
X		break;
X
X	    case 'c':				/* Command on line	*/
X		ps1->status &= ~EXPORT;
X		ps2->status &= ~EXPORT;
X		setval (ps1, null);
X		setval (ps2, null);
X		cflag = 1;
X
X		PUSHIO (aword, optarg, iof = nlchar);
X		break;
X
X	    case 'q':				/* No quit ints		*/
X#ifdef SIGQUIT
X		qflag = SIG_DFL;
X#endif
X		break;
X
X	    case 's':				/* standard input	*/
X		break;
X
X	    case 't':				/* One command		*/
X		ps1->status &= ~EXPORT;
X		setval (ps1, null);
X		iof = linechar;
X		break;
X
X	    case 'i':				/* Set interactive	*/
X		talking++;
X
X	    default:
X		if (islower (sc))
X		    FL_SET (sc);
X	}
X    }
X
X    argv += optind;
X    argc -= optind;
X
X/* Execute one off command - disable prompts */
X
X    if ((iof == filechar) && (argc > 0))
X    {
X	setval (ps1, null);
X	setval (ps2, null);
X	ps1->status &= ~EXPORT;
X	ps2->status &= ~EXPORT;
X
X	f = 0;
X
X/* Open the file if necessary */
X
X	if (strcmp ((name = *argv), "-") != 0)
X	{
X	    if ((f = O_for_execute (name)) < 0)
X	    {
X		print_error ("%s: cannot open\n", name);
X		exit (1);
X	    }
X	}
X
X	next (remap (f));		/* Load into I/O stack	*/
X    }
X
X/* Set up the $- variable */
X
X    setdash ();
X
X/* Load terminal I/O structure if necessary and load the history file */
X
X    if (e.iop < iostack)
X    {
X	PUSHIO (afile, 0, iof);
X
X	if (isatty (0) && isatty (1) && !cflag)
X	{
X	    fprintf (stderr, Copy_Right1, _osmajor, _osminor);
X	    fputs (Copy_Right2, stderr);
X
X	    talking++;
X	    History_Enabled = TRUE;
X	    Load_History ();
X	}
X    }
X
X#ifdef SIGQUIT
X    signal (SIGQUIT, qflag);
X#endif
X
X/* Read profile ? */
X
X    if (((name != (char *)NULL) && (*name == '-')) || level0)
X    {
X	talking++;
X
X	if ((f = O_for_execute ("/etc/profile")) >= 0)
X	    next (remap(f));
X
X	if ((f = O_for_execute ("profile")) >= 0)
X	    next (remap(f));
X    }
X
X/* Set up signals */
X
X    if (talking)
X	signal (SIGTERM, sig);
X
X    if (signal (SIGINT, SIG_IGN) != SIG_IGN)
X	signal (SIGINT, onintr);
X
X/* Load any parameters */
X
X    dolv = argv;
X    dolc = argc;
X    dolv[0] = name;
X
X    if (dolc > 1)
X    {
X	for (ap = ++argv; --argc > 0;)
X	{
X	    if (assign (*ap = *argv++, !COPYV))
X		dolc--;					/* keyword */
X
X	    else
X		ap++;
X	}
X    }
X
X    setval (lookup ("#", TRUE), putn ((--dolc < 0) ? (dolc = 0) : dolc));
X
X/* Execute the command loop */
X
X    while (1)
X    {
X	if (talking && e.iop <= iostack)
X	{
X	    Check_Mail ();
X	    put_prompt (ps1->value);
X	    r_flag = l_rflag;
X	}
X
X	onecommand ();
X    }
X}
X
X/*
X * Set up the value of $-
X */
X
Xvoid	setdash ()
X{
X    register char	*cp, c;
X    char		m['z' - 'a' + 1];
X
X    for (cp = m, c = 'a'; c <= 'z'; ++c)
X    {
X	if (FL_TEST (c))
X	    *(cp++) = c;
X    }
X
X    *cp = 0;
X    setval (lookup ("-", TRUE), m);
X}
X
X/* Execute a command */
X
Xstatic void	onecommand ()
X{
X    register int	i;
X    jmp_buf		m1;
X    C_Op		*outtree;
X
X
X/* Exit any previous environments */
X
X    while (e.oenv)
X	quitenv ();
X
X/* initialise space */
X
X    areanum = 1;
X    freehere (areanum);
X    freearea (areanum);
X    wdlist = (Word_B *)NULL;
X    iolist = (Word_B *)NULL;
X    e.errpt = (int *)NULL;
X    e.cline = space (LINE_MAX);
X    e.eline = e.cline + LINE_MAX - 5;
X    e.linep = e.cline;
X    yynerrs = 0;
X    multiline = 0;
X    inparse = 1;
X    SW_intr = 0;
X    execflg = 0;
X
X/* Get the line and process it */
X
X    if (setjmp (failpt = m1) || ((outtree = yyparse ()) == (C_Op *)NULL) ||
X	SW_intr)
X    {
X
X/* Failed - clean up */
X
X	while (e.oenv)
X	    quitenv ();
X
X	scraphere ();
X
X	if (!talking && SW_intr)
X	    leave ();
X
X/* Exit */
X
X	inparse = 0;
X	SW_intr = 0;
X	return;
X    }
X
X/* Ok - reset some variables and then execute the command tree */
X
X    inparse = 0;
X    Break_List = (Break_C *)NULL;
X    Return_List = (Break_C *)NULL;
X    SShell_List = (Break_C *)NULL;
X    SW_intr = 0;
X    execflg = 0;
X
X/* Set execute function recursive level and the SubShell count to zero */
X
X    Execute_stack_depth = 0;
X
X/* Set up Redirection IO (Saved) array and SubShell Environment information */
X
X    NSave_IO_E = 0;		/* Number of entries		*/
X    MSave_IO_E = 0;		/* Max Number of entries	*/
X    NSubShells = 0;		/* Number of entries		*/
X    MSubShells = 0;		/* Max Number of entries	*/
X
X/* Save the environment information */
X
X    if (talking && e.iop <= iostack)
X	Add_History (FALSE);
X
X    if (!FL_TEST ('n'))
X	execute (outtree, NOPIPE, NOPIPE, 0);
X
X/* Make sure the I/O and environment are back at level 0 and then clear them */
X
X    Execute_stack_depth = 0;
X
X    if (NSubShells != 0)
X	Delete_G_VL ();
X
X    if (NSave_IO_E)
X	restore_std (0);
X
X    if (MSubShells)
X	DELETE (SubShells);
X
X    if (MSave_IO_E)
X	DELETE (SSave_IO);
X
X/* Check for interrupts */
X
X    if (!talking && SW_intr)
X    {
X	execflg = 0;
X	leave ();
X    }
X
X/* Run any traps that are required */
X
X    if ((i = trapset) != 0)
X    {
X	trapset = 0;
X	runtrap (i);
X    }
X}
X
X/*
X * Terminate current environment with an error
X */
X
Xvoid	fail ()
X{
X    longjmp (failpt, 1);
X
X    /* NOTREACHED */
X}
X
X/*
X * Exit the shell
X */
X
Xvoid	leave ()
X{
X    if (execflg)
X	fail ();
X
X/* Clean up */
X
X    scraphere ();
X    freehere (1);
X
X/* Trap zero on exit */
X
X    runtrap (0);
X
X/* Dump history on exit */
X
X    if (talking && isatty(0))
X	Dump_History ();
X
X    closeall ();
X    exit (exstat);
X
X/* NOTREACHED */
X}
X
X/*
X * Output warning message
X */
X
Xvoid	print_warn (fmt)
Xchar	*fmt;
X{
X    va_list	ap;
X    char	x[100];
X
X    va_start (ap, fmt);
X    vsprintf (x, fmt, ap);
X    S_puts (x);
X    exstat = -1;
X
X/* If leave on error - exit */
X
X    if (FL_TEST ('e'))
X	leave ();
X
X    va_end (ap);
X}
X
X/*
X * Output error message
X */
X
Xvoid	print_error (fmt)
Xchar	*fmt;
X{
X    va_list	ap;
X    char	x[100];
X
X/* Error message processing */
X
X    va_start (ap, fmt);
X    vsprintf (x, fmt, ap);
X    S_puts (x);
X    exstat = -1;
X
X    if (FL_TEST ('e'))
X	leave ();
X
X    va_end (ap);
X
X/* Error processing */
X
X    if (FL_TEST ('n'))
X	return;
X
X/* If not interactive - exit */
X
X    if (!talking)
X	leave ();
X
X    if (e.errpt)
X	longjmp (e.errpt, 1);
X
X/* closeall (); Removed - caused problems.  There may be problems
X * remaining with files left open?
X */
X
X    e.iop = e.iobase = iostack;
X}
X
X/*
X * Create or delete a new environment.  If f is set, delete the environment
X */
X
Xbool	newenv (f)
Xint	f;
X{
X    register Environ	*ep;
X
X/* Delete environment? */
X
X    if (f)
X    {
X	quitenv ();
X	return TRUE;
X    }
X
X/* Create a new environment */
X
X    if ((ep = (Environ *) space (sizeof (Environ))) == (Environ *)NULL)
X    {
X	while (e.oenv)
X	    quitenv ();
X
X	fail ();
X    }
X
X    *ep = e;
X    e.oenv = ep;
X    e.errpt = errpt;
X    return FALSE;
X}
X
X/*
X * Exit the current environment successfully
X */
X
Xvoid	quitenv ()
X{
X    register Environ	*ep;
X    register int	fd;
X
X/* Restore old environment, delete the space and close any files opened in
X * this environment
X */
X
X    if ((ep = e.oenv) != (Environ *)NULL)
X    {
X	fd = e.iofd;
X	e = *ep;
X
X	DELETE (ep);
X
X	while (--fd >= e.iofd)
X	    S_close (fd, TRUE);
X    }
X}
X
X/*
X * Is character c in s?
X */
X
Xbool		any (c, s)
Xregister char	c;
Xregister char	*s;
X{
X    while (*s)
X    {
X	if (*(s++) == c)
X	    return TRUE;
X    }
X
X    return FALSE;
X}
X
X/*
X * Convert binary to ascii
X */
X
Xchar		*putn (n)
Xregister int	n;
X{
X    static char		nt[10];
X
X    sprintf (nt, "%u", n);
X    return nt;
X}
X
X/*
X * Add a file to the input stack
X */
X
Xvoid	next (f)
Xint	f;
X{
X    PUSHIO (afile, f, filechar);
X}
X
X/*
X * SIGINT interrupt processing
X */
X
Xvoid	onintr (signo)
Xint	signo;
X{
X
X/* Restore signal processing and set SIGINT detected flag */
X
X    signal (SIGINT, onintr);
X    SW_intr = 1;
X
X/* Are we talking to the user?  Yes - check in parser */
X
X    if (talking)
X    {
X	if (inparse)
X	{
X	    S_putc (NL);
X	    fail ();
X	}
X    }
X
X/* No - exit */
X
X    else
X    {
X	execflg = 0;
X	leave ();
X    }
X}
X
X/*
X * Grap some space and check for an error
X */
X
Xchar	*space (n)
Xint	n;
X{
X    register char *cp;
X
X    if ((cp = getcell (n)) == (char *)NULL)
X	print_error ("sh: out of string space\n");
X
X    return cp;
X}
X
X/*
X * Save a string in a given area
X */
X
Xchar		*strsave (s, a)
Xregister char	*s;
X{
X    register char	*cp;
X
X    if ((cp = space (strlen (s) + 1)) != (char *)NULL)
X    {
X	setarea ((char *)cp, a);
X	return strcpy (cp, s);
X    }
X
X    return null;
X}
X
X/*
X * trap handling - Save signal number and restore signal processing
X */
X
Xvoid		sig (i)
Xregister int	i;
X{
X    trapset = i;
X    signal (i, sig);
X}
X
X/*
X * Execute a trap command
X */
X
Xvoid	runtrap (i)
Xint	i;
X{
X    char	*trapstr;
X    char	tval[10];
X
X    sprintf (tval, "~%d", i);
X
X    if (((trapstr = lookup (tval, FALSE)->value)) == null)
X	return;
X
X/* If signal zero, save a copy of the trap value and then delete the trap */
X
X    if (i == 0)
X    {
X	trapstr = strsave (trapstr, areanum);
X	unset (tval, TRUE);
X    }
X
X    RUN (aword, trapstr, nlchar);
X}
X
X/*
X * Find the given name in the dictionary and return its value.  If the name was
X * not previously there, enter it now and return a null value.
X */
X
XVar_List	*lookup (n, cflag)
Xregister char	*n;
Xbool		cflag;
X{
X    register Var_List	*vp;
X    register char	*cp;
X    register int	c;
X    static Var_List	dummy;
X
X/* Set up the dummy variable */
X
X    dummy.name = n;
X    dummy.status = RONLY;
X    dummy.value = null;
X
X/* If digit string - use the dummy to return the value */
X
X    if (isdigit (*n))
X    {
X	for (c = 0; isdigit (*n) && (c < 1000); n++)
X	    c = c * 10 + *n - '0';
X
X	dummy.value = (c <= dolc) ? dolv[c] : null;
X	return &dummy;
X    }
X
X/* Look up in list */
X
X    for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
X    {
X	if (eqname (vp->name, n))
X	    return vp;
X    }
X
X/* If we don't want to create it, return a dummy */
X
X    if (!cflag)
X	return &dummy;
X
X/* Create a new variable */
X
X    cp = findeq (n);
X
X    if (((vp = (Var_List *)space (sizeof (Var_List))) == (Var_List *)NULL)
X	|| (vp->name = space ((int)(cp - n) + 2)) == (char *)NULL)
X    {
X	dummy.name = null;
X	return &dummy;
X    }
X
X/* Set area for space to zero - no auto-delete */
X
X    setarea ((char *)vp, 0);
X    setarea ((char *)vp->name, 0);
X
X/* Just the name upto the equals sign, no more */
X
X    copy_to_equals (vp->name, n);
X
X/* Link into list */
X
X    vp->value = null;
X    vp->next = vlist;
X    vp->status = GETCELL;
X    vlist = vp;
X    return vp;
X}
X
X/*
X * give variable at `vp' the value `val'.
X */
X
Xvoid		setval(vp, val)
XVar_List	*vp;
Xchar		*val;
X{
X    nameval (vp, val, (char *)NULL, FALSE);
X}
X
X/*
X * Copy and check that it terminates in an equals sign
X */
X
Xstatic char	*copy_to_equals (d, s)
Xchar		*d, *s;
X{
X    int		n = (int) (findeq (s) - s);
X
X    strncpy (d, s, n);
X    *(d += n) = '=';
X    *(++d) = 0;
X    return d;
X}
X
X/*
X * Set up new value for name
X *
X * If name is not NULL, it must be a prefix of the space `val', and end with
X * `='.  This is all so that exporting values is reasonably painless.
X */
X
Xstatic void		nameval (vp, val, name, disable)
Xregister Var_List	*vp;
Xchar			*val;
Xchar			*name;
Xbool			disable;
X{
X    register char	*xp;
X    int			fl = 0;
X
X/* Check if variable is read only */
X
X    if (vp->status & RONLY)
X    {
X	char	c = *(xp = findeq (vp->name));
X
X	*xp = 0;
X	S_puts (xp);
X	*xp = c;
X	print_error (" is read-only\n");
X	return;
X    }
X
X/* Check for $PATH reset in restricted shell */
X
X    if (!disable && (strcmp (vp->name, Path) == 0) && check_rsh (Path))
X	return;
X
X/* Get space for string ? */
X
X    if (name == (char *)NULL)
X    {
X	if ((xp = space (strlen (vp->name) + strlen (val) + 2)) == (char *)NULL)
X	    return;
X
X/* make string:  name=value */
X
X	setarea ((char *)xp, 0);
X	name = xp;
X
X	xp = copy_to_equals (xp, vp->name);
X	strcpy (xp, val);
X	val = xp;
X	fl = GETCELL;
X    }
X
X    if (vp->status & GETCELL)
X	DELETE (vp->name);	/* form new string `name=value' */
X
X    vp->name = name;
X    vp->value = val;
X    vp->status |= fl;
X
X    if (FL_TEST ('a'))
X	s_vstatus (vp, EXPORT);
X}
X
X/*
X * Set the status of an environment variable
X */
X
Xvoid		s_vstatus (vp, flag)
XVar_List	*vp;
Xint		flag;			/* Addition status flags	*/
X{
X    if (isalpha (*vp->name))		/* not an internal symbol ($# etc) */
X	vp->status |= flag;
X}
X
X/*
X * Check for assignment X=Y
X */
X
Xbool		isassign (s)
Xregister char	*s;
X{
X    if (!isalpha (*s))
X	return FALSE;
X
X    for (; *s != '='; s++)
X    {
X	if (!*s || !isalnum (*s))
X	    return FALSE;
X    }
X
X    return TRUE;
X}
X
X/*
X * Execute an assignment.  If a valid assignment, load it into the variable
X * list.
X */
X
Xbool		assign (s, cf)
Xregister char	*s;
Xint		cf;
X{
X    register char	*cp;
X    Var_List		*vp;
X
X    if (!isalpha (*s))
X	return FALSE;
X
X    for (cp = s; *cp != '='; cp++)
X    {
X	if (!*cp || !isalnum (*cp))
X	    return FALSE;
X    }
X
X    nameval ((vp = lookup (s, TRUE)), ++cp, (cf == COPYV ? (char *)NULL : s),
X	     FALSE);
X
X    if (cf != COPYV)
X	vp->status &= ~GETCELL;
X
X    return TRUE;
X}
X
X/*
X * Compare two environment strings
X */
X
Xbool			eqname(n1, n2)
Xregister char		*n1, *n2;
X{
X    for (; *n1 != '=' && *n1 != 0; n1++)
X    {
X	if (*n2++ != *n1)
X	    return FALSE;
X    }
X
X    return (!*n2 || (*n2 == '=')) ? TRUE : FALSE;
X}
X
X/*
X * Find the equals sign in a string
X */
X
Xchar		*findeq (cp)
Xregister char	*cp;
X{
X    while (*cp && (*cp != '='))
X	cp++;
X
X    return cp;
X}
X
X/*
X * Duplicate the Variable List for a Subshell
X *
X * Create a new Var_list environment for a Sub Shell
X */
X
Xint	Create_NG_VL ()
X{
X    int			i;
X    S_SubShell		*sp;
X    Var_List		*vp, *vp1;
X
X    for (sp = SubShells, i = 0; (i < NSubShells) &&
X			       (SubShells[i].depth < Execute_stack_depth);
X	 i++);
X
X/* If depth is greater or equal to the Execute_stack_depth - we should panic
X * as this should not happen.  However, for the moment, I'll ignore it
X */
X
X    if (NSubShells == MSubShells)
X    {
X	sp = (S_SubShell *)space ((MSubShells + SSAVE_IO_SIZE) * sizeof (S_SubShell));
X
X/* Check for error */
X
X	if (sp == (S_SubShell *)NULL)
X	{
X	    errno = ENOMEM;
X	    return -1;
X	}
X
X/* Save original data */
X
X	if (MSubShells != 0)
X	{
X	    memcpy (sp, SubShells, sizeof (S_SubShell) * MSubShells);
X	    DELETE (SubShells);
X	}
X
X	setarea ((char *)sp, 0);
X	SubShells = sp;
X	MSubShells += SSAVE_IO_SIZE;
X    }
X
X/* Save the depth and the old vlist value */
X
X    sp = &SubShells[NSubShells++];
X    sp->depth  = Execute_stack_depth;
X    sp->header = vlist;
X    vlist = (Var_List *)NULL;
X
X/* Duplicate the old Variable list */
X
X    for (vp = sp->header; vp != (Var_List *)NULL; vp = vp->next)
X    {
X	nameval ((vp1 = lookup (vp->name, TRUE)), findeq (vp->name) + 1,
X		 (char *)NULL, TRUE);
X
X	vp1->status |= (vp->status & (C_MSDOS | PONLY | EXPORT | RONLY));
X    }
X
X/* Reset global values */
X
X    Load_G_VL ();
X    return 0;
X}
X
X/*
X * Delete a SubShell environment and restore the original
X */
X
Xvoid	Delete_G_VL ()
X{
X    int		j;
X    S_SubShell	*sp;
X    Var_List	*vp;
X
X    for (j = NSubShells; j > 0; j--)
X    {
X       sp = &SubShells[j - 1];
X
X       if (sp->depth < Execute_stack_depth)
X	   break;
X
X/* Reduce number of entries */
X
X	--NSubShells;
X
X/* Release the space */
X
X	for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
X	{
X	    if (vp->status & GETCELL)
X		DELETE (vp->name);
X
X	    DELETE (vp);
X	}
X
X/* Restore vlist information */
X
X	vlist = sp->header;
X	Load_G_VL ();
X    }
X}
X
X/*
X * Load GLobal Var List values
X */
X
Xstatic void	Load_G_VL ()
X{
X    path  = lookup (Path, TRUE);
X    ifs   = lookup ("IFS", TRUE);
X    ps1   = lookup ("PS1", TRUE);
X    ps2   = lookup ("PS2", TRUE);
X    C_dir = lookup ("~", TRUE);
X    Restore_Dir ();
X}
X
X/*
X * Match a pattern as in sh(1).
X */
X
Xbool		gmatch (s, p, IgnoreCase)
Xregister char	*s, *p;
Xbool		IgnoreCase;
X{
X    register int	sc, pc;
X
X    if ((s == (char *)NULL) || (p == (char *)NULL))
X	return FALSE;
X
X    while ((pc = *(p++) & CMASK) != '\0')
X    {
X	sc = *(s++) & QMASK;
X
X	switch (pc)
X	{
X	    case '[':			/* Class expression		*/
X		if ((p = cclass (p, sc, IgnoreCase)) == (char *)NULL)
X		    return FALSE;
X
X		break;
X
X	    case '?':			/* Match any character		*/
X		if (sc == 0)
X		    return FALSE;
X
X		break;
X
X	    case '*':			/* Match as many as possible	*/
X		s--;
X		do
X		{
X		    if (!*p || gmatch (s, p, IgnoreCase))
X			return TRUE;
X
X		} while (*(s++));
X
X		return FALSE;
X
X	    default:
X		if (IgnoreCase)
X		{
X		    sc = tolower (sc);
X		    pc = tolower ((pc & ~QUOTE));
X		}
X
X		if (sc != (pc & ~QUOTE))
X		    return FALSE;
X	}
X    }
X
X    return (*s == 0) ? TRUE : FALSE;
X}
X
X/*
X * Process a class expression - []
X */
X
Xstatic char	*cclass (p, sub, IgnoreCase)
Xregister char	*p;
Xregister int	sub;
Xbool		IgnoreCase;
X{
X    register int	c, d, not, found;
X
X/* Exclusive or inclusive class */
X
X    if ((not = *p == NOT) != 0)
X	p++;
X
X    found = not;
X
X    do
X    {
X	if (!*p)
X	    return (char *)NULL;
X
X/* Get the next character in class, converting to lower case if necessary */
X
X	c = IgnoreCase ? tolower ((*p & CMASK)) : (*p & CMASK);
X
X/* If this is a range, get the end of range character */
X
X	if ((*(p + 1) == '-') && (*(p + 2) != ']'))
X	{
X	    d = IgnoreCase ? tolower ((*(p + 2) & CMASK)) : (*(p + 2) & CMASK);
X	    p++;
X	}
X
X	else
X	    d = c;
X
X/* Is the current character in the class? */
X
X	if ((c <= sub) && (sub <= d))
X	    found = !not;
X
X    } while (*(++p) != ']');
X
X    return found ? p + 1 : (char *)NULL;
X}
X
X/*
X * Get a string in a malloced area
X */
X
Xchar		*getcell(nbytes)
Xunsigned int	nbytes;
X{
X    s_region		*np;
X
X    if (nbytes == 0)
X	abort ();	/* silly and defeats the algorithm */
X
X/* Grab some space */
X
X    if ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1)) == (s_region *)NULL)
X        return (char *)NULL;
X
X/* Link into chain */
X
X    np->next = areastart;
X    np->area = areanum;
X    areastart = np;
X
X    return ((char *)np) + sizeof (s_region);
X}
X
X/*
X * Free a string in a malloced area
X */
X
Xvoid	freecell (s)
Xchar	*s;
X{
X    register s_region	*cp = areastart;
X    s_region		*lp = (s_region *)NULL;
X    s_region		*sp = (s_region *)(s - sizeof (s_region));
X
X/* Find the string in the chain */
X
X    if (s != (char *)NULL)
X    {
X	while (cp != (s_region *)NULL)
X	{
X	    if (cp != sp)
X	    {
X		lp = cp;
X		cp = cp->next;
X		continue;
X	    }
X
X/* First in chain ? */
X
X	    else if (lp == (s_region *)NULL)
X		areastart = cp->next;
X
X/* Delete the current entry and relink */
X
X	    else
X		lp->next = cp->next;
X
X	    free (cp);
X	    break;
X	}
X    }
X}
X
X/*
X * Autodelete space nolonger required.  Ie. Free all the strings in a malloced
X * area
X */
X
Xvoid		freearea (a)
Xregister int	a;
X{
X    register s_region	*cp = areastart;
X    s_region		*lp = (s_region *)NULL;
X
X    while (cp != (s_region *)NULL)
X    {
X
X/* Is the area number less than that specified - yes, continue */
X	if (cp->area < a)
X	{
X	    lp = cp;
X	    cp = cp->next;
X	}
X
X/* OK - delete the area.  Is it the first in chain ?  Yes, delete, relink
X * and update start location
X */
X
X	else if (lp == (s_region *)NULL)
X	{
X	    lp = cp;
X	    cp = cp->next;
X	    areastart = cp;
X
X	    free (lp);
X	    lp = (char *)NULL;
X	}
X
X/* Not first, delete the current entry and relink */
X
X	else
X	{
X	    lp->next = cp->next;
X	    free (cp);
X	    cp = lp->next;
X	}
X    }
X}
X
X/*
X * Set the area number for a malloced string.  This allows autodeletion of
X * space that is nolonger required.
X */
X
Xvoid	setarea (cp,a)
Xchar	*cp;
Xint	a;
X{
X    s_region	*sp = (s_region *)(cp - sizeof (s_region));
X
X    if (cp != (char *)NULL)
X	sp->area = a;
X}
X
X/*
X * Get the area number for a malloced string
X */
X
Xint	getarea (cp)
Xchar	*cp;
X{
X    s_region	*sp = (s_region *)(cp - sizeof (s_region));
X
X    return sp->area;
X}
X
X/* Output one of the Prompt.  We save the prompt for the history part of
X * the program
X */
X
Xvoid	put_prompt (s)
Xchar	*s;
X{
X    struct dosdate_t 	d_date;
X    struct dostime_t	d_time;
X    int			i;
X    char		buf[PATH_MAX + 4];
X
X    last_prompt = s;		/* Save the Last prompt output		*/
X
X    _dos_gettime (&d_time);	/* Get the date and time in case	*/
X    _dos_getdate (&d_date);
X
X    while (*s)
X    {
X
X/* If a format character, process it */
X
X	if (*s == '%')
X	{
X	    s++;
X	    *s = tolower(*s);
X
X	    if (*s == '%')
X		S_putc ('%');
X
X	    else
X	    {
X		*buf = 0;
X
X		switch (*s)
X		{
X		    case 'e':		    /* Current event number */
X			if (History_Enabled)
X			    sprintf (buf, "%d", Current_Event + 1);
X
X			break;
X
X		    case 't':		    /* time	    */
X			sprintf (buf,"%.2d:%.2d", d_time.hour, d_time.minute);
X			break;
X
X		    case 'd':		    /* date	    */
X			sprintf (buf, "%.3s %.2d-%.2d-%.2d",
X				 &"SunMonTueWedThuFriSat"[d_date.dayofweek * 3],
X				 d_date.day, d_date.month, d_date.year % 100);
X			break;
X
X		    case 'p':		    /* directory    */
X		    case 'n':		    /* default drive */
X			strcpy (buf, C_dir->value);
X
X			if (*s == 'n')
X			    buf[1] = 0;
X
X			break;
X
X		    case 'v':		    /* version	    */
X			sprintf (buf, "MS-DOS %.2d:%.2d", _osmajor, _osminor);
X			break;
X		}
X
X/* Output the string */
X
X		S_puts (buf);
X	    }
X	}
X
X/* Escaped character ? */
X
X	else if (*s == '\\')
X	{
X	    if ((i = Process_Escape (&s)) == -1)
X		i = 0;
X
X	    S_putc (i);
X	}
X
X	else
X	    S_putc (*s);
X
X/* Go to the next character */
X
X	s++;
X    }
X}
X
X/*
X * Get the current path in UNIX format and save it in the environment
X * variable $~
X */
X
Xvoid	Getcwd ()
X{
X    char	ldir[PATH_MAX + 6];
X    char	*cp = getcwd (ldir, PATH_MAX + 4);
X
X    strlwr (cp);
X
X/* Convert to Unix format */
X
X    while (*cp)
X    {
X	if (*cp == '\\')
X	    *cp = '/';
X
X	++cp;
X    }
X
X/* Save in environment */
X
X    setval ((C_dir = lookup ("~", TRUE)), ldir);
X}
X
X/*
X * Patch up various parts of the system for the shell.  At the moment, we
X * modify the ctype table so that _ is an upper case character.
X */
X
Xstatic void	patch_up ()
X{
X/* Patch the ctype table as a cheat */
X
X    (_ctype+1)['_'] |= _UPPER;
X}
X
X/*
X * Mail Check processing.  Every $MAILCHECK seconds, we check either $MAIL
X * or $MAILPATH to see if any file has changed its modification time since
X * we last looked.  In $MAILCHECK, the files are separated by colons (:).
X * If the filename contains a %, the string following the % is the message
X * to display if the file has changed.
X */
X
Xstatic void	Check_Mail ()
X{
X    int			delay = atoi (lookup ("MAILCHECK", FALSE)->value);
X    Var_List		*mail = lookup ("MAIL", FALSE);
X    Var_List		*mailp = lookup ("MAILPATH", FALSE);
X    static time_t	last = 0L;
X    time_t		current = time ((time_t *)NULL);
X    struct stat		st;
X    char		*cp, *sp, *ap;
X
X/* Have we waited long enough */
X
X    if ((current - last) < delay)
X	return;
X
X/* Yes - Check $MAILPATH.  If it is defined, process it.  Otherwise, use
X * $MAIL
X */
X
X    if (mailp->value != null)
X    {
X
X/* Check MAILPATH */
X
X	sp = mailp->value;
X
X/* Look for the next separator */
X
X	while ((cp = strchr (sp, ':')) != (char *)NULL)
X	{
X	    *cp = 0;
X
X/* % in string ? */
X
X	    if ((ap = strchr (ap, '%')) != (char *)NULL)
X		*ap = 0;
X
X/* Check the file name */
X
X	    if ((stat (sp, &st) != -1) && (st.st_mtime > last))
X	    {
X		if (ap != (char *)NULL)
X		{
X		    S_puts (ap + 1);
X		    S_putc (NL);
X		}
X
X		else
X		    S_puts (ymail);
X	    }
X
X/* Restore the % */
X
X	    if (ap != (char *)NULL)
X		*ap = '%';
X
X/* Restore the colon and find the next one */
X
X	    *cp = ':';
X	    sp = cp + 1;
X	}
X    }
X
X/* Just check MAIL */
X
X    else if ((mail->value != null) && (stat (mail->value, &st) != -1) &&
X	     (st.st_mtime > last))
X	S_puts (ymail);
X
X/* Save the last check time */
X
X    last = current;
X}
X
X/*
X * Preprocess Argv to get handle of options in the format /x
X *
X * Some programs invoke the shell using / instead of - to mark the options.
X * We need to convert to -.  Also /c is a special case.  The rest of the
X * command line is the command to execute.  So, we get the command line
X * from the original buffer instead of argv array.
X */
X
Xstatic void	Pre_Process_Argv (argv)
Xchar		**argv;
X{
X    char	*ocl = (char far *)((((long)_psp) << 16) + 0x081L);
X
X
X/* Check for these options */
X
X    while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
X	   (**argv == '/'))
X    {
X       *strlwr (*argv) = '-';
X
X/* Get the original information from the command line */
X
X       if ((*argv)[1] == 'c')
X       {
X	    while ((*ocl != '/') && (*(ocl + 1) != 'c') && (*ocl) &&
X		   (*ocl != '\r'))
X		++ocl;
X
X	    if (*ocl != '/')
X		continue;
X
X/* Find the start of the string */
X
X	    ocl += 2;
X
X	    while (isspace (*ocl) && (*ocl != '\r'))
X		++ocl;
X
X	    if (*ocl == '\r')
X		continue;
X
X/* Found the start.  Set up next parameter and ignore the rest */
X
X	    if (*(argv + 1) == (char *)NULL)
X		continue;
X
X	    *(argv + 1) = ocl;
X	    *(argv + 2) = (char *)NULL;
X
X	    if ((ocl = strchr (ocl, '\r')) != (char *)NULL)
X		*ocl = 0;
X
X	    return;
X       }
X    }
X}
SHAR_EOF
chmod 0644 shell/sh1.c || echo "restore of shell/sh1.c fails"
set `wc -c shell/sh1.c`;Sum=$1
if test "$Sum" != "28366"
then echo original size 28366, current size $Sum;fi
echo "x - extracting shell/sh2.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh2.c &&
X/* MS-DOS SHELL - Parser
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
X *
X * This code is based on (in part) the shell program written by Charles
X * Forsyth and is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh2.c 1.1 90/01/25 13:41:12 MS_user Exp $
X *
X *    $Log:	sh2.c $
X * Revision 1.1  90/01/25  13:41:12  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <stddef.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <string.h>
X#include <ctype.h>
X#include <unistd.h>
X#include "sh.h"
X
X/*
X * shell: syntax (C version)
X */
X
Xtypedef union {
X    char	*cp;
X    char	**wp;
X    int		i;
X    C_Op	*o;
X} YYSTYPE;
X
X#define	WORD	256
X#define	LOGAND	257
X#define	LOGOR	258
X#define	BREAK	259
X#define	IF	260
X#define	THEN	261
X#define	ELSE	262
X#define	ELIF	263
X#define	FI	264
X#define	CASE	265
X#define	ESAC	266
X#define	FOR	267
X#define	WHILE	268
X#define	UNTIL	269
X#define	DO	270
X#define	DONE	271
X#define	IN	272
X#define	YYERRCODE 300
X
X/* flags to yylex */
X
X#define	CONTIN	01	/* skip new lines to complete command */
X
Xstatic bool		startl;
Xstatic int		peeksym;
Xstatic bool		Allow_funcs;
Xstatic int		iounit = IODEFAULT;
Xstatic C_Op		*tp;
Xstatic YYSTYPE		yylval;
Xstatic char		*syntax_err = "sh: syntax error\n";
X
Xstatic C_Op		*pipeline (int);
Xstatic C_Op		*andor (void);
Xstatic C_Op		*c_list (bool);
Xstatic bool		synio (int);
Xstatic void		musthave (int, int);
Xstatic C_Op		*simple (void);
Xstatic C_Op		*nested (int, int);
Xstatic C_Op		*command (int);
Xstatic C_Op		*dogroup (int);
Xstatic C_Op		*thenpart (void);
Xstatic C_Op		*elsepart (void);
Xstatic C_Op		*caselist (void);
Xstatic C_Op		*casepart (void);
Xstatic char		**pattern (void);
Xstatic char		**wordlist (void);
Xstatic C_Op		*list (C_Op *, C_Op *);
Xstatic C_Op		*block (int, C_Op *, C_Op *, char **);
Xstatic int		rlookup (char *);
Xstatic C_Op		*namelist (C_Op *);
Xstatic char		**copyw (void);
Xstatic void		word (char *);
Xstatic IO_Actions	**copyio (void);
Xstatic IO_Actions	*io (int, int, char *);
Xstatic void		yyerror (char *);
Xstatic int		yylex (int);
Xstatic int		collect (int, int);
Xstatic int		dual (int);
Xstatic void		diag (int);
Xstatic char		*tree (unsigned int);
X
XC_Op	*yyparse ()
X{
X    C_Op	*outtree;
X
X    startl  = TRUE;
X    peeksym = 0;
X    yynerrs = 0;
X    outtree = c_list (TRUE);
X    musthave (NL, 0);
X
X    return (yynerrs != 0) ? (C_Op *)NULL : outtree;
X}
X
Xstatic C_Op	*pipeline (cf)
Xint		cf;
X{
X    register C_Op	*t, *p;
X    register int	c;
X
X    if ((t = command (cf)) != (C_Op *)NULL)
X    {
X	Allow_funcs = FALSE;
X	while ((c = yylex (0)) == '|') 
X	{
X	    if ((p = command (CONTIN)) == (C_Op *)NULL)
X		yyerror (syntax_err);
X
X/* shell statement */
X
X	    if ((t->type != TPAREN) && (t->type != TCOM))
X		t = block (TPAREN, t, NOBLOCK, NOWORDS);
X
X	    t = block (TPIPE, t, p, NOWORDS);
X	}
X
X	peeksym = c;
X    }
X
X    return t;
X}
X
Xstatic C_Op	*andor ()
X{
X    register C_Op	*t, *p;
X    register int	c;
X
X    if ((t = pipeline (0)) != (C_Op *)NULL)
X    {
X	Allow_funcs = FALSE;
X	while (((c = yylex (0)) == LOGAND) || (c == LOGOR))
X	{
X	    if ((p = pipeline (CONTIN)) == (C_Op *)NULL)
X		yyerror (syntax_err);
X
X	    t = block ((c == LOGAND) ? TAND : TOR, t, p, NOWORDS);
X	}
X
X	peeksym = c;
X    }
X
X    return t;
X}
X
Xstatic C_Op	*c_list (allow)
Xbool		allow;
X{
X    register C_Op	*t, *p;
X    register int	c;
X
X/* Functions are only allowed at the start of a line */
X
X    Allow_funcs = allow;
X
X    if ((t = andor ()) != (C_Op *)NULL)
X    {
X	Allow_funcs = FALSE;
X
X	if ((peeksym = yylex (0)) == '&')
X	    t = block (TASYNC, t, NOBLOCK, NOWORDS);
X
X	while ((c = yylex(0)) == ';' || c == '&' || multiline && c == NL)
X	{
X	    if ((p = andor ()) == (C_Op *)NULL)
X		return t;
X
X	    if ((peeksym = yylex (0)) == '&')
X		p = block (TASYNC, p, NOBLOCK, NOWORDS);
X
X	    t = list (t, p);
X	}
X	peeksym = c;
X    }
X
X    return t;
X}
X
X
Xstatic bool	synio (cf)
Xint		cf;
X{
X    register IO_Actions	*iop;
X    register int	i;
X    register int	c;
X
X    if (((c = yylex (cf)) != '<') && (c != '>'))
X    {
X	peeksym = c;
X	return FALSE;
X    }
X
X    i = yylval.i;
X    musthave (WORD, 0);
X    iop = io (iounit, i, yylval.cp);
X    iounit = IODEFAULT;
X
X    if (i & IOHERE)
X	markhere (yylval.cp, iop);
X    
X    return TRUE;
X}
X
Xstatic void	musthave (c, cf)
Xint		c, cf;
X{
X    if ((peeksym = yylex (cf)) != c)
X	yyerror (syntax_err);
X
X    peeksym = 0;
X}
X
Xstatic C_Op	*simple ()
X{
X    register C_Op	*t = (C_Op *)NULL;
X
X    while (1)
X    {
X	switch (peeksym = yylex (0)) 
X	{
X	    case '<':
X	    case '>':
X		synio (0);
X		break;
X
X	    case WORD:
X		if (t == (C_Op *)NULL) 
X		    (t = (C_Op *)tree (sizeof (C_Op)))->type = TCOM;
X
X		peeksym = 0;
X		word (yylval.cp);
X		break;
X
X/* Check for function - name () { word; } */
X
X	    case '(':
X		if ((t != (C_Op *)NULL) && (Allow_funcs == TRUE) &&
X		    (wdlist != (Word_B *)NULL) && (wdlist->w_nword == 1))
X		{
X		    Word_B	*save;
X
X		    peeksym = 0;
X		    musthave (')', 0);
X		    musthave ('{', 0);
X		    save = wdlist;
X		    wdlist = (Word_B *)NULL;
X		    t->type = TFUNC;
X		    t->left = nested (TBRACE, '}');
X		    wdlist = save;
X		    Allow_funcs = FALSE;
X		    musthave (NL, 0);
X		    peeksym = NL;
X		}
X
X	    default:
X		return t;
X	}
X    }
X}
X
Xstatic C_Op	*nested (type, mark)
Xint		type, mark;
X{
X    register C_Op	*t;
X
X    multiline++;
X    t = c_list (FALSE);
X    musthave (mark, 0);
X    multiline--;
X    return block (type, t, NOBLOCK, NOWORDS);
X}
X
Xstatic C_Op	*command (cf)
Xint		cf;
X{
X    register C_Op	*t;
X    Word_B		*iosave = iolist;
X    register int	c;
X
X    iolist = (Word_B *)NULL;
X
X    if (multiline)
X	cf |= CONTIN;
X
X    while (synio (cf))
X	cf = 0;
X
X    switch (c = yylex (cf)) 
X    {
X	default:
X	    peeksym = c;
X
X	    if ((t = simple ()) == (C_Op *)NULL)
SHAR_EOF
echo "End of part 2"
echo "File shell/sh2.c is continued in part 3"
echo "3" > s2_seq_.tmp
exit 0

-- 
Regards,

Ian Stewartson
Data Logic Ltd.