Long slumber — apologies. TL;DR — you may close this bug report. The original report was against Mac OS X El Capitan 10.11. Later versions of OS X 10.11 (definitively from 10.11.4, possibly earlier) have the problem fixed.
Gory details:
On 10.11.5, I ran the t.c file you provided and it worked cleanly. It
exited with status 4, but there was no problem with the directory cleanup.
I still have a gruesome directory which 'Finder' (the OS X GUI tool) cannot
clean up when it is in Trash. I've placed it into a directory
$HOME/tmp/top-level and renamed 'confdir-14B---' to names such as '
confdir-000000' .. 'confdir-000335' (using the rename-it.sh script
attached) I run into problems with 'confdir-000335'; although its mode
field is 40700 (a directory with 700 permissions), I get errno 2 'no such
file or directory' when I execute 'chdir("confdir-000335")' from directory
"top-level/…/confdir-000334". I created a program do_cd.c (attached, along
with the error reporting code I use, stderr.[ch]), loosely related to t.c
that runs into a hard wall at level 335, whether run from top-level or from
top-level/…/confdir-000129. The shell (Bash 3.2.57(1)) gives up (cannot do
cd) after around top-level/…/confdir-000129, even trying 'cd
top-level/…/confdir-000061' (successful) followed by 'cd
confdir-000062/…/confdir-000129' (that succeeds, but 131 fails with 'path
name too long').
I finally used 'cd top-level/…/confdir-000061; mv * ~/tmp/top-level' to
move the hierarchy up the file system — so I had, briefly,
top-level/confdir-000001 with a path down to confdir-000061, and
top-level/confdir-000062 with a path down to confdir-000335. Running 'rm
-fr confdir-000001' was able to clean that section up. Using do_cd, there
were still problems with 'chdir("confdir-000335")' , but 'rm -fr
confdir-000062' (from the ~/tmp/top-level directory) worked, so I finally
don't have the wreckage of the attempted build cluttering up my file system.
I agree that what I found was a bug in OSX, and so apparently does Apple
since the failure no longer occurs.
JFTR: the versions of Mac OS X 10.11.x El Capitan so far were announced in
email on these dates:
10.11 — 2015-09-30
10.11.1 — 2015-10-21
10.11.2 — 2015-12-15
10.11.3 — 2016-01-19
10.11.4 — 2016-04-21
10.11.5 — 2016-05-16
Since I reported the problem on 2015-10-12, I must have been using 10.11 at
the time, as I reported.
On Tue, Oct 13, 2015 at 11:39 AM, Paul Eggert <[email protected]> wrote:
> On 10/12/2015 02:00 PM, Jonathan Leffler wrote:
>
>> 1. Has anyone else seen anything similar on El Capitan?
>> 2. Does anyone have an idea how to get rid of the trashed directories?
>> 3. How can we modify the test so it doesn't fail catastrophically like
>> this on El Capitan?
>> 4. Do I need to report a bug to Apple separately from this report to GNU
>> Tar?
>>
>
> For (4), OS X is clearly busted here. A user-mode application shouldn't be
> able to create a directory that 'rm -fr' can't remove. Please feel free to
> report the bug to Apple. The problem will occur with many GNU tools'
> installation procedure, as they're all using Gnulib and Gnulib tries to
> check for getcwd bugs like this one.
>
> For (2) I suggest using coreutils; 'rm -fr directory should do the trick
> if you're using GNU rm.
>
> For (3) we need to figure out why the test doesn't clean up after itself
> on El Capitan. It's supposed to. Please compile and run the attached test
> program in a place where you don't mind having long directory chains. On
> my GNU/Linux host, I can do something like this:
>
> gcc t.c
> strace ./a.out
>
> and the 'strace' output contains the following, showing that the test
> program cleans up after itself. Please find out why it's not doing so under
> El Capitan. OS X lacks strace, but you can use a debugger or dtruss or
> whatever your favorite tool is. Thanks.
>
> mkdir("confdir-14B---", 0700) = 0
> chdir("confdir-14B---") = 0
> mkdir("confdir-14B---", 0700) = 0
> chdir("confdir-14B---") = 0
> ...
> mkdir("confdir-14B---", 0700) = 0
> chdir("confdir-14B---") = 0
> getcwd(0x1736010, 4096) = -1 ENAMETOOLONG (File name too
> long)
> [a whole bunch of other stuff, which eventually succeeds. Now comes
> cleanup time....]
> rmdir("confdir-14B---") = -1 ENOENT (No such file or
> directory)
> chdir("..") = 0
> rmdir("confdir-14B---") = 0
> chdir("..") = 0
> ...
> chdir("..") = 0
> rmdir("confdir-14B---") = 0
> exit_group(0) = ?
>
>
--
Jonathan Leffler <[email protected]> #include <disclaimer.h>
Guardian of DBD::Informix - v2015.1101 - http://dbi.perl.org
"Blessed are we who can laugh at ourselves, for we shall never cease to be
amused."
#include "stderr.h"
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 2)
err_usage("directory");
struct stat sb;
char *name = argv[1];
if (stat(name, &sb) != 0)
err_syserr("Unable to stat directory '%s': ", name);
if (!S_ISDIR(sb.st_mode))
err_error("File '%s' is not a directory (mode = %o)\n", name, sb.st_mode);
size_t bufmax = 4096;
char *buffer = malloc(bufmax);
if (buffer == 0)
err_syserr("Failed to malloc %zu bytes: ", bufmax);
strcpy(buffer, name);
size_t buflen = strlen(buffer);
assert(buflen < bufmax);
size_t levels = 0;
while (buflen < bufmax)
{
if (chdir(name) != 0)
{
err_sysrem("Failed to chdir into '%s'\n", name);
err_remark("%3zu (%4zu): %s\n", ++levels, buflen, buffer);
err_error("Mode = %o\n", sb.st_mode);
}
printf("%3zu (%4zu): %s\n", ++levels, buflen, buffer);
DIR *dp = opendir(".");
struct dirent *nm;
while ((nm = readdir(dp)) != 0)
{
if (strcmp(nm->d_name, ".") == 0 || strcmp(nm->d_name, "..") == 0)
continue;
if (stat(nm->d_name, &sb) != 0)
err_syserr("Unable to stat name '%s': ", nm->d_name);
if (!S_ISDIR(sb.st_mode))
{
err_sysrem("Name '%s' is not a directory (mode = %o)\n", nm->d_name, sb.st_mode);
if (unlink(nm->d_name) != 0)
err_syserr("Failed to remove non-directory '%s': ", nm->d_name);
err_remark("Removed '%s' from '%s'\n", nm->d_name, buffer);
continue;
}
size_t namlen = strlen(nm->d_name);
if (buflen + namlen + 1 >= bufmax)
{
err_remark("Allocate space - name is too long: additional name '%s' - (%zu levels) %zu '%s'\n",
nm->d_name, levels, buflen, buffer);
size_t newmax = 2 * bufmax;
char *newbuf = realloc(buffer, newmax);
if (newbuf == 0)
err_syserr("Failed to reallocate %zu bytes memory: ", newmax);
bufmax = newmax;
buffer = newbuf;
}
buffer[buflen++] = '/';
strcpy(&buffer[buflen], nm->d_name);
name = &buffer[buflen];
buflen += namlen;
break;
}
closedir(dp);
}
return 0;
}
/*
@(#)File: $RCSfile: stderr.c,v $
@(#)Version: $Revision: 10.14 $
@(#)Last changed: $Date: 2015/06/02 03:04:32 $
@(#)Purpose: Error reporting routines
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1988-91,1996-99,2001,2003,2005-11,2013,2015
@(#)Product: :PRODUCT:
*/
/*TABSTOP=4*/
/*
** Configuration:
** USE_STDERR_SYSLOG - include syslog functionality
** USE_STDERR_FILEDESC - include file descriptor functionality
** JLSS_STDERR - force support for syslog and file descriptors
*/
#include "posixver.h"
#include "stderr.h" /* Includes config.h if available */
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#else
extern int getpid(void);
#endif /* HAVE_UNISTD_H */
enum { MAX_MSGLEN = 2048 };
/* Find sub-second timing mechanism */
#if defined(HAVE_CLOCK_GETTIME)
/* Uses <time.h> */
#elif defined(HAVE_GETTIMEOFDAY)
/* Mac OS X 10.10.3 does not have clock_gettime() yet */
#include <sys/time.h>
#else
/* No sub-second timing */
#endif
#if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG) && defined(USE_STDERR_SYSLOG)
#include <syslog.h>
extern const char jlss_id_stderr_c_with_syslog[];
const char jlss_id_stderr_c_with_syslog[] =
"@(#)" __FILE__ " configured with USE_STDERR_SYSLOG";
#else
#undef USE_STDERR_SYSLOG
#undef HAVE_SYSLOG_H
#undef HAVE_SYSLOG
#endif /* syslog configuration */
#if defined(USE_STDERR_FILEDESC)
extern const char jlss_id_stderr_c_with_filedesc[];
const char jlss_id_stderr_c_with_filedesc[] =
"@(#)" __FILE__ " configured with USE_STDERR_FILEDESC";
#endif /* USE_STDERR_FILEDESC */
static const char def_format[] = "%Y-%m-%d %H:%M:%S";
static const char *tm_format = def_format;
static char arg0[ERR_MAXLEN_ARGV0+1] = "**undefined**";
/* Permitted default error flags */
enum { ERR_LOGTIME = ERR_STAMP | ERR_MILLI | ERR_MICRO | ERR_NANO };
enum { ERR_LOGOPTS = ERR_NOFLUSH | ERR_EXIT | ERR_ABORT | ERR_LOGTIME |
ERR_NOARG0 | ERR_PID | ERR_ERRNO };
static int err_flags = 0; /* Default error flags (ERR_STAMP, ERR_PID, etc) */
/* Where do messages go?
** if (defined USE_STDERR_SYSLOG && errlog != 0) ==> syslog
** elif (err_fd >= 0) ==> file descriptor
** else ==> file pointer
*/
#ifdef USE_STDERR_SYSLOG
static int errlog = 0;
#endif /* USE_STDERR_SYSLOG */
static FILE *errout = 0;
#ifdef USE_STDERR_FILEDESC
static int err_fd = -1;
#endif /* USE_STDERR_FILEDESC */
/*
** err_???_print() functions are named systematically, and are all static.
**
** err_[ev][crx][fn]_print():
** -- e takes ellipsis argument
** -- v takes va_list argument
** -- c conditionally exits
** -- r returns (no exit)
** -- x exits (no return)
** -- f takes file pointer
** -- n no file pointer (use errout)
**
** NB: no-return and printf-like can only be attached to declarations, not definitions.
*/
static NORETURN void err_vxf_print(FILE *fp, int flags, int estat, const char *format, va_list args);
static NORETURN void err_vxn_print(int flags, int estat, const char *format, va_list args);
static NORETURN void err_exn_print(int flags, int estat, const char *format, ...)
PRINTFLIKE(3,4);
static NORETURN void err_terminate(int flags, int estat);
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_stderr_c[];
const char jlss_id_stderr_c[] = "@(#)$Id: stderr.c,v 10.14 2015/06/02 03:04:32 jleffler Exp $";
#endif /* lint */
/*
** Set default log options, returning old value.
** Setting ERR_EXIT and ERR_ABORT is permitted but not recommended.
*/
int err_setlogopts(int new_opts)
{
int old_opts = err_flags;
err_flags = new_opts & ERR_LOGOPTS;
return(old_opts);
}
/* Return default log options */
int err_getlogopts(void)
{
return(err_flags);
}
/* Change the definition of 'stderr', reporting on the old one too */
/* NB: using err_stderr((FILE *)0) simply reports the current 'stderr' */
FILE *(err_stderr)(FILE *newerr)
{
FILE *old;
if (errout == 0)
errout = stderr;
old = errout;
if (newerr != 0)
errout = newerr;
return(old);
}
#if defined(USE_STDERR_FILEDESC)
/* Change the definition of 'stderr', reporting on the old one too */
/* NB: using err_use_fd() with a negative value turns off 'errors to file descriptor' */
int (err_use_fd)(int new_fd)
{
int old_fd = err_fd;
if (new_fd < 0)
new_fd = -1;
err_fd = new_fd;
return(old_fd);
}
#endif /* USE_STDERR_FILEDESC */
#if defined(USE_STDERR_SYSLOG)
/*
** Configure the use of syslog
** If not configured to use syslog(), this is a no-op.
** If configured to use syslog(), the facility argument should be one of
** the standard facilities (POSIX defines LOG_USER and LOG_LOCAL0 to
** LOG_LOCAL7) to turn on syslog(), or a negative value to turn it off.
** The logopts should be the bitwise combination of 0 and the options
** LOG_PID, LOG_CONS, LOG_NDELAY, LOG_ODELAY, LOG_NOWAIT. However, the
** STDERR package sets LOG_PID regardless.
** The ident used in openlog() corresponds to the value in arg0.
** Note that when formatting the message for syslog(), the time, the PID
** and arg0 are not needed (and hence not provided). The downside is
** you are stuck with the date formatted by syslog().
*/
int (err_use_syslog)(int logopts, int facility)
{
if (facility < 0)
{
/* Turn off syslog() */
closelog();
errlog = 0;
}
else
{
openlog(arg0, LOG_PID|logopts, facility);
errlog = 1;
}
return(errlog);
}
#endif /* USE_STDERR_SYSLOG */
/* Return stored basename of command */
const char *(err_getarg0)(void)
{
return(arg0);
}
/* Store basename of command, excluding trailing slashes */
void (err_setarg0)(const char *argv0)
{
/* Ignore three pathological program names -- NULL, "/" and "" */
if (argv0 != 0 && *argv0 != '\0' && (*argv0 != '/' || *(argv0 + 1) != '\0'))
{
const char *cp;
size_t nbytes = sizeof(arg0) - 1;
if ((cp = strrchr(argv0, '/')) == 0)
{
/* Basename of file only */
cp = argv0;
}
else if (*(cp + 1) != '\0')
{
/* Regular pathname containing slashes but not trailing slashes */
cp++;
}
else
{
/* Skip backwards over trailing slashes */
const char *ep = cp;
while (ep > argv0 && *ep == '/')
ep--;
/* Skip backwards over non-slashes */
cp = ep;
while (cp > argv0 && *cp != '/')
cp--;
assert(ep >= cp);
cp++;
nbytes = (size_t)(ep - cp) + 1;
if (nbytes > sizeof(arg0) - 1)
nbytes = sizeof(arg0) - 1;
}
strncpy(arg0, cp, nbytes);
arg0[nbytes] = '\0';
}
}
const char *(err_rcs_string)(const char *s2, char *buffer, size_t buflen)
{
const char *src = s2;
char *dst = buffer;
char *end = buffer + buflen - 1;
/*
** Bother RCS! We've probably been given something like:
** "$Revision: 10.14 $ ($Date: 2015/06/02 03:04:32 $)"
** We only want to emit "7.5 (2001/08/11 06:25:48)".
** Skip the components between '$' and ': ', copy up to ' $',
** repeating as necessary. And we have to test for overflow!
** Also work with the unexpanded forms of keywords ($Keyword$).
** Never needed this with SCCS!
*/
while (*src != '\0' && dst < end)
{
while (*src != '\0' && *src != '$')
{
*dst++ = *src++;
if (dst >= end)
break;
}
if (*src == '$')
src++;
while (*src != '\0' && *src != ':' && *src != '$')
src++;
if (*src == '\0')
break;
if (*src == '$')
{
/* Unexpanded keyword '$Keyword$' notation */
src++;
continue;
}
if (*src == ':')
src++;
if (*src == ' ')
src++;
while (*src != '\0' && *src != '$')
{
/* Map / in 2009/02/15 to dash */
/* Heuristic - maps slashes surrounded by digits to dashes */
char c = *src++;
if (c == '/' && isdigit(*src) && isdigit(*(src-2)))
c = '-';
*dst++ = c;
if (dst >= end)
break;
}
if (*src == '$')
{
if (*(dst-1) == ' ')
dst--;
src++;
}
}
*dst = '\0';
return(buffer);
}
/* Similar to, but different from, Time in timer.h */
typedef struct Time
{
time_t tv_sec;
long tv_nsec;
} Time;
static Time now(void)
{
Time clk;
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
clk.tv_sec = ts.tv_sec;
clk.tv_nsec = ts.tv_nsec;
#elif defined(HAVE_GETTIMEOFDAY)
struct timeval tv;
gettimeofday(&tv, 0);
clk.tv_sec = tv.tv_sec;
clk.tv_nsec = 1000 * tv.tv_usec;
#else
clk.tv_sec = time(0);
clk.tv_nsec = 0;
#endif
return clk;
}
/* Format a time string for now (using ISO8601 format) */
/* Allow for future settable time format with tm_format */
static char *err_time(int flags, char *buffer, size_t buflen)
{
Time clk = now();
struct tm *tp = localtime(&clk.tv_sec);
size_t nb = strftime(buffer, buflen, tm_format, tp);
if (flags & (ERR_NANO | ERR_MICRO | ERR_MILLI))
{
char subsec[12];
size_t ss_len;
if (flags & ERR_NANO)
ss_len = snprintf(subsec, sizeof(subsec), ".%.9ld", clk.tv_nsec);
else if (flags & ERR_MICRO)
ss_len = snprintf(subsec, sizeof(subsec), ".%.6ld", clk.tv_nsec / 1000);
else /* (flags & ERR_MILLI) */
ss_len = snprintf(subsec, sizeof(subsec), ".%.3ld", clk.tv_nsec / (1000 * 1000));
if (ss_len + nb + 1 < buflen)
strcpy(buffer + nb, subsec);
}
return(buffer);
}
/* err_stdio - report error via stdio */
static void (err_stdio)(FILE *fp, int flags, int errnum, const char *format, va_list args)
{
if ((flags & ERR_NOARG0) == 0)
fprintf(fp, "%s: ", arg0);
if (flags & ERR_LOGTIME)
{
char timbuf[48];
fprintf(fp, "%s - ", err_time(flags, timbuf, sizeof(timbuf)));
}
if (flags & ERR_PID)
fprintf(fp, "pid=%d: ", (int)getpid());
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
vfprintf(fp, format, args);
#pragma GCC diagnostic pop
if (flags & ERR_ERRNO)
fprintf(fp, "error (%d) %s\n", errnum, strerror(errnum));
}
#if defined(USE_STDERR_FILEDESC) || defined(USE_STDERR_SYSLOG)
static char *fmt_string(char *curr, const char *end, const char *format, va_list args)
{
char *new_end = curr;
if (curr < end - 1)
{
size_t size = (size_t)(end - curr);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int more = vsnprintf(curr, size, format, args);
#pragma GCC diagnostic pop
if (more >= 0)
new_end += ((size_t)more >= size) ? size : (size_t)more;
}
return(new_end);
}
static char *fmt_strdots(char *curr, const char *end, const char *format, ...)
{
va_list args;
char *new_end;
va_start(args, format);
new_end = fmt_string(curr, end, format, args);
va_end(args);
return new_end;
}
#endif /* USE_STDERR_FILEDESC || USE_STDERR_SYSLOG */
#if defined(USE_STDERR_SYSLOG)
/* err_syslog() - report error via syslog
**
** syslog() automatically adds PID and program name (configured in openlog()) and time stamp.
*/
static void (err_syslog)(int flags, int errnum, const char *format, va_list args)
{
char buffer[MAX_MSGLEN];
char *curpos = buffer;
char *bufend = buffer + sizeof(buffer);
int priority;
curpos = fmt_string(curpos, bufend, format, args);
if (flags & ERR_ERRNO)
curpos = fmt_strdots(curpos, bufend,
"error (%d) %s\n", errnum, strerror(errnum));
if (flags & ERR_ABORT)
priority = LOG_CRIT;
else if (flags & ERR_EXIT)
priority = LOG_ERR;
else
priority = LOG_WARNING;
syslog(priority, "%s", buffer);
}
#endif /* USE_STDERR_SYSLOG */
#if defined(USE_STDERR_FILEDESC)
/* err_filedes() - report error via file descriptor */
static void (err_filedes)(int fd, int flags, int errnum, const char *format, va_list args)
{
char buffer[MAX_MSGLEN];
char *curpos = buffer;
char *bufend = buffer + sizeof(buffer);
ssize_t nbytes;
buffer[0] = '\0'; /* Not strictly necessary */
if ((flags & ERR_NOARG0) == 0)
curpos = fmt_strdots(curpos, bufend, "%s: ", arg0);
if (flags & ERR_LOGTIME)
{
char timbuf[32];
curpos = fmt_strdots(curpos, bufend,
"%s - ", err_time(flags, timbuf, sizeof(timbuf)));
}
if (flags & ERR_PID)
curpos = fmt_strdots(curpos, bufend,
"pid=%d: ", (int)getpid());
curpos = fmt_string(curpos, bufend, format, args);
if (flags & ERR_ERRNO)
curpos = fmt_strdots(curpos, bufend,
"error (%d) %s\n", errnum, strerror(errnum));
/* There's no sensible way to handle short writes! */
nbytes = write(fd, buffer, (size_t)(curpos - buffer));
assert(nbytes == curpos - buffer);
}
#endif /* USE_STDERR_FILEDESC */
/* Most fundamental (and flexible) error message printing routine - always returns */
static void (err_vrf_print)(FILE *fp, int flags, const char *format, va_list args)
{
int errnum = errno; /* Capture errno before it is damaged! */
if ((flags & ERR_NOFLUSH) == 0)
fflush(0);
#if defined(USE_STDERR_SYSLOG)
if (errlog)
err_syslog(flags, errnum, format, args);
else
#endif /* USE_STDERR_SYSLOG */
#if defined(USE_STDERR_FILEDESC)
if (err_fd >= 0)
err_filedes(err_fd, flags, errnum, format, args);
else
#endif /* USE_STDERR_FILEDESC */
err_stdio(fp, flags, errnum, format, args);
fflush(fp);
}
/* Terminate program - abort or exit */
static void err_terminate(int flags, int estat)
{
assert(flags & (ERR_ABORT|ERR_EXIT));
if (flags & ERR_ABORT)
abort();
exit(estat);
}
/* Most fundamental (and flexible) error message printing routine - may return */
static void (err_vcf_print)(FILE *fp, int flags, int estat, const char *format, va_list args)
{
err_vrf_print(fp, flags, format, args);
if (flags & (ERR_ABORT|ERR_EXIT))
err_terminate(flags, estat);
}
/* Analog of err_vcf_print() which guarantees 'no return' */
static void (err_vxf_print)(FILE *fp, int flags, int estat, const char *format, va_list args)
{
err_vrf_print(fp, flags, format, args);
err_terminate(flags, estat);
}
/* External interface to err_vcf_print() - may return */
void (err_logmsg)(FILE *fp, int flags, int estat, const char *format, ...)
{
va_list args;
va_start(args, format);
err_vcf_print(fp, flags, estat, format, args);
va_end(args);
}
/* Print error message to current error output - no return */
static void (err_vxn_print)(int flags, int estat, const char *format, va_list args)
{
if (errout == 0)
errout = stderr;
err_vxf_print(errout, flags, estat, format, args);
}
/* Print error message to current error output - no return */
static void err_exn_print(int flags, int estat, const char *format, ...)
{
va_list args;
va_start(args, format);
err_vxn_print(flags, estat, format, args);
va_end(args);
}
/* Print error message to nominated output - always returns */
static void err_erf_print(FILE *fp, int flags, const char *format, ...)
{
va_list args;
va_start(args, format);
err_vrf_print(fp, flags, format, args);
va_end(args);
}
/* Print message using current error file */
void (err_print)(int flags, int estat, const char *format, va_list args)
{
if (errout == 0)
errout = stderr;
err_vcf_print(errout, flags, estat, format, args);
}
static void err_vrn_print(int flags, const char *format, va_list args)
{
if (errout == 0)
errout = stderr;
err_vrf_print(errout, flags, format, args);
}
/* Report warning including message from errno */
void (err_sysrem)(const char *format, ...)
{
va_list args;
va_start(args, format);
err_vrn_print(ERR_SYSREM | err_getlogopts(), format, args);
va_end(args);
}
/* Report warning including message from errno */
void (err_sysremark)(int errnum, const char *format, ...)
{
va_list args;
int old_errno = errno;
errno = errnum;
va_start(args, format);
err_vrn_print(ERR_SYSREM | err_getlogopts(), format, args);
va_end(args);
errno = old_errno;
}
/* Report error including message from errno */
void (err_syserr)(const char *format, ...)
{
va_list args;
va_start(args, format);
err_vxn_print(ERR_SYSERR | err_getlogopts(), ERR_STAT, format, args);
va_end(args);
}
/* Report error including message from errno */
void (err_syserror)(int errnum, const char *format, ...)
{
va_list args;
int old_errno = errno;
errno = errnum;
va_start(args, format);
err_vxn_print(ERR_SYSERR | err_getlogopts(), ERR_STAT, format, args);
va_end(args);
errno = old_errno;
}
/* Report warning */
void (err_remark)(const char *format, ...)
{
va_list args;
va_start(args, format);
err_vrn_print(ERR_REM | err_getlogopts(), format, args);
va_end(args);
}
/* Report error */
void (err_error)(const char *format, ...)
{
va_list args;
va_start(args, format);
err_vxn_print(ERR_ERR | err_getlogopts(), ERR_STAT, format, args);
va_end(args);
}
/* Report message - sometimes exiting too */
void (err_report)(int flags, int estat, const char *format, ...)
{
va_list args;
va_start(args, format);
err_print(flags, estat, format, args);
va_end(args);
}
/* Print usage message and exit with failure status */
void (err_usage)(const char *s1)
{
err_exn_print(ERR_NOARG0|ERR_EXIT, EXIT_FAILURE, "Usage: %s %s\n", err_getarg0(), s1);
}
/* Report failure and generate core dump */
void (err_abort)(const char *format, ...)
{
va_list args;
va_start(args, format);
err_vxn_print(ERR_ABORT | err_getlogopts(), EXIT_FAILURE, format, args);
va_end(args);
}
/* Report version information (no exit), removing embedded RCS keyword strings (but not values) */
void (err_printversion)(const char *program, const char *verinfo)
{
char buffer[64];
if (strchr(verinfo, '$'))
verinfo = err_rcs_string(verinfo, buffer, sizeof(buffer));
err_erf_print(stdout, ERR_DEFAULT, "%s Version %s\n", program, verinfo);
}
/* Report version information and exit, removing embedded RCS keyword strings (but not values) */
void (err_version)(const char *program, const char *verinfo)
{
err_printversion(program, verinfo);
exit(EXIT_SUCCESS);
}
/* Report an internal error and exit */
/* Abort if JLSS_INTERNAL_ERROR_ABORT set in environment */
void (err_internal)(const char *function, const char *format, ...)
{
va_list args;
int flags = ERR_EXIT;
const char *ev = getenv("JLSS_INTERNAL_ERROR_ABORT");
va_start(args, format);
if (ev != 0 && *ev != '\0')
flags = ERR_ABORT; /* Generate core dump */
err_remark("unrecoverable internal error in function %s():\n", function);
err_vxn_print(flags | err_getlogopts(), EXIT_FAILURE, format, args);
va_end(args);
}
#ifdef TEST
#error Use separate test program test.stderr.c
#endif /* TEST */
/*
@(#)File: $RCSfile: stderr.h,v $
@(#)Version: $Revision: 10.10 $
@(#)Last changed: $Date: 2015/10/14 23:12:19 $
@(#)Purpose: Header file for standard error functions
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1989-93,1996-99,2003,2005-11,2015
@(#)Product: :PRODUCT:
*/
#if !defined(STDERR_H)
#define STDERR_H
#if defined(MAIN_PROGRAM)
#if !defined(lint)
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_stderr_h[];
const char jlss_id_stderr_h[] = "@(#)$Id: stderr.h,v 10.10 2015/10/14 23:12:19 jleffler Exp $";
#endif /* lint */
#endif
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif /* HAVE_CONFIG_H */
#if defined(JLSS_STDERR)
#undef USE_STDERR_FILEDESC
#define USE_STDERR_FILEDESC
#if defined(HAVE_SYSLOG) && defined(HAVE_SYSLOG_H)
#undef USE_STDERR_SYSLOG
#define USE_STDERR_SYSLOG
#endif /* HAVE_SYSLOG && HAVE_SYSLOG_H */
#endif /* JLSS_STDERR */
#include <stdio.h>
#include <stdarg.h>
#if !defined(NORETURN)
#if __STDC_VERSION__ >= 201112L
#define NORETURN _Noreturn
#elif defined(__GNUC__)
#define NORETURN __attribute__((noreturn))
#else
#define NORETURN /* If only */
#endif /* __STDC_VERSION__ || __GNUC__ */
#endif /* NORETURN */
#if !defined(PRINTFLIKE)
#if defined(__GNUC__)
#define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
#else
#define PRINTFLIKE(n,m) /* If only */
#endif /* __GNUC__ */
#endif /* PRINTFLIKE */
/* -- Definitions for error handling */
enum { ERR_STAT = 1 }; /* Default exit status */
enum { ERR_DEFAULT = 0x0000 }; /* Default flag */
enum { ERR_NOFLUSH = 0x0001 }; /* Do not flush open files */
enum { ERR_EXIT = 0x0004 }; /* Exit -- do not return */
enum { ERR_ABORT = 0x0008 }; /* Abort -- do not return */
enum { ERR_STAMP = 0x0020 }; /* Timestamp messages (whole second) */
enum { ERR_NOARG0 = 0x0040 }; /* Do not print arg0 prefix */
enum { ERR_PID = 0x0080 }; /* Include pid=nnnnn info */
enum { ERR_ERRNO = 0x0100 }; /* Include system error */
enum { ERR_MILLI = 0x0200 }; /* Timestamp messages (millisecond) */
enum { ERR_MICRO = 0x0400 }; /* Timestamp messages (microsecond) */
enum { ERR_NANO = 0x0800 }; /* Timestamp messages (nanosecond) */
#if defined(USE_STDERR_SYSLOG)
/* Definitions related to using syslog */
enum { ERR_LOG_EMERG = 0x01000 }; /* system is unusable */
enum { ERR_LOG_ALERT = 0x02000 }; /* action must be taken immediately */
enum { ERR_LOG_CRIT = 0x04000 }; /* critical conditions */
enum { ERR_LOG_ERR = 0x08000 }; /* error conditions */
enum { ERR_LOG_WARNING = 0x10000 }; /* warning conditions */
enum { ERR_LOG_NOTICE = 0x20000 }; /* normal but significant condition */
enum { ERR_LOG_INFO = 0x40000 }; /* informational */
enum { ERR_LOG_DEBUG = 0x80000 }; /* debug-level messages */
enum { ERR_LOG_LEVEL_HI = ERR_LOG_EMERG|ERR_LOG_ALERT|ERR_LOG_CRIT|ERR_LOG_ERR };
enum { ERR_LOG_LEVEL_LO = ERR_LOG_WARNING|ERR_LOG_NOTICE|ERR_LOG_INFO|ERR_LOG_DEBUG };
enum { ERR_LOG_LEVEL = ERR_LOG_LEVEL_HI|ERR_LOG_LEVEL_LO };
#endif /* USE_STDERR_SYSLOG */
/* -- Standard combinations of flags */
enum { ERR_REM = ERR_DEFAULT };
enum { ERR_ERR = ERR_EXIT };
enum { ERR_ABT = ERR_ABORT };
enum { ERR_LOG = ERR_STAMP|ERR_PID };
enum { ERR_SYSREM = ERR_REM|ERR_ERRNO };
enum { ERR_SYSERR = ERR_ERR|ERR_ERRNO };
/* -- Maximum recorded length of argv[0]; extra is truncated */
enum { ERR_MAXLEN_ARGV0 = 63 };
/* -- Global definitions */
extern const char *err_getarg0(void);
extern void err_setarg0(const char *argv0);
extern FILE *err_stderr(FILE *fp);
extern const char *err_rcs_string(const char *s, char *buffer, size_t buflen);
extern NORETURN void err_abort(const char *format, ...) PRINTFLIKE(1,2);
extern NORETURN void err_error(const char *format, ...) PRINTFLIKE(1,2);
extern NORETURN void err_error1(const char *s1);
extern NORETURN void err_error2(const char *s1, const char *s2);
extern NORETURN void err_help(const char *use_str, const char *hlp_str);
extern NORETURN void err_helplist(const char *use_str, const char * const *help_list);
extern NORETURN void err_internal(const char *function, const char *format, ...) PRINTFLIKE(2,3);
extern NORETURN void err_syserr(const char *format, ...) PRINTFLIKE(1,2);
extern NORETURN void err_syserr1(const char *s1);
extern NORETURN void err_syserr2(const char *s1, const char *s2);
extern NORETURN void err_syserror(int errnum, const char *format, ...) PRINTFLIKE(2,3);
extern NORETURN void err_usage(const char *usestr);
extern NORETURN void err_version(const char *program, const char *verinfo);
extern void err_logmsg(FILE *fp, int flags, int estat, const char *format, ...) PRINTFLIKE(4,5);
extern void err_print(int flags, int estat, const char *format, va_list args);
extern void err_printversion(const char *program, const char *verinfo);
extern void err_remark(const char *format, ...) PRINTFLIKE(1,2);
extern void err_remark1(const char *s1);
extern void err_remark2(const char *s1, const char *s2);
extern void err_report(int flags, int estat, const char *format, ...) PRINTFLIKE(3,4);
extern void err_sysrem(const char *format, ...) PRINTFLIKE(1,2);
extern void err_sysrem1(const char *s1);
extern void err_sysrem2(const char *s1, const char *s2);
extern void err_sysremark(int errnum, const char *format, ...) PRINTFLIKE(2,3);
extern int err_getlogopts(void); /* Get default log options */
extern int err_setlogopts(int new_opts); /* Set default log options */
#if defined(USE_STDERR_FILEDESC)
extern int err_use_fd(int fd); /* Use file descriptor */
#endif /* USE_STDERR_FILEDESC */
#if defined(USE_STDERR_SYSLOG)
/* In case of doubt, use zero for both logopts and facility */
extern int err_use_syslog(int logopts, int facility); /* Configure/use syslog() */
#endif /* USE_STDERR_SYSLOG */
/*
** JL 2003-07-31: Security Note.
** Question: given that err_remark("abc\n") and err_remark1("abc")
** produce the same output, when should you use err_remark1()
** instead of err_remark()?
** Answer 1: trivia - when you can't put the newline in the string.
** Answer 2: security - when the argument contains user input and could,
** therefore, contain conversion specifiers, etc. The code in
** err_remark() does not (and cannot) verify that you have
** passed correct arguments for the conversion specifiers in
** the format string.
** Answer 3: inertia - when migrating code that uses remark().
**
** Generalizing: when you use a function that has 'const char *format'
** in the prototype above, make sure your code is fully in charge of the
** format string to avoid security lapses. Do not allow the user to
** provide that string unless you stringently check it beforehand.
*/
#endif /* STDERR_H */
rename-it.sh
Description: Bourne shell script
