1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-11-19 13:33:52 +01:00
zsh/Src/compat.c
2000-08-05 05:59:53 +00:00

393 lines
8.9 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
#ifdef HAVE_PATHCONF
/* 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. *
* *
* This is less useful than may be, as one must reset errno to 0 (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. *
* *
* This is replaced by a macro from system.h if not HAVE_PATHCONF. *
*
* Note that the length of a relative path is compared without first *
* prepending the current directory, if pathconf() does not return *
* an error. This is for consistency with the macro and with older *
* zsh behavior; it may be problematic in the ENOENT/ENOTDIR cases. */
/**/
mod_export long
zpathmax(char *dir)
{
long pathmax;
if (!dir || !*dir)
dir = ".";
errno = 0;
if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) {
/* This code is redundant if pathconf works correctly, but *
* some versions of glibc pathconf return a hardwired value. */
if (strlen(dir) < pathmax)
return pathmax;
else
errno = ENAMETOOLONG;
} else if (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 = '/';
if (pathmax > 0) {
if (strlen(dir) < pathmax)
return pathmax;
else
errno = ENAMETOOLONG;
}
}
/* else *
* Either we're at the root (tail == dir) or we're on the first *
* component of a relative path (tail == NULL). Either way we *
* have nothing to do here, the error from pathconf() is real. *
* Perhaps our current working directory has been removed? */
}
if (errno)
return -1;
else
return 0; /* pathmax should be considered unlimited */
}
#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 */