/*
 *	This file contains intersystem compatibility routines derived from
 *	various public domain sources.  This file is not subject to any of
 *	the licensing or distribution restrictions applicable to the rest
 *	of the bru source code.  It is provided to improved transportability
 *	of bru source code between various flavors of unix.
 *
 *	Fred Fish, Oct 1985.
 */
  
/*
 *	NOTE:  The directory access routines are derived from a
 *	public domain version of the BSD compatible directory access
 *	library by Doug Gwyn at BRL.  They are not subject to any distribution
 *	restrictions and Doug's contribution to the Unix community
 *	is gratefully acknowledged.  They are provided for
 *	convenience in using the bru source code on non-BSD systems.
 *	They have been reformatted to fit in with the style in the
 *	rest of the bru source code but other than that are essentially
 *	unchanged.
 *
 *	To disable use of these internal routines, and use the equivalent
 *	in a library available on your system, define "HAVE_SEEKDIR" in
 *	either CFLAGS in the makefile or in autoconfig.h.
 */

#include "globals.h"

#if (unix || xenix) && !HAVE_SEEKDIR

#ifndef NULL
#define NULL 0
#endif

#define DIRSIZ	14

struct olddir {
    ino_t od_ino;		/* inode */
    char od_name[DIRSIZ];	/* filename */
};


/*
 *	closedir -- C library extension routine
 *
 *	last edit:	21-Jan-1984	D A Gwyn
 */

VOID closedir (dirp)
DIR *dirp;		/* stream from opendir() */
{
    (VOID) s_close (dirp -> dd_fd);
    s_free ((char *) dirp);
}


/*
 *	opendir -- C library extension routine
 *
 *	last edit:	09-Jul-1983	D A Gwyn
 */

DIR *opendir (filename)
char *filename;			/* name of directory */
{
    DIR *dirp;		/* -> malloc'ed storage */
    int fd;		/* file descriptor for read */
    struct bstat bsbuf;		/* result of bfstat() */

    if ((fd = s_open (filename, 0, 0)) < 0) {
	return (NULL);
    }
    if (bfstat (fd, &sbuf) == NULL
	    || (bsbuf.bst_mode & S_IFMT) != S_IFDIR
	    || (dirp = (DIR *) get_memory (sizeof (DIR), 0)) == NULL
	) {
	(VOID) s_close (fd);
	return (NULL);		/* bad luck today */
    }
    dirp -> dd_fd = fd;
    dirp -> dd_loc = dirp -> dd_size = 0;	/* refill needed */
    return (dirp);
}


/*
 *	readdir -- C library extension routine for non-BSD UNIX
 *
 *	last edit:	09-Jul-1983	D A Gwyn
 */

struct direct *readdir (dirp)
DIR *dirp;			/* stream from opendir() */
{
    struct olddir *dp;		/* -> directory data */

    for (;;) {
	if (dirp -> dd_loc >= dirp -> dd_size) {
	    dirp -> dd_loc = dirp -> dd_size = 0;
	}
	if (dirp -> dd_size == 0	/* refill buffer */
		&& (dirp -> dd_size = s_read (dirp -> dd_fd, dirp -> dd_buf,
			DIRBLKSIZ
		    )
		) <= 0
	    )
	    return (NULL);	/* error or EOF */
	dp = (struct olddir *) &(dirp -> dd_buf[dirp -> dd_loc]);
	dirp -> dd_loc += sizeof (struct olddir);
	if (dp -> od_ino != 0) {		/* not deleted entry */
	    static struct direct dir;		/* simulated */
	    dir.d_ino = dp -> od_ino;
	    (VOID) s_strncpy (dir.d_name, dp -> od_name, DIRSIZ);
	    dir.d_name[DIRSIZ] = '\0';
	    dir.d_namlen = s_strlen (dir.d_name);
	    dir.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3;
	    dir.d_reclen += dir.d_namlen - dir.d_namlen % 4;
	    return (&dir);		/* -> simulated structure */
	}
    }
}


/*
 *	seekdir -- C library extension routine
 *
 *	last edit:	21-Jan-1984	D A Gwyn
 */

VOID seekdir (dirp, loc)
DIR *dirp;		/* stream from opendir() */
long loc;			/* position from telldir() */
{
    long base;			/* file location of block */
    long offset;		/* offset within block */

    if (telldir (dirp) == loc) {
	return;			/* save time */
    }
    offset = loc % DIRBLKSIZ;
    base = loc - offset;
    (VOID) s_lseek (dirp -> dd_fd, base, 0);	/* change blocks */
    dirp -> dd_loc = dirp -> dd_size = 0;
    while (dirp -> dd_loc < offset) {		/* skip entries */
	if (readdir (dirp) == NULL) {
	    return;				/* "can't happen" */
	}
    }
}


