[comp.sources.atari.st] v02i050: gnutar -- GNU's version of UNIX "tar" part04/08

koreth%panarthea.ebay@sun.com (Steven Grimm) (07/20/89)

Submitted-by: 7103_300@uwovax.uwo.ca (Eric R. Smith)
Posting-number: Volume 2, Issue 50
Archive-name: gnutar/part04

#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file NAMES.C continued
#
CurArch=4
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 NAMES.C"
sed 's/^X//' << 'SHAR_EOF' >> NAMES.C
XRefer to the GNU tar General Public License for full details.
X
XEveryone is granted permission to copy, modify and redistribute GNU tar,
Xbut only under the conditions described in the GNU tar General Public
XLicense.  A copy of this license is supposed to have been given to you
Xalong with GNU tar so you can know your rights and responsibilities.  It
Xshould be in a file named COPYING.  Among other things, the copyright
Xnotice and this notice must be preserved on all copies.
X
XIn other words, go ahead and share GNU tar, but don't try to stop
Xanyone else from sharing it farther.  Help stamp out software hoarding!
X*/
X
X/*
X * Look up user and/or group names.
X *
X * This file should be modified for non-unix systems to do something
X * reasonable.
X *
X * @(#)names.c 1.3 10/30/87 - gnu
X */ 
X#ifdef atarist
X#include <types.h>
X#else
X#include <sys/types.h>
X#endif
X
X#include "tar.h"
X
Xextern	char	*strncpy();
X
X#ifndef NONAMES
X/* Whole module goes away if NONAMES defined.  Otherwise... */
X#include <pwd.h>
X#include <grp.h>
X
Xstatic int	saveuid = -993;
Xstatic char	saveuname[TUNMLEN];
Xstatic int	my_uid = -993;
X
Xstatic int	savegid = -993;
Xstatic char	savegname[TGNMLEN];
Xstatic int	my_gid = -993;
X
X#define myuid	( my_uid < 0? (my_uid = getuid()): my_uid )
X#define	mygid	( my_gid < 0? (my_gid = getgid()): my_gid )
X
X/*
X * Look up a user or group name from a uid/gid, maintaining a cache.
X * FIXME, for now it's a one-entry cache.
X * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
X *
X * This is ifdef'd because on Suns, it drags in about 38K of "yellow
X * pages" code, roughly doubling the program size.  Thanks guys.
X */
Xvoid
Xfinduname(uname, uid)
X	char	uname[TUNMLEN];
X	int	uid;
X{
X	struct passwd	*pw;
X	extern struct passwd *getpwuid ();
X
X	if (uid != saveuid) {
X		saveuid = uid;
X		saveuname[0] = '\0';
X		pw = getpwuid(uid); 
X		if (pw) 
X			strncpy(saveuname, pw->pw_name, TUNMLEN);
X	}
X	strncpy(uname, saveuname, TUNMLEN);
X}
X
Xint
Xfinduid(uname)
X	char	uname[TUNMLEN];
X{
X	struct passwd	*pw;
X	extern struct passwd *getpwnam();
X
X	if (uname[0] != saveuname[0]	/* Quick test w/o proc call */
X	    || 0!=strncmp(uname, saveuname, TUNMLEN)) {
X		strncpy(saveuname, uname, TUNMLEN);
X		pw = getpwnam(uname); 
X		if (pw) {
X			saveuid = pw->pw_uid;
X		} else {
X			saveuid = myuid;
X		}
X	}
X	return saveuid;
X}
X
X
Xvoid
Xfindgname(gname, gid)
X	char	gname[TGNMLEN];
X	int	gid;
X{
X	struct group	*gr;
X	extern struct group *getgrgid ();
X
X	if (gid != savegid) {
X		savegid = gid;
X		savegname[0] = '\0';
X		(void)setgrent();
X		gr = getgrgid(gid); 
X		if (gr) 
X			strncpy(savegname, gr->gr_name, TGNMLEN);
X	}
X	(void) strncpy(gname, savegname, TGNMLEN);
X}
X
X
Xint
Xfindgid(gname)
X	char	gname[TUNMLEN];
X{
X	struct group	*gr;
X	extern struct group *getgrnam();
X
X	if (gname[0] != savegname[0]	/* Quick test w/o proc call */
X	    || 0!=strncmp(gname, savegname, TUNMLEN)) {
X		strncpy(savegname, gname, TUNMLEN);
X		gr = getgrnam(gname); 
X		if (gr) {
X			savegid = gr->gr_gid;
X		} else {
X			savegid = mygid;
X		}
X	}
X	return savegid;
X}
X#endif
SHAR_EOF
echo "File NAMES.C is complete"
chmod 0644 NAMES.C || echo "restore of NAMES.C fails"
echo "x - extracting OPEN3.H (Text)"
sed 's/^X//' << 'SHAR_EOF' > OPEN3.H &&
X/*
X
X	Copyright (C) 1988 Free Software Foundation
X
XGNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
XWARRANTY.  No author or distributor accepts responsibility to anyone
Xfor the consequences of using it or for whether it serves any
Xparticular purpose or works at all, unless he says so in writing.
XRefer to the GNU tar General Public License for full details.
X
XEveryone is granted permission to copy, modify and redistribute GNU tar,
Xbut only under the conditions described in the GNU tar General Public
XLicense.  A copy of this license is supposed to have been given to you
Xalong with GNU tar so you can know your rights and responsibilities.  It
Xshould be in a file named COPYING.  Among other things, the copyright
Xnotice and this notice must be preserved on all copies.
X
XIn other words, go ahead and share GNU tar, but don't try to stop
Xanyone else from sharing it farther.  Help stamp out software hoarding!
X*/
X
X/*
X * @(#)open3.h 1.4 87/11/11
X *
X * open3.h -- #defines for the various flags for the Sys V style 3-argument
X * open() call.  On BSD or System 5, the system already has this in an
X * include file.  This file is needed for V7 and MINIX systems for the
X * benefit of open3() in port.c, a routine that emulates the 3-argument
X * call using system calls available on V7/MINIX. 
X *
X * This file is needed by PD tar even if we aren't using the
X * emulator, since the #defines for O_WRONLY, etc. are used in
X * a couple of places besides the open() calls, (e.g. in the assignment
X * to openflag in extract.c).  We just #include this rather than
X * #ifdef them out.
X *
X * Written 6/10/87 by rmtodd@uokmax (Richard Todd).
X *
X * The names have been changed by John Gilmore, 31 July 1987, since
X * Richard called it "bsdopen", and really this change was introduced in
X * AT&T Unix systems before BSD picked it up.
X */
X
X/* Only one of the next three should be specified */
X#define O_RDONLY	 0 /* only allow read */
X#define	O_WRONLY	 1 /* only allow write */
X#define	O_RDWR		 2 /* both are allowed */
X
X/* The rest of these can be OR-ed in to the above. */
X/*
X * O_NDELAY isn't implemented by the emulator.  It's only useful (to tar) on
X * systems that have named pipes anyway; it prevents tar's hanging by
X * opening a named pipe.  We #ifndef it because some systems already have
X * it defined.
X */
X#ifndef O_NDELAY
X#define O_NDELAY	 4 /* don't block on opening devices that would
X			    * block on open -- ignored by emulator. */
X#endif
X#define O_CREAT		 8 /* create file if needed */
X#define O_EXCL		16 /* file cannot already exist */
X#define O_TRUNC		32 /* truncate file on open */
X#define O_APPEND	64 /* always write at end of file -- ignored by emul */
X
X#ifdef EMUL_OPEN3
X/*
X * make emulation transparent to rest of file -- redirect all open() calls
X * to our routine
X */
X#define open	open3
X#endif
SHAR_EOF
chmod 0644 OPEN3.H || echo "restore of OPEN3.H fails"
echo "x - extracting PORT.C (Text)"
sed 's/^X//' << 'SHAR_EOF' > PORT.C &&
X/*
X
X	Copyright (C) 1988 Free Software Foundation
X
XGNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
XWARRANTY.  No author or distributor accepts responsibility to anyone
Xfor the consequences of using it or for whether it serves any
Xparticular purpose or works at all, unless he says so in writing.
XRefer to the GNU tar General Public License for full details.
X
XEveryone is granted permission to copy, modify and redistribute GNU tar,
Xbut only under the conditions described in the GNU tar General Public
XLicense.  A copy of this license is supposed to have been given to you
Xalong with GNU tar so you can know your rights and responsibilities.  It
Xshould be in a file named COPYING.  Among other things, the copyright
Xnotice and this notice must be preserved on all copies.
X
XIn other words, go ahead and share GNU tar, but don't try to stop
Xanyone else from sharing it farther.  Help stamp out software hoarding!
X*/
X
X/*
X * @(#)port.c 1.15	87/11/05	by John Gilmore, 1986
X *
X * These are routines not available in all environments.
X *
X * I know this introduces an extra level of subroutine calls and is
X * slightly slower.  Frankly, my dear, I don't give a damn.  Let the
X * Missed-Em Vee losers suffer a little.  This software is proud to
X * have been written on a BSD system.
X */
X#include <stdio.h>
X#ifdef atarist
X#include <types.h>
X#include <time.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X#include <signal.h>
X#include <errno.h>
X
X#ifdef MSDOS
X#include <fcntl.h>
X#else
X#include <sys/file.h>
X#endif
X
X#include "tar.h"
X#include "port.h"
X
Xextern size_t strlen();
X
Xextern long baserec;
X/*
X * Some people (e.g. V7) don't have a #define for these.
X */
X#ifndef	O_BINARY
X#define	O_BINARY	0
X#endif
X#ifndef	O_RDONLY
X#define	O_RDONLY	0
X#endif
X#ifndef NULL
X#define NULL 0
X#endif
X
X/* JF: modified so all configuration information can appear here, instead of
X   being scattered through the file.  Add all the machine-dependent #ifdefs
X   here */
X#undef WANT_DUMB_GETDATE/* WANT_DUMB_GETDATE --> getdate() */
X#undef WANT_LSTAT	/* WANT_LSTAT --> lstat() */
X#undef WANT_VALLOC	/* WANT_VALLOC --> valloc() */
X#undef WANT_MKDIR	/* WANT_MKDIR --> mkdir() */
X#undef WANT_STRING	/* WANT_STRING --> index() bcopy() bzero() bcmp() */
X#undef WANT_BZERO	/* WANT_BZERO --> bzero() bcmp() execlp() */
X			/* EMUL_OPEN3 --> open3() */
X#undef WANT_MKNOD	/* WANT_MKNOD --> mknod() link() chown() geteuid() */
X#undef WANT_UTILS	/* WANT_UTILS --> panic() ck_*() *_buffer()
X			   merge_sort() quote_copy_string() un_quote_string() */
X#undef WANT_CK_PIPE	/* WANT_CK_PIPE --> ck_pipe() getwd() */
X#undef WANT_GETOPT	/* WANT_GETOPT --> getopt() optind optarg */
X#undef WANT_STRSTR	/* WANT_STRSTR --> strstr() */
X
X/* Define only ONE of these four . . . */
X#undef DOPRNT_MSG	/* Define this one if you have _doprnt() and
X			   no varargs support */
X#undef VARARGS_MSG	/* Define this one if you have varargs.h and
X			   vfprintf() */
X#undef STDC_MSG		/* Define this one if you are using ANSI C and
X			   and have vfprintf() */
X#undef LOSING_MSG	/* Define this one if you don't have any of the
X			   above */
X#ifdef USG
X#define WANT_STRING
X#define WANT_VALLOC
X#endif
X
X#ifdef MINIX
X#define WANT_BZERO
X#endif
X
X/* MSDOS stuff changed around for atarist by ERS */
X#ifdef MSDOS
X# define WANT_LSTAT
X# define WANT_MKNOD
X# define WANT_UTILS
X# define WANT_GETOPT
X# define WANT_VALLOC
X# ifdef atarist
X#  define WANT_DUMB_GETDATE
X#  define VARARGS_MSG
X# else
X#  define WANT_STRING
X# endif
X#else
X# define VARARGS_MSG
X# define WANT_UTILS
X# define WANT_CK_PIPE
X# define WANT_STRSTR
X# define WANT_GETOPT
X#endif
X
X/* End of system-dependent #ifdefs */
X
X#ifdef WANT_DUMB_GETDATE
X/* JF a getdate() routine takes a date/time/etc and turns it into a time_t */
X/* This one is a quick hack I wrote in about five minutes to see if the N
X   option works.  Someone should replace it with one that works */
X
X/* This getdate takes an arg of the form mm/dd/yyyy hh:mm:ss and turns it
X   into a time_t .  Its not well tested or anything. . .  */
X/* In general, you should use the getdate() supplied in getdate.y */
X
X/*#define OFF_FROM_GMT 18000		/* Change for your time zone! */
X#define OFF_FROM_GMT 0
X
Xtime_t
Xgetdate(str)
Xchar *str;
X{
X	int month,day,year,hour,minute,second;
X#ifdef atarist
X	struct tm t;
X	int	n;
X#else
X	time_t ret;
X	int	n;
X
X#define SECS_PER_YEAR (365L*SECS_PER_DAY)
X#define SECS_PER_LEAP_YEAR (366L*SECS_PER_DAY)
X
X#define SECS_PER_DAY (24L*60*60)
X	static int days_per_month[2][12] = {
X		31,28,31,30,31,30,31,31,30,31,30,31,
X		31,29,31,30,31,30,31,31,30,31,30,31
X	};
X
X	static int days_per_year[2]={365,366};
X#endif
X	month=day=year=hour=minute=second=0;
X	n=sscanf(str,"%d/%d/%d %d:%d:%d",&month,&day,&year,&hour,&minute,&second);
X	if(n<3)
X		return 0;
X	if(year<100)
X		year+=1900;
X	if(year<1970)
X		return 0;
X#ifdef atarist
X	t.tm_sec = second;
X	t.tm_min = minute;
X	t.tm_hour = hour;
X	t.tm_mday = day;
X	t.tm_mon = month - 1;
X	t.tm_year = year - 1900;
X	return mktime(&t);
X#else
X	ret=0;
X
X	ret+=OFF_FROM_GMT;
X
X	for(n=1970;n<year;n++)
X		if(n%4==0 && n%400!=0)
X			ret+=SECS_PER_LEAP_YEAR;
X		else
X			ret+=SECS_PER_YEAR;
X
X	month--;
X	for(n=0;n<month;n++) {
X		if(year%4==0 && year%400!=0)
X			ret+=SECS_PER_DAY*days_per_month[1][n];
X		else
X			ret+=SECS_PER_DAY*days_per_month[0][n];
X	}
X	ret+=SECS_PER_DAY*(day-1);
X	ret+=second+minute*60+hour*60*60;
X	return ret;
X#endif /* atarist */
X}
X#endif
X
X/*
X * lstat() is a stat() which does not follow symbolic links.
X * If there are no symbolic links, just use stat().
X */
X#ifdef WANT_LSTAT
Xint
Xlstat (path, buf)
X	char *path;
X	struct stat *buf;
X{
X	extern int stat ();
X	return (stat (path, buf));
X}
X#endif
X
X#ifdef WANT_VALLOC
X/*
X * valloc() does a malloc() on a page boundary.  On some systems,
X * this can make large block I/O more efficient.
X */
Xchar *
Xvalloc (size)
X	unsigned size;
X{
X	extern char *malloc ();
X	return (malloc (size));
X}
X#endif
X/*
X *				NMKDIR.C
X *
X * Written by Robert Rother, Mariah Corporation, August 1985. 
X *
X * I wrote this out of shear disgust with myself because I couldn't
X * figure out how to do this in /bin/sh.
X *
X * If you want it, it's yours.  All I ask in return is that if you
X * figure out how to do this in a Bourne Shell script you send me
X * a copy.
X *					sdcsvax!rmr or rmr@uscd
X*
X* Severely hacked over by John Gilmore to make a 4.2BSD compatible
X* subroutine.	11Mar86; hoptoad!gnu
X*
X* Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
X* subroutine didn't return EEXIST.  It does now.
X*/
X
X/*
X * Make a directory.  Compatible with the mkdir() system call on 4.2BSD.
X */
X#ifdef WANT_MKDIR
Xint
Xmkdir(dpath, dmode)
X	char *dpath;
X	int dmode;
X{
X	int cpid, status;
X	struct stat statbuf;
X	extern int errno;
X
X	if (stat(dpath,&statbuf) == 0) {
X		errno = EEXIST;		/* Stat worked, so it already exists */
X		return -1;
X	}
X
X	/* If stat fails for a reason other than non-existence, return error */
X	if (errno != ENOENT) return -1; 
X
X	switch (cpid = fork()) {
X
X	case -1:			/* Error in fork() */
X		return(-1);		/* Errno is set already */
X
X	case 0:				/* Child process */
X		/*
X		 * Cheap hack to set mode of new directory.  Since this
X		 * child process is going away anyway, we zap its umask.
X		 * FIXME, this won't suffice to set SUID, SGID, etc. on this
X		 * directory.  Does anybody care?
X		 */
X		status = umask(0);	/* Get current umask */
X		status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
X		execl("/bin/mkdir", "mkdir", dpath, (char *)0);
X		_exit(-1);		/* Can't exec /bin/mkdir */
X	
X	default:			/* Parent process */
X		while (cpid != wait(&status)) ;	/* Wait for kid to finish */
X	}
X
X	if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
X		errno = EIO;		/* We don't know why, but */
X		return -1;		/* /bin/mkdir failed */
X	}
X
X	return 0;
X}
X#endif
X
X#ifdef WANT_STRING
X/*
X * Translate V7 style into Sys V style.
X */
X#include <string.h>
X#include <memory.h>
X
Xchar *
Xindex (s, c)
X	char *s;
X	int c;
X{
X	return (strchr (s, c));
X}
X
Xchar *
Xrindex(s,c)
Xchar *s;
Xint c;
X{
X	return strrchr(s,c);
X}
X
Xvoid
Xbcopy (s1, s2, n)
X	char *s1, *s2;
X	int n;
X{
X	(void) memcpy (s2, s1, n);
X}
X
Xvoid
Xbzero (s1, n)
X	char *s1;
X	int n;
X{
X	(void) memset(s1, 0, n);
X}
X
Xint
Xbcmp(s1, s2, n)
X	char	*s1, *s2;
X	int	n;
X{
X	return memcmp(s1, s2, n);
X}
X#endif
X
X#ifdef WANT_BZERO
X/* Minix has bcopy but not bzero, and no memset.  Thanks, Andy. */
Xvoid
Xbzero (s1, n)
X	register char *s1;
X	register int n;
X{
X	while (n--) *s1++ = '\0';
X}
X
X/* It also has no bcmp() */
Xint
Xbcmp (s1, s2, n) 
X	register char *s1,*s2;
X	register int n;
X{
X	for ( ; n-- ; ++s1, ++s2) {
X		if (*s1 != *s2) return *s1 - *s2;
X	}
X	return 0;
X}
X
X/*
X * Groan, Minix doesn't have execlp either!
X *
X * execlp(file,arg0,arg1...argn,(char *)NULL)
X * exec a program, automatically searching for the program through
X * all the directories on the PATH.
X *
X * This version is naive about variable argument lists, it assumes
X * a straightforward C calling sequence.  If your system has odd stacks
X * *and* doesn't have execlp, YOU get to fix it.
X */
Xint
Xexeclp(filename, arg0)
X	char *filename, *arg0;
X{
X	register char *p, *path;    
X	register char *fnbuffer;
X	char **argstart = &arg0;
X	struct stat statbuf;
X	extern char **environ;
X	extern int errno;
X	extern char *malloc(), *getenv(), *index();
X
X	if ((p = getenv("PATH")) == NULL) {
X		/* couldn't find path variable -- try to exec given filename */
X		return execve(filename, argstart, environ);
X	}
X
X	/*
X	 * make a place to build the filename.  We malloc larger than we
X	 * need, but we know it will fit in this.
X	 */
X	fnbuffer = malloc( strlen(p) + 1 + strlen(filename) );
X	if (fnbuffer == NULL) {
X		errno = ENOMEM;
X		return -1;
X	}
X
X	/*
X	 * try each component of the path to see if the file's there
X	 * and executable.
X	 */
X	for (path = p ; path ; path = p) {
X		/* construct full path name to try */
X		if ((p = index(path,':')) == NULL) {
X			strcpy(fnbuffer, path);
X		} else {
X			strncpy(fnbuffer, path, p-path);
X			fnbuffer[p-path] = '\0';
X			p++;		/* Skip : for next time */
X		}
X		if (strlen(fnbuffer) != 0)
X			strcat(fnbuffer,"/");
X		strcat(fnbuffer,filename);
X
X		/* check to see if file is there and is a normal file */
X		if (stat(fnbuffer, &statbuf) < 0) {
X			if (errno == ENOENT)
X				continue; /* file not there,keep on looking */
X			else
X				goto fail; /* failed for some reason, return */
X		}
X		if ( (statbuf.st_mode & S_IFMT) != S_IFREG) continue;
X
X		if (execve(fnbuffer, argstart, environ) < 0
X		    && errno != ENOENT
X		    && errno != ENOEXEC) {
X			/* failed, for some other reason besides "file
X			 * not found" or "not a.out format"
X			 */
X			goto fail;
X		}
X
X		/*
X		 * If we got error ENOEXEC, the file is executable but is
X		 * not an object file.  Try to execute it as a shell script,
X		 * returning error if we can't execute /bin/sh.
X		 *
X		 * FIXME, this code is broken in several ways.  Shell
X		 * scripts should not in general be executed by the user's
X		 * SHELL variable program.  On more mature systems, the
X		 * script can specify with #!/bin/whatever.  Also, this
X		 * code clobbers argstart[-1] if the exec of the shell
X		 * fails.
X		 */
X		if (errno == ENOEXEC) {
X			char *shell;
X
X			/* Try to execute command "sh arg0 arg1 ..." */
X			if ((shell = getenv("SHELL")) == NULL)
X				shell = "/bin/sh";
X			argstart[-1] = shell;
X			argstart[0] = fnbuffer;
X			execve(shell, &argstart[-1], environ);
X			goto fail;	/* Exec didn't work */
X		}
X
X		/* 
X		 * If we succeeded, the execve() doesn't return, so we
X		 * can only be here is if the file hasn't been found yet.
X		 * Try the next place on the path.
X		 */
X	}
X
X	/* all attempts failed to locate the file.  Give up. */
X	errno = ENOENT;
X
Xfail:
X	free(fnbuffer);
X	return -1;
X}
X#endif
X
X
X#ifdef EMUL_OPEN3
X#include "open3.h"
X/*
X * open3 -- routine to emulate the 3-argument open system
X * call that is present in most modern Unix systems.
X * This version attempts to support all the flag bits except for O_NDELAY
X * and O_APPEND, which are silently ignored.  The emulation is not as efficient
X * as the real thing (at worst, 4 system calls instead of one), but there's
X * not much I can do about that.
X *
X * Written 6/10/87 by rmtodd@uokmax
X *
X * open3(path, flag, mode)
X * Attempts to open the file specified by
X * the given pathname.  The following flag bits (#defined in tar.h)
X * specify options to the routine:
X *	O_RDONLY	file open for read only
X *	O_WRONLY	file open for write only
X *	O_RDWR		file open for both read & write
X * (Needless to say, you should only specify one of the above).
X * 	O_CREAT		file is created with specified mode if it needs to be.
X *	O_TRUNC		if file exists, it is truncated to 0 bytes
X *	O_EXCL		used with O_CREAT--routine returns error if file exists
X * Function returns file descriptor if successful, -1 and errno if not.
X */
X
X/*
X * array to give arguments to access for various modes
X * FIXME, this table depends on the specific integer values of O_XXX,
X * and also contains integers (args to 'access') that should be #define's.
X */
Xstatic int modes[] =
X	{
X		04, /* O_RDONLY */
X		02, /* O_WRONLY */
X		06, /* O_RDWR */
X		06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */
X	};
X
X/* Shut off the automatic emulation of open(), we'll need it. */
X#undef open
X
Xint
Xopen3(path, flags, mode)
Xchar *path;
Xint flags, mode;
X{
X	extern int errno;
X	int exists = 1;
X	int call_creat = 0;
X	int fd;
X	/*
X	 * We actually do the work by calling the open() or creat() system
X	 * call, depending on the flags.  Call_creat is true if we will use 
X	 * creat(), false if we will use open().
X	 */
X
X	/*
X	 * See if the file exists and is accessible in the requested mode. 
X	 *
X	 * Strictly speaking we shouldn't be using access, since access checks
X	 * against real uid, and the open call should check against euid.
X	 * Most cases real uid == euid, so it won't matter.   FIXME.
X	 * FIXME, the construction "flags & 3" and the modes table depends
X	 * on the specific integer values of the O_XXX #define's.  Foo!
X	 */
X	if (access(path,modes[flags & 3]) < 0) {
X		if (errno == ENOENT) {
X			/* the file does not exist */
X			exists = 0;
X		} else {
X			/* probably permission violation */
X			if (flags & O_EXCL) {
X				/* Oops, the file exists, we didn't want it. */
X				/* No matter what the error, claim EEXIST. */
X				errno = EEXIST;
X			}
X			return -1;
X		}
X	}
X
X	/* if we have the O_CREAT bit set, check for O_EXCL */
X	if (flags & O_CREAT) {
X		if ((flags & O_EXCL) && exists) {
X			/* Oops, the file exists and we didn't want it to. */
X			errno = EEXIST;
X			return -1;
X		}
X		/*
X		 * If the file doesn't exist, be sure to call creat() so that
X		 * it will be created with the proper mode.
X		 */
X		if (!exists) call_creat = 1;
X	} else {
X		/* If O_CREAT isn't set and the file doesn't exist, error. */
X		if (!exists) {
X			errno = ENOENT;
X			return -1;
X		}
X	}
X
X	/*
X	 * If the O_TRUNC flag is set and the file exists, we want to call
X	 * creat() anyway, since creat() guarantees that the file will be
X	 * truncated and open()-for-writing doesn't.
X	 * (If the file doesn't exist, we're calling creat() anyway and the
X	 * file will be created with zero length.)
X	 */
X	if ((flags & O_TRUNC) && exists) call_creat = 1;
X	/* actually do the call */
X	if (call_creat) {
X		/*
X		 * call creat.  May have to close and reopen the file if we
X		 * want O_RDONLY or O_RDWR access -- creat() only gives
X		 * O_WRONLY.
X		 */
X		fd = creat(path,mode);
X		if (fd < 0 || (flags & O_WRONLY)) return fd;
X		if (close(fd) < 0) return -1;
X		/* Fall out to reopen the file we've created */
X	}
X
X	/*
X	 * calling old open, we strip most of the new flags just in case.
X	 */
X	return open(path, flags & (O_RDONLY|O_WRONLY|O_RDWR|O_BINARY));
X}
X#endif
X
X#ifdef	WANT_MKNOD
X/* Fake mknod by complaining */
Xint
Xmknod(path, mode, dev)
X	char		*path;
X	unsigned short	mode;
X	dev_t		dev;
X{
X	extern int	errno;
X	int		fd;
X	
X	errno = ENXIO;		/* No such device or address */
X	return -1;		/* Just give an error */
X}
X
X/* Fake links by copying */
Xint
Xlink(path1, path2)
X	char		*path1;
X	char		*path2;
X{
X	char	buf[256];
X	int	ifd, ofd;
X	int	nrbytes;
X	int	nwbytes;
X
X	fprintf(stderr, "%s: %s: cannot link to %s, copying instead\n",
X		tar, path1, path2);
X	if ((ifd = open(path1, O_RDONLY|O_BINARY)) < 0)
X		return -1;
X	if ((ofd = creat(path2, 0666)) < 0)
X		return -1;
X	setmode(ofd, O_BINARY);
X	while ((nrbytes = read(ifd, buf, sizeof(buf))) > 0) {
X		if ((nwbytes = write(ofd, buf, nrbytes)) != nrbytes) {
X			nrbytes = -1;
X			break;
X		}
X	}
X	/* Note use of "|" rather than "||" below: we want to close
X	 * the files even if an error occurs.
X	 */
X	if ((nrbytes < 0) | (0 != close(ifd)) | (0 != close(ofd))) {
X		unlink(path2);
X		return -1;
X	}
X	return 0;
X}
X
X#ifndef atarist
X/* everyone owns everything on MS-DOS (or is it no one owns anything?) */
Xint
Xchown(path, uid, gid)
X	char	*path;
X	int	uid;
X	int	gid;
X{
X	return 0;
X}
X
Xint
Xgeteuid()
X{
X	return 0;
X}
X#endif
X#endif	/* WANT_MKNOD */
X
X#ifdef WANT_UTILS
X/* Stash argv[0] here so panic will know what the program is called */
Xchar *myname = 0;
X
Xvoid
Xpanic(s)
Xchar *s;
X{
X	if(myname)
X		fprintf(stderr,"%s:",myname);
X	fprintf(stderr,s);
X	putc('\n',stderr);
X	exit(12);
X}
X
Xchar *
Xck_realloc(ptr,size)
Xchar *ptr;
Xsize_t size;
X{
X	char *ret;
X	char *realloc();
X
X	ret=realloc(ptr,size);
X	if(ret==0)
X		panic("Couldn't re-allocate memory");
X	return ret;
X}
X
X
X/* Implement a variable sized buffer of 'stuff'.  We don't know what it is,
X   nor do we care, as long as it doesn't mind being aligned on a char boundry.
X */
X
Xstruct buffer {
X	int	allocated;
X	int	length;
X	char	*b;
X};
X
X#define MIN_ALLOCATE 50
X
Xchar *
Xinit_buffer()
X{
X	struct buffer *b;
X	char *ck_malloc();
X
X	b=(struct buffer *)ck_malloc(sizeof(struct buffer));
X	b->allocated=MIN_ALLOCATE;
X	b->b=(char *)ck_malloc(MIN_ALLOCATE);
X	b->length=0;
X	return (char *)b;
X}
X
Xvoid
Xflush_buffer(bb)
Xchar *bb;
X{
X	struct buffer *b;
X
X	b=(struct buffer *)bb;
X	free(b->b);
X	b->b=0;
X	b->allocated=0;
X	b->length=0;
X	free((void *)b);
X}
X
Xvoid
Xadd_buffer(bb,p,n)
Xchar *bb;
Xchar *p;
Xint n;
X{
X	struct buffer *b;
X
X	b=(struct buffer *)bb;
X	if(b->length+n>b->allocated) {
X		b->allocated*=2;
X		b->b=(char *)ck_realloc(b->b,b->allocated);
X	}
X	bcopy(p,b->b+b->length,n);
X	b->length+=n;
X}
X
Xchar *
Xget_buffer(bb)
Xchar *bb;
X{
X	struct buffer *b;
X
X	b=(struct buffer *)bb;
X	return b->b;
X}
X
Xchar *
Xmerge_sort(list,n,off,cmp)
Xchar *list;
Xint (*cmp)();
Xunsigned n;
Xint off;
X{
X	char *ret;
X
X	char *alist,*blist;
X	unsigned alength,blength;
X
X	char *tptr;
X	int tmp;
X	char **prev;
X#define NEXTOF(ptr)	(* ((char **)(((char *)(ptr))+off) ) )
X	if(n==1)
X		return list;
X	if(n==2) {
X		if((*cmp)(list,NEXTOF(list))>0) {
X			ret=NEXTOF(list);
X			NEXTOF(ret)=list;
X			NEXTOF(list)=0;
X			return ret;
X		}
X		return list;
X	}
X	alist=list;
X	alength=(n+1)/2;
X	blength=n/2;
X	for(tptr=list,tmp=(n-1)/2;tmp;tptr=NEXTOF(tptr),tmp--)
X		;
X	blist=NEXTOF(tptr);
X	NEXTOF(tptr)=0;
X
X	alist=merge_sort(alist,alength,off,cmp);
X	blist=merge_sort(blist,blength,off,cmp);
X	prev = &ret;
X	for(;alist && blist;) {
X		if((*cmp)(alist,blist)<0) {
X			tptr=NEXTOF(alist);
X			*prev = alist;
X			prev = &(NEXTOF(alist));
X			alist=tptr;
X		} else {
X			tptr=NEXTOF(blist);
X			*prev = blist;
X			prev = &(NEXTOF(blist));
X			blist=tptr;
X		}
X	}
X	if(alist)
X		*prev = alist;
X	else
X		*prev = blist;
X
X	return ret;
X}
X
Xchar *
Xck_malloc(size)
Xsize_t size;
X{
X	char *ret;
X	char *malloc();
X
X	ret=malloc(size);
X	if(ret==0)
X		panic("Couldn't allocate memory");
X	return ret;
X}
X
Xvoid
Xck_close(fd)
Xint fd;
X{
X	if(close(fd)<0) {
X		msg_perror("can't close a file");
X		exit(EX_SYSTEM);
X	}
X}
X
X#include <ctype.h>
X#ifdef sparc
X#include "alloca.h"
X#endif
X
X/* Quote_copy_string is like quote_string, but instead of modifying the
X   string in place, it malloc-s a copy  of the string, and returns that.
X   If the string does not have to be quoted, it returns the NULL string.
X   The allocated copy can, of course, be freed with free() after the
X   caller is done with it.
X */
Xchar *
Xquote_copy_string(string)
Xchar *string;
X{
X	char	*ret;
X	char	*from_here;
X	char	*to_there = 0;
X	char	*copy_buf = 0;
X	int	c;
X	int	copying = 0;
X	void	*alloca();
X
X	ret=string;
X
X	from_here=string;
X	while(*from_here) {
X		c= *from_here++;
X		if(c=='\\') {
X			if(!copying) {
X				int n;
X
X				n=(from_here-string)-1;
X				copying++;
X				copy_buf=(char *)malloc(n+1+strlen(from_here)*4);
X				bcopy(string,copy_buf,n);
X				to_there=copy_buf+n;
X			}
X			*to_there++='\\';
X			*to_there++='\\';
X		} else if(isprint(c)) {
X			if(copying)
X				*to_there++= c;
X		} else {
X			if(!copying) {
X				int	n;
X
X				n=(from_here-string)-1;
X				copying++;
X				copy_buf=(char *)malloc(n+1+strlen(from_here)*4);
X				bcopy(string,copy_buf,n);
X				to_there=copy_buf+n;
X			}
X			*to_there++='\\';
X			if(c=='\n') *to_there++='n';
X			else if(c=='\t') *to_there++='t';
X			else if(c=='\f') *to_there++='f';
X			else if(c=='\b') *to_there++='b';
X			else if(c=='\r') *to_there++='r';
X			else if(c=='\177') *to_there++='?';
X			else {
X				to_there[0]=(c>>6)+'0';
X				to_there[1]=((c>>3)&07)+'0';
X				to_there[2]=(c&07)+'0';
X				to_there+=3;
X			}
X		}
X	}
X	if(copying) {
X		*to_there='\0';
X		return copy_buf;
X	}
X	return (char *)0;
X}
X
X
X/* Un_quote_string takes a quoted c-string (like those produced by
X   quote_string or quote_copy_string and turns it back into the
X   un-quoted original.  This is done in place.
X
X   HACK FOR ATARI ST: change '\' into '/', so people can use GEMDOS
X   style path names; this loses if un_quote_string gets called by
X   anyone but "name_next"!!!!!!!
X */
X
X/* There is no un-quote-copy-string.  Write it yourself */
X
Xchar *un_quote_string(string)
Xchar *string;
X{
X	char *ret;
X	char *from_here;
X	char *to_there;
X	int	tmp;
X
X	ret=string;
X	to_there=string;
X	from_here=string;
X	while(*from_here) {
X		if(*from_here!='\\') {
X			if(from_here!=to_there)
X				*to_there++= *from_here++;
X			else
X				from_here++,to_there++;
X			continue;
X		}
X#ifdef atarist
X		*to_there++ = '/';
X		from_here++;
X#else
X		switch(*++from_here) {
X		case '\\':
X			*to_there++= *from_here++;
X			break;
X		case 'n':
X			*to_there++= '\n';
X			from_here++;
X			break;
X		case 't':
X			*to_there++= '\t';
X			from_here++;
X			break;
X		case 'f':
X			*to_there++= '\f';
X			from_here++;
X			break;
X		case 'b':
X			*to_there++= '\b';
X			from_here++;
X			break;
X		case 'r':
X			*to_there++= '\r';
X			from_here++;
X			break;
X		case '?':
X			*to_there++= 0177;
X			from_here++;
X			break;
X		case '0':
X		case '1':
X		case '2':
X		case '3':
X		case '4':
X		case '5':
X		case '6':
X		case '7':
X			tmp= *from_here - '0';
X			from_here++;
X			if(*from_here<'0' || *from_here>'7') {
X				*to_there++= tmp;
X				break;
X			}
X			tmp= tmp*8 + *from_here-'0';
X			from_here++;
X			if(*from_here<'0' || *from_here>'7') {
X				*to_there++= tmp;
X				break;
X			}
X			tmp=tmp*8 + *from_here-'0';
X			from_here++;
X			*to_there=tmp;
X			break;
X		default:
X			ret=0;
X			*to_there++='\\';
X			*to_there++= *from_here++;
X			break;
X		}
X#endif
X	}
X	*to_there++='\0';
X	return ret;
X}
X#endif
X
X#ifdef WANT_CK_PIPE
Xvoid ck_pipe(pipes)
Xint *pipes;
X{
X	if(pipe(pipes)<0) {
X		msg_perror("can't open a pipe");
X		exit(EX_SYSTEM);
X	}
X}
X
Xchar *
Xgetwd(path)
Xchar *path;
X{
X	FILE *fp;
X	FILE *popen();
X
X	fp=popen("pwd","r");
X	if(!fp)
X		return 0;
X	if(!fgets(path,100,fp))
X		return 0;
X	if(!pclose(fp))
X		return 0;
X	return path;
X}
X#endif /* WANT_CK_PIPE */
X
X
X#ifdef WANT_GETOPT
X/* Getopt for GNU.
X   Copyright (C) 1987 Free Software Foundation, Inc.
X
X		       NO WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X		GENERAL PUBLIC LICENSE TO COPY
X
X  1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X (C) 1987 Free Software Foundation, Inc."; and include following the
Xcopyright notice a verbatim copy of the above disclaimer of warranty
Xand of this License.  You may charge a distribution fee for the
Xphysical act of transferring a copy.
X
X  2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of this
X    program or any part thereof, to be licensed at no charge to all
X    third parties on terms identical to those contained in this
X    License Agreement (except that you may choose to grant more
X    extensive warranty protection to third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
X  3. You may copy and distribute this program or any portion of it in
Xcompiled, executable or object code form under the terms of Paragraphs
X1 and 2 above provided that you do the following:
X
X    a) cause each such copy to be accompanied by the
X    corresponding machine-readable source code, which must
X    be distributed under the terms of Paragraphs 1 and 2 above; or,
X
X    b) cause each such copy to be accompanied by a
X    written offer, with no time limit, to give any third party
X    free (except for a nominal shipping charge) a machine readable
X    copy of the corresponding source code, to be distributed
X    under the terms of Paragraphs 1 and 2 above; or,
X
X    c) in the case of a recipient of this program in compiled, executable
X    or object code form (without the corresponding source code) you
X    shall cause copies you distribute to be accompanied by a copy
X    of the written offer of source code which you received along
X    with the copy you received.
X
X  4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of this program into other free
Xprograms whose distribution conditions are different, write to the Free
XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
Xworked out a simple rule that can be stated here, but we will often permit
Xthis.  We will be guided by the two goals of preserving the free status of
Xall derivatives of our free software and of promoting the sharing and reuse of
Xsoftware.
X
X
XIn other words, you are welcome to use, share and improve this program.
XYou are forbidden to forbid anyone else to use, share and improve
Xwhat you give them.   Help stamp out software-hoarding!  */
X
X/* This version of `getopt' appears to the caller like standard Unix `getopt'
X   but it behaves differently for the user, since it allows the user
X   to intersperse the options with the other arguments.
X
X   As `getopt' works, it permutes the elements of `argv' so that,
X   when it is done, all the options precede everything else.  Thus
X   all application programs are extended to handle flexible argument order.
X
X   Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
X   Then the behavior is completely standard.
X
X   GNU application programs can use a third alternative mode in which
X   they can distinguish the relative order of options and other arguments.  */
X
X#include <stdio.h>
X
X/* For communication from `getopt' to the caller.
X   When `getopt' finds an option that takes an argument,
X   the argument value is returned here.
X   Also, when `ordering' is RETURN_IN_ORDER,
X   each non-option ARGV-element is returned here.  */
X
Xchar *optarg = 0;
X
X/* Index in ARGV of the next element to be scanned.
X   This is used for communication to and from the caller
X   and for communication between successive calls to `getopt'.
X
X   On entry to `getopt', zero means this is the first call; initialize.
X
X   When `getopt' returns EOF, this is the index of the first of the
X   non-option elements that the caller should itself scan.
X
X   Otherwise, `optind' communicates from one call to the next
X   how much of ARGV has been scanned so far.  */
X
Xint optind = 0;
X
X/* The next char to be scanned in the option-element
X   in which the last option character we returned was found.
X   This allows us to pick up the scan where we left off.
X
X   If this is zero, or a null string, it means resume the scan
X   by advancing to the next ARGV-element.  */
X
Xstatic char *nextchar;
X
X/* Callers store zero here to inhibit the error message
X   for unrecognized options.  */
X
Xint opterr = 1;
X
X/* Describe how to deal with options that follow non-option ARGV-elements.
X
X   UNSPECIFIED means the caller did not specify anything;
X   the default is then REQUIRE_ORDER if the environment variable
X   _OPTIONS_FIRST is defined, PERMUTE otherwise.
X
X   REQUIRE_ORDER means don't recognize them as options.
X   Stop option processing when the first non-option is seen.
X   This is what Unix does.
X
X   PERMUTE is the default.  We permute the contents of `argv' as we scan,
X   so that eventually all the options are at the end.  This allows options
X   to be given in any order, even with programs that were not written to
X   expect this.
X
X   RETURN_IN_ORDER is an option available to programs that were written
X   to expect options and other ARGV-elements in any order and that care about
X   the ordering of the two.  We describe each non-option ARGV-element
X   as if it were the argument of an option with character code zero.
X   Using `-' as the first character of the list of option characters
X   requests this mode of operation.
X
X   The special argument `--' forces an end of option-scanning regardless
X   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
X   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
X
Xstatic enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
X
X/* Handle permutation of arguments.  */
X
X/* Describe the part of ARGV that contains non-options that have
X   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
X   `last_nonopt' is the index after the last of them.  */
X
Xstatic int first_nonopt;
Xstatic int last_nonopt;
X
X/* Exchange two adjacent subsequences of ARGV.
X   One subsequence is elements [first_nonopt,last_nonopt)
X    which contains all the non-options that have been skipped so far.
X   The other is elements [last_nonopt,optind), which contains all
X    the options processed since those non-options were skipped.
X
X   `first_nonopt' and `last_nonopt' are relocated so that they describe
X    the new indices of the non-options in ARGV after they are moved.  */
X
Xstatic void
Xexchange (argv)
X     char **argv;
X{
X  int nonopts_size
X    = (last_nonopt - first_nonopt) * sizeof (char *);
X  char **temp = (char **) alloca (nonopts_size);
X
X  /* Interchange the two blocks of data in argv.  */
X
X  bcopy (&argv[first_nonopt], temp, nonopts_size);
X  bcopy (&argv[last_nonopt], &argv[first_nonopt],
X	 (optind - last_nonopt) * sizeof (char *));
X  bcopy (temp, &argv[first_nonopt + optind - last_nonopt],
X	 nonopts_size);
X
X  /* Update records for the slots the non-options now occupy.  */
X
X  first_nonopt += (optind - last_nonopt);
X  last_nonopt = optind;
X}
X
X/* Scan elements of ARGV (whose length is ARGC) for option characters
X   given in OPTSTRING.
X
X   If an element of ARGV starts with '-', and is not exactly "-" or "--",
X   then it is an option element.  The characters of this element
X   (aside from the initial '-') are option characters.  If `getopt'
X   is called repeatedly, it returns successively each of theoption characters
X   from each of the option elements.
X
X   If `getopt' finds another option character, it returns that character,
X   updating `optind' and `nextchar' so that the next call to `getopt' can
X   resume the scan with the following option character or ARGV-element.
X
X   If there are no more option characters, `getopt' returns `EOF'.
X   Then `optind' is the index in ARGV of the first ARGV-element
X   that is not an option.  (The ARGV-elements have been permuted
X   so that those that are not options now come last.)
X
X   OPTSTRING is a string containing the legitimate option characters.
X   A colon in OPTSTRING means that the previous character is an option
X   that wants an argument.  The argument is taken from the rest of the
X   current ARGV-element, or from the following ARGV-element,
X   and returned in `optarg'.
X
X   If an option character is seen that is not listed in OPTSTRING,
X   return '?' after printing an error message.  If you set `opterr' to
X   zero, the error message is suppressed but we still return '?'.
X
X   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
X   so the following text in the same ARGV-element, or the text of the following
X   ARGV-element, is returned in `optarg.  Two colons mean an option that
X   wants an optional arg; if there is text in the current ARGV-element,
X   it is returned in `optarg'.
X
X   If OPTSTRING starts with `-', it requests a different method of handling the
X   non-option ARGV-elements.  See the comments about RETURN_IN_ORDER, above.  */
X
Xint
Xgetopt (argc, argv, optstring)
X     int argc;
X     char **argv;
X     char *optstring;
X{
X  /* Initialize the internal data when the first call is made.
X     Start processing options with ARGV-element 1 (since ARGV-element 0
X     is the program name); the sequence of previously skipped
X     non-option ARGV-elements is empty.  */
X
X  if (optind == 0)
X    {
X      first_nonopt = last_nonopt = optind = 1;
X
X      nextchar = 0;
X
X      /* Determine how to handle the ordering of options and nonoptions.  */
X
X      if (optstring[0] == '-')
X	ordering = RETURN_IN_ORDER;
X      else if (getenv ("_POSIX_OPTION_ORDER") != 0)
X	ordering = REQUIRE_ORDER;
X      else
X	ordering = PERMUTE;
X    }
X
X  if (nextchar == 0 || *nextchar == 0)
X    {
X      if (ordering == PERMUTE)
X	{
X	  /* If we have just processed some options following some non-options,
X	     exchange them so that the options come first.  */
X
X	  if (first_nonopt != last_nonopt && last_nonopt != optind)
X	    exchange (argv);
X	  else if (last_nonopt != optind)
X	    first_nonopt = optind;
X
X	  /* Now skip any additional non-options
X	     and extend the range of non-options previously skipped.  */
X
X	  while (optind < argc
X		 && (argv[optind][0] != '-'
X		     || argv[optind][1] == 0))
SHAR_EOF
echo "End of part 4"
echo "File PORT.C is continued in part 5"
echo "5" > s2_seq_.tmp
exit 0