mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-11-04 07:21:06 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			426 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * compat.c - compatibiltiy routines for the deprived
 | 
						|
 *
 | 
						|
 * This file is part of zsh, the Z shell.
 | 
						|
 *
 | 
						|
 * Copyright (c) 1992-1997 Paul Falstad
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, without written agreement and without
 | 
						|
 * license or royalty fees, to use, copy, modify, and distribute this
 | 
						|
 * software and to distribute modified versions of this software for any
 | 
						|
 * purpose, provided that the above copyright notice and the following
 | 
						|
 * two paragraphs appear in all copies of this software.
 | 
						|
 *
 | 
						|
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 | 
						|
 * to any party for direct, indirect, special, incidental, or consequential
 | 
						|
 * damages arising out of the use of this software and its documentation,
 | 
						|
 * even if Paul Falstad and the Zsh Development Group have been advised of
 | 
						|
 * the possibility of such damage.
 | 
						|
 *
 | 
						|
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 | 
						|
 * warranties, including, but not limited to, the implied warranties of
 | 
						|
 * merchantability and fitness for a particular purpose.  The software
 | 
						|
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | 
						|
 * Zsh Development Group have no obligation to provide maintenance,
 | 
						|
 * support, updates, enhancements, or modifications.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include "zsh.mdh"
 | 
						|
#include "compat.pro"
 | 
						|
 | 
						|
/* Return pointer to first occurence of string t *
 | 
						|
 * in string s.  Return NULL if not present.     */
 | 
						|
 | 
						|
#ifndef HAVE_STRSTR
 | 
						|
char *
 | 
						|