/*
 *	telldir -- C library extension routine
 *
 *	last edit:	09-Jul-1983	D A Gwyn
 */

long telldir (dirp)
DIR *dirp;			/* stream from opendir() */
{
    return (s_lseek (dirp -> dd_fd, 0L, 1) - (long) dirp -> dd_size
	+ (long) dirp -> dd_loc);
}

#endif  /* !HAVE_SEEKDIR */


/* strtok --- s1 is tokens separated by stuff in s2, see S5 man page */

#if !HAVE_STRTOK

char *strtok (s1, s2)
char *s1, *s2;
{
    static char *save = NULL;	/* save the string between calls */
    char *cp;

    DBUG_ENTER ("strtok");
    DBUG_PRINT ("strtok", ("s1 = <%s>, s2 = <%s>", s1, s2));

    if (s1 != NULL) {		/* first call */
    	save = s1;
    } else if (save == NULL || *save == EOS) {	/* no saved string, or empty */
    	DBUG_RETURN ((char *) NULL);
    }
    
    for (; *save && s_strchr (s2, *save) != NULL; save++) { ; }
		/* skip leading token separators */
    
    if (*save == EOS) {
    	save = NULL;
    	DBUG_RETURN ((char *) NULL);
    }

    cp = save;		/* save start of string */
    while (*save != EOS && s_strchr (s2, *save) == NULL) {
   	 save++;	/* skip non separators (the body of the token) */
    }
    
    if (*save == EOS) {
    	if (cp == save) {	/* do the test before clearing save */
    	    cp = save = NULL;
	}
    	/* else cp is ok */
    	DBUG_RETURN (cp);
    } else {
    	*save++ = EOS;
    }
    /* clobber trailing separator, and increment save */
    if (cp == save) {
	cp = NULL;
    }
    DBUG_RETURN (cp);
}

#endif	/* HAVE_STRTOK */


/* strtol --- convert an arbitrary base number to a long int, see S5 manual */

#if !HAVE_STRTOL

long strtol (str, ptr, base)
char *str, **ptr;
int base;
{
    long val = 0L;
    int sign = FALSE;
    int converted = FALSE;
    char *save = str;
    int lim;				/* limiting character not in base */

    DBUG_ENTER ("strtol");

    while (isspace (*str)) {
	str++;
    }
    if (*str == '-') {
        str++;
        sign = TRUE;
    } else if (*str == '+') {
        str++;
    }
    if (base < 0 || base > 36) {
        base = 0;    /* don't use it */
    }
    if (base == 0) {
        if (*str == '0' && s_tolower (str[1]) == 'x') {
            str += 2;
            base = 16;
        } else if (*str == '0') {
            base = 8;
        } else {
            base = 10;
	}
    } else if (base == 16 && *str == '0' && s_tolower (str[1]) == 'x') {
        str += 2;
    }
    if (base == 10) {
	lim = '9' + 1;
    } else if (base > 10) {
        lim = base - 10 + 'a';	/* >= lim is not range */
    } else {
        lim = base + '0';
    }
    DBUG_PRINT ("strtol", ("base %d lim '%c'", base, lim));
    while (*str == '0') {
        str++;
    }
    for (; *str; str++) {
	DBUG_PRINT ("strtol", ("examining '%c'", *str));
        if (! isalnum (*str) || s_tolower(*str) >= lim
                || s_tolower(*str) < '0') {
            break;
	}
        val *= base;
        if (isalpha (*str)) {
            val += s_tolower (*str) - 'a';
	} else {
            val += *str - '0';
	}
        converted = TRUE;
    }
    if (sign) {
        val = -val;
	DBUG_PRINT ("strtol", ("number is negative"));
    }
    if (ptr != NULL) {
        if (! converted) {
            *ptr = save;
            val = 0L;
        } else {
            *ptr = str;
	}
    }
    DBUG_RETURN (val);
}

#endif /* HAVE_STRTOL */


/*
 * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov  3 14:34:15 1985
 * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
 * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
 * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
 * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
 * Newsgroups: mod.std.unix
 * Subject: public domain AT&T getopt source
 * Message-ID: <3352@ut-sally.UUCP>
 * Date: 3 Nov 85 19:34:15 GMT
 * Date-Received: 4 Nov 85 12:25:09 GMT
 * Organization: IEEE/P1003 Portable Operating System Environment Committee
 * Lines: 91
 * Approved: jsq@ut-sally.UUCP
 * 
 * Here's something you've all been waiting for:  the AT&T public domain
 * source for getopt(3).  It is the code which was given out at the 1985
 * UNIFORUM conference in Dallas.  I obtained it by electronic mail
 * directly from AT&T.  The people there assure me that it is indeed
 * in the public domain.
 * 
 * There is no manual page.  That is because the one they gave out at
 * UNIFORUM was slightly different from the current System V Release 2
 * manual page.  The difference apparently involved a note about the
 * famous rules 5 and 6, recommending using white space between an option
 * and its first argument, and not grouping options that have arguments.
 * Getopt itself is currently lenient about both of these things White
 * space is allowed, but not mandatory, and the last option in a group can
 * have an argument.  That particular version of the man page evidently
 * has no official existence, and my source at AT&T did not send a copy.
 * The current SVR2 man page reflects the actual behavor of this getopt.
 * However, I am not about to post a copy of anything licensed by AT&T.
 * 
 * I will submit this source to Berkeley as a bug fix.
 * 
 * I, personally, make no claims or guarantees of any kind about the
 * following source.  I did compile it to get some confidence that
 * it arrived whole, but beyond that you're on your own.
 * 
 */

