This is zendutil.c in view mode; [Download] [Up]
static char rcsid[] = "$Id: zendutil.c,v 1.12 1993/02/02 21:00:00 gerben Exp $";
/*
Zend --- a mail transport program for large files and directories
Copyright (C) 1992 Gerben C. Th. Wierda
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Gerben Wierda
Goudriaanstraat 1
1222 SG Hilversum
The Netherlands
gerben@rna.indiv.nluug.nl
*/
/*
*
* zendutil.c -- various routines for supporting zend.
*
*/
#include "zend.h"
/*
* read_config() -- open HOMEDIR/config and parse the file.
*/
#if _ANSI_DEFUN_
extern int read_config( void)
#else
extern int read_config()
#endif
{
char buf[PATHBUFSIZ];
FILE *configStream;
strcpy( buf, HOMEDIR);
strcat( buf, "/config");
if ((configStream = fopen( buf, "r")) == NIL)
{
return -1;
}
else
{
while (parsestreamtoglob( global, configStream) != PARSEEOF)
;
fclose( configStream);
}
if (get_global_string( global, SYSTEMNAMEPROMPT) == NIL)
{
return -2;
}
return 0;
}
/*
* Find a Variable in the array sentinel-terminated array `globs', by name.
* Return a pointer to that variable, and NULL if not found.
*/
static Variable *
#if _ANSI_DEFUN_
get_var( Variable vars[], const char *prompt)
#else
get_var( vars, prompt)
Variable vars[];
char *prompt;
#endif
{
register Variable *varptr;
for (varptr = vars; varptr->type != NONE; varptr++)
{
if (strcmp( varptr->prompt, prompt) == 0)
{
return varptr;
}
}
return NULL;
}
#if _ANSI_DEFUN_
extern int set_global( Variable *globs, const char *prompt, ...)
#else
extern int set_global( globs, prompt, va_alist)
Variable *globs;
char *prompt;
va_dcl
#endif
{
register va_list argpt;
register Variable *varptr = get_var( globs, prompt);
if (varptr == NULL)
{
return -1;
}
va_begin( argpt, prompt);
switch( varptr->type)
{
case STRING:
if (varptr->value.string)
{
free(varptr->value.string);
}
if (varptr->value.string = va_arg( argpt, char *))
{
varptr->value.string =
strcpy( safecalloc( strlen( varptr->value.string) + 1,
sizeof( char)),
varptr->value.string);
}
break;
case ULONG:
varptr->value.ulong = va_arg( argpt, Ulong);
break;
case USHORT:
varptr->value.ushort = (Ushort)va_arg( argpt, unsigned int);
break;
case BOOLEAN:
varptr->value.bool = va_arg( argpt, Boolean);
break;
default:
;
}
va_end( argpt);
return (int)(varptr - globs);
}
#if _ANSI_DEFUN_
extern Ulong get_global_ulong( Variable *globs, const char *prompt)
#else
extern Ulong get_global_ulong( globs, prompt)
Variable *globs;
char *prompt;
#endif
{
register Variable *varptr = get_var(globs, prompt);
if (varptr == NULL || varptr->type != ULONG)
{
zerr( "get_global_ulong with wrong prompt %s\n",
prompt);
abort();
}
return varptr->value.ulong;
}
#if _ANSI_DEFUN_
extern Ushort get_global_ushort( Variable *globs, const char *prompt)
#else
extern Ushort get_global_ushort( globs, prompt)
Variable *globs;
char *prompt;
#endif
{
register Variable *varptr = get_var(globs, prompt);
if (varptr == NULL || varptr->type != USHORT)
{
zerr( "get_global_ushort with wrong prompt %s\n",
prompt);
abort();
}
return varptr->value.ushort;
}
#if _ANSI_DEFUN_
extern StringPointer get_global_string( Variable *globs, const char *prompt)
#else
extern StringPointer get_global_string( globs, prompt)
Variable *globs;
char *prompt;
#endif
{
register Variable *varptr = get_var(globs, prompt);
if (varptr == NULL || varptr->type != STRING)
{
zerr( "get_global_string with wrong prompt %s\n",
prompt);
abort();
}
return varptr->value.string;
}
#if _ANSI_DEFUN_
extern Boolean get_global_boolean( Variable *globs, const char *prompt)
#else
extern Boolean get_global_boolean( globs, prompt)
Variable *globs;
char *prompt;
#endif
{
register Variable *varptr = get_var(globs, prompt);
if (varptr == NULL || varptr->type != BOOLEAN)
{
zerr( "get_global_boolean with wrong prompt %s\n",
prompt);
abort();
}
return varptr->value.bool;
}
/*
* parsebuftoglob() -- parse the buffer for a prompt from the global set
* and get the accopanying value.
*/
#if _ANSI_DEFUN_
extern int parsebuftoglob( Variable *globs, const char *buf)
#else
extern int parsebuftoglob( globs, buf)
Variable *globs;
char *buf;
#endif
{
register Variable *varptr;
for (varptr = globs; varptr->type != NONE; varptr++)
{
if (strncmp( varptr->prompt, buf, strlen( varptr->prompt)) == 0)
{
buf += (strlen( varptr->prompt));
switch (varptr->type)
{
Ulong tmpulong;
case STRING:
if (strlen( buf) == 0)
{
varptr->value.string = NIL;
}
else
{
varptr->value.string =
(StringPointer)safecalloc( strlen(buf)+1, sizeof( char));
strcpy( varptr->value.string, buf);
}
break;
case USHORT:
sscanf( buf, "%lu", &tmpulong);
varptr->value.ushort = (Ushort)tmpulong;
break;
case ULONG:
sscanf( buf, "%lu", &tmpulong);
varptr->value.ulong = tmpulong;
break;
case BOOLEAN:
if (strncmp( buf, BOOLTRUESTRING, strlen( BOOLTRUESTRING)) == 0)
{
varptr->value.bool = TRUE;
}
else if (strncmp( buf, BOOLFALSESTRING, strlen( BOOLFALSESTRING)) == 0)
{
varptr->value.bool = FALSE;
}
else
{
zerr( "ERROR, illegal value for global boolean, set to %s\n",
BOOLFALSESTRING);
varptr->value.bool = FALSE;
}
break;
default:
/* Only for compiler warning. */
;
}
return (int)(varptr - globs);
}
}
return PARSEUNKNOWN;
}
/*
* writeglobtobuf() -- Write the prompt and the value to buf.
*/
#if _ANSI_DEFUN_
extern int writeglobtobuf( Variable *globs, const char *prompt, char *buf)
#else
extern int writeglobtobuf( globs, prompt, buf)
Variable *globs;
char *prompt;
char *buf;
#endif
{
register Variable *varptr = get_var( globs, prompt);
if (varptr == NULL)
{
return -1;
}
switch (varptr->type)
{
char *boolstr;
case STRING:
sprintf( buf, "%s%s", varptr->prompt,
varptr->value.string == NIL ? "" : varptr->value.string);
break;
case USHORT:
sprintf( buf, "%s%u", varptr->prompt, (unsigned)varptr->value.ushort);
break;
case ULONG:
sprintf( buf, "%s%lu", varptr->prompt, varptr->value.ulong);
break;
case BOOLEAN:
boolstr = "";
switch (varptr->value.bool)
{
case FALSE:
boolstr = BOOLFALSESTRING;
break;
case TRUE:
boolstr = BOOLTRUESTRING;
break;
}
sprintf( buf, "%s%s", varptr->prompt, boolstr);
break;
default:
/* Programmer error */
;
}
return (int)(varptr - globs);
}
/*
* parsestreamtoglob() -- read a line from the stream and run parsebuftoglob()
* reads until some variable is found or EOF.
*/
#if _ANSI_DEFUN_
extern int parsestreamtoglob( Variable *globs, FILE *stream)
#else
extern int parsestreamtoglob( globs, stream)
Variable *globs;
FILE *stream;
#endif
{
char buf[PATHBUFSIZ];
while (fgets( buf, PATHBUFSIZ, stream))
{
if (strncmp( buf, ZENDDELIMITER, strlen( ZENDDELIMITER)) == 0)
{
return PARSEDELIMITER;
}
if (buf[strlen( buf)-1] == '\n')
{
buf[strlen( buf)-1] = NUL;
}
return parsebuftoglob( globs, buf);
}
return PARSEEOF;
}
/*
* writeglobtostream() -- run writeglobtobuf() and write buf to stream
*/
#if _ANSI_DEFUN_
extern int writeglobtostream( Variable *globs, const char *prompt, FILE *stream)
#else
extern int writeglobtostream( globs, prompt, stream)
Variable *globs;
char *prompt;
FILE *stream;
#endif
{
char buf[PATHBUFSIZ];
int varno;
if ((varno = writeglobtobuf( globs, prompt, buf)) != -1)
{
strcat( buf, "\n");
fputs( buf, stream);
#if DEBUG
if (dump && stderr != stream)
{
zerr( "%s", buf);
}
#endif
}
return varno;
}
extern Boolean
#if _ANSI_DEFUN_
write_globs_to_jobfile( Variable *globs, Ulong jobId)
#else
write_globs_to_jobfile( globs, jobId)
Variable *globs;
Ulong jobId;
#endif
{
char buf[PATHBUFSIZ];
FILE *tmpStream;
int try;
int oldumask;
sprintf( buf, "%s/C.%lu", HOMEDIR, jobId);
add_file_to_cleanup( buf, FALSE);
if (one_at_a_time( jobId, RDWR_JOBFILE) != 0)
{
return TRUE;
}
oldumask = umask( ~(S_IRUSR|S_IWUSR));
tmpStream = fopen( buf, "w");
umask( oldumask);
if (tmpStream == NIL)
{
zerr( "Unable to create job config %s\n", buf);
end_one_at_a_time( jobId, RDWR_JOBFILE);
return FALSE;
}
for (try=0; try<nrOfGlobalVariables; try++)
{
writeglobtostream( global, global[try].prompt, tmpStream);
}
if (fclose( tmpStream) != 0)
{
zerr( "Unable to save job config %s\n", buf);
end_one_at_a_time( jobId, RDWR_JOBFILE);
return FALSE;
}
end_one_at_a_time( jobId, RDWR_JOBFILE);
return TRUE;
}
#if _ANSI_DEFUN_
extern int read_jobfile_to_globs( Variable *globs, Ulong jobId)
#else
extern int read_jobfile_to_globs( globs, jobId)
Variable *globs;
Ulong jobId;
#endif
{
char buf[PATHBUFSIZ];
FILE *tmpStream;
sprintf( buf, "%s/C.%lu", HOMEDIR, jobId);
if (one_at_a_time( jobId, RDWR_JOBFILE) != 0)
{
return -1;
}
if ((tmpStream = fopen( buf, "r")) == NIL)
{
end_one_at_a_time( jobId, RDWR_JOBFILE);
return -1;
}
while( parsestreamtoglob( globs, tmpStream) != PARSEEOF)
;
fclose( tmpStream);
end_one_at_a_time( jobId, RDWR_JOBFILE);
return 0;
}
#if _ANSI_DEFUN_
extern int one_at_a_time( Ulong jobId, char type)
#else
extern int one_at_a_time( jobId, type)
Ulong jobId;
char type;
#endif
{
char buf[PATHBUFSIZ];
char buf2[PATHBUFSIZ];
int fd;
sprintf( buf, "%s/D.%lu/X.%05u%c", HOMEDIR, jobId, process_id, type);
add_file_to_cleanup( buf, FALSE);
if ((fd = creat( buf, 0)) == -1)
{
remove_file_to_cleanup( buf);
zerr( "Failed lock (%lu,%c)\n", jobId, type);
return -1;
}
close( fd);
if (jobId == RESERVEDID)
{
sprintf( buf2, "%s/LCK.%c", HOMEDIR, type);
}
else
{
sprintf( buf2, "%s/D.%lu/LCK.%c", HOMEDIR, jobId, type);
}
while (link( buf, buf2) != 0)
{
sleep( 2);
}
add_file_to_cleanup( buf2, FALSE);
#if DEBUG
zerr( "Got lock (%lu,%c)\n", jobId, type);
#endif
unlink( buf);
remove_file_to_cleanup( buf);
return 0;
}
#if _ANSI_DEFUN_
extern int end_one_at_a_time( Ulong jobId, char type)
#else
extern int end_one_at_a_time( jobId, type)
Ulong jobId;
char type;
#endif
{
char buf[PATHBUFSIZ];
if (jobId == RESERVEDID)
{
sprintf( buf, "%s/LCK.%c", HOMEDIR, type);
}
else
{
sprintf( buf, "%s/D.%lu/LCK.%c", HOMEDIR, jobId, type);
}
if (unlink( buf) != 0)
{
zerr( "Failed unlock (%lu,%c)\n", jobId, type);
return -1;
}
remove_file_to_cleanup( buf);
#if DEBUG
zerr( "Got unlock (%lu,%c)\n", jobId, type);
#endif
return 0;
}
extern void
#if _ANSI_DEFUN_
cleanup_stale_locks( void)
#else
cleanup_stale_locks()
#endif
{
char buf[PATHBUFSIZ];
sprintf( buf, "cd %s && %s -f LCK.* */LCK.* */X.*", HOMEDIR, RMPROG);
system_as_zend( buf);
}
/*
* Create a directory for this transfer. The name of the directory defines
* the jobID. If not found within RETRY_LIMIT tries, give up.
*/
extern Ulong
#if _ANSI_DEFUN_
new_job( void)
#else
new_job()
#endif
{
char buf[PATHBUFSIZ];
int try;
Ulong jobId;
#if DEBUG
zerr( "Trying to find job slot within retry limit (%d)\n", RETRY_LIMIT);
#endif
srandom( (int)time( NIL));
for (try=0; try<RETRY_LIMIT; try++)
{
if ((jobId = (Ulong)random()) == RESERVEDID)
{
continue;
}
sprintf( buf, "%s/D.%lu", HOMEDIR, jobId);
add_file_to_cleanup( buf, TRUE);
if (mkdir( buf, S_IRWXU) == 0)
{
#if DEBUG
zerr( "Found job slot %lu\n", jobId);
#endif
return jobId;
}
remove_file_to_cleanup( buf);
}
zerr( "No job slot found within retry limit (%d)!\n", RETRY_LIMIT);
return RESERVEDID;
}
/*
* Kill a job. (but only if it exists.)
*/
extern void
#if _ANSI_DEFUN_
kill_job( Ulong jobId)
#else
kill_job( jobId)
Ulong jobId;
#endif
{
char buf[PATHBUFSIZ];
struct stat statBuf;
sprintf( buf, "%s/D.%lu", HOMEDIR, jobId);
#if 0
if (access( buf, F_OK) == 0)
#endif
if (stat( buf, &statBuf) == 0)
{
sprintf( buf, "cd %s && %s -rf C.%lu D.%lu F.%lu", HOMEDIR, RMPROG,
jobId, jobId, jobId);
system_as_zend( buf);
}
}
/*
* See if `str' is listed as an element in a comma-separated `list'.
*/
extern Boolean
#if _ANSI_DEFUN_
str_listed( const char *str, const char *list)
#else
str_listed(str, list)
char *str, *list;
#endif
{
const char *lp;
if (lp = list)
{
int len = strlen(str);
for (; lp = strchr( lp, str[0]); lp++)
{
if ((lp == list || lp[-1] == ',') &&
(strncmp( lp, str, len) == 0) &&
(lp[len] == '\0' || lp[len] == ','))
{
return TRUE;
}
}
}
return FALSE;
}
static void
#if _ANSI_DEFUN_
vzlog( const char *format, va_list argpt)
#else
vzlog( format, argpt)
char *format;
va_list argpt;
#endif
{
char logfilename[PATHBUFSIZ];
FILE *logStream;
struct tm *t;
struct stat statBuf;
sprintf( logfilename, "%s/LOGFILE", HOMEDIR);
/*
* Log only if logfile already exists.
*/
#if 0
if (access( logfilename, F_OK) != 0)
#endif
if (stat( logfilename, &statBuf) != 0)
{
return;
}
/* {{one_at_a_time()?}} */
if ((logStream = fopen( logfilename, "a")) == NULL)
{
return;
}
t = localtime( ×tamp);
fprintf( logStream,
"%04d/%02d/%02d-%02d:%02d (%lu) %s %s %s %s %s/%s: ",
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min,
(get_global_string( global, ROLEPROMPT) ?
get_global_ulong( global,
(strcmp( get_global_string( global, ROLEPROMPT),
ROLE_SENDER) == 0) ?
SENDINGJOBIDPROMPT : RECEIVINGJOBIDPROMPT) :
RESERVEDID),
get_global_string( global, ROLEPROMPT),
get_global_string( global, SENDINGUSERPROMPT),
get_global_string( remoteglobal, SYSTEMNAMEPROMPT),
get_global_string( global, RECEIVINGUSERPROMPT),
get_global_string( global, DIRPROMPT),
get_global_string( global, FILEPROMPT));
vfprintf( logStream, format, argpt);
fprintf( logStream, "\n");
fclose( logStream);
}
extern void
#if _ANSI_DEFUN_
zlog( const char *format, ...)
#else
zlog( format, va_alist)
char *format;
va_dcl
#endif
{
va_list argpt;
va_begin( argpt, format);
vzlog( format, argpt);
va_end( argpt);
}
#if _ANSI_DEFUN_
extern void zerr( const char *s, ...)
#else
extern void zerr( s, va_alist)
char *s;
va_dcl
#endif
{
va_list argpt;
va_begin( argpt, s);
if ( logErrors)
{
char formatbuf[PATHBUFSIZ];
int len = strlen(s);
/* Kludge to remove trailing newline (if any) */
if (len > 0 && s[len-1] == '\n')
{
--len;
}
sprintf( formatbuf, "error (%.*s)", len, s);
vzlog( formatbuf, argpt);
}
else
{
fprintf( stderr, "Zend: ");
vfprintf( stderr, s, argpt);
}
va_end( argpt);
}
/* support functions for signal handler. */
struct filelist {
struct filelist *next;
Boolean isDirectory;
char name[1]; /* Dynamically allocated. */
};
static struct filelist *filesToCleanup;
extern void
#if _ANSI_DEFUN_
add_file_to_cleanup( const char *name, Boolean isDirectory)
#else
add_file_to_cleanup( name, isDirectory)
char *name;
Boolean isDirectory;
#endif
{
struct filelist *np;
for (np = filesToCleanup; np; np = np->next)
{
if (strcmp( name, np->name) == 0)
{
/* this shouldn't happen. */
zerr( "BUG (add_file_to_cleanup) duplicate filename %s\n", name);
return;
}
}
np = (struct filelist *)safecalloc( 1, strlen( name) + sizeof( *np));
np->next = filesToCleanup;
filesToCleanup = np;
strcpy( np->name, name);
np->isDirectory = isDirectory;
}
extern void
#if _ANSI_DEFUN_
remove_file_to_cleanup( const char *name)
#else
remove_file_to_cleanup( name)
char *name;
#endif
{
struct filelist *np, **head = &filesToCleanup;
for (head = &filesToCleanup; np = (*head); head = &np->next)
{
if (strcmp( name, np->name) == 0)
{
(*head) = np->next;
free(np);
return;
}
}
/* This shouldn't happen. */
zerr( "BUG (remove_file_to_cleanup) missing filename %s\n", name);
}
extern void
#if _ANSI_DEFUN_
file_cleanup( void)
#else
file_cleanup()
#endif
{
struct filelist *np;
while (np = filesToCleanup)
{
if (np->isDirectory)
{
rmdir( np->name);
}
else
{
unlink( np->name);
}
filesToCleanup = np->next;
free( np);
}
}
/* Like system(), but user and group IDs of child process can be given.
Also this doesn't ignore signals, so it can be interrupted. */
extern int
#if _ANSI_DEFUN_
system_as( const char *command, uid_t uid, gid_t gid)
#else
system_as( command, uid, gid)
const char *command;
uid_t uid;
gid_t gid;
#endif
{
pid_t child_pid;
WAIT_T status;
if ((child_pid = fork()) == 0)
{
/* child. */
setuid( uid);
setgid( gid);
execl( "/bin/sh", "sh(zend)", "-c", command, NULL);
exit( 127);
}
if (child_pid < 0)
{
#if DEBUG
zerr( "Unable to fork()\n");
#endif
return -1;
}
/* parent */
if (waitpid( child_pid, &status, 0) < 0)
{
#if DEBUG
zerr( "system_as: waitpid( %d) failed. (%s)\n",
child_pid, strerror( errno));
#endif
return -1;
}
return *(int *)&status; /* {cast since WAIT_T may be `union wait'} */
}
/* Like popen(), but user and group IDs of child process can be given.
Also this doesn't ignore signals, so it can be interrupted.
{NOTE: Also note that popen_as() should be paired with pclose_as();
the aliases for popen/pclose in zend.h help to enforce this.} */
static struct pinfo_listitem
{
FILE *stream;
pid_t child_pid;
struct pinfo_listitem *next;
} popen_info_listhead;
extern FILE *
#if _ANSI_DEFUN_
popen_as( const char *command, const char *mode, uid_t uid, gid_t gid)
#else
popen_as( command, mode, uid, gid)
const char *command;
const char *mode;
uid_t uid;
gid_t gid;
#endif
{
int pipe_fd[2];
int writing;
struct pinfo_listitem *pinfo = &popen_info_listhead;
assert(mode && (*mode == 'r' || *mode == 'w'));
writing = (*mode == 'w');
while (pinfo->stream != NULL && pinfo->next != NULL)
{
#if DEBUG
zerr( "popen_as: nested call.\n");
#endif
pinfo = pinfo->next;
}
if (pinfo->stream != NULL)
{
pinfo->next = (struct pinfo_listitem *)
calloc( 1, sizeof( struct pinfo_listitem));
if (pinfo->next == NULL)
{
#if DEBUG
zerr( "popen_as: pipe() failed. (Out of memory)\n");
#endif
return NULL;
}
pinfo = pinfo->next;
}
if (pipe( pipe_fd) != 0)
{
#if DEBUG
zerr( "popen_as: pipe() failed. (%s)\n", strerror( errno));
#endif
return NULL;
}
if ((pinfo->stream = fdopen( pipe_fd[writing], mode)) == NULL)
{
#if DEBUG
zerr( "popen_as: fdopen() failed. (%s)\n", strerror( errno));
#endif
close(pipe_fd[0]);
close(pipe_fd[1]);
return NULL;
}
if ((pinfo->child_pid = fork()) == 0)
{
/* child. */
setuid( uid);
setgid( gid);
/* Connect pipe. (in child, read and write reverse roles) */
writing = !writing;
if (dup2( pipe_fd[writing], writing) != writing)
{
exit( 127);
}
close( pipe_fd[0]);
close( pipe_fd[1]);
execl( "/bin/sh", "sh(zend)", "-c", command, NULL);
exit( 127);
}
/* parent */
close( pipe_fd[!writing]);
if (pinfo->child_pid < 0)
{
#if DEBUG
zerr( "popen_as: fork() failed. (%s)\n", strerror( errno));
#endif
fclose(pinfo->stream);
pinfo->stream = NULL;
}
return pinfo->stream;
}
extern int
#if _ANSI_DEFUN_
pclose_as( FILE *stream)
#else
pclose_as( stream)
FILE *stream;
#endif
{
WAIT_T status;
struct pinfo_listitem *pinfo = &popen_info_listhead;
while (stream != pinfo->stream && pinfo->next != NULL)
{
pinfo = pinfo->next;
}
if (stream != pinfo->stream)
{
errno = EINVAL;
return -1;
}
/* Close the pipe BEFORE wait()ing for the child to finish.
(so the child, if still running, sees an end-of-file,
or gets a broken pipe) */
fclose( stream);
pinfo->stream = NULL;
if (waitpid( pinfo->child_pid, &status, 0) < 0)
{
#if DEBUG
zerr( "pclose_as: waitpid( %d) failed. (%s)\n",
pinfo->child_pid, strerror( errno));
#endif
return -1; /* {{really?}} */
}
return *(int *)&status; /* {cast since WAIT_T may be `union wait'} */
}
/*
* Return basename of `filename'.
*/
extern char *
#if _ANSI_DEFUN_
basename( const char *filename)
#else
basename( filename)
char *filename;
#endif
{
char *sep;
return (sep = strrchr(filename, '/')) ? (sep + 1) : (char *) filename;
}
/*
* Quote `str' for use in a shell command; return result in a dynamically
* allocated string.
*/
extern char *
#if _ANSI_DEFUN_
quote_for_sh( const char *str)
#else
quote_for_sh( str)
char *str;
#endif
{
const char *s;
char *buf, *d;
int nquotes = 0;
/* count number of quote (') characters in string. */
for (s = str; *s != '\0'; )
{
if (*s++ == '\'')
{
nquotes++;
}
}
/* allocate space for result. */
d = buf = safecalloc( 1 + (size_t)(s - str) + nquotes * 3 + 1 + 1,
sizeof( char));
*d++ = '\'';
for (s = str; (*d++ = *s) != '\0'; )
{
if (*s++ == '\'')
{
*d++ = '\\', *d++ = '\'', *d++ = '\'';
}
}
d[-1] = '\'';
d[0] = '\0';
return buf;
}
extern void_*
#if _ANSI_DEFUN_
safecalloc( size_t n, size_t size)
#else
safecalloc( n, size)
size_t n;
size_t size;
#endif
{
void_*p;
if ((p = calloc( n, size)) == NULL)
{
zerr("Out of memory!");
abort();
}
return p;
}
#if !HAVE_STRERROR
# ifdef strerror
# if _ANSI_DEFUN_
extern char *strerror( int errnum)
# else
extern char *strerror( errnum)
int errnum;
# endif
{
# ifndef sys_nerr
extern int sys_nerr;
# endif
# ifndef sys_errlist
extern char *sys_errlist[];
# endif
if ((unsigned) errnum < sys_nerr)
{
return sys_errlist[errnum];
}
else
{
static char buf[20];
sprintf( buf, "Error %d", errnum);
return( buf);
}
}
# endif /* strerror */
#endif /* HAVE_STRERROR */
#if !__POSIX__
/* Emulation of Posix.1 waitpid().
Semantics:
pid > 0 - wait for single child process with process id `pid';
pid == 0 - wait for any child process in the caller's process group;
pid == -1 - wait for any child process;
pid < -1 - wait for any child process in process group `-pid'.
Currently process group waits (i.e. pid == 0 or pid < -1) are not supported.
Also WNOHANG and WUNTRACED options are supported only on systems that
have at least the wait3() call.
*/
#if !HAVE_WAIT4
static pid_t wait_lookup __(( pid_t _(pid), WAIT_T *_(stat_loc) ));
static int wait_store __(( pid_t _(pid), const WAIT_T *_(stat_loc) ));
#endif
extern pid_t
#if _ANSI_DEFUN_
waitpid( pid_t pid, WAIT_T *stat_loc, int options)
#else
waitpid( pid, stat_loc, options)
pid_t pid;
WAIT_T *stat_loc;
int options;
#endif
{
#if HAVE_WAIT4
if (pid <= 0)
{
if (pid != -1)
{
errno = EINVAL;
return -1;
}
pid = 0; /* wait4() expects pid=0 for indiscriminate wait. */
}
return wait4( pid, stat_loc, options, NULL);
#else /* !HAVE_WAIT4 */
pid_t child_pid;
if (pid <= 0 && pid != -1)
{
errno = EINVAL;
return -1;
}
if ((child_pid = wait_lookup( pid, stat_loc)) < 0)
{
# if !HAVE_WAIT3
if (options & WNOHANG)
{
errno = EINVAL;
return -1;
}
while ((child_pid = wait( stat_loc)) > 0)
# else /* HAVE_WAIT3 */
while ((child_pid = wait3( stat_loc, options, NULL)) > 0)
# endif /* HAVE_WAIT3 */
{
if (pid == -1 || pid == child_pid)
{
break;
}
if (wait_store( pid, stat_loc) != 0)
{
return -1;
}
}
}
return child_pid;
#endif /* !HAVE_WAIT4 */
}
#if !HAVE_WAIT4
/* Store status of previously-encountered waited-for children
in an array sorted on process id (so we can do a binary search.) */
static struct wait_list
{
struct wait_info
{
pid_t wait_pid;
WAIT_T wait_status;
}
*wait_base;
int wait_size;
int wait_used;
}
waitlist;
static int
#if _ANSI_DEFUN_
wait_search( pid_t pid)
#else
wait_search( pid)
pid_t pid;
#endif
{
/* Binary search on `pid'. */
int top = 0, end = waitlist.wait_used;
while (top < end)
{
int mid = (top + end) >> 1;
struct wait_info *wp = &waitlist.wait_base[mid];
if (pid < wp->wait_pid)
{
end = mid;
}
else if (pid > wp->wait_pid)
{
top = mid + 1;
}
else /* (pid == wp->wait_pid) -- match! */
{
return mid;
}
}
/* No match: return (-(the location where `pid' should be inserted) - 1) */
return (-top - 1);
}
static pid_t
#if _ANSI_DEFUN_
wait_lookup( pid_t pid, WAIT_T *stat_loc)
#else
wait_lookup( pid, stat_loc)
pid_t pid;
WAIT_T *stat_loc;
#endif
{
struct wait_info *wp = waitlist.wait_base;
int where;
if ((where = (pid == -1) ? waitlist.wait_used - 1 : wait_search( pid)) < 0)
{
return -1;
}
pid = wp[where].wait_pid;
(*stat_loc) = wp[where].wait_status;
/* Remove entry from array. */
while (++where < waitlist.wait_used)
{
wp[where - 1] = wp[where];
}
--waitlist.wait_used;
return pid;
}
static pid_t
#if _ANSI_DEFUN_
wait_store( pid_t pid, const WAIT_T *stat_loc)
#else
wait_store( pid, stat_loc)
pid_t pid;
WAIT_T *stat_loc;
#endif
{
struct wait_info *wp = waitlist.wait_base;
int i, where;
if ((where = wait_search( pid)) < 0) /* add a new entry. */
{
where = -where - 1;
/* Expand the array if necessary. */
if (waitlist.wait_used == waitlist.wait_size)
{
if (!(wp = malloc( (waitlist.wait_size + 16) * sizeof( *wp))))
{
return -1;
}
/* Deliberately no realloc() since that would invalidate the
entries we already have when it fails... */
if (waitlist.wait_base != NULL)
{
i = waitlist.wait_size; /* PRE: > 0 */
do
{
--i;
wp[i] = waitlist.wait_base[i];
}
while (i);
free( waitlist.wait_base);
}
waitlist.wait_base = wp;
waitlist.wait_size += 16;
}
/* Shift tail upward to make room for new entry. */
for (i = waitlist.wait_used; i > where; --i)
{
wp[i] = wp[i - 1];
}
++waitlist.wait_used;
}
wp[where].wait_pid = pid;
wp[where].wait_status = (*stat_loc);
return 0;
}
#endif /* !HAVE_WAIT4 */
#endif /* __POSIX__ */
/*======================================================================
* $Log: zendutil.c,v $
* Revision 1.12 1993/02/02 21:00:00 gerben
* *** empty log message ***
*
* Revision 1.12 1993/02/02 15:41:09 tom
* allow multiple active popen()ed streams since this was needed for
* check_timeout(); in conneection with this, add emulation of waitpid() for
* non-Posix systems.
*
* Revision 1.11 1993/01/28 18:54:26 tom
* add popen_as(), pclose_as() for secure piping; allow setuid non-root
* installation; add a real safecalloc(); fix typo in non-ANSI C code.
*
* Revision 1.10 1993/01/10 20:20:25 tom
* add basename(), quote_for_shell(); make vzlog() more robust.
*
* Revision 1.9 1992/12/22 15:11:58 tom
* revision of HAVE_STRERROR.
*
* Revision 1.8 1992/12/18 23:11:13 tom
* fix some non-ANSI C discrepancies.
*
* Revision 1.7 1992/12/10 05:27:34 tom
* add support for file-cleanup on signal; stale-lock cleanup; plug security
* holes; add support for safe program execution.
*
*======================================================================*/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.