/* gawkmisc.c --- miscellaneous gawk routines that are OS specific. -*-C-*- */ /* * Copyright (C) 1986, 1988, 1989, 1991 - 2003, 2012, 2016 * the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Progamming Language. * * GAWK 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 3 of the License, or * (at your option) any later version. * * GAWK 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ char quote = '\''; char envsep = ';'; # ifdef DEFPATH char *defpath = DEFPATH; # else char *defpath = ".;c:\\lib\\awk;c:\\gnu\\lib\\awk"; # endif /* the Makefile should define DEFLIBPATH */ char *deflibpath = DEFLIBPATH; #ifdef __EMX__ #include static int _os2_is_abs_path(const char *dirname); static char* _os2_unixroot(const char *path); static const char* _os2_unixroot_path(const char *path); #endif #ifdef __MINGW32__ #ifdef HAVE_SOCKETS #include #undef socket #undef setsockopt #undef bind #undef connect #undef listen #undef accept #undef recvfrom #undef shutdown #endif #define WIN32_LEAN_AND_MEAN #include #endif /* gawk_name --- pull out the "gawk" part from how the OS called us */ char * gawk_name(filespec) const char *filespec; { char *p, *q; p = (char *) filespec; /* Sloppy... */ /* OS/2 allows / for directory separator too */ if ((q = strrchr(p, '\\')) != NULL) p = q + 1; if ((q = strrchr(p, '/')) != NULL && (p == NULL || q > p)) /* support mixed d:\foo/bar\gawk.exe */ p = q + 1; if ((q = strchr(p, '.')) != NULL) *q = '\0'; return strlwr(p); } /* * memcpy_long() & memset_ulong() are 32-bit replacements for MSC which * has a 16-bit size_t. */ char * memcpy_ulong (dest, src, l) register char *dest; register const char *src; register unsigned long l; { register char *ret = dest; while (l--) *dest++ = *src++; return ret; } void * memset_ulong(dest, val, l) void *dest; register int val; register unsigned long l; { register char *ret = dest; register char *d = dest; while (l--) *d++ = val; return ((void *) ret); } /* os_arg_fixup --- fixup the command line */ void os_arg_fixup(argcp, argvp) int *argcp; char ***argvp; { #ifdef __EMX__ # ifdef initialize_main initialize_main(argcp, argvp); # else _wildcard(argcp, argvp); _response(argcp, argvp); # endif setvbuf(stdout, NULL, _IOLBF, BUFSIZ); defpath = (char*) _os2_unixroot_path(defpath); #endif /* __EMX__ */ return; } /* os_devopen --- open special per-OS devices */ int os_devopen(name, flag) const char *name; int flag; { #ifdef __EMX__ /* do not use open(name, flag) here !!! */ return -1; #else if (strcmp(name, "/dev/null") == 0) return open("NUL", flag); /* FIXME: */ /* else if (strcmp(name, "/dev/tty") == 0) * return open("???", flag); */ return -1; #endif } /* optimal_bufsize --- determine optimal buffer size */ size_t optimal_bufsize(fd, stb) int fd; struct stat *stb; { /* force all members to zero in case OS doesn't use all of them. */ memset(stb, '\0', sizeof(struct stat)); /* * DOS doesn't have the file system block size in the * stat structure. So we have to make some sort of reasonable * guess. We use stdio's BUFSIZ, since that is what it was * meant for in the first place. */ #define DEFBLKSIZE BUFSIZ if (fstat(fd, stb) == -1) fatal("can't stat fd %d (%s)", fd, strerror(errno)); if (S_ISREG(stb->st_mode) && 0 < stb->st_size && stb->st_size < DEFBLKSIZE) /* small file */ return stb->st_size; return DEFBLKSIZE; } /* ispath --- return true if path has directory components */ int ispath(file) const char *file; { #ifdef __EMX__ return (strpbrk(file, "/\\") != NULL || (toupper(file[0]) >= 'A' && toupper(file[0]) <= 'Z' && file[1] == ':')); #else for (; *file; file++) { switch (*file) { case '/': case '\\': case ':': return 1; } } return 0; #endif } /* isdirpunct --- return true if char is a directory separator */ int isdirpunct(c) int c; { return (strchr(":\\/", c) != NULL); } /* os_close_on_exec --- set close on exec flag, print warning if fails */ void os_close_on_exec(fd, name, what, dir) int fd; const char *name, *what, *dir; { #if (defined(__DJGPP__) && (__DJGPP__ > 2 || __DJGPP_MINOR__ >= 4)) || defined __EMX__ if (fd <= 2) /* sanity */ return; if (fcntl(fd, F_SETFD, 1) < 0) warning("%s %s `%s': could not set close-on-exec: %s", what, dir, name, strerror(errno)); #endif #ifdef __MINGW32__ HANDLE fh = (HANDLE)_get_osfhandle(fd); if (fh && fh != INVALID_HANDLE_VALUE) SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0); #endif } /* os_isdir --- is this an fd on a directory? */ #if ! defined(S_ISDIR) && defined(S_IFDIR) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif int os_isdir(fd) int fd; { struct stat sbuf; return (fstat(fd, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)); } /* os_isreadable --- fd can be read from */ int os_isreadable(const awk_input_buf_t *iobuf, bool *isdir) { *isdir = false; switch (iobuf->sbuf.st_mode & S_IFMT) { case S_IFREG: case S_IFCHR: /* ttys, /dev/null, .. */ #ifdef S_IFSOCK case S_IFSOCK: #endif #ifdef S_IFIFO case S_IFIFO: #endif return true; case S_IFDIR: *isdir = true; /* fall through */ default: return false; } } /* os_is_setuid --- true if running setuid root */ int os_is_setuid() { #ifdef __EMX__ long uid, euid; uid = getuid(); euid = geteuid(); return (euid == 0 && euid != uid); #else return 0; #endif } /* os_setbinmode --- set binary mode on file */ #ifdef __DJGPP__ #include #include #endif static int orig_tty_mode = -1; int os_setbinmode(fd, mode) int fd, mode; { int prev_mode = setmode(fd, mode); #ifdef __DJGPP__ if ((mode & O_BINARY) != 0) __djgpp_set_ctrl_c(1); /* allow to interrupt with Ctrl-C */ #endif /* Save the original tty mode as we found it. */ if (orig_tty_mode == -1 && fd >= 0 && fd <= 2) orig_tty_mode = prev_mode; return prev_mode; } /* os_restore_mode --- restore the original mode of the console device */ void os_restore_mode (fd) int fd; { if (orig_tty_mode != -1) { setmode(fd, orig_tty_mode); } } /* os_isatty --- return true if fd is a tty */ int os_isatty(int fd) { #if defined(__MINGW32__) || defined(_MSC_VER) return (isatty(fd) && lseek(fd, SEEK_CUR, 0) == -1); #else return isatty(fd); #endif } /* files_are_same --- return true if files are identical */ int files_are_same(char *path, SRCFILE *src) { struct stat st; size_t pathlen; char *p, *s; if (stat (path, & st) == 0) { /* If they have a working `stat', honor that. */ if (!(st.st_dev == src->sbuf.st_dev && st.st_ino == src->sbuf.st_ino)) return 0; /* Compare modification times. */ if (st.st_mtime != src->mtime) return 0; /* Compare absolute file names case-insensitively, and treat forward- and back-slashes as equal. */ pathlen = strlen(path); for (p = path, s = src->fullpath; p <= path + pathlen; p++, s++) { if (tolower(*p) != tolower(*s) && !((*p == '/' || *p == '\\') && (*s == '/' || *s == '\\'))) return 0; } return 1; } return 0; } #ifdef __EMX__ # ifndef PATH_SEPARATOR # define PATH_SEPARATOR ';' # endif /* result is 0 if dirname is no absolute path, 1 otherwise */ static int _os2_is_abs_path(const char *dirname) { int result = 0; if (dirname != NULL && dirname[0] != '\0') { /* if dirname contains a valid drive letter like "c:" */ if (((dirname[0] >= 'A' && dirname[0] <= 'Z') || (dirname[0] >= 'a' && dirname[0] <= 'z')) && dirname[1] == ':') dirname += 2; /* remove the drive letter */ if (dirname[0] == '/' || dirname[0] == '\\') result = 1; /* asbolute path */ } return result; } /* path is assumed to be a list of directories separated by PATH_SEPARATOR. This function determines if the first directory of path is on the drive specified by the environment variable UNIXROOT. If it is the case, NULL is returned, otherwise a new directory name is allocated using the drive letter from UNIXROOT and returned as result. If the first directory is a relative path NULL is returned, too. The new directory name is allocated by malloc(). Example (UNIXROOT is set to "e:"): "c:/usr/share" -> "e:/usr/share" "e:/usr/share" -> NULL (already on the $UNIXROOT drive) "/usr/share" -> "e:/usr/share" "." -> NULL (not an absolute path) "usr/share" -> NULL (not an absolute path) "c:usr/share" -> NULL (not an absolute path) "c:/usr/share;d:/etc" -> "e:/usr/share" (only the first directory in path is used) */ static char* _os2_unixroot(const char *path) { static const char *unixroot = NULL; static int unixroot_init = 0; char *result = NULL; if (unixroot_init == 0) { /* get $UNIXROOT only one time */ unixroot = getenv("UNIXROOT"); /* check whether unixroot is valid (must be "x:") */ if (unixroot != NULL) { int drive = toupper(unixroot[0]); if (drive < 'A' || drive > 'Z' || unixroot[1] != ':' || unixroot[2] != '\0') unixroot = NULL; /* unixroot not valid */ } unixroot_init = 1; /* initialized */ } /* note: if unixroot != NULL then it contains a valid drive letter */ if (unixroot != NULL && _os2_is_abs_path(path)) { /* dirname is an absolute path and unixroot is a drive letter, "c:" for example */ size_t old_path_len = strlen(path); /* end points to the first ';' in path or to NULL */ const char *end = strchr(path, PATH_SEPARATOR); /* dir_len is the length of the first directory in path */ size_t dir_len = (end) ? end - path : old_path_len; if (toupper(unixroot[0]) != toupper(path[0]) || path[1] != ':') { /* the first directory of path does not start with the string $UNIXROOT */ if (path[1] == ':') { /* if there is a drive letter remove it */ dir_len -= 2; path += 2; } result = malloc(dir_len + 3); if (result) { /* do nothing if we are out of memory */ result[0] = unixroot[0]; result[1] = unixroot[1]; memcpy(result + 2, path, dir_len); result[dir_len + 2] = '\0'; } } } return result; } /* path is assumed to be a list of directories separated by PATH_SEPARATOR. Every directory is processed. _os2_unixroot() is used to find out whether these directories are on the drive specified by the environment variable UNIXROOT. If this is not the case the same directory on the UNIXROOT drive is added to the end of path. If path is a valid path this function returns a valid path, too. Example ($UNIXROOT is set to "e:"): ".;c:/usr/local;d:/usr/local;d:/etc;e:/etc" -> ".;c:/usr/local;d:/usr/local;d:/etc;e:/etc;e:/usr/local;e:/usr/local;e:/etc" */ static const char* _os2_unixroot_path(const char *path) { char *result = NULL; const char *p = path; unsigned dir_count = 1; if (path == NULL || path[0] == '\0') return NULL; /* empty path */ /* save number of path components in dir_count */ while(*p) { if (*p++ == PATH_SEPARATOR && *p != '\0' && *p != PATH_SEPARATOR) dir_count += 1; } { const char *list[dir_count]; /* list of char pointers */ size_t dir_len[dir_count]; /* the according directory length */ size_t old_path_len = strlen(path); /* the old path length */ size_t total_len; unsigned i = 0; if (path[old_path_len - 1] == PATH_SEPARATOR) /* last character is ';' */ old_path_len--; list[0] = p = path; /* first directory */ while(*p) { if (*p++ == PATH_SEPARATOR && *p != '\0' && *p != PATH_SEPARATOR) list[++i] = p; } /* now list[i] contains the ith directory of path (no 0-terminated strings!!!) */ /* determine the total length for the new path */ total_len = old_path_len; for(i = 0; i < dir_count; i++) { list[i] = _os2_unixroot(list[i]); if (list[i] != NULL) { dir_len[i] = strlen(list[i]); total_len += dir_len[i] + 1; /* one character for ';' or '\0' */ } else dir_len[i] = 0; } /* now list[] contains the according directories on the UNIXROOT drive or NULL total_len contains the total length for the new path */ result = malloc(total_len + 1); if (result) { /* copy the old path and the new directories into the new path */ char *q = result; memcpy(q, path, old_path_len); q += old_path_len; for(i = 0; i < dir_count; i++) { if (dir_len[i] != 0) { *q++ = PATH_SEPARATOR; memcpy(q, list[i], dir_len[i]); q += dir_len[i]; } } *q = '\0'; /* terminating '\0' */ } for(i = 0; i < dir_count; i++) free((void*) list[i]); } return (result) ? (const char*) result : path; } /* limit a length of DLL name up to 8 characters. If dst is not enough for a fixed dll name, it is truncated. */ char *os2_fixdllname(char *dst, const char *src, size_t n) { char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char name[_MAX_FNAME]; char ext[_MAX_EXT]; char dll_file[_MAX_PATH]; _splitpath(src, drive, dir, name, ext); if (strlen(name) > 8) name[8] = '\0'; _makepath(dll_file, drive, dir, name, ext); strncpy(dst, dll_file, n); dst[n - 1] = '\0'; return dst; } #ifdef __KLIBC__ /* replacement of dlopen(). This limits a length of a base name up to 8 characters. */ void *os2_dlopen(const char *file, int mode) { char dll_file[strlen(file) + 1]; return (dlopen)(os2_fixdllname(dll_file, file, sizeof(dll_file)), mode); } /* replacement of dlsym(). This prepends '_' to name. */ void *os2_dlsym(void *handle, const char *name) { char sym[strlen(name) + 1 + 1]; /* 1 for '_', 1 for NUL */ sym[0] = '_'; strcpy(sym + 1, name); return (dlsym)(handle, sym); } #endif /* __KLIBC__ */ #endif /* __EMX__ */ #ifdef __MINGW32__ extern void *xmalloc (size_t); int setenv (const char *name, const char *value, int rewrite) { char *entry; if (*value == '=') ++value; if (getenv (name) && !rewrite) return 0; entry = xmalloc (strlen (name) + 1 + strlen (value) + 1); strcat (strcat (strcpy (entry, name), "="), value); if (putenv (entry) != 0) { free (entry); return -1; } return 0; } int unsetenv (const char *name) { if (!name || !*name || strchr (name, '=') != NULL) return -1; return setenv (name, "", 1); } /* MinGW 3.21 and later defines usleep as an inline function in unistd.h, which conflicts with the version below. */ #if __MINGW32_MAJOR_VERSION + (__MINGW32_MINOR_VERSION > 20) < 4 int usleep(unsigned int usec) { double msecf = usec / 1000.0; Sleep ((DWORD)msecf); return usec - msecf * 1000 < 0 ? 0 : (int)(usec - msecf * 1000); } #endif /* The implementation of wctob in the MS runtime is problematic because it doesn't allow to distinguish between WEOF and 0xff, due to integer sign extension. It also causes failures in dfa.c when characters with the 8th bit set are involved. This replacement version fixes that. */ #include int wctob (wint_t wc) { char buf[64]; if (!(MB_CUR_MAX <= sizeof (buf))) abort (); /* Handle the case where WEOF is a value that does not fit in a wchar_t. */ if (wc == (wchar_t)wc) if (wctomb (buf, (wchar_t)wc) == 1) return (unsigned char) buf[0]; return EOF; } #undef setlocale #include /* On Posix systems, 'setlocale' looks at LC_* variables in the environment, and Gawk users might expect that on Windows as well. The replacement implementation below does that, and also fixes a few additional quirks with locales on Windows. */ static const char * lc_var (int category) { static const char *loc_name[LC_MAX - LC_MIN + 1] = { "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME" }; /* This function assumes LC_* categories are small numbers between 0 and 5, as shown above, so if that changes at some point, complain vociferously. */ if (LC_ALL != 0 || LC_CTYPE != 2 || LC_TIME != 5) abort (); /* Ensured by the caller, so should never happen. */ if (category < LC_MIN || category > LC_MAX) return "????"; return loc_name[category]; } char * w32_setlocale (int category, const char *value) { const char *new_locale = value; if (LC_MIN <= category && category <= LC_MAX && value && *value == '\0') { const char *lc_val = getenv ("LC_ALL"); if (!lc_val) lc_val = getenv (lc_var (category)); if (!lc_val) lc_val = getenv ("LANG"); if (lc_val) new_locale = lc_val; } /* If VALUE includes a codeset, i.e. a Windows codepage number, we must also set the LC_CTYPE locale to the same value, because LC_CTYPE is the only category which is documented to be able to change the codepage. */ if (category != LC_ALL && category != LC_CTYPE) { const char *p = strchr (new_locale, '.'); if (p && isdigit (p[1])) setlocale (LC_CTYPE, new_locale); } return setlocale (category, new_locale); } /* * On MS-Windows with MinGW, execvp causes the shell and the re-exec'ed * dgawk to compete for the keyboard input. * * This will need work if we ever need a real version of execvp. */ int execvp(const char *file, const char *const *argv) { if (_spawnvp(_P_WAIT, file, (const char * const *)argv) != -1) exit(EXIT_SUCCESS); return -1; } #ifdef DYNAMIC #include static DWORD last_err; void * dlopen (const char *file, int mode) { char dllfn[MAX_PATH], *p; HANDLE dllhandle; if (mode != RTLD_LAZY) { errno = EINVAL; last_err = ERROR_INVALID_PARAMETER; return NULL; } /* MSDN says to be sure to use backslashes in the DLL file name. */ strcpy (dllfn, file); for (p = dllfn; *p; p++) if (*p == '/') *p = '\\'; dllhandle = LoadLibrary (dllfn); if (!dllhandle) last_err = GetLastError (); return dllhandle; } char * dlerror (void) { static char errbuf[1024]; DWORD ret; if (!last_err) return NULL; ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_err, 0, errbuf, sizeof (errbuf), NULL); while (ret > 0 && (errbuf[ret - 1] == '\n' || errbuf[ret - 1] == '\r')) --ret; errbuf[ret] = '\0'; if (!ret) sprintf (errbuf, "Error code %lu", last_err); last_err = 0; return errbuf; } int dlclose (void *handle) { if (!handle || handle == INVALID_HANDLE_VALUE) return -1; if (!FreeLibrary (handle)) return -1; return 0; } void * dlsym (void *handle, const char *name) { FARPROC addr = NULL; if (!handle || handle == INVALID_HANDLE_VALUE) { last_err = ERROR_INVALID_PARAMETER; return NULL; } addr = GetProcAddress (handle, name); if (!addr) last_err = GetLastError (); return (void *)addr; } #endif /* DYNAMIC */ #ifdef HAVE_SOCKETS int socket_to_fd(SOCKET s) { return (s == INVALID_SOCKET ? INVALID_HANDLE : _open_osfhandle (s, O_BINARY | O_NOINHERIT)); } int w32_socket(int family, int type, int protocol) { /* We need to use WSASocket rather than socket, since the latter creates overlapped sockets that cannot be used in file I/O APIs. */ SOCKET s = WSASocket (family, type, protocol, NULL, 0, 0); if (s == INVALID_SOCKET) { switch (WSAGetLastError ()) { case WSAEMFILE: errno = EMFILE; break; case WSANOTINITIALISED: case WSAENETDOWN: errno = EACCES; break; case WSAENOBUFS: errno = ENOMEM; break; case WSAEFAULT: errno = EFAULT; break; default: errno = EINVAL; break; } } return socket_to_fd (s); } int w32_setsockopt (int fd, int level, int optname, const char *optval, int optlen) { SOCKET s = FD_TO_SOCKET (fd); return setsockopt (s, level, optname, optval, optlen); } int w32_bind (int fd, const struct sockaddr *name, int namelen) { SOCKET s = FD_TO_SOCKET (fd); return bind (s, name, namelen); } int w32_connect (int fd, const struct sockaddr *name, int namelen) { SOCKET s = FD_TO_SOCKET (fd); return connect (s, name, namelen); } int w32_listen (int fd, int backlog) { SOCKET s = FD_TO_SOCKET (fd); return listen (s, backlog); } int w32_accept (int fd, struct sockaddr *addr, int *addrlen) { SOCKET s = FD_TO_SOCKET (fd); return socket_to_fd (accept (s, addr, addrlen)); } SOCKET valid_socket (int fd) { SOCKET s = FD_TO_SOCKET (fd); int ov, ol = 4; if (s == INVALID_SOCKET || (getsockopt (s, SOL_SOCKET, SO_TYPE, (char *)&ov, &ol) == SOCKET_ERROR && WSAGetLastError() == WSAENOTSOCK)) return (SOCKET)0; return s; } int w32_closesocket (int fd) { SOCKET s = valid_socket (fd); int res1, res2 = 0; if (!s && fd == FAKE_FD_VALUE) return 0; res1 = close (fd); if (s) res2 = closesocket (s); if (res1 == -1 || res2 == SOCKET_ERROR) return -1; return 0; } int w32_recvfrom (int fd, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) { SOCKET s = FD_TO_SOCKET (fd); return recvfrom (s, buf, len, flags, from, fromlen); } int w32_shutdown (int fd, int how) { SOCKET s = FD_TO_SOCKET (fd); return shutdown (s, how); } #endif /* HAVE_SOCKETS */ /* Translate abnormal exit status of Windows programs into the signal that terminated the program. This is required to support scm_kill and WTERMSIG. */ #include struct signal_and_status { int sig; unsigned status; }; static const struct signal_and_status sigtbl[] = { {SIGSEGV, 0xC0000005}, /* access to invalid address */ {SIGSEGV, 0xC0000008}, /* invalid handle */ {SIGILL, 0xC000001D}, /* illegal instruction */ {SIGILL, 0xC0000025}, /* non-continuable instruction */ {SIGSEGV, 0xC000008C}, /* array bounds exceeded */ {SIGFPE, 0xC000008D}, /* float denormal */ {SIGFPE, 0xC000008E}, /* float divide by zero */ {SIGFPE, 0xC000008F}, /* float inexact */ {SIGFPE, 0xC0000090}, /* float invalid operation */ {SIGFPE, 0xC0000091}, /* float overflow */ {SIGFPE, 0xC0000092}, /* float stack check */ {SIGFPE, 0xC0000093}, /* float underflow */ {SIGFPE, 0xC0000094}, /* integer divide by zero */ {SIGFPE, 0xC0000095}, /* integer overflow */ {SIGILL, 0xC0000096}, /* privileged instruction */ {SIGSEGV, 0xC00000FD}, /* stack overflow */ {SIGTERM, 0xC000013A}, /* Ctrl-C exit */ {SIGINT, 0xC000013A} }; int w32_status_to_termsig (unsigned status) { int i; for (i = 0; i < sizeof (sigtbl) / sizeof (sigtbl[0]); i++) if (status == sigtbl[i].status) return sigtbl[i].sig; return SIGTERM; } void w32_maybe_set_errno (void) { DWORD w32err = GetLastError (); switch (w32err) { /* When stdout is redirected to a pipe, and the program that reads the pipe (e.g., a pager) exits, Windows doesn't set errno to a useful value. Help it DTRT. */ case ERROR_BAD_PIPE: case ERROR_PIPE_BUSY: case ERROR_NO_DATA: case ERROR_PIPE_NOT_CONNECTED: errno = EPIPE; break; default: errno = EINVAL; break; } } #endif /* __MINGW32__ */ #if defined(__DJGPP__) || defined(__MINGW32__) || defined(__EMX__) void init_sockets(void) { #if defined(HAVE_SOCKETS) && !defined(__EMX__) WSADATA winsockData; int errcode; if ((errcode = WSAStartup (0x101, &winsockData)) != 0 || winsockData.wVersion != 0x101) fatal(_("cannot start Winsock (%d)"), errcode); #endif } #endif /* __DJGPP__ || __MINGW32__ */ #ifdef __DJGPP__ # if __DJGPP__ == 2 && __DJGPP_MINOR__ < 4 int unsetenv (const char *name) { if (!name || !*name || strchr (name, '=') != NULL) return -1; return putenv (name); } #endif /* This is needed to defeat too-clever GCC warnings in dfa.c about comparison being always false due to limited range of data type. */ wint_t btowc (int c) { return c; } wint_t putwc(wchar_t wc, FILE *stream) { return MB_CUR_MAX == 1 && wc > 0 && wc <= UCHAR_MAX && putc((unsigned char)wc, stream) != EOF ? (wint_t)wc : WEOF; } #endif /* __DJGPP__ */