strstr(const char *s, const char *t)
 | 
						|
{
 | 
						|
    char *p1, *p2;
 | 
						|
 
 | 
						|
    for (; *s; s++) {
 | 
						|
        for (p1 = s, p2 = t; *p2; p1++, p2++)
 | 
						|
            if (*p1 != *p2)
 | 
						|
                break;
 | 
						|
        if (!*p2)
 | 
						|
            return (char *)s;
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifndef HAVE_GETHOSTNAME
 | 
						|
int
 | 
						|
gethostname(char *name, size_t namelen)
 | 
						|
{
 | 
						|
    struct utsname uts;
 | 
						|
 | 
						|
    uname(&uts);
 | 
						|
    if(strlen(uts.nodename) >= namelen) {
 | 
						|
	errno = EINVAL;
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    strcpy(name, uts.nodename);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifndef HAVE_GETTIMEOFDAY
 | 
						|
int
 | 
						|
gettimeofday(struct timeval *tv, struct timezone *tz)
 | 
						|
{
 | 
						|
    tv->tv_usec = 0;
 | 
						|
    tv->tv_sec = (long)time((time_t) 0);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* compute the difference between two calendar times */
 | 
						|
 | 
						|
#ifndef HAVE_DIFFTIME
 | 
						|
double
 | 
						|
difftime(time_t t2, time_t t1)
 | 
						|
{
 | 
						|
    return ((double)t2 - (double)t1);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#ifndef HAVE_STRERROR
 | 
						|
extern char *sys_errlist[];
 | 
						|
 | 
						|
/* Get error message string associated with a particular  *
 | 
						|
 * error number, and returns a pointer to that string.    *
 | 
						|
 * This is not a particularly robust version of strerror. */
 | 
						|
 | 
						|
char *
 | 
						|
strerror(int errnum)
 | 
						|
{
 | 
						|
    return (sys_errlist[errnum]);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#if 0
 | 
						|
/* pathconf(_PC_PATH_MAX) is not currently useful to zsh.  The value *
 | 
						|
 * returned varies depending on a number of factors, e.g. the amount *
 | 
						|
 * of memory available to the operating system at a given time; thus *
 | 
						|
 * it can't be used for buffer allocation, or even as an indication  *
 | 
						|
 * of whether an attempt to use or create a given pathname may fail  *
 | 
						|
 * at any future time.                                               *
 | 
						|
 *                                                                   *
 | 
						|
 * The call is also permitted to fail if the argument path is not an *
 | 
						|
 * existing directory, so even to make sense of that one must search *
 | 
						|
 * for a valid directory somewhere in the path and adjust.  Even if  *
 | 
						|
 * it succeeds, the return value is relative to the input directory, *
 | 
						|
 * and therefore potentially relative to the length of the shortest  *
 | 
						|
 * path either to that directory or to our working directory.        *
 | 
						|
 *                                                                   *
 | 
						|
 * Finally, see the note below for glibc; detection of pathconf() is *
 | 
						|
 * not by itself an indication that it works reliably.               */
 | 
						|
 | 
						|
/* The documentation for pathconf() says something like:             *
 | 
						|
 *     The limit is returned, if one exists.  If the system  does    *
 | 
						|
 *     not  have  a  limit  for  the  requested  resource,  -1 is    *
 | 
						|
 *     returned, and errno is unchanged.  If there is  an  error,    *
 | 
						|
 *     -1  is returned, and errno is set to reflect the nature of    *
 | 
						|
 *     the error.                                                    *
 | 
						|
 *                                                                   *
 | 
						|
 * System calls are not permitted to set errno to 0; but we must (or *
 | 
						|
 * some other flag value) in order to determine that the resource is *
 | 
						|
 * unlimited.  What use is leaving errno unchanged?  Instead, define *
 | 
						|
 * a wrapper that resets errno to 0 and returns 0 for "the system    *
 | 
						|
 * does not have a limit," so that -1 always means a real error.     */
 | 
						|
 | 
						|
/**/
 | 
						|
mod_export long
 | 
						|
zpathmax(char *dir)
 | 
						|
{
 | 
						|
#ifdef HAVE_PATHCONF
 | 
						|
    long pathmax;
 | 
						|
 | 
						|
    errno = 0;
 | 
						|
    if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) {
 | 
						|
	/* Some versions of glibc pathconf return a hardwired value! */
 | 
						|
	return pathmax;
 | 
						|
    } else if (errno == EINVAL || errno == ENOENT || errno == ENOTDIR) {
 | 
						|
	/* Work backward to find a directory, until we run out of path. */
 | 
						|
	char *tail = strrchr(dir, '/');
 | 
						|
	while (tail > dir && tail[-1] == '/')
 | 
						|
	    --tail;
 | 
						|
	if (tail > dir) {
 | 
						|
	    *tail = 0;
 | 
						|
	    pathmax = zpathmax(dir);
 | 
						|
	    *tail = '/';
 | 
						|
	} else {
 | 
						|
	    errno = 0;
 | 
						|
	    if (tail)
 | 
						|
		pathmax = pathconf("/", _PC_PATH_MAX);
 | 
						|
	    else
 | 
						|
		pathmax = pathconf(".", _PC_PATH_MAX);
 | 
						|
	}
 | 
						|
	if (pathmax > 0) {
 | 
						|
	    long taillen = (tail ? strlen(tail) : (strlen(dir) + 1));
 | 
						|
	    if (taillen < pathmax)
 | 
						|
		return pathmax - taillen;
 | 
						|
	    else
 | 
						|
		errno = ENAMETOOLONG;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (errno)
 | 
						|
	return -1;
 | 
						|
    else
 | 
						|
	return 0; /* pathmax should be considered unlimited */
 | 
						|
#else
 | 
						|
    long dirlen = strlen(dir);
 | 
						|
 | 
						|
    /* The following is wrong if dir is not an absolute path. */
 | 
						|
    return ((long) ((dirlen >= PATH_MAX) ?
 | 
						|
		    ((errno = ENAMETOOLONG), -1) :
 | 
						|
		    ((errno = 0), PATH_MAX - dirlen)));
 | 
						|
#endif
 | 
						|
}
 | 
						|
#endif // 0
 | 
						|
 | 
						|
#ifdef HAVE_SYSCONF
 | 
						|
/* This is replaced by a macro from system.h if not HAVE_SYSCONF.    *
 | 
						|
 * 0 is returned by sysconf if _SC_OPEN_MAX is unavailable;          *
 | 
						|
 * -1 is returned on error                                           *
 | 
						|
 *                                                                   *
 | 
						|
 * Neither of these should happen, but resort to OPEN_MAX rather     *
 | 
						|
 * than return 0 or -1 just in case.                                 */
 | 
						|
 | 
						|
/**/
 | 
						|
mod_export long
 | 
						|
zopenmax(void)
 | 
						|
{
 | 
						|
    long openmax = sysconf(_SC_OPEN_MAX);
 | 
						|
 | 
						|
    if (openmax < 1)
 | 
						|
	return OPEN_MAX;
 | 
						|
    else
 | 
						|
	return openmax;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**/
 | 
						|
mod_export char *
 | 
						|
zgetdir(struct dirsav *d)
 | 
						|
{
 | 
						|
    char nbuf[PATH_MAX+3];
 | 
						|
    char *buf;
 | 
						|
    int bufsiz, pos;
 | 
						|
    struct stat sbuf;
 | 
						|
    ino_t pino;
 | 
						|
    dev_t pdev;
 | 
						|
#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
 | 
						|
    struct dirent *de;
 | 
						|
    DIR *dir;
 | 
						|
    dev_t dev;
 | 
						|
    ino_t ino;
 | 
						|
    int len;
 | 
						|
#endif
 | 
						|
 | 
						|
    buf = zhalloc(bufsiz = PATH_MAX);
 | 
						|
    pos = bufsiz - 1;
 | 
						|
    buf[pos] = '\0';
 | 
						|
    strcpy(nbuf, "../");
 | 
						|
    if (stat(".", &sbuf) < 0) {
 | 
						|
	if (d)
 | 
						|
	    return NULL;
 | 
						|
	buf[0] = '.';
 | 
						|
	buf[1] = '\0';
 | 
						|
	return buf;
 | 
						|
    }
 | 
						|
 | 
						|
    pino = sbuf.st_ino;
 | 
						|
    pdev = sbuf.st_dev;
 | 
						|
    if (d)
 | 
						|
	d->ino = pino, d->dev = pdev;
 | 
						|
#ifdef HAVE_FCHDIR
 | 
						|
    else
 | 
						|
#endif
 | 
						|
#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
 | 
						|
	holdintr();
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
	if (stat("..", &sbuf) < 0)
 | 
						|
	    break;
 | 
						|
 | 
						|
	ino = pino;
 | 
						|
	dev = pdev;
 | 
						|
	pino = sbuf.st_ino;
 | 
						|
	pdev = sbuf.st_dev;
 | 
						|
 | 
						|
	if (ino == pino && dev == pdev) {
 | 
						|
	    if (!buf[pos])
 | 
						|
		buf[--pos] = '/';
 | 
						|
	    if (d) {
 | 
						|
#ifndef HAVE_FCHDIR
 | 
						|
		zchdir(buf + pos);
 | 
						|
		noholdintr();
 | 
						|
#endif
 | 
						|
		return d->dirname = ztrdup(buf + pos);
 | 
						|
	    }
 | 
						|
	    zchdir(buf + pos);
 | 
						|
	    noholdintr();
 | 
						|
	    return buf + pos;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(dir = opendir("..")))
 | 
						|
	    break;
 | 
						|
 | 
						|
	while ((de = readdir(dir))) {
 | 
						|
	    char *fn = de->d_name;
 | 
						|
	    /* Ignore `.' and `..'. */
 | 
						|
	    if (fn[0] == '.' &&
 | 
						|
		(fn[1] == '\0' ||
 | 
						|
		 (fn[1] == '.' && fn[2] == '\0')))
 | 
						|
		continue;
 | 
						|
#ifdef HAVE_STRUCT_DIRENT_D_STAT
 | 
						|
	    if(de->d_stat.st_dev == dev && de->d_stat.st_ino == ino) {
 | 
						|
		strncpy(nbuf + 3, fn, PATH_MAX);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
#else /* !HAVE_STRUCT_DIRENT_D_STAT */
 | 
						|
# ifdef HAVE_STRUCT_DIRENT_D_INO
 | 
						|
	    if (dev != pdev || (ino_t) de->d_ino == ino)
 | 
						|
# endif /* HAVE_STRUCT_DIRENT_D_INO */
 | 
						|
	    {
 | 
						|
		strncpy(nbuf + 3, fn, PATH_MAX);
 | 
						|
		lstat(nbuf, &sbuf);
 | 
						|
		if (sbuf.st_dev == dev && sbuf.st_ino == ino)
 | 
						|
		    break;
 | 
						|
	    }
 | 
						|
#endif /* !HAVE_STRUCT_DIRENT_D_STAT */
 | 
						|
	}
 | 
						|
	closedir(dir);
 | 
						|
	if (!de)
 | 
						|
	    break;
 | 
						|
	len = strlen(nbuf + 2);
 | 
						|
	pos -= len;
 | 
						|
	while (pos <= 1) {
 | 
						|
	    char *newbuf = zhalloc(2*bufsiz);
 | 
						|
	    memcpy(newbuf + bufsiz, buf, bufsiz);
 | 
						|
	    buf = newbuf;
 | 
						|
	    pos += bufsiz;
 | 
						|
	    bufsiz *= 2;
 | 
						|
	}
 | 
						|
	memcpy(buf + pos, nbuf + 2, len);
 | 
						|
#ifdef HAVE_FCHDIR
 | 
						|
	if (d)
 | 
						|
	    return d->dirname = ztrdup(buf + pos + 1);
 | 
						|
#endif
 | 
						|
	if (chdir(".."))
 | 
						|
	    break;
 | 
						|
    }
 | 
						|
    if (d) {
 | 
						|
#ifndef HAVE_FCHDIR
 | 
						|
	if (*buf)
 | 
						|
	    zchdir(buf + pos + 1);
 | 
						|
	noholdintr();
 | 
						|
#endif
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
    if (*buf)
 | 
						|
	zchdir(buf + pos + 1);
 | 
						|
    noholdintr();
 | 
						|
 | 
						|
#else  /* __CYGWIN__, USE_GETCWD cases */
 | 
						|
 | 
						|
    if (!getcwd(buf, bufsiz)) {
 | 
						|
	if (d) {
 | 
						|
	    return NULL;
 | 
						|
	}
 | 
						|
    } else {
 | 
						|
	if (d) {
 | 
						|
	    return d->dirname = ztrdup(buf);
 | 
						|
	}
 | 
						|
	return buf;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    buf[0] = '.';
 | 
						|
    buf[1] = '\0';
 | 
						|
    return buf;
 | 
						|
}
 | 
						|
 | 
						|
/**/
 | 
						|
char *
 | 
						|
zgetcwd(void)
 | 
						|
{
 | 
						|
    return zgetdir(NULL);
 | 
						|
}
 | 
						|
 | 
						|
/* chdir with arbitrary long pathname.  Returns 0 on success, 0 on normal *
 | 
						|
 * faliliure and -2 when chdir failed and the current directory is lost.  */
 | 
						|
 | 
						|
/**/
 | 
						|
mod_export int
 | 
						|
zchdir(char *dir)
 | 
						|
{
 | 
						|
    char *s;
 | 
						|
    int currdir = -2;
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
	if (!*dir)
 | 
						|
	    return 0;
 | 
						|
	if (!chdir(dir))
 | 
						|
	    return 0;
 | 
						|
	if ((errno != ENAMETOOLONG && errno != ENOMEM) ||
 | 
						|
	    strlen(dir) < PATH_MAX)
 | 
						|
	    break;
 | 
						|
	for (s = dir + PATH_MAX - 1; s > dir && *s != '/'; s--);
 | 
						|
	if (s == dir)
 | 
						|
	    break;
 | 
						|
#ifdef HAVE_FCHDIR
 | 
						|
	if (currdir == -2)
 | 
						|
	    currdir = open(".", O_RDONLY|O_NOCTTY);
 | 
						|
#endif
 | 
						|
	*s = '\0';
 | 
						|
	if (chdir(dir)) {
 | 
						|
	    *s = '/';
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
#ifndef HAVE_FCHDIR
 | 
						|
	currdir = -1;
 | 
						|
#endif
 | 
						|
	*s = '/';
 | 
						|
	while (*++s == '/');
 | 
						|
	dir = s;
 | 
						|
    }
 | 
						|
#ifdef HAVE_FCHDIR
 | 
						|
    if (currdir == -1 || (currdir >= 0 && fchdir(currdir))) {
 | 
						|
	if (currdir >= 0)
 | 
						|
	    close(currdir);
 | 
						|
	return -2;
 | 
						|
    }
 | 
						|
    if (currdir >= 0)
 | 
						|
	close(currdir);
 | 
						|
    return -1;
 | 
						|
#else
 | 
						|
    return currdir == -2 ? -1 : -2;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * How to print out a 64 bit integer.  This isn't needed (1) if longs
 | 
						|
 * are 64 bit, since ordinary %ld will work (2) if we couldn't find a
 | 
						|
 * 64 bit type anyway.
 | 
						|
 */
 | 
						|
/**/
 | 
						|
#ifdef ZSH_64_BIT_TYPE
 | 
						|
/**/
 | 
						|
mod_export char *
 | 
						|
output64(zlong val)
 | 
						|
{
 | 
						|
    static char llbuf[DIGBUFSIZE];
 | 
						|
    convbase(llbuf, val, 0);
 | 
						|
    return llbuf;
 | 
						|
}
 | 
						|
/**/
 | 
						|
#endif /* ZSH_64_BIT_TYPE */
 |