509 lines
16 KiB
C++
509 lines
16 KiB
C++
/* errno.cc: errno-related functions
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#define _sys_nerr FOO_sys_nerr
|
|
#define sys_nerr FOOsys_nerr
|
|
#define _sys_errlist FOO_sys_errlist
|
|
#define strerror_r FOO_strerror_r
|
|
#define __INSIDE_CYGWIN__
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "winsup.h"
|
|
#include "cygtls.h"
|
|
#include "ntdll.h"
|
|
#undef _sys_nerr
|
|
#undef sys_nerr
|
|
#undef _sys_errlist
|
|
#undef strerror_r
|
|
|
|
/* Table to map Windows error codes to Errno values. */
|
|
/* FIXME: Doing things this way is a little slow. It's trivial to change
|
|
this into a big case statement if necessary. Left as is for now. */
|
|
|
|
#define X(w, e) {ERROR_##w, #w, e}
|
|
|
|
static const errmap_t errmap[] =
|
|
{
|
|
/* FIXME: Some of these choices are arbitrary! */
|
|
X (ACCESS_DENIED, EACCES),
|
|
X (ACTIVE_CONNECTIONS, EAGAIN),
|
|
X (ALREADY_EXISTS, EEXIST),
|
|
X (BAD_DEVICE, ENODEV),
|
|
X (BAD_EXE_FORMAT, ENOEXEC),
|
|
X (BAD_NETPATH, ENOENT),
|
|
X (BAD_NET_NAME, ENOENT),
|
|
X (BAD_NET_RESP, ENOSYS),
|
|
X (BAD_PATHNAME, ENOENT),
|
|
X (BAD_PIPE, EINVAL),
|
|
X (BAD_UNIT, ENODEV),
|
|
X (BAD_USERNAME, EINVAL),
|
|
X (BEGINNING_OF_MEDIA, EIO),
|
|
X (BROKEN_PIPE, EPIPE),
|
|
X (BUSY, EBUSY),
|
|
X (BUS_RESET, EIO),
|
|
X (CALL_NOT_IMPLEMENTED, ENOSYS),
|
|
X (CANCELLED, EINTR),
|
|
X (CANNOT_MAKE, EPERM),
|
|
X (CHILD_NOT_COMPLETE, EBUSY),
|
|
X (COMMITMENT_LIMIT, EAGAIN),
|
|
X (CONNECTION_REFUSED, ECONNREFUSED),
|
|
X (CRC, EIO),
|
|
X (DEVICE_DOOR_OPEN, EIO),
|
|
X (DEVICE_IN_USE, EAGAIN),
|
|
X (DEVICE_REQUIRES_CLEANING, EIO),
|
|
X (DEV_NOT_EXIST, ENOENT),
|
|
X (DIRECTORY, ENOTDIR),
|
|
X (DIR_NOT_EMPTY, ENOTEMPTY),
|
|
X (DISK_CORRUPT, EIO),
|
|
X (DISK_FULL, ENOSPC),
|
|
X (DS_GENERIC_ERROR, EIO),
|
|
X (DUP_NAME, ENOTUNIQ),
|
|
X (EAS_DIDNT_FIT, ENOSPC),
|
|
X (EAS_NOT_SUPPORTED, ENOTSUP),
|
|
X (EA_LIST_INCONSISTENT, EINVAL),
|
|
X (EA_TABLE_FULL, ENOSPC),
|
|
X (END_OF_MEDIA, ENOSPC),
|
|
X (EOM_OVERFLOW, EIO),
|
|
X (EXE_MACHINE_TYPE_MISMATCH, ENOEXEC),
|
|
X (EXE_MARKED_INVALID, ENOEXEC),
|
|
X (FILEMARK_DETECTED, EIO),
|
|
X (FILENAME_EXCED_RANGE, ENAMETOOLONG),
|
|
X (FILE_CORRUPT, EEXIST),
|
|
X (FILE_EXISTS, EEXIST),
|
|
X (FILE_INVALID, ENXIO),
|
|
X (FILE_NOT_FOUND, ENOENT),
|
|
X (HANDLE_DISK_FULL, ENOSPC),
|
|
X (HANDLE_EOF, ENODATA),
|
|
X (INVALID_ADDRESS, EINVAL),
|
|
X (INVALID_AT_INTERRUPT_TIME, EINTR),
|
|
X (INVALID_BLOCK_LENGTH, EIO),
|
|
X (INVALID_DATA, EINVAL),
|
|
X (INVALID_DRIVE, ENODEV),
|
|
X (INVALID_EA_NAME, EINVAL),
|
|
X (INVALID_EXE_SIGNATURE, ENOEXEC),
|
|
X (INVALID_FUNCTION, EBADRQC),
|
|
X (INVALID_HANDLE, EBADF),
|
|
X (INVALID_NAME, ENOENT),
|
|
X (INVALID_PARAMETER, EINVAL),
|
|
X (INVALID_SIGNAL_NUMBER, EINVAL),
|
|
X (IOPL_NOT_ENABLED, ENOEXEC),
|
|
X (IO_DEVICE, EIO),
|
|
X (IO_INCOMPLETE, EAGAIN),
|
|
X (IO_PENDING, EAGAIN),
|
|
X (LOCK_VIOLATION, EBUSY),
|
|
X (MAX_THRDS_REACHED, EAGAIN),
|
|
X (META_EXPANSION_TOO_LONG, EINVAL),
|
|
X (MOD_NOT_FOUND, ENOENT),
|
|
X (MORE_DATA, EMSGSIZE),
|
|
X (NEGATIVE_SEEK, EINVAL),
|
|
X (NETNAME_DELETED, ENOENT),
|
|
X (NOACCESS, EFAULT),
|
|
X (NONE_MAPPED, EINVAL),
|
|
X (NONPAGED_SYSTEM_RESOURCES, EAGAIN),
|
|
X (NOT_CONNECTED, ENOLINK),
|
|
X (NOT_ENOUGH_MEMORY, ENOMEM),
|
|
X (NOT_ENOUGH_QUOTA, EIO),
|
|
X (NOT_OWNER, EPERM),
|
|
X (NOT_READY, ENOMEDIUM),
|
|
X (NOT_SAME_DEVICE, EXDEV),
|
|
X (NOT_SUPPORTED, ENOSYS),
|
|
X (NO_DATA, EPIPE),
|
|
X (NO_DATA_DETECTED, EIO),
|
|
X (NO_MEDIA_IN_DRIVE, ENOMEDIUM),
|
|
X (NO_MORE_FILES, ENMFILE),
|
|
X (NO_MORE_ITEMS, ENMFILE),
|
|
X (NO_MORE_SEARCH_HANDLES, ENFILE),
|
|
X (NO_PROC_SLOTS, EAGAIN),
|
|
X (NO_SIGNAL_SENT, EIO),
|
|
X (NO_SYSTEM_RESOURCES, EFBIG),
|
|
X (NO_TOKEN, EINVAL),
|
|
X (OPEN_FAILED, EIO),
|
|
X (OPEN_FILES, EAGAIN),
|
|
X (OUTOFMEMORY, ENOMEM),
|
|
X (PAGED_SYSTEM_RESOURCES, EAGAIN),
|
|
X (PAGEFILE_QUOTA, EAGAIN),
|
|
X (PATH_NOT_FOUND, ENOENT),
|
|
X (PIPE_BUSY, EBUSY),
|
|
X (PIPE_CONNECTED, EBUSY),
|
|
X (PIPE_LISTENING, ECOMM),
|
|
X (PIPE_NOT_CONNECTED, ECOMM),
|
|
X (POSSIBLE_DEADLOCK, EDEADLOCK),
|
|
X (PRIVILEGE_NOT_HELD, EPERM),
|
|
X (PROCESS_ABORTED, EFAULT),
|
|
X (PROC_NOT_FOUND, ESRCH),
|
|
X (REM_NOT_LIST, ENONET),
|
|
X (SECTOR_NOT_FOUND, EINVAL),
|
|
X (SEEK, EINVAL),
|
|
X (SERVICE_REQUEST_TIMEOUT, EBUSY),
|
|
X (SETMARK_DETECTED, EIO),
|
|
X (SHARING_BUFFER_EXCEEDED, ENOLCK),
|
|
X (SHARING_VIOLATION, EBUSY),
|
|
X (SIGNAL_PENDING, EBUSY),
|
|
X (SIGNAL_REFUSED, EIO),
|
|
X (SXS_CANT_GEN_ACTCTX, ELIBBAD),
|
|
X (THREAD_1_INACTIVE, EINVAL),
|
|
X (TIMEOUT, EBUSY),
|
|
X (TOO_MANY_LINKS, EMLINK),
|
|
X (TOO_MANY_OPEN_FILES, EMFILE),
|
|
X (UNEXP_NET_ERR, EIO),
|
|
X (WAIT_NO_CHILDREN, ECHILD),
|
|
X (WORKING_SET_QUOTA, EAGAIN),
|
|
X (WRITE_PROTECT, EROFS),
|
|
{ 0, NULL, 0}
|
|
};
|
|
|
|
extern "C" {
|
|
const char *_sys_errlist[] =
|
|
{
|
|
/* NOERROR 0 */ "No error",
|
|
/* EPERM 1 */ "Operation not permitted",
|
|
/* ENOENT 2 */ "No such file or directory",
|
|
/* ESRCH 3 */ "No such process",
|
|
/* EINTR 4 */ "Interrupted system call",
|
|
/* EIO 5 */ "Input/output error",
|
|
/* ENXIO 6 */ "No such device or address",
|
|
/* E2BIG 7 */ "Argument list too long",
|
|
/* ENOEXEC 8 */ "Exec format error",
|
|
/* EBADF 9 */ "Bad file descriptor",
|
|
/* ECHILD 10 */ "No child processes",
|
|
/* EAGAIN 11 */ "Resource temporarily unavailable",
|
|
/* ENOMEM 12 */ "Cannot allocate memory",
|
|
/* EACCES 13 */ "Permission denied",
|
|
/* EFAULT 14 */ "Bad address",
|
|
/* ENOTBLK 15 */ "Block device required",
|
|
/* EBUSY 16 */ "Device or resource busy",
|
|
/* EEXIST 17 */ "File exists",
|
|
/* EXDEV 18 */ "Invalid cross-device link",
|
|
/* ENODEV 19 */ "No such device",
|
|
/* ENOTDIR 20 */ "Not a directory",
|
|
/* EISDIR 21 */ "Is a directory",
|
|
/* EINVAL 22 */ "Invalid argument",
|
|
/* ENFILE 23 */ "Too many open files in system",
|
|
/* EMFILE 24 */ "Too many open files",
|
|
/* ENOTTY 25 */ "Inappropriate ioctl for device",
|
|
/* ETXTBSY 26 */ "Text file busy",
|
|
/* EFBIG 27 */ "File too large",
|
|
/* ENOSPC 28 */ "No space left on device",
|
|
/* ESPIPE 29 */ "Illegal seek",
|
|
/* EROFS 30 */ "Read-only file system",
|
|
/* EMLINK 31 */ "Too many links",
|
|
/* EPIPE 32 */ "Broken pipe",
|
|
/* EDOM 33 */ "Numerical argument out of domain",
|
|
/* ERANGE 34 */ "Numerical result out of range",
|
|
/* ENOMSG 35 */ "No message of desired type",
|
|
/* EIDRM 36 */ "Identifier removed",
|
|
/* ECHRNG 37 */ "Channel number out of range",
|
|
/* EL2NSYNC 38 */ "Level 2 not synchronized",
|
|
/* EL3HLT 39 */ "Level 3 halted",
|
|
/* EL3RST 40 */ "Level 3 reset",
|
|
/* ELNRNG 41 */ "Link number out of range",
|
|
/* EUNATCH 42 */ "Protocol driver not attached",
|
|
/* ENOCSI 43 */ "No CSI structure available",
|
|
/* EL2HLT 44 */ "Level 2 halted",
|
|
/* EDEADLK 45 */ "Resource deadlock avoided",
|
|
/* ENOLCK 46 */ "No locks available",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
/* EBADE 50 */ "Invalid exchange",
|
|
/* EBADR 51 */ "Invalid request descriptor",
|
|
/* EXFULL 52 */ "Exchange full",
|
|
/* ENOANO 53 */ "No anode",
|
|
/* EBADRQC 54 */ "Invalid request code",
|
|
/* EBADSLT 55 */ "Invalid slot",
|
|
/* EDEADLOCK 56 */ "File locking deadlock error",
|
|
/* EBFONT 57 */ "Bad font file format",
|
|
NULL,
|
|
NULL,
|
|
/* ENOSTR 60 */ "Device not a stream",
|
|
/* ENODATA 61 */ "No data available",
|
|
/* ETIME 62 */ "Timer expired",
|
|
/* ENOSR 63 */ "Out of streams resources",
|
|
/* ENONET 64 */ "Machine is not on the network",
|
|
/* ENOPKG 65 */ "Package not installed",
|
|
/* EREMOTE 66 */ "Object is remote",
|
|
/* ENOLINK 67 */ "Link has been severed",
|
|
/* EADV 68 */ "Advertise error",
|
|
/* ESRMNT 69 */ "Srmount error",
|
|
/* ECOMM 70 */ "Communication error on send",
|
|
/* EPROTO 71 */ "Protocol error",
|
|
NULL,
|
|
NULL,
|
|
/* EMULTIHOP 74 */ "Multihop attempted",
|
|
/* ELBIN 75 */ "Inode is remote (not really error)",
|
|
/* EDOTDOT 76 */ "RFS specific error",
|
|
/* EBADMSG 77 */ "Bad message",
|
|
NULL,
|
|
/* EFTYPE 79 */ "Inappropriate file type or format",
|
|
/* ENOTUNIQ 80 */ "Name not unique on network",
|
|
/* EBADFD 81 */ "File descriptor in bad state",
|
|
/* EREMCHG 82 */ "Remote address changed",
|
|
/* ELIBACC 83 */ "Can not access a needed shared library",
|
|
/* ELIBBAD 84 */ "Accessing a corrupted shared library",
|
|
/* ELIBSCN 85 */ ".lib section in a.out corrupted",
|
|
/* ELIBMAX 86 */ "Attempting to link in too many shared libraries",
|
|
/* ELIBEXEC 87 */ "Cannot exec a shared library directly",
|
|
/* ENOSYS 88 */ "Function not implemented",
|
|
/* ENMFILE 89 */ "No more files",
|
|
/* ENOTEMPTY 90 */ "Directory not empty",
|
|
/* ENAMETOOLONG 91 */ "File name too long",
|
|
/* ELOOP 92 */ "Too many levels of symbolic links",
|
|
NULL,
|
|
NULL,
|
|
/* EOPNOTSUPP 95 */ "Operation not supported",
|
|
/* EPFNOSUPPORT 96 */ "Protocol family not supported",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
/* ECONNRESET 104 */ "Connection reset by peer",
|
|
/* ENOBUFS 105 */ "No buffer space available",
|
|
/* EAFNOSUPPORT 106 */ "Address family not supported by protocol",
|
|
/* EPROTOTYPE 107 */ "Protocol wrong type for socket",
|
|
/* ENOTSOCK 108 */ "Socket operation on non-socket",
|
|
/* ENOPROTOOPT 109 */ "Protocol not available",
|
|
/* ESHUTDOWN 110 */ "Cannot send after transport endpoint shutdown",
|
|
/* ECONNREFUSED 111 */ "Connection refused",
|
|
/* EADDRINUSE 112 */ "Address already in use",
|
|
/* ECONNABORTED 113 */ "Software caused connection abort",
|
|
/* ENETUNREACH 114 */ "Network is unreachable",
|
|
/* ENETDOWN 115 */ "Network is down",
|
|
/* ETIMEDOUT 116 */ "Connection timed out",
|
|
/* EHOSTDOWN 117 */ "Host is down",
|
|
/* EHOSTUNREACH 118 */ "No route to host",
|
|
/* EINPROGRESS 119 */ "Operation now in progress",
|
|
/* EALREADY 120 */ "Operation already in progress",
|
|
/* EDESTADDRREQ 121 */ "Destination address required",
|
|
/* EMSGSIZE 122 */ "Message too long",
|
|
/* EPROTONOSUPPORT 123 */ "Protocol not supported",
|
|
/* ESOCKTNOSUPPORT 124 */ "Socket type not supported",
|
|
/* EADDRNOTAVAIL 125 */ "Cannot assign requested address",
|
|
/* ENETRESET 126 */ "Network dropped connection on reset",
|
|
/* EISCONN 127 */ "Transport endpoint is already connected",
|
|
/* ENOTCONN 128 */ "Transport endpoint is not connected",
|
|
/* ETOOMANYREFS 129 */ "Too many references: cannot splice",
|
|
/* EPROCLIM 130 */ "Too many processes",
|
|
/* EUSERS 131 */ "Too many users",
|
|
/* EDQUOT 132 */ "Disk quota exceeded",
|
|
/* ESTALE 133 */ "Stale NFS file handle",
|
|
/* ENOTSUP 134 */ "Not supported",
|
|
/* ENOMEDIUM 135 */ "No medium found",
|
|
/* ENOSHARE 136 */ "No such host or network path",
|
|
/* ECASECLASH 137 */ "Filename exists with different case",
|
|
/* EILSEQ 138 */ "Invalid or incomplete multibyte or wide character",
|
|
/* EOVERFLOW 139 */ "Value too large for defined data type",
|
|
/* ECANCELED 140 */ "Operation canceled",
|
|
/* ENOTRECOVERABLE 141 */ "State not recoverable",
|
|
/* EOWNERDEAD 142 */ "Previous owner died",
|
|
/* ESTRPIPE 143 */ "Streams pipe error"
|
|
};
|
|
|
|
int NO_COPY_INIT _sys_nerr = sizeof (_sys_errlist) / sizeof (_sys_errlist[0]);
|
|
};
|
|
|
|
int __reg2
|
|
geterrno_from_win_error (DWORD code, int deferrno)
|
|
{
|
|
for (int i = 0; errmap[i].w != 0; ++i)
|
|
if (code == errmap[i].w)
|
|
{
|
|
syscall_printf ("windows error %u == errno %d", code, errmap[i].e);
|
|
return errmap[i].e;
|
|
}
|
|
|
|
syscall_printf ("unknown windows error %u, setting errno to %d", code,
|
|
deferrno);
|
|
return deferrno; /* FIXME: what's so special about EACCESS? */
|
|
}
|
|
|
|
/* seterrno_from_win_error: Given a Windows error code, set errno
|
|
as appropriate. */
|
|
void __reg3
|
|
seterrno_from_win_error (const char *file, int line, DWORD code)
|
|
{
|
|
syscall_printf ("%s:%d windows error %u", file, line, code);
|
|
errno = _impure_ptr->_errno = geterrno_from_win_error (code, EACCES);
|
|
}
|
|
|
|
int __reg2
|
|
geterrno_from_nt_status (NTSTATUS status, int deferrno)
|
|
{
|
|
return geterrno_from_win_error (RtlNtStatusToDosError (status));
|
|
}
|
|
|
|
/* seterrno_from_nt_status: Given a NT status code, set errno
|
|
as appropriate. */
|
|
void __reg3
|
|
seterrno_from_nt_status (const char *file, int line, NTSTATUS status)
|
|
{
|
|
DWORD code = RtlNtStatusToDosError (status);
|
|
SetLastError (code);
|
|
syscall_printf ("%s:%d status %y -> windows error %u",
|
|
file, line, status, code);
|
|
errno = _impure_ptr->_errno = geterrno_from_win_error (code, EACCES);
|
|
}
|
|
|
|
static char *
|
|
strerror_worker (int errnum)
|
|
{
|
|
char *res;
|
|
if (errnum >= 0 && errnum < _sys_nerr)
|
|
res = (char *) _sys_errlist [errnum];
|
|
else
|
|
res = NULL;
|
|
return res;
|
|
}
|
|
|
|
/* Newlib requires this override for perror and friends to avoid
|
|
clobbering strerror() buffer, without having to differentiate
|
|
between strerror_r signatures. This function is intentionally not
|
|
exported, so that only newlib can use it. */
|
|
extern "C" char *
|
|
_strerror_r (struct _reent *, int errnum, int internal, int *errptr)
|
|
{
|
|
char *errstr = strerror_worker (errnum);
|
|
if (!errstr)
|
|
{
|
|
errstr = internal ? _my_tls.locals.strerror_r_buf
|
|
: _my_tls.locals.strerror_buf;
|
|
__small_sprintf (errstr, "Unknown error %d", errnum);
|
|
if (errptr)
|
|
*errptr = EINVAL;
|
|
}
|
|
return errstr;
|
|
}
|
|
|
|
/* strerror: convert from errno values to error strings. Newlib's
|
|
strerror_r returns "" for unknown values, so we override it to
|
|
provide a nicer thread-safe result string and set errno. */
|
|
extern "C" char *
|
|
strerror (int errnum)
|
|
{
|
|
int error = 0;
|
|
char *result = _strerror_r (NULL, errnum, 0, &error);
|
|
if (error)
|
|
set_errno (error);
|
|
return result;
|
|
}
|
|
|
|
extern "C" char *
|
|
strerror_l (int errnum, locale_t locale)
|
|
{
|
|
/* We don't provide localized system error messages (yet?). */
|
|
return strerror (errnum);
|
|
}
|
|
|
|
/* Newlib's <string.h> provides declarations for two strerror_r
|
|
variants, according to preprocessor feature macros. However, it
|
|
returns "" instead of "Unknown error ...", so we override both
|
|
versions. */
|
|
extern "C" char *
|
|
strerror_r (int errnum, char *buf, size_t n)
|
|
{
|
|
int error = 0;
|
|
char *errstr = _strerror_r (NULL, errnum, 1, &error);
|
|
if (error)
|
|
set_errno (error);
|
|
if (strlen (errstr) >= n)
|
|
return errstr;
|
|
return strcpy (buf, errstr);
|
|
}
|
|
|
|
extern "C" int
|
|
__xpg_strerror_r (int errnum, char *buf, size_t n)
|
|
{
|
|
if (!n)
|
|
return ERANGE;
|
|
int result = 0;
|
|
char *error = strerror_worker (errnum);
|
|
char tmp[sizeof "Unknown error -2147483648"];
|
|
if (!error)
|
|
{
|
|
__small_sprintf (error = tmp, "Unknown error %d", errnum);
|
|
result = EINVAL;
|
|
}
|
|
if (strlen (error) >= n)
|
|
{
|
|
memcpy (buf, error, n - 1);
|
|
buf[n - 1] = '\0';
|
|
return ERANGE;
|
|
}
|
|
strcpy (buf, error);
|
|
return result;
|
|
}
|
|
|
|
unsigned int error_message_count = 0;
|
|
int error_one_per_line = 0;
|
|
void (*error_print_progname) (void) = NULL;
|
|
|
|
static void
|
|
_verror (int status, int errnum, const char *filename, unsigned int lineno, const char *fmt, va_list ap)
|
|
{
|
|
error_message_count++;
|
|
|
|
fflush (stdout);
|
|
|
|
if (error_print_progname)
|
|
(*error_print_progname) ();
|
|
else
|
|
fprintf (stderr, "%s:%s", program_invocation_name, filename ? "" : " ");
|
|
|
|
if (filename)
|
|
fprintf (stderr, "%s:%d: ", filename, lineno);
|
|
|
|
vfprintf (stderr, fmt, ap);
|
|
|
|
if (errnum != 0)
|
|
fprintf (stderr, ": %s", strerror (errnum));
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
if (status != 0)
|
|
exit (status);
|
|
}
|
|
|
|
extern "C" void
|
|
error (int status, int errnum, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, fmt);
|
|
_verror (status, errnum, NULL, 0, fmt, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
extern "C" void
|
|
error_at_line (int status, int errnum, const char *filename, unsigned int lineno, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (error_one_per_line != 0)
|
|
{
|
|
static const char *last_filename;
|
|
static unsigned int last_lineno;
|
|
|
|
/* strcmp(3) will SEGV if filename or last_filename are NULL */
|
|
if (lineno == last_lineno
|
|
&& ((!filename && !last_filename)
|
|
|| (filename && last_filename && strcmp (filename, last_filename) == 0)))
|
|
return;
|
|
|
|
last_filename = filename;
|
|
last_lineno = lineno;
|
|
}
|
|
|
|
va_start (ap, fmt);
|
|
_verror (status, errnum, filename, lineno, fmt, ap);
|
|
va_end (ap);
|
|
}
|