/*LINTLIBRARY*/

#if !HAVE_GETOPT

#ifndef NULL
#define NULL	0
#endif

#ifndef EOF
#define EOF	(-1)
#endif

#define ERR(s, c)	if(opterr){\
	char errbuf[2];\
	errbuf[0] = c; errbuf[1] = '\n';\
	(VOID) s_write(2, argv[0], (unsigned)s_strlen(argv[0]));\
	(VOID) s_write(2, s, (unsigned)s_strlen(s));\
	(VOID) s_write(2, errbuf, (unsigned)2);}

int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;

int
getopt(argc, argv, opts)
int	argc;
char	**argv, *opts;
{
	static int sp = 1;
	int c;
	char *cp;

	if(sp == 1)
		if(optind >= argc ||
		   argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		else if(s_strcmp(argv[optind], "--") == NULL) {
			optind++;
			return(EOF);
		}
	optopt = c = argv[optind][sp];
	if(c == ':' || (cp=s_strchr(opts, c)) == NULL) {
		ERR(": illegal option -- ", c);
		if(argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if(*++cp == ':') {
		if(argv[optind][sp+1] != '\0')
			optarg = &argv[optind++][sp+1];
		else if(++optind >= argc) {
			ERR(": option requires an argument -- ", c);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if(argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return(c);
}

#endif		/* HAVE_GETOPT */

#if !HAVE_UNAME

static int s_gethostname PROTO((char *name, int namelen));

#if (unix || xenix)
extern int gethostname PROTO((char *name, int namelen));
#endif


/*
	uname -- system call emulation for 4.2BSD

	based on System V Emulation from BRL
	original's last edit:	05-Mar-1984	D A Gwyn

	last edit:	18-Jun-1985	Arnold Robbins
*/

/* note that all stuff which originates at BRL is public domain */

#ifndef EFAULT
#define EFAULT 0
#endif

int uname (name)
struct utsname *name;	/* where to put results */
{
	char *np;	/* -> array being cleared */

	if (name == NULL) {
		errno = EFAULT;
		return -1;
	}

	for (np = name->nodename; np < &name->nodename[sizeof(name->nodename)];
				np++)
		*np = '\0';		/* for cleanliness */

	if (s_gethostname (name->nodename, sizeof (name->nodename)) != 0)
		(VOID) s_strcpy (name->nodename, "unknown");

	(VOID) s_strncpy (name->sysname, name->nodename, sizeof (name->sysname));

#if amigados
	(VOID) s_strncpy (name->release, "AmigaDos", sizeof (name->release));
#else
	(VOID) s_strncpy (name->release, "4.2BSD", sizeof (name->release));
#endif
	(VOID) s_strncpy (name->version, "vm", sizeof (name->version));

	(VOID) s_strncpy (name->machine,
#ifdef	interdata
		       "interdata",
#else
#ifdef	pdp11
		       "pdp11",
#else
#ifdef	sun
		       "sun",
#else
#ifdef	u370
		       "u370",
#else
#ifdef	u3b
		       "u3b",
#else
#ifdef	vax
		       "vax",
#else
#ifdef	pyr
		       "pyramid",
#else
#ifdef	amigados
		       "amiga",
#else
		       "unknown",
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
		       sizeof name->machine);

	/* add a trailing end of string, just to be sure -- ADR */
	name->sysname [sizeof (name->sysname) - 1] = '\0';
	name->nodename [sizeof (name->nodename) - 1] = '\0';
	name->release [sizeof (name->release) - 1] = '\0';
	name->version [sizeof (name->version) - 1] = '\0';
	name->machine [sizeof (name->machine) - 1] = '\0';

	return 0;
}

static int s_gethostname (name, namelen)
char *name;
int namelen;
{
#if unix || xenix
#ifdef lint
    return (gethostname (name, (int *) namelen));	/* Stupid lint lib! */
#else
    return (gethostname (name, namelen));
#endif  /* lint */
#else
    return (-1);
#endif
}

#endif  /* HAVE_UNAME */
