summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faylor <cgf@redhat.com>2000-02-17 19:38:31 +0000
committerChristopher Faylor <cgf@redhat.com>2000-02-17 19:38:31 +0000
commitbd592806f45307757c24ec3f75bdefe13abfd51f (patch)
tree35cb30ab8b3f38691a715fe691879afdfbc3023f
parenta44117a86b0b18d788522dac573da385a11b2f3b (diff)
downloadgdb-winsup-2000-02-17.tar.gz
import winsup-2000-02-17 snapshotwinsup-2000-02-17
-rw-r--r--winsup/cygwin/autoload.h76
-rw-r--r--winsup/cygwin/cygrun.c50
-rw-r--r--winsup/cygwin/dll_entry.cc17
-rw-r--r--winsup/cygwin/dll_main.cc42
-rw-r--r--winsup/cygwin/external.h50
-rw-r--r--winsup/cygwin/include/cygwin/ip.h1
-rw-r--r--winsup/cygwin/include/rapi.h64
-rw-r--r--winsup/cygwin/include/wchar.h27
-rw-r--r--winsup/cygwin/libc/getopt.c390
-rw-r--r--winsup/cygwin/libccrt0.cc95
-rw-r--r--winsup/cygwin/libcmain.cc34
-rw-r--r--winsup/cygwin/localtime.c2250
-rw-r--r--winsup/cygwin/regexp/regerror.c24
-rw-r--r--winsup/cygwin/regexp/regexp.3321
-rw-r--r--winsup/cygwin/regexp/regexp.c1326
-rw-r--r--winsup/cygwin/regexp/regsub.c87
-rw-r--r--winsup/cygwin/shared.h552
-rw-r--r--winsup/cygwin/test.c165
-rw-r--r--winsup/cygwin/testsuite/README42
-rw-r--r--winsup/cygwin/testsuite/config/default.exp6
-rw-r--r--winsup/cygwin/testsuite/winsup.api/devzero.c113
-rw-r--r--winsup/cygwin/testsuite/winsup.api/samples/sample-pass.c4
-rw-r--r--winsup/cygwin/testsuite/winsup.api/samples/xf-sample-fail.c4
-rw-r--r--winsup/cygwin/testsuite/winsup.api/samples/xf-sample-miscompile.c1
-rw-r--r--winsup/cygwin/testsuite/winsup.api/winsup.exp43
-rw-r--r--winsup/doc/sites.texinfo83
-rw-r--r--winsup/mingw/dirent.c313
-rw-r--r--winsup/mingw/profile/ChangeLog25
-rw-r--r--winsup/utils/cygwin.cc126
-rw-r--r--winsup/w32api/README91
-rw-r--r--winsup/w32api/include/excpt.h17
31 files changed, 6439 insertions, 0 deletions
diff --git a/winsup/cygwin/autoload.h b/winsup/cygwin/autoload.h
new file mode 100644
index 00000000000..262eaa0b007
--- /dev/null
+++ b/winsup/cygwin/autoload.h
@@ -0,0 +1,76 @@
+/* autoload.h: Define functions for auto-loading symbols from a DLL.
+
+ Copyright 1999 Cygnus Solutions.
+
+ Written by Christopher Faylor <cgf@cygnus.com>
+
+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 LoadDLLinitfunc(dllname) \
+HANDLE NO_COPY dllname ## _handle = NULL; \
+static int dllname ## _init () __asm__ (#dllname "_init") __attribute__ ((unused)); \
+static int dllname ## _init ()
+
+#define LoadDLLinitnow(dllname) \
+ ({__asm__ ("movl $cygwin_dll_func_load, " #dllname "_init_holder"); dllname##_init ();})
+
+#define _LoadDLLinitnow(dllname) \
+ __asm__ ("movl $cygwin_dll_func_load, " #dllname "_init_holder"); \
+ __asm__ ("call " #dllname "_init"); \
+
+#define LoadDLLinit(dllname) \
+ __asm__ (".section .data_cygwin_nocopy,\"w\""); \
+ __asm__ (#dllname "_init_holder: .long " #dllname "_init_and_load"); \
+ __asm__ (".text"); \
+ __asm__ (#dllname "_init_and_load:"); \
+ _LoadDLLinitnow (dllname); \
+ __asm__ ("jmp cygwin_dll_func_load");
+
+
+/* Macro for defining "auto-load" functions.
+ * Note that this is self-modifying code *gasp*.
+ * The first invocation of a routine will trigger the loading of
+ * the DLL. This will then be followed by the discovery of
+ * the procedure's entry point, which is placed into the location
+ * pointed to by the stack pointer. This code then changes
+ * the "call" operand which invoked it to a "jmp" which will
+ * transfer directly to the DLL function on the next invocation.
+ *
+ * Subsequent calls to routines whose transfer address has not been
+ * determined will skip the "load the dll" step, starting at the
+ * "discovery of the DLL" step.
+ *
+ * So, immediately following the the call to one of the above routines
+ * we have:
+ * foojmp (4 bytes) Pointer to a word containing the routine used
+ * to eventually invokethe function. Initially
+ * points to an init function which loads the
+ * DLL, gets the processes load address,
+ * changes the contents here to point to the
+ * function address, and changes the call *(%eax)
+ * to a jmp %eax. If the initialization has been
+ * done, only the load part is done.
+ * DLL handle (4 bytes) The handle to use when loading the DLL.
+ * func name (n bytes) asciz string containing the name of the function
+ * to be loaded.
+ */
+
+#define LoadDLLfunc(name, mangled, dllname) \
+__asm__ (".section .data_cygwin_nocopy,\"w\""); \
+__asm__ (".global _" #mangled); \
+__asm__ (".global _win32_" #mangled); \
+__asm__ (".align 8"); \
+__asm__ ("_" #mangled ":"); \
+__asm__ ("_win32_" #mangled ":"); \
+__asm__ ("movl (" #name "jump),%eax"); \
+__asm__ ("call *(%eax)"); \
+__asm__ (#name "jump: .long " #dllname "_init_holder"); \
+__asm__ (" .long _" #dllname "_handle"); \
+__asm__ (".asciz \"" #name "\""); \
+__asm__ (".text");
+
+extern "C" void cygwin_dll_func_load () __asm__ ("cygwin_dll_func_load");
diff --git a/winsup/cygwin/cygrun.c b/winsup/cygwin/cygrun.c
new file mode 100644
index 00000000000..8aa3e30a115
--- /dev/null
+++ b/winsup/cygwin/cygrun.c
@@ -0,0 +1,50 @@
+/* cygrun.c: testsuite support program
+
+ Copyright 1999 Cygnus Solutions.
+
+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. */
+
+/* This program is intended to be used only by the testsuite. It runs
+ programs without using the cygwin api, so that the just-built dll
+ can be tested without interference from the currently installed
+ dll. */
+
+#include <stdio.h>
+#include <windows.h>
+
+int
+main(int argc, char **argv)
+{
+ STARTUPINFO sa;
+ PROCESS_INFORMATION pi;
+ DWORD ec = 1;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage: cygrun [program]\n");
+ exit (0);
+ }
+
+ setenv("CYGWIN_TESTING", "1");
+ SetEnvironmentVariable("CYGWIN_TESTING", "1");
+
+ memset(&sa, 0, sizeof(sa));
+ memset(&pi, 0, sizeof(pi));
+ if (!CreateProcess(0, argv[1], 0, 0, 1, 0, 0, 0, &sa, &pi))
+ {
+ fprintf(stderr, "CreateProcess %s failed\n", argv[1]);
+ exit(1);
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ GetExitCodeProcess(pi.hProcess, &ec);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return ec;
+}
diff --git a/winsup/cygwin/dll_entry.cc b/winsup/cygwin/dll_entry.cc
new file mode 100644
index 00000000000..6d405b6cd00
--- /dev/null
+++ b/winsup/cygwin/dll_entry.cc
@@ -0,0 +1,17 @@
+/* dll_entry.cc: Provide the default user DLL linker entry point.
+
+ Copyright 1998, 2000 Cygnus Solutions.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* Here we simply instantiate the DECLARE_CYGWIN_DLL to define the
+ linker entry point, __cygwin_dll_entry@12, which in turn calls
+ _DllMain@12 to do user-specific initialization, if any. There is a
+ default DllMain stub in the library if there is no user supplied
+ one. */
+
+#include "cygwin/cygwin_dll.h"
+
+DECLARE_CYGWIN_DLL (DllMain);
diff --git a/winsup/cygwin/dll_main.cc b/winsup/cygwin/dll_main.cc
new file mode 100644
index 00000000000..44ed13e55c4
--- /dev/null
+++ b/winsup/cygwin/dll_main.cc
@@ -0,0 +1,42 @@
+/* dll_main.cc: Provide the DllMain stub that the user can override.
+
+ Copyright 1998, 2000 Cygnus Solutions.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include <windows.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <stdio.h>
+
+extern "C"
+BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason,
+ LPVOID reserved /* Not used. */ );
+
+BOOL APIENTRY
+DllMain (
+ HINSTANCE hInst /* Library instance handle. */ ,
+ DWORD reason /* Reason this function is being called. */ ,
+ LPVOID reserved /* Not used. */ )
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ break;
+
+ case DLL_PROCESS_DETACH:
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/winsup/cygwin/external.h b/winsup/cygwin/external.h
new file mode 100644
index 00000000000..745c6a81b7d
--- /dev/null
+++ b/winsup/cygwin/external.h
@@ -0,0 +1,50 @@
+/* external.h: interface to Cygwin internals from external programs.
+
+ Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
+
+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. */
+
+typedef enum
+ {
+ CW_LOCK_PINFO,
+ CW_UNLOCK_PINFO,
+ CW_GETTHREADNAME,
+ CW_GETPINFO,
+ CW_SETPINFO,
+ CW_SETTHREADNAME,
+ CW_GETVERSIONINFO,
+ CW_READ_V1_MOUNT_TABLES
+ } cygwin_getinfo_types;
+
+struct external_pinfo
+ {
+ pid_t pid;
+ pid_t ppid;
+ HANDLE hProcess;
+ DWORD dwProcessId, dwSpawnedProcessId;
+ uid_t uid;
+ gid_t gid;
+ pid_t pgid;
+ pid_t sid;
+ int ctty;
+ mode_t umask;
+
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+
+ char progname[MAX_PATH];
+
+ DWORD strace_mask;
+ HANDLE strace_file;
+
+ DWORD process_state;
+};
+
+extern "C" DWORD cygwin_internal (cygwin_getinfo_types, ...);
+
+#define CW_NEXTPID 0x80000000 // or with pid to get next one
diff --git a/winsup/cygwin/include/cygwin/ip.h b/winsup/cygwin/include/cygwin/ip.h
new file mode 100644
index 00000000000..e4f23c7533d
--- /dev/null
+++ b/winsup/cygwin/include/cygwin/ip.h
@@ -0,0 +1 @@
+/* ip.h */
diff --git a/winsup/cygwin/include/rapi.h b/winsup/cygwin/include/rapi.h
new file mode 100644
index 00000000000..9d63d06a9e5
--- /dev/null
+++ b/winsup/cygwin/include/rapi.h
@@ -0,0 +1,64 @@
+/*
+ rapi.h - main header file for the RAPI API
+
+ Copyright 1999 Cygnus Solutions.
+
+ 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.
+*/
+
+#ifndef _RAPI_H
+#define _RAPI_H
+
+typedef struct IRAPIStream
+{
+ struct IRAPIStreamVtbl * lpVtbl;
+} IRAPIStream;
+
+typedef struct IRAPIStreamVtbl IRAPIStreamVtbl;
+
+typedef enum tagRAPISTREAMFLAG
+{
+ STREAM_TIMEOUT_READ
+} RAPISTREAMFLAG;
+
+struct IRAPIStreamVtbl
+{
+ HRESULT (__stdcall * SetRapiStat)( IRAPIStream * This, RAPISTREAMFLAG Flag, DWORD dwValue) ;
+ HRESULT (__stdcall * GetRapiStat)( IRAPIStream * This, RAPISTREAMFLAG Flag, DWORD *pdwValue) ;
+};
+
+// RAPI extension on Windows CE (e.g., MyFunctionFOO) called via CeRapiInvoke should be declared as:
+// EXTERN_C RAPIEXT MyFunctionFOO;
+typedef HRESULT (STDAPICALLTYPE RAPIEXT)(
+ DWORD cbInput, // [IN]
+ BYTE *pInput, // [IN]
+ DWORD *pcbOutput, // [OUT]
+ BYTE **ppOutput, // [OUT]
+ IRAPIStream *pIRAPIStream // [IN]
+ );
+
+typedef struct _RAPIINIT
+{
+ DWORD cbSize;
+ HANDLE heRapiInit;
+ HRESULT hrRapiInit;
+} RAPIINIT;
+
+STDAPI CeRapiInit();
+STDAPI CeRapiInitEx(RAPIINIT*);
+STDAPI_(BOOL) CeCreateProcess(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES,
+ BOOL, DWORD, LPVOID, LPWSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
+STDAPI CeRapiUninit();
+
+STDAPI_(BOOL) CeWriteFile(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
+STDAPI_(HANDLE) CeCreateFile(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
+STDAPI_(BOOL) CeCreateDirectory(LPCWSTR, LPSECURITY_ATTRIBUTES);
+STDAPI_(DWORD) CeGetLastError(void);
+STDAPI_(BOOL) CeGetFileTime(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME);
+STDAPI_(BOOL) CeCloseHandle(HANDLE);
+
+#endif /* _RAPI_H */
diff --git a/winsup/cygwin/include/wchar.h b/winsup/cygwin/include/wchar.h
new file mode 100644
index 00000000000..8c7c94dca77
--- /dev/null
+++ b/winsup/cygwin/include/wchar.h
@@ -0,0 +1,27 @@
+/* wchar.h
+
+ Copyright 1998 Cygnus Solutions
+
+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. */
+
+#ifndef _WCHAR_H
+#define _WCHAR_H
+
+#include <sys/cdefs.h>
+
+/* Get wchar_t from <stddef.h>. */
+#define __need_wchar_t
+#include <stddef.h>
+
+__BEGIN_DECLS
+
+int wcscmp (wchar_t *__s1, wchar_t *__s2);
+int wcslen (wchar_t *__s1);
+
+__END_DECLS
+
+#endif /* _WCHAR_H */
diff --git a/winsup/cygwin/libc/getopt.c b/winsup/cygwin/libc/getopt.c
new file mode 100644
index 00000000000..a5883f07cd7
--- /dev/null
+++ b/winsup/cygwin/libc/getopt.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 1987, 1993, 1994, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt; /* character checked for validity */
+int optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+static char * __progname (char *);
+int getopt_internal (int, char * const *, const char *);
+
+static char * __progname(nargv0)
+ char * nargv0;
+{
+ char * tmp = strrchr(nargv0, '/');
+ if (tmp) tmp++; else tmp = nargv0;
+ return(tmp);
+}
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_internal(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static const char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ /* ++optind; */
+ place = EMSG;
+ return (-2);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)fprintf(stderr,
+ "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = (char *)place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if ((opterr) && (*ostr != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ __progname(nargv[0]), optopt);
+ return (BADARG);
+ } else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ int retval;
+
+ if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
+ retval = -1;
+ ++optind;
+ }
+ return(retval);
+}
+
+/*
+ * getopt_long --
+ * Parse argc/argv argument vector.
+ */
+int
+getopt_long(nargc, nargv, options, long_options, index)
+ int nargc;
+ char ** nargv;
+ char * options;
+ struct option * long_options;
+ int * index;
+{
+ int retval;
+
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+ char *current_argv = nargv[optind++] + 2, *has_equal;
+ int i, current_argv_len, match = -1;
+
+ if (*current_argv == '\0') {
+ return(-1);
+ }
+ if ((has_equal = strchr(current_argv, '='))) {
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else
+ current_argv_len = strlen(current_argv);
+
+ for (i = 0; long_options[i].name; i++) {
+ if (strncmp(current_argv, long_options[i].name, current_argv_len))
+ continue;
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ match = i;
+ break;
+ }
+ if (match == -1)
+ match = i;
+ }
+ if (match != -1) {
+ if (long_options[match].has_arg) {
+ if (has_equal)
+ optarg = has_equal;
+ else
+ optarg = nargv[optind++];
+ }
+ if ((long_options[match].has_arg == 1) && (optarg == NULL)) {
+ /* Missing option, leading : indecates no error */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ __progname(nargv[0]), current_argv);
+ return (BADARG);
+ }
+ } else { /* No matching argument */
+ if ((opterr) && (*options != ':'))
+ (void)fprintf(stderr,
+ "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
+ return (BADCH);
+ }
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ retval = 0;
+ } else
+ retval = long_options[match].val;
+ if (index)
+ *index = match;
+ }
+ return(retval);
+}
+/*****************************************************************/
+
+
+
+
+
+
+#include <stdio.h>
+#include "getopt.h"
+
+/* Stuff for getopt */
+static struct option long_options[] = {
+ { (char *)"simple", 0, NULL, 's' },
+ { (char *)"t", 0, NULL, 't' },
+ { (char *)"u", 1, NULL, 'u' },
+ { (char *)"v", 0, NULL, 'v' },
+ /* Do not reorder the following */
+ { (char *)"yy", 0, NULL, 'Y' },
+ { (char *)"y", 0, NULL, 'y' },
+ { (char *)"zz", 0, NULL, 'z' },
+ { (char *)"zzz", 0, NULL, 'Z' },
+ { NULL, 0, NULL, 0 }
+};
+extern char * optarg;
+extern int optreset;
+extern int optind;
+
+int test_getopt_long(args, expected_result)
+ char ** args, * expected_result;
+{
+ char actual_result[256];
+ int count, pass, i;
+
+ pass = 0;
+ optind = 1;
+ optreset = 1;
+ for (count = 0; args[count]; count++);
+ while ((i = getopt_long(count, args, (char *)"ab:", long_options, NULL)) != EOF) {
+ switch(i) {
+ case 'u':
+ if (strcmp(optarg, "bogus")) {
+ printf("--u option does not have bogus optarg.\n");
+ return(1);
+ }
+ case 'Y':
+ case 's':
+ case 't':
+ case 'v':
+ case 'y':
+ case 'z':
+ actual_result[pass++] = i;
+ break;
+ default:
+ actual_result[pass++] = '?';
+ break;
+ }
+ }
+
+ actual_result[pass] = '\0';
+ return(strcmp(actual_result, expected_result));
+
+}
+
+#if 0
+int usage(value)
+ int value;
+{
+ printf("test_getopt [-d]\n");
+ exit(value);
+}
+#endif
+
+#if 0
+
+/*
+ * Static arglists for individual tests
+ * This is ugly and maybe I should just use a variable arglist
+ */
+const char *argv1[] = { "Test simple", "--s", NULL };
+const char *argv2[] = { "Test multiple", "--s", "--t", NULL };
+const char *argv3[] = { "Test optarg with space", "--u", "bogus", NULL };
+const char *argv4[] = { "Test optarg with equal", "--u=bogus", NULL };
+const char *argv5[] = { "Test complex", "--s", "--t", "--u", "bogus", "--v", NULL };
+const char *argv6[] = { "Test exact", "--y", NULL };
+const char *argv7[] = { "Test abbr", "--z", NULL };
+const char *argv8[] = { "Test simple termination", "--z", "foo", "--z", NULL };
+const char *argv9[] = { "Test -- termination", "--z", "--", "--z", NULL };
+
+int debug = 0;
+int main(argc, argv)
+ int argc;
+ char ** argv;
+{
+ int i;
+
+ /* Of course if getopt() has a bug this won't work */
+ while ((i = getopt(argc, argv, "d")) != EOF) {
+ switch(i) {
+ case 'd':
+ debug++;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+
+ /* Test getopt_long() */
+ {
+ if (test_getopt_long(argv1, "s")) {
+ printf("Test simple failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test multiple arguments */
+ {
+ if (test_getopt_long(argv2, "st")) {
+ printf("Test multiple failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test optarg with space */
+ {
+ if (test_getopt_long(argv3, "u")) {
+ printf("Test optarg with space failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test optarg with equal */
+ {
+ if (test_getopt_long(argv4, "u")) {
+ printf("Test optarg with equal failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test complex */
+ {
+ if (test_getopt_long(argv5, "stuv")) {
+ printf("Test complex failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test that exact matches override abbr matches */
+ {
+ if (test_getopt_long(argv6, "y")) {
+ printf("Test exact failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test that abbr matches are first match. */
+ {
+ if (test_getopt_long(argv7, "z")) {
+ printf("Test abbr failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test that option termination succeeds */
+ {
+ if (test_getopt_long(argv8, "z")) {
+ printf("Test simple termination failed.\n");
+ exit(1);
+ }
+ }
+
+ /* Test that "--" termination succeeds */
+ {
+ if (test_getopt_long(argv9, "z")) {
+ printf("Test -- termination failed.\n");
+ exit(1);
+ }
+ }
+ exit(0);
+}
+#endif
diff --git a/winsup/cygwin/libccrt0.cc b/winsup/cygwin/libccrt0.cc
new file mode 100644
index 00000000000..6f7d534b86c
--- /dev/null
+++ b/winsup/cygwin/libccrt0.cc
@@ -0,0 +1,95 @@
+/* libccrt0.cc: crt0 for libc [newlib calls this one]
+
+ Copyright 1996, 1998, 1999, 2000 Cygnus Solutions.
+
+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. */
+
+#undef MALLOC_DEBUG
+
+#include "winsup.h"
+#include <reent.h>
+#include <stdlib.h>
+
+typedef int (*MainFunc) (int argc, char *argc[], char **env);
+
+extern "C"
+{
+ char **environ;
+ void cygwin_crt0 (MainFunc);
+ int cygwin_attach_dll (HMODULE, MainFunc);
+ int cygwin_attach_noncygwin_dll (HMODULE, MainFunc);
+ int main (int, char **, char **);
+ struct _reent *_impure_ptr;
+ int _fmode;
+};
+
+static per_process this_proc;
+
+/* Set up pointers to various pieces so the dll can then use them,
+ and then jump to the dll. */
+
+static void
+cygwin_crt0_common (MainFunc f)
+{
+ /* This is used to record what the initial sp was. The value is needed
+ when copying the parent's stack to the child during a fork. */
+ int onstack;
+
+ /* The version numbers are the main source of compatibility checking.
+ As a backup to them, we use the size of the per_process struct. */
+ this_proc.magic_biscuit = sizeof (per_process);
+
+ /* cygwin.dll version number in effect at the time the app was created. */
+ this_proc.dll_major = CYGWIN_VERSION_DLL_MAJOR;
+ this_proc.dll_minor = CYGWIN_VERSION_DLL_MINOR;
+ this_proc.api_major = CYGWIN_VERSION_API_MAJOR;
+ this_proc.api_minor = CYGWIN_VERSION_API_MINOR;
+
+ this_proc.ctors = &__CTOR_LIST__;
+ this_proc.dtors = &__DTOR_LIST__;
+ this_proc.envptr = &environ;
+ this_proc.impure_ptr_ptr = &_impure_ptr;
+ this_proc.main = f;
+ this_proc.fmode_ptr = &_fmode;
+ this_proc.initial_sp = (char *) &onstack;
+
+ /* Remember whatever the user linked his application with - or
+ point to entries in the dll. */
+ this_proc.malloc = &malloc;
+ this_proc.free = &free;
+ this_proc.realloc = &realloc;
+ this_proc.calloc = &calloc;
+
+ /* Setup the module handle so fork can get the path name. */
+ this_proc.hmodule = GetModuleHandle (0);
+
+ /* variables for fork */
+ this_proc.data_start = &_data_start__;
+ this_proc.data_end = &_data_end__;
+ this_proc.bss_start = &_bss_start__;
+ this_proc.bss_end = &_bss_end__;
+}
+
+/* for main module */
+void
+cygwin_crt0 (MainFunc f)
+{
+ cygwin_crt0_common (f);
+
+ /* Jump into the dll. */
+ dll_crt0 (&this_proc);
+}
+
+/* for a loaded dll */
+int
+cygwin_attach_dll (HMODULE h, MainFunc f)
+{
+ cygwin_crt0_common (f);
+
+ /* jump into the dll. */
+ return dll_dllcrt0 (h, &this_proc);
+}
diff --git a/winsup/cygwin/libcmain.cc b/winsup/cygwin/libcmain.cc
new file mode 100644
index 00000000000..a8ae341392f
--- /dev/null
+++ b/winsup/cygwin/libcmain.cc
@@ -0,0 +1,34 @@
+/* libcmain.cc
+
+ Copyright 1996, 1997, 1998 Cygnus Solutions.
+
+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. */
+
+#include <windows.h>
+
+/* Allow apps which don't have a main work, as long as they define WinMain */
+extern "C" int main ()
+{
+ HMODULE x = GetModuleHandleA(0);
+ char *s = GetCommandLineA ();
+ STARTUPINFO si;
+
+ /* GetCommandLineA returns the entire command line including the
+ program name, but WinMain is defined to accept the command
+ line without the program name. */
+ while (*s != ' ' && *s != '\0')
+ ++s;
+ while (*s == ' ')
+ ++s;
+
+ GetStartupInfo (&si);
+
+ WinMain (x, 0, s,
+ ((si.dwFlags & STARTF_USESHOWWINDOW) != 0
+ ? si.wShowWindow
+ : SW_SHOWNORMAL));
+}
diff --git a/winsup/cygwin/localtime.c b/winsup/cygwin/localtime.c
new file mode 100644
index 00000000000..5784f3cb0d6
--- /dev/null
+++ b/winsup/cygwin/localtime.c
@@ -0,0 +1,2250 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+/* Temporarily merged private.h and tzfile.h for ease of management - DJ */
+
+/* CYGNUS LOCAL */
+#define lint
+#include <windows.h>
+#include <sys/strace.h>
+
+extern DWORD strace_active;
+
+#define USG_COMPAT
+
+/* END CYGNUS LOCAL */
+
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)localtime.c 7.66";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
+** POSIX-style TZ environment variable handling from Guy Harris
+** (guy@auspex.com).
+*/
+
+/*LINTLIBRARY*/
+
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char privatehid[] = "@(#)private.h 7.48";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME 1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY 3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 0
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H 0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "limits.h" /* for CHAR_BIT */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT - 0
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#if HAVE_UNISTD_H - 0
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H - 0 */
+
+#if !(HAVE_UNISTD_H - 0)
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !(HAVE_UNISTD_H - 0) */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** SunOS 4.1.1 cc lacks const.
+*/
+
+#ifndef const
+#ifndef __STDC__
+#define const
+#endif /* !defined __STDC__ */
+#endif /* !defined const */
+
+/*
+** SunOS 4.1.1 cc lacks prototypes.
+*/
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x) ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX 1024 /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+
+/*
+** Private function declarations.
+*/
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+char * scheck P((const char *string, const char *format));
+
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
+
+#endif /* !defined PRIVATE_H */
+
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char tzfilehid[] = "@(#)tzfile.h 7.14";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/local/etc/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_reserved[16]; /* reserved for future use */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+** time is UTC, if FALSE,
+** transition time is local time
+** if absent, transition times are
+** assumed to be local time
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+/*
+** The TZ_MAX_TIMES value below is enough to handle a bit more than a
+** year's worth of solar time (corrected daily to the nearest second) or
+** 138 years of Pacific Presidential Election time
+** (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES 370
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew <earl@hpato.aus.hp.com>.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+/*
+** Accurate only for the past couple of centuries;
+** that will probably do.
+*/
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+#ifndef USG
+
+/*
+** Use of the underscored variants may cause problems if you move your code to
+** certain System-V-based systems; for maximum portability, use the
+** underscore-free variants. The underscored variants are provided for
+** backward compatibility only; they may disappear from future versions of
+** this file.
+*/
+
+#define SECS_PER_MIN SECSPERMIN
+#define MINS_PER_HOUR MINSPERHOUR
+#define HOURS_PER_DAY HOURSPERDAY
+#define DAYS_PER_WEEK DAYSPERWEEK
+#define DAYS_PER_NYEAR DAYSPERNYEAR
+#define DAYS_PER_LYEAR DAYSPERLYEAR
+#define SECS_PER_HOUR SECSPERHOUR
+#define SECS_PER_DAY SECSPERDAY
+#define MONS_PER_YEAR MONSPERYEAR
+
+#endif /* !defined USG */
+
+#endif /* !defined TZFILE_H */
+
+#include "fcntl.h"
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+** 1. They might reference tzname[0] before calling tzset (explicitly
+** or implicitly).
+** 2. They might reference tzname[1] before calling tzset (explicitly
+** or implicitly).
+** 3. They might reference tzname[1] after setting to a time zone
+** in which Daylight Saving Time is never observed.
+** 4. They might reference tzname[0] after setting to a time zone
+** in which Standard Time is never observed.
+** 5. They might reference tm.TM_ZONE after calling offtime.
+** What's best to do in the above cases is open to debate;
+** for now, we just set things up so that in any of the five cases
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
+** string "tzname[0] used before set", and similarly for the other cases.
+** And another: initialize tzname[0] to "ERA", with an explanation in the
+** manual page of what this "time zone abbreviation" means (doing this so
+** that tzname[0] has the "normal" length of three characters).
+*/
+#define WILDABBR " "
+#endif /* !defined WILDABBR */
+
+static char wildabbr[] = WILDABBR;
+
+static const char gmt[] = "GMT";
+
+struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long detzcode P((const char * codep));
+static const char * getzname P((const char * strp));
+static const char * getnum P((const char * strp, int * nump, int min,
+ int max));
+static const char * getsecs P((const char * strp, long * secsp));
+static const char * getoffset P((const char * strp, long * offsetp));
+static const char * getrule P((const char * strp, struct rule * rulep));
+static void gmtload P((struct state * sp));
+static void gmtsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static void localsub P((const time_t * timep, long offset,
+ struct tm * tmp));
+static int increment_overflow P((int * number, int delta));
+static int normalize_overflow P((int * tensptr, int * unitsptr,
+ int base));
+static void settzname P((void));
+static time_t time1 P((struct tm * tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm *)),
+ long offset));
+static time_t time2 P((struct tm *tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm*)),
+ long offset, int * okayp));
+static time_t time2sub P((struct tm *tmp,
+ void(*funcp) P((const time_t *,
+ long, struct tm*)),
+ long offset, int * okayp, int do_norm_secs));
+static void timesub P((const time_t * timep, long offset,
+ const struct state * sp, struct tm * tmp));
+static int tmcomp P((const struct tm * atmp,
+ const struct tm * btmp));
+static time_t transtime P((time_t janfirst, int year,
+ const struct rule * rulep, long offset));
+static int tzload P((const char * name, struct state * sp));
+static int tzparse P((const char * name, struct state * sp,
+ int lastditch));
+
+#ifdef ALL_STATE
+static struct state * lclptr;
+static struct state * gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state lclmem;
+static struct state gmtmem;
+#define lclptr (&lclmem)
+#define gmtptr (&gmtmem)
+#endif /* State Farm */
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+static char lcl_TZname[TZ_STRLEN_MAX + 1];
+static int lcl_is_set;
+static int gmt_is_set;
+
+#define tzname _tzname
+#undef _tzname
+
+char * tzname[2] = {
+ wildabbr,
+ wildabbr
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+** Except for the strftime function, these functions [asctime,
+** ctime, gmtime, localtime] return values in one of two static
+** objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+*/
+
+static struct tm tm;
+
+
+/* These variables are initialized by tzset. The macro versions are
+ defined in time.h, and indirect through the __imp_ pointers. */
+
+#define timezone _timezone
+#define daylight _daylight
+#undef _timezone
+#undef _daylight
+
+#ifdef USG_COMPAT
+time_t timezone = 0;
+int daylight = 0;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t altzone = 0;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(codep)
+const char * const codep;
+{
+ register long result;
+ register int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0L;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static void
+settzname P((void))
+{
+ register struct state * const sp = lclptr;
+ register int i;
+
+ tzname[0] = wildabbr;
+ tzname[1] = wildabbr;
+#ifdef USG_COMPAT
+ daylight = 0;
+ timezone = 0;
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ altzone = 0;
+#endif /* defined ALTZONE */
+#ifdef ALL_STATE
+ if (sp == NULL) {
+ tzname[0] = tzname[1] = gmt;
+ return;
+ }
+#endif /* defined ALL_STATE */
+ for (i = 0; i < sp->typecnt; ++i) {
+ register const struct ttinfo * const ttisp = &sp->ttis[i];
+
+ tzname[ttisp->tt_isdst] =
+ &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+ if (ttisp->tt_isdst)
+ daylight = 1;
+ if (i == 0 || !ttisp->tt_isdst)
+ timezone = -(ttisp->tt_gmtoff);
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ if (i == 0 || ttisp->tt_isdst)
+ altzone = -(ttisp->tt_gmtoff);
+#endif /* defined ALTZONE */
+ }
+ /*
+ ** And to get the latest zone names into tzname. . .
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ register const struct ttinfo * const ttisp =
+ &sp->ttis[
+ sp->types[i]];
+
+ tzname[ttisp->tt_isdst] =
+ &sp->chars[ttisp->tt_abbrind];
+ }
+}
+
+#include "tz_posixrules.h"
+
+static int
+tzload(name, sp)
+register const char * name;
+register struct state * const sp;
+{
+ register const char * p;
+ register int i;
+ register int fid;
+
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ register int doaccess;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ (void) strcpy(fullname, p);
+ (void) strcat(fullname, "/");
+ (void) strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = TRUE;
+ name = fullname;
+ }
+#if 0
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+#endif
+ if ((fid = open(name, OPEN_MODE)) == -1)
+ {
+ const char *base = strrchr(name, '/');
+ if (base)
+ base++;
+ else
+ base = name;
+ if (strcmp(base, "posixrules"))
+ return -1;
+
+ /* We've got a built-in copy of posixrules just in case */
+ fid = -2;
+ }
+ }
+ {
+ struct tzhead * tzhp;
+ union {
+ struct tzhead tzhead;
+ char buf[sizeof *sp + sizeof *tzhp];
+ } u;
+ int ttisstdcnt;
+ int ttisgmtcnt;
+
+ if (fid == -2)
+ {
+ memcpy(u.buf, _posixrules_data, sizeof(_posixrules_data));
+ i = sizeof(_posixrules_data);
+ }
+ else
+ {
+ i = read(fid, u.buf, sizeof u.buf);
+ if (close(fid) != 0)
+ return -1;
+ }
+ ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+ ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+ sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+ sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+ sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+ sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+ p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return -1;
+ if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * (4 + 2) + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (4 + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->ats[i] = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->types[i] = (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return -1;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ register struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = detzcode(p);
+ p += 4;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = FALSE;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != TRUE &&
+ ttisp->tt_ttisstd != FALSE)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = FALSE;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != TRUE &&
+ ttisp->tt_ttisgmt != FALSE)
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char *
+getzname(strp)
+register const char * strp;
+{
+ register char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(strp, nump, min, max)
+register const char * strp;
+int * const nump;
+const int min;
+const int max;
+{
+ register char c;
+ register int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(strp, secsp)
+register const char * strp;
+long * const secsp;
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(strp, offsetp)
+register const char * strp;
+long * const offsetp;
+{
+ register int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(strp, rulep)
+const char * strp;
+register struct rule * const rulep;
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t
+transtime(janfirst, year, rulep, offset)
+const time_t janfirst;
+const int year;
+register const struct rule * const rulep;
+const long offset;
+{
+ register int leapyear;
+ register time_t value;
+ register int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "dow" is the day-of-week of the first day of the month. Get
+ ** the day-of-month (zero-origin) of the first "dow" day of the
+ ** month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char * name;
+register struct state * const sp;
+const int lastditch;
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ register time_t * atp;
+ register unsigned char * typep;
+ register char * cp;
+ register int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ if (stdlen < 3)
+ return -1;
+ if (*name == '\0')
+ return -1;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ if (dstlen < 3)
+ return -1;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR to 2037.
+ */
+ sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
+ if (sp->timecnt > TZ_MAX_TIMES)
+ return -1;
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ for (year = EPOCH_YEAR; year <= 2037; ++year) {
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ janfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ }
+ } else {
+ register long theirstdoffset;
+ register long theirdstoffset;
+ register long theiroffset;
+ register int isdst;
+ register int i;
+ register int j;
+
+ if (*name != '\0')
+ return -1;
+ if (load_result != 0)
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = FALSE;
+ sp->ttis[0].tt_abbrind = 0;
+ sp->ttis[1].tt_gmtoff = -dstoffset;
+ sp->ttis[1].tt_isdst = TRUE;
+ sp->ttis[1].tt_abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const sp;
+{
+ if (tzload(gmt, sp) != 0)
+ (void) tzparse(gmt, sp, TRUE);
+}
+
+#ifndef STD_INSPIRED
+/*
+** A non-static declaration of tzsetwall in a system header file
+** may cause a warning about this upcoming static declaration...
+*/
+static
+#endif /* !defined STD_INSPIRED */
+void
+tzsetwall P((void))
+{
+ if (lcl_is_set < 0)
+ return;
+ lcl_is_set = -1;
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+#ifdef _WIN32
+#define is_upper(c) ((unsigned)(c) - 'A' <= 26)
+ {
+ TIME_ZONE_INFORMATION tz;
+ char buf[BUFSIZ];
+ char *cp, *dst;
+ wchar_t *src;
+ div_t d;
+ GetTimeZoneInformation(&tz);
+ dst = cp = buf;
+ for (src = tz.StandardName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if (cp == dst)
+ {
+ /* In Asian Windows, tz.StandardName may not contain
+ the timezone name. */
+ strcpy(cp, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.StandardBias, 60);
+ sprintf(cp, "%d", d.quot);
+ if (d.rem)
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ if(tz.StandardDate.wMonth) {
+ cp = strchr(cp, 0);
+ dst = cp;
+ for (src = tz.DaylightName; *src; src++)
+ if (is_upper(*src)) *dst++ = *src;
+ if (cp == dst)
+ {
+ /* In Asian Windows, tz.StandardName may not contain
+ the daylight name. */
+ strcpy(buf, wildabbr);
+ cp += strlen(wildabbr);
+ }
+ else
+ cp = dst;
+ d = div(tz.Bias+tz.DaylightBias, 60);
+ sprintf(cp, "%d", d.quot);
+ if (d.rem)
+ sprintf(cp=strchr(cp, 0), ":%d", abs(d.rem));
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.DaylightDate.wMonth,
+ tz.DaylightDate.wDay,
+ tz.DaylightDate.wDayOfWeek,
+ tz.DaylightDate.wHour);
+ if (tz.DaylightDate.wMinute || tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wMinute);
+ if (tz.DaylightDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.DaylightDate.wSecond);
+ cp = strchr(cp, 0);
+ sprintf(cp=strchr(cp, 0), ",M%d.%d.%d/%d",
+ tz.StandardDate.wMonth,
+ tz.StandardDate.wDay,
+ tz.StandardDate.wDayOfWeek,
+ tz.StandardDate.wHour);
+ if (tz.StandardDate.wMinute || tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wMinute);
+ if (tz.StandardDate.wSecond)
+ sprintf(cp=strchr(cp, 0), ":%d", tz.StandardDate.wSecond);
+ }
+ /* printf("TZ deduced as `%s'\n", buf); */
+ if (tzparse(buf, lclptr, FALSE) == 0) {
+ settzname();
+ setenv("TZ", buf, 1);
+ return;
+ }
+ }
+#endif
+ if (tzload((char *) NULL, lclptr) != 0)
+ gmtload(lclptr);
+ settzname();
+}
+
+void
+tzset P((void))
+{
+ const char * name = getenv("TZ");
+
+ if (name == NULL) {
+ tzsetwall();
+ return;
+ }
+
+ if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
+ return;
+ lcl_is_set = (strlen(name) < sizeof(lcl_TZname));
+ if (lcl_is_set)
+ (void) strcpy(lcl_TZname, name);
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (*name == '\0') {
+ /*
+ ** User wants it fast rather than right.
+ */
+ lclptr->leapcnt = 0; /* so, we're off a little */
+ lclptr->timecnt = 0;
+ lclptr->ttis[0].tt_gmtoff = 0;
+ lclptr->ttis[0].tt_abbrind = 0;
+ (void) strcpy(lclptr->chars, gmt);
+ } else if (tzload(name, lclptr) != 0) {
+ if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
+ (void) gmtload(lclptr);
+ }
+ settzname();
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static void
+localsub(timep, offset, tmp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+{
+ register struct state * sp;
+ register const struct ttinfo * ttisp;
+ register int i;
+ const time_t t = *timep;
+
+ sp = lclptr;
+#ifdef ALL_STATE
+ if (sp == NULL) {
+ gmtsub(timep, offset, tmp);
+ return;
+ }
+#endif /* defined ALL_STATE */
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ } else {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (t < sp->ats[i])
+ break;
+ i = sp->types[i - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->tt_isdst;
+ tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+}
+
+struct tm *
+localtime(timep)
+const time_t * const timep;
+{
+ tzset();
+ localsub(timep, 0L, &tm);
+ return &tm;
+}
+
+/*
+ * Re-entrant version of localtime
+ */
+struct tm *
+localtime_r(timep, tm)
+const time_t * const timep;
+struct tm * tm;
+{
+ localsub(timep, 0L, tm);
+ return tm;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static void
+gmtsub(timep, offset, tmp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+{
+ if (!gmt_is_set) {
+ gmt_is_set = TRUE;
+#ifdef ALL_STATE
+ gmtptr = (struct state *) malloc(sizeof *gmtptr);
+ if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+ gmtload(gmtptr);
+ }
+ timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = wildabbr;
+ else {
+#ifdef ALL_STATE
+ if (gmtptr == NULL)
+ tmp->TM_ZONE = gmt;
+ else tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+ }
+#endif /* defined TM_ZONE */
+}
+
+struct tm *
+gmtime(timep)
+const time_t * const timep;
+{
+ gmtsub(timep, 0L, &tm);
+ return &tm;
+}
+
+/*
+ * Re-entrant version of gmtime
+ */
+struct tm *
+gmtime_r(timep, tm)
+const time_t * const timep;
+struct tm * tm;
+{
+ gmtsub(timep, 0L, tm);
+ return tm;
+}
+
+#ifdef STD_INSPIRED
+
+struct tm *
+offtime(timep, offset)
+const time_t * const timep;
+const long offset;
+{
+ gmtsub(timep, offset, &tm);
+ return &tm;
+}
+
+#endif /* defined STD_INSPIRED */
+
+static void
+timesub(timep, offset, sp, tmp)
+const time_t * const timep;
+const long offset;
+register const struct state * const sp;
+register struct tm * const tmp;
+{
+ register const struct lsinfo * lp;
+ register long days;
+ register long rem;
+ register int y;
+ register int yleap;
+ register const int * ip;
+ register long corr;
+ register int hit;
+ register int i;
+
+ corr = 0;
+ hit = 0;
+#ifdef ALL_STATE
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ i = sp->leapcnt;
+#endif /* State Farm */
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ if (*timep == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ days = *timep / SECSPERDAY;
+ rem = *timep % SECSPERDAY;
+#ifdef mc68k
+ if (*timep == 0x80000000) {
+ /*
+ ** A 3B1 muffs the division on the most negative number.
+ */
+ days = -24855;
+ rem = -11648;
+ }
+#endif /* defined mc68k */
+ rem += (offset - corr);
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --days;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++days;
+ }
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem = rem % SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ y = EPOCH_YEAR;
+#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+ while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
+ register int newy;
+
+ newy = y + days / DAYSPERNYEAR;
+ if (days < 0)
+ --newy;
+ days -= (newy - y) * DAYSPERNYEAR +
+ LEAPS_THRU_END_OF(newy - 1) -
+ LEAPS_THRU_END_OF(y - 1);
+ y = newy;
+ }
+ tmp->tm_year = y - TM_YEAR_BASE;
+ tmp->tm_yday = (int) days;
+ ip = mon_lengths[yleap];
+ for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ days = days - (long) ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (days + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+}
+
+char *
+ctime(timep)
+const time_t * const timep;
+{
+/*
+** Section 4.12.3.2 of X3.159-1989 requires that
+** The ctime function converts the calendar time pointed to by timer
+** to local time in the form of a string. It is equivalent to
+** asctime(localtime(timer))
+*/
+ return asctime(localtime(timep));
+}
+
+char *
+ctime_r(timep, buf)
+const time_t * const timep;
+char * buf;
+{
+ struct tm tm;
+
+ return asctime_r(localtime_r(timep, &tm), buf);
+}
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** [kridle@xinet.com as of 1996-01-16.]
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+*/
+
+static int
+increment_overflow(number, delta)
+int * number;
+int delta;
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
+normalize_overflow(tensptr, unitsptr, base)
+int * const tensptr;
+int * const unitsptr;
+const int base;
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+tmcomp(atmp, btmp)
+register const struct tm * const atmp;
+register const struct tm * const btmp;
+{
+ register int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+static time_t
+time2sub(tmp, funcp, offset, okayp, do_norm_secs)
+struct tm * const tmp;
+void (* const funcp) P((const time_t*, long, struct tm*));
+const long offset;
+int * const okayp;
+const int do_norm_secs;
+{
+ register const struct state * sp;
+ register int dir;
+ register int bits;
+ register int i, j ;
+ register int saved_seconds;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
+
+ *okayp = FALSE;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn yourtm.tm_year into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (increment_overflow(&yourtm.tm_year, -1))
+ return WRONG;
+ i = yourtm.tm_year + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(i)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ i = yourtm.tm_year + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(i)];
+ if (increment_overflow(&yourtm.tm_year, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (increment_overflow(&yourtm.tm_year, 1))
+ return WRONG;
+ }
+ }
+ if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+ return WRONG;
+ if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Divide the search space in half
+ ** (this works whether time_t is signed or unsigned).
+ */
+ bits = TYPE_BIT(time_t) - 1;
+ /*
+ ** If time_t is signed, then 0 is just above the median,
+ ** assuming two's complement arithmetic.
+ ** If time_t is unsigned, then (1 << bits) is just above the median.
+ */
+ t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
+ for ( ; ; ) {
+ (*funcp)(&t, offset, &mytm);
+ dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (bits-- < 0)
+ return WRONG;
+ if (bits < 0)
+ --t; /* may be needed if new t is minimal */
+ else if (dir > 0)
+ t -= ((time_t) 1) << bits;
+ else t += ((time_t) 1) << bits;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ sp = (const struct state *)
+ (((void *) funcp == (void *) localsub) ?
+ lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = t + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ (*funcp)(&newt, offset, &mytm);
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ (*funcp)(&t, offset, tmp);
+ *okayp = TRUE;
+ return t;
+}
+
+static time_t
+time2(tmp, funcp, offset, okayp)
+struct tm * const tmp;
+void (* const funcp) P((const time_t*, long, struct tm*));
+const long offset;
+int * const okayp;
+{
+ time_t t;
+
+ /*
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, offset, okayp, FALSE);
+ return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);
+}
+
+static time_t
+time1(tmp, funcp, offset)
+struct tm * const tmp;
+void (* const funcp) P((const time_t *, long, struct tm *));
+const long offset;
+{
+ register time_t t;
+ register const struct state * sp;
+ register int samei, otheri;
+ int okay;
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, offset, &okay);
+#ifdef PCTS
+ /*
+ ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
+ lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (samei = sp->typecnt - 1; samei >= 0; --samei) {
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+time_t
+mktime(tmp)
+struct tm * const tmp;
+{
+ tzset();
+ return time1(tmp, localsub, 0L);
+}
+
+#ifdef STD_INSPIRED
+
+time_t
+timelocal(tmp)
+struct tm * const tmp;
+{
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
+}
+
+time_t
+timegm(tmp)
+struct tm * const tmp;
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, 0L);
+}
+
+time_t
+timeoff(tmp, offset)
+struct tm * const tmp;
+const long offset;
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, offset);
+}
+
+#endif /* defined STD_INSPIRED */
+
+#ifdef CMUCS
+
+/*
+** The following is supplied for compatibility with
+** previous versions of the CMUCS runtime library.
+*/
+
+long
+gtime(tmp)
+struct tm * const tmp;
+{
+ const time_t t = mktime(tmp);
+
+ if (t == WRONG)
+ return -1;
+ return t;
+}
+
+#endif /* defined CMUCS */
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/*
+** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
+** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which
+** is not the case if we are accounting for leap seconds.
+** So, we provide the following conversion routines for use
+** when exchanging timestamps with POSIX conforming systems.
+*/
+
+static long
+leapcorr(timep)
+time_t * timep;
+{
+ register struct state * sp;
+ register struct lsinfo * lp;
+ register int i;
+
+ sp = lclptr;
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+time_t
+time2posix(t)
+time_t t;
+{
+ tzset();
+ return t - leapcorr(&t);
+}
+
+time_t
+posix2time(t)
+time_t t;
+{
+ time_t x;
+ time_t y;
+
+ tzset();
+ /*
+ ** For a positive leap second hit, the result
+ ** is not unique. For a negative leap second
+ ** hit, the corresponding time doesn't exist,
+ ** so we return an adjacent second.
+ */
+ x = t + leapcorr(&t);
+ y = x - leapcorr(&x);
+ if (y < t) {
+ do {
+ x++;
+ y = x - leapcorr(&x);
+ } while (y < t);
+ if (t != y)
+ return x - 1;
+ } else if (y > t) {
+ do {
+ --x;
+ y = x - leapcorr(&x);
+ } while (y > t);
+ if (t != y)
+ return x + 1;
+ }
+ return x;
+}
+
+#endif /* defined STD_INSPIRED */
diff --git a/winsup/cygwin/regexp/regerror.c b/winsup/cygwin/regexp/regerror.c
new file mode 100644
index 00000000000..ab6954e086a
--- /dev/null
+++ b/winsup/cygwin/regexp/regerror.c
@@ -0,0 +1,24 @@
+#if 0
+#ifndef lint
+static char *rcsid = "$Id$";
+#endif /* not lint */
+#endif
+
+#include "regexp.h"
+#include <stdio.h>
+
+void
+regerror(s)
+const char *s;
+{
+#ifdef ERRAVAIL
+ error("regexp: %s", s);
+#else
+/*
+ fprintf(stderr, "regexp(3): %s\n", s);
+ exit(1);
+*/
+ return; /* let std. egrep handle errors */
+#endif
+ /* NOTREACHED */
+}
diff --git a/winsup/cygwin/regexp/regexp.3 b/winsup/cygwin/regexp/regexp.3
new file mode 100644
index 00000000000..d1a3a000d80
--- /dev/null
+++ b/winsup/cygwin/regexp/regexp.3
@@ -0,0 +1,321 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)regexp.3 8.1 (Berkeley) 6/4/93
+.\"
+.Dd June 4, 1993
+.Dt REGEXP 3
+.Os
+.Sh NAME
+.Nm regcomp ,
+.Nm regexec ,
+.Nm regsub ,
+.Nm regerror
+.Nd regular expression handlers
+.Sh SYNOPSIS
+.Fd #include <regexp.h>
+.Ft regexp *
+.Fn regcomp "const char *exp"
+.Ft int
+.Fn regexec "const regexp *prog" "const char *string"
+.Ft void
+.Fn regsub "const regexp *prog" "const char *source" "char *dest"
+.Sh DESCRIPTION
+.Bf -symbolic
+This interface is made obsolete by
+.Xr regex 3 .
+It is available from the compatibility library, libcompat.
+.Ef
+.Pp
+The
+.Fn regcomp ,
+.Fn regexec ,
+.Fn regsub ,
+and
+.Fn regerror
+functions
+implement
+.Xr egrep 1 Ns -style
+regular expressions and supporting facilities.
+.Pp
+The
+.Fn regcomp
+function
+compiles a regular expression into a structure of type
+.Xr regexp ,
+and returns a pointer to it.
+The space has been allocated using
+.Xr malloc 3
+and may be released by
+.Xr free .
+.Pp
+The
+.Fn regexec
+function
+matches a
+.Dv NUL Ns -terminated
+.Fa string
+against the compiled regular expression
+in
+.Fa prog .
+It returns 1 for success and 0 for failure, and adjusts the contents of
+.Fa prog Ns 's
+.Em startp
+and
+.Em endp
+(see below) accordingly.
+.Pp
+The members of a
+.Xr regexp
+structure include at least the following (not necessarily in order):
+.Bd -literal -offset indent
+char *startp[NSUBEXP];
+char *endp[NSUBEXP];
+.Ed
+.Pp
+where
+.Dv NSUBEXP
+is defined (as 10) in the header file.
+Once a successful
+.Fn regexec
+has been done using the
+.Fn regexp ,
+each
+.Em startp Ns - Em endp
+pair describes one substring
+within the
+.Fa string ,
+with the
+.Em startp
+pointing to the first character of the substring and
+the
+.Em endp
+pointing to the first character following the substring.
+The 0th substring is the substring of
+.Fa string
+that matched the whole
+regular expression.
+The others are those substrings that matched parenthesized expressions
+within the regular expression, with parenthesized expressions numbered
+in left-to-right order of their opening parentheses.
+.Pp
+The
+.Fn regsub
+function
+copies
+.Fa source
+to
+.Fa dest ,
+making substitutions according to the
+most recent
+.Fn regexec
+performed using
+.Fa prog .
+Each instance of `&' in
+.Fa source
+is replaced by the substring
+indicated by
+.Em startp Ns Bq
+and
+.Em endp Ns Bq .
+Each instance of
+.Sq \e Ns Em n ,
+where
+.Em n
+is a digit, is replaced by
+the substring indicated by
+.Em startp Ns Bq Em n
+and
+.Em endp Ns Bq Em n .
+To get a literal `&' or
+.Sq \e Ns Em n
+into
+.Fa dest ,
+prefix it with `\e';
+to get a literal `\e' preceding `&' or
+.Sq \e Ns Em n ,
+prefix it with
+another `\e'.
+.Pp
+The
+.Fn regerror
+function
+is called whenever an error is detected in
+.Fn regcomp ,
+.Fn regexec ,
+or
+.Fn regsub .
+The default
+.Fn regerror
+writes the string
+.Fa msg ,
+with a suitable indicator of origin,
+on the standard
+error output
+and invokes
+.Xr exit 2 .
+The
+.Fn regerror
+function
+can be replaced by the user if other actions are desirable.
+.Sh REGULAR EXPRESSION SYNTAX
+A regular expression is zero or more
+.Em branches ,
+separated by `|'.
+It matches anything that matches one of the branches.
+.Pp
+A branch is zero or more
+.Em pieces ,
+concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.Pp
+A piece is an
+.Em atom
+possibly followed by `*', `+', or `?'.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a match of the atom, or the null string.
+.Pp
+An atom is a regular expression in parentheses (matching a match for the
+regular expression), a
+.Em range
+(see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of the input string), `$' (matching the null string at the
+end of the input string), a `\e' followed by a single character (matching
+that character), or a single character with no other significance
+(matching that character).
+.Pp
+A
+.Em range
+is a sequence of characters enclosed in `[]'.
+It normally matches any single character from the sequence.
+If the sequence begins with `^',
+it matches any single character
+.Em not
+from the rest of the sequence.
+If two characters in the sequence are separated by `\-', this is shorthand
+for the full list of
+.Tn ASCII
+characters between them
+(e.g. `[0-9]' matches any decimal digit).
+To include a literal `]' in the sequence, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character.
+.Sh AMBIGUITY
+If a regular expression could match two different parts of the input string,
+it will match the one which begins earliest.
+If both begin in the same place but match different lengths, or match
+the same length in different ways, life gets messier, as follows.
+.Pp
+In general, the possibilities in a list of branches are considered in
+left-to-right order, the possibilities for `*', `+', and `?' are
+considered longest-first, nested constructs are considered from the
+outermost in, and concatenated constructs are considered leftmost-first.
+The match that will be chosen is the one that uses the earliest
+possibility in the first choice that has to be made.
+If there is more than one choice, the next will be made in the same manner
+(earliest possibility) subject to the decision on the first choice.
+And so forth.
+.Pp
+For example,
+.Sq Li (ab|a)b*c
+could match
+`abc' in one of two ways.
+The first choice is between `ab' and `a'; since `ab' is earlier, and does
+lead to a successful overall match, it is chosen.
+Since the `b' is already spoken for,
+the `b*' must match its last possibility\(emthe empty string\(emsince
+it must respect the earlier choice.
+.Pp
+In the particular case where no `|'s are present and there is only one
+`*', `+', or `?', the net effect is that the longest possible
+match will be chosen.
+So
+.Sq Li ab* ,
+presented with `xabbbby', will match `abbbb'.
+Note that if
+.Sq Li ab* ,
+is tried against `xabyabbbz', it
+will match `ab' just after `x', due to the begins-earliest rule.
+(In effect, the decision on where to start the match is the first choice
+to be made, hence subsequent choices must respect it even if this leads them
+to less-preferred alternatives.)
+.Sh RETURN VALUES
+The
+.Fn regcomp
+function
+returns
+.Dv NULL
+for a failure
+.Pf ( Fn regerror
+permitting),
+where failures are syntax errors, exceeding implementation limits,
+or applying `+' or `*' to a possibly-null operand.
+.Sh SEE ALSO
+.Xr ed 1 ,
+.Xr ex 1 ,
+.Xr expr 1 ,
+.Xr egrep 1 ,
+.Xr fgrep 1 ,
+.Xr grep 1 ,
+.Xr regex 3
+.Sh HISTORY
+Both code and manual page for
+.Fn regcomp ,
+.Fn regexec ,
+.Fn regsub ,
+and
+.Fn regerror
+were written at the University of Toronto
+and appeared in
+.Bx 4.3 tahoe .
+They are intended to be compatible with the Bell V8
+.Xr regexp 3 ,
+but are not derived from Bell code.
+.Sh BUGS
+Empty branches and empty regular expressions are not portable to V8.
+.Pp
+The restriction against
+applying `*' or `+' to a possibly-null operand is an artifact of the
+simplistic implementation.
+.Pp
+Does not support
+.Xr egrep Ns 's
+newline-separated branches;
+neither does the V8
+.Xr regexp 3 ,
+though.
+.Pp
+Due to emphasis on
+compactness and simplicity,
+it's not strikingly fast.
+It does give special attention to handling simple cases quickly.
diff --git a/winsup/cygwin/regexp/regexp.c b/winsup/cygwin/regexp/regexp.c
new file mode 100644
index 00000000000..2f848655ecf
--- /dev/null
+++ b/winsup/cygwin/regexp/regexp.c
@@ -0,0 +1,1326 @@
+/*
+ * regcomp and regexec -- regsub and regerror are elsewhere
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to |
+ *** to assist in implementing egrep.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching
+ *** as in BSD grep and ex.
+ *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore,
+ *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \.
+ *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods,
+ *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy.
+ *** THIS IS AN ALTERED VERSION. It was altered by Geoffrey Noer,
+ *** noer@cygnus.com, on 6 Oct 1997, to change the prototype format
+ *** for inclusion in the Cygwin32 library.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions. Serious changes in
+ * regular-expression syntax might require a total rethink.
+ */
+
+#if 0
+#ifndef lint
+static char *rcsid = "$Id$";
+#endif /* not lint */
+#endif
+
+#include "regexp.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "regmagic.h"
+
+/*
+ * The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases. They are:
+ *
+ * regstart char that must begin a match; '\0' if none obvious
+ * reganch is the match anchored (at beginning-of-line only)?
+ * regmust string (pointer into program) that match must include, or NULL
+ * regmlen length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot. Regmust permits fast rejection
+ * of lines that cannot possibly match. The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup). Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/*
+ * Structure for regexp "program". This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology). Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives. (Here we
+ * have one of the subtle syntax dependencies: an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.) The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM. In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure: the tail of the branch connects
+ * to the thing following the set of BRANCHes.) The opcodes are:
+ */
+
+/* definition number opnd? meaning */
+#define END 0 /* no End of program. */
+#define BOL 1 /* no Match "" at beginning of line. */
+#define EOL 2 /* no Match "" at end of line. */
+#define ANY 3 /* no Match any one character. */
+#define ANYOF 4 /* str Match any character in this string. */
+#define ANYBUT 5 /* str Match any character not in this string. */
+#define BRANCH 6 /* node Match this alternative, or the next... */
+#define BACK 7 /* no Match "", "next" ptr points backward. */
+#define EXACTLY 8 /* str Match this string. */
+#define NOTHING 9 /* no Match empty string. */
+#define STAR 10 /* node Match this (simple) thing 0 or more times. */
+#define PLUS 11 /* node Match this (simple) thing 1 or more times. */
+#define WORDA 12 /* no Match "" at wordchar, where prev is nonword */
+#define WORDZ 13 /* no Match "" at nonwordchar, where prev is word */
+#define OPEN 20 /* no Mark this point in input as start of #n. */
+ /* OPEN+1 is number 1, etc. */
+#define CLOSE 30 /* no Analogous to OPEN. */
+
+/*
+ * Opcode notes:
+ *
+ * BRANCH The set of branches constituting a single choice are hooked
+ * together with their "next" pointers, since precedence prevents
+ * anything being concatenated to any individual branch. The
+ * "next" pointer of the last BRANCH in a choice points to the
+ * thing following the whole choice. This is also where the
+ * final "next" pointer of each individual branch points; each
+ * branch starts with the operand node of a BRANCH node.
+ *
+ * BACK Normal "next" pointers all implicitly point forward; BACK
+ * exists to make loop structures possible.
+ *
+ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular
+ * BRANCH structures using BACK. Simple cases (one character
+ * per match) are implemented with STAR and PLUS for speed
+ * and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE ...are numbered at compile time.
+ */
+
+/*
+ * A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first. The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node. (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define OP(p) (*(p))
+#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define OPERAND(p) ((p) + 3)
+
+/*
+ * See regmagic.h for one further detail of program structure.
+ */
+
+
+/*
+ * Utility definitions.
+ */
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+#define FAIL(m) { regerror(m); return(NULL); }
+#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?')
+
+/*
+ * Flags to be passed up and down.
+ */
+#define HASWIDTH 01 /* Known never to match null string. */
+#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */
+#define SPSTART 04 /* Starts with * or +. */
+#define WORST 0 /* Worst case. */
+
+/*
+ * Global work variables for regcomp().
+ */
+static char *regparse; /* Input-scan pointer. */
+static int regnpar; /* () count. */
+static char regdummy;
+static char *regcode; /* Code-emit pointer; &regdummy = don't. */
+static long regsize; /* Code size. */
+
+/*
+ * Forward declarations for regcomp()'s friends.
+ */
+#ifndef STATIC
+#define STATIC static
+#endif
+STATIC char *reg (int, int *);
+STATIC char *regbranch (int *);
+STATIC char *regpiece (int *);
+STATIC char *regatom (int *);
+STATIC char *regnode (char);
+STATIC char *regnext (char *);
+STATIC void regc (char);
+STATIC void reginsert (char, char *);
+STATIC void regtail (char *, char *);
+STATIC void regoptail (char *, char *);
+#ifdef STRCSPN
+STATIC int strcspn (char *, char *);
+#endif
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code. So we cheat: we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it. (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *
+regcomp(exp)
+const char *exp;
+{
+ register regexp *r;
+ register char *scan;
+ register char *longest;
+ register int len;
+ int flags;
+
+ if (exp == NULL)
+ FAIL("NULL argument");
+
+ /* First pass: determine size, legality. */
+#ifdef notdef
+ if (exp[0] == '.' && exp[1] == '*') exp += 2; /* aid grep */
+#endif
+ regparse = (char *)exp;
+ regnpar = 1;
+ regsize = 0L;
+ regcode = &regdummy;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Small enough for pointer-storage convention? */
+ if (regsize >= 32767L) /* Probably could be 65535L. */
+ FAIL("regexp too big");
+
+ /* Allocate space. */
+ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+ if (r == NULL)
+ FAIL("out of space");
+
+ /* Second pass: emit code. */
+ regparse = (char *)exp;
+ regnpar = 1;
+ regcode = r->program;
+ regc(MAGIC);
+ if (reg(0, &flags) == NULL)
+ return(NULL);
+
+ /* Dig out information for optimizations. */
+ r->regstart = '\0'; /* Worst-case defaults. */
+ r->reganch = 0;
+ r->regmust = NULL;
+ r->regmlen = 0;
+ scan = r->program+1; /* First BRANCH. */
+ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */
+ scan = OPERAND(scan);
+
+ /* Starting-point info. */
+ if (OP(scan) == EXACTLY)
+ r->regstart = *OPERAND(scan);
+ else if (OP(scan) == BOL)
+ r->reganch++;
+
+ /*
+ * If there's something expensive in the r.e., find the
+ * longest literal string that must appear and make it the
+ * regmust. Resolve ties in favor of later strings, since
+ * the regstart check works with the beginning of the r.e.
+ * and avoiding duplication strengthens checking. Not a
+ * strong reason, but sufficient in the absence of others.
+ */
+ if (flags&SPSTART) {
+ longest = NULL;
+ len = 0;
+ for (; scan != NULL; scan = regnext(scan))
+ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ longest = OPERAND(scan);
+ len = strlen(OPERAND(scan));
+ }
+ r->regmust = longest;
+ r->regmlen = len;
+ }
+ }
+
+ return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *
+reg(paren, flagp)
+int paren; /* Parenthesized? */
+int *flagp;
+{
+ register char *ret;
+ register char *br;
+ register char *ender;
+ register int parno = 0;
+ int flags;
+
+ *flagp = HASWIDTH; /* Tentatively. */
+
+ /* Make an OPEN node, if parenthesized. */
+ if (paren) {
+ if (regnpar >= NSUBEXP)
+ FAIL("too many ()");
+ parno = regnpar;
+ regnpar++;
+ ret = regnode(OPEN+parno);
+ } else
+ ret = NULL;
+
+ /* Pick up the branches, linking them together. */
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ if (ret != NULL)
+ regtail(ret, br); /* OPEN -> first. */
+ else
+ ret = br;
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ while (*regparse == '|' || *regparse == '\n') {
+ regparse++;
+ br = regbranch(&flags);
+ if (br == NULL)
+ return(NULL);
+ regtail(ret, br); /* BRANCH -> BRANCH. */
+ if (!(flags&HASWIDTH))
+ *flagp &= ~HASWIDTH;
+ *flagp |= flags&SPSTART;
+ }
+
+ /* Make a closing node, and hook it on the end. */
+ ender = regnode((paren) ? CLOSE+parno : END);
+ regtail(ret, ender);
+
+ /* Hook the tails of the branches to the closing node. */
+ for (br = ret; br != NULL; br = regnext(br))
+ regoptail(br, ender);
+
+ /* Check for proper termination. */
+ if (paren && *regparse++ != ')') {
+ FAIL("unmatched ()");
+ } else if (!paren && *regparse != '\0') {
+ if (*regparse == ')') {
+ FAIL("unmatched ()");
+ } else
+ FAIL("junk on end"); /* "Can't happen". */
+ /* NOTREACHED */
+ }
+
+ return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *
+regbranch(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char *chain;
+ register char *latest;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ ret = regnode(BRANCH);
+ chain = NULL;
+ while (*regparse != '\0' && *regparse != ')' &&
+ *regparse != '\n' && *regparse != '|') {
+ latest = regpiece(&flags);
+ if (latest == NULL)
+ return(NULL);
+ *flagp |= flags&HASWIDTH;
+ if (chain == NULL) /* First piece. */
+ *flagp |= flags&SPSTART;
+ else
+ regtail(chain, latest);
+ chain = latest;
+ }
+ if (chain == NULL) /* Loop ran zero times. */
+ (void) regnode(NOTHING);
+
+ return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized: they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *
+regpiece(flagp)
+int *flagp;
+{
+ register char *ret;
+ register char op;
+ register char *next;
+ int flags;
+
+ ret = regatom(&flags);
+ if (ret == NULL)
+ return(NULL);
+
+ op = *regparse;
+ if (!ISMULT(op)) {
+ *flagp = flags;
+ return(ret);
+ }
+
+ if (!(flags&HASWIDTH) && op != '?')
+ FAIL("*+ operand could be empty");
+ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+
+ if (op == '*' && (flags&SIMPLE))
+ reginsert(STAR, ret);
+ else if (op == '*') {
+ /* Emit x* as (x&|), where & means "self". */
+ reginsert(BRANCH, ret); /* Either x */
+ regoptail(ret, regnode(BACK)); /* and loop */
+ regoptail(ret, ret); /* back */
+ regtail(ret, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '+' && (flags&SIMPLE))
+ reginsert(PLUS, ret);
+ else if (op == '+') {
+ /* Emit x+ as x(&|), where & means "self". */
+ next = regnode(BRANCH); /* Either */
+ regtail(ret, next);
+ regtail(regnode(BACK), ret); /* loop back */
+ regtail(next, regnode(BRANCH)); /* or */
+ regtail(ret, regnode(NOTHING)); /* null. */
+ } else if (op == '?') {
+ /* Emit x? as (x|) */
+ reginsert(BRANCH, ret); /* Either x */
+ regtail(ret, regnode(BRANCH)); /* or */
+ next = regnode(NOTHING); /* null. */
+ regtail(ret, next);
+ regoptail(ret, next);
+ }
+ regparse++;
+ if (ISMULT(*regparse))
+ FAIL("nested *?+");
+
+ return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization: gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run. Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *
+regatom(flagp)
+int *flagp;
+{
+ register char *ret;
+ int flags;
+
+ *flagp = WORST; /* Tentatively. */
+
+ switch (*regparse++) {
+ /* FIXME: these chars only have meaning at beg/end of pat? */
+ case '^':
+ ret = regnode(BOL);
+ break;
+ case '$':
+ ret = regnode(EOL);
+ break;
+ case '.':
+ ret = regnode(ANY);
+ *flagp |= HASWIDTH|SIMPLE;
+ break;
+ case '[': {
+ register int class;
+ register int classend;
+
+ if (*regparse == '^') { /* Complement of range. */
+ ret = regnode(ANYBUT);
+ regparse++;
+ } else
+ ret = regnode(ANYOF);
+ if (*regparse == ']' || *regparse == '-')
+ regc(*regparse++);
+ while (*regparse != '\0' && *regparse != ']') {
+ if (*regparse == '-') {
+ regparse++;
+ if (*regparse == ']' || *regparse == '\0')
+ regc('-');
+ else {
+ class = UCHARAT(regparse-2)+1;
+ classend = UCHARAT(regparse);
+ if (class > classend+1)
+ FAIL("invalid [] range");
+ for (; class <= classend; class++)
+ regc(class);
+ regparse++;
+ }
+ } else
+ regc(*regparse++);
+ }
+ regc('\0');
+ if (*regparse != ']')
+ FAIL("unmatched []");
+ regparse++;
+ *flagp |= HASWIDTH|SIMPLE;
+ }
+ break;
+ case '(':
+ ret = reg(1, &flags);
+ if (ret == NULL)
+ return(NULL);
+ *flagp |= flags&(HASWIDTH|SPSTART);
+ break;
+ case '\0':
+ case '|':
+ case '\n':
+ case ')':
+ FAIL("internal urp"); /* Supposed to be caught earlier. */
+ break;
+ case '?':
+ case '+':
+ case '*':
+ FAIL("?+* follows nothing");
+ break;
+ case '\\':
+ switch (*regparse++) {
+ case '\0':
+ FAIL("trailing \\");
+ break;
+ case '<':
+ ret = regnode(WORDA);
+ break;
+ case '>':
+ ret = regnode(WORDZ);
+ break;
+ /* FIXME: Someday handle \1, \2, ... */
+ default:
+ /* Handle general quoted chars in exact-match routine */
+ goto de_fault;
+ }
+ break;
+ de_fault:
+ default:
+ /*
+ * Encode a string of characters to be matched exactly.
+ *
+ * This is a bit tricky due to quoted chars and due to
+ * '*', '+', and '?' taking the SINGLE char previous
+ * as their operand.
+ *
+ * On entry, the char at regparse[-1] is going to go
+ * into the string, no matter what it is. (It could be
+ * following a \ if we are entered from the '\' case.)
+ *
+ * Basic idea is to pick up a good char in ch and
+ * examine the next char. If it's *+? then we twiddle.
+ * If it's \ then we frozzle. If it's other magic char
+ * we push ch and terminate the string. If none of the
+ * above, we push ch on the string and go around again.
+ *
+ * regprev is used to remember where "the current char"
+ * starts in the string, if due to a *+? we need to back
+ * up and put the current char in a separate, 1-char, string.
+ * When regprev is NULL, ch is the only char in the
+ * string; this is used in *+? handling, and in setting
+ * flags |= SIMPLE at the end.
+ */
+ {
+ char *regprev;
+ register char ch = 0;
+
+ regparse--; /* Look at cur char */
+ ret = regnode(EXACTLY);
+ for ( regprev = 0 ; ; ) {
+ ch = *regparse++; /* Get current char */
+ switch (*regparse) { /* look at next one */
+
+ default:
+ regc(ch); /* Add cur to string */
+ break;
+
+ case '.': case '[': case '(':
+ case ')': case '|': case '\n':
+ case '$': case '^':
+ case '\0':
+ /* FIXME, $ and ^ should not always be magic */
+ magic:
+ regc(ch); /* dump cur char */
+ goto done; /* and we are done */
+
+ case '?': case '+': case '*':
+ if (!regprev) /* If just ch in str, */
+ goto magic; /* use it */
+ /* End mult-char string one early */
+ regparse = regprev; /* Back up parse */
+ goto done;
+
+ case '\\':
+ regc(ch); /* Cur char OK */
+ switch (regparse[1]){ /* Look after \ */
+ case '\0':
+ case '<':
+ case '>':
+ /* FIXME: Someday handle \1, \2, ... */
+ goto done; /* Not quoted */
+ default:
+ /* Backup point is \, scan * point is after it. */
+ regprev = regparse;
+ regparse++;
+ continue; /* NOT break; */
+ }
+ }
+ regprev = regparse; /* Set backup point */
+ }
+ done:
+ regc('\0');
+ *flagp |= HASWIDTH;
+ if (!regprev) /* One char? */
+ *flagp |= SIMPLE;
+ }
+ break;
+ }
+
+ return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char * /* Location. */
+regnode(op)
+char op;
+{
+ register char *ret;
+ register char *ptr;
+
+ ret = regcode;
+ if (ret == &regdummy) {
+ regsize += 3;
+ return(ret);
+ }
+
+ ptr = ret;
+ *ptr++ = op;
+ *ptr++ = '\0'; /* Null "next" pointer. */
+ *ptr++ = '\0';
+ regcode = ptr;
+
+ return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void
+regc(b)
+char b;
+{
+ if (regcode != &regdummy)
+ *regcode++ = b;
+ else
+ regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void
+reginsert(op, opnd)
+char op;
+char *opnd;
+{
+ register char *src;
+ register char *dst;
+ register char *place;
+
+ if (regcode == &regdummy) {
+ regsize += 3;
+ return;
+ }
+
+ src = regcode;
+ regcode += 3;
+ dst = regcode;
+ while (src > opnd)
+ *--dst = *--src;
+
+ place = opnd; /* Op node, where operand used to be. */
+ *place++ = op;
+ *place++ = '\0';
+ *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void
+regtail(p, val)
+char *p;
+char *val;
+{
+ register char *scan;
+ register char *temp;
+ register int offset;
+
+ if (p == &regdummy)
+ return;
+
+ /* Find last node. */
+ scan = p;
+ for (;;) {
+ temp = regnext(scan);
+ if (temp == NULL)
+ break;
+ scan = temp;
+ }
+
+ if (OP(scan) == BACK)
+ offset = scan - val;
+ else
+ offset = val - scan;
+ *(scan+1) = (offset>>8)&0377;
+ *(scan+2) = offset&0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void
+regoptail(p, val)
+char *p;
+char *val;
+{
+ /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+ return;
+ regtail(OPERAND(p), val);
+}
+
+/*
+ * regexec and friends
+ */
+
+/*
+ * Global work variables for regexec().
+ */
+static char *reginput; /* String-input pointer. */
+static char *regbol; /* Beginning of input, for ^ check. */
+static char **regstartp; /* Pointer to startp array. */
+static char **regendp; /* Ditto for endp. */
+
+/*
+ * Forwards.
+ */
+STATIC int regtry (const regexp *, const char *);
+STATIC int regmatch (char *);
+STATIC int regrepeat (char *);
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump __P((regexp *));
+STATIC char *regprop __P((char *));
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int
+regexec(prog, string)
+register const regexp *prog;
+register const char *string;
+{
+ register char *s;
+
+ /* Be paranoid... */
+ if (prog == NULL || string == NULL) {
+ regerror("NULL parameter");
+ return(0);
+ }
+
+ /* Check validity of program. */
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("corrupted program");
+ return(0);
+ }
+
+ /* If there is a "must appear" string, look for it. */
+ if (prog->regmust != NULL) {
+ s = (char *)string;
+ while ((s = strchr(s, prog->regmust[0])) != NULL) {
+ if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ break; /* Found it. */
+ s++;
+ }
+ if (s == NULL) /* Not present. */
+ return(0);
+ }
+
+ /* Mark beginning of line for ^ . */
+ regbol = (char *)string;
+
+ /* Simplest case: anchored match need be tried only once. */
+ if (prog->reganch)
+ return(regtry(prog, string));
+
+ /* Messy cases: unanchored match. */
+ s = (char *)string;
+ if (prog->regstart != '\0')
+ /* We know what char it must start with. */
+ while ((s = strchr(s, prog->regstart)) != NULL) {
+ if (regtry(prog, s))
+ return(1);
+ s++;
+ }
+ else
+ /* We don't -- general case. */
+ do {
+ if (regtry(prog, s))
+ return(1);
+ } while (*s++ != '\0');
+
+ /* Failure. */
+ return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int /* 0 failure, 1 success */
+regtry(prog, string)
+const regexp *prog;
+const char *string;
+{
+ register int i;
+ register char **sp;
+ register char **ep;
+
+ reginput = (char *)string; /* XXX */
+ regstartp = (char **)prog->startp; /* XXX */
+ regendp = (char **)prog->endp; /* XXX */
+
+ sp = (char **)prog->startp; /* XXX */
+ ep = (char **)prog->endp; /* XXX */
+ for (i = NSUBEXP; i > 0; i--) {
+ *sp++ = NULL;
+ *ep++ = NULL;
+ }
+ if (regmatch((char *)prog->program + 1)) { /* XXX */
+ ((regexp *)prog)->startp[0] = (char *)string; /* XXX */
+ ((regexp *)prog)->endp[0] = reginput; /* XXX */
+ return(1);
+ } else
+ return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple: check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly. In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int /* 0 failure, 1 success */
+regmatch(prog)
+char *prog;
+{
+ register char *scan; /* Current node. */
+ char *next; /* Next node. */
+
+ scan = prog;
+#ifdef DEBUG
+ if (scan != NULL && regnarrate)
+ fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+ while (scan != NULL) {
+#ifdef DEBUG
+ if (regnarrate)
+ fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+ next = regnext(scan);
+
+ switch (OP(scan)) {
+ case BOL:
+ if (reginput != regbol)
+ return(0);
+ break;
+ case EOL:
+ if (*reginput != '\0')
+ return(0);
+ break;
+ case WORDA:
+ /* Must be looking at a letter, digit, or _ */
+ if ((!isalnum(*reginput)) && *reginput != '_')
+ return(0);
+ /* Prev must be BOL or nonword */
+ if (reginput > regbol &&
+ (isalnum(reginput[-1]) || reginput[-1] == '_'))
+ return(0);
+ break;
+ case WORDZ:
+ /* Must be looking at non letter, digit, or _ */
+ if (isalnum(*reginput) || *reginput == '_')
+ return(0);
+ /* We don't care what the previous char was */
+ break;
+ case ANY:
+ if (*reginput == '\0')
+ return(0);
+ reginput++;
+ break;
+ case EXACTLY: {
+ register int len;
+ register char *opnd;
+
+ opnd = OPERAND(scan);
+ /* Inline the first character, for speed. */
+ if (*opnd != *reginput)
+ return(0);
+ len = strlen(opnd);
+ if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ return(0);
+ reginput += len;
+ }
+ break;
+ case ANYOF:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
+ return(0);
+ reginput++;
+ break;
+ case ANYBUT:
+ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
+ return(0);
+ reginput++;
+ break;
+ case NOTHING:
+ break;
+ case BACK:
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - OPEN;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set startp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regstartp[no] == NULL)
+ regstartp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9: {
+ register int no;
+ register char *save;
+
+ no = OP(scan) - CLOSE;
+ save = reginput;
+
+ if (regmatch(next)) {
+ /*
+ * Don't set endp if some later
+ * invocation of the same parentheses
+ * already has.
+ */
+ if (regendp[no] == NULL)
+ regendp[no] = save;
+ return(1);
+ } else
+ return(0);
+ }
+ break;
+ case BRANCH: {
+ register char *save;
+
+ if (OP(next) != BRANCH) /* No choice. */
+ next = OPERAND(scan); /* Avoid recursion. */
+ else {
+ do {
+ save = reginput;
+ if (regmatch(OPERAND(scan)))
+ return(1);
+ reginput = save;
+ scan = regnext(scan);
+ } while (scan != NULL && OP(scan) == BRANCH);
+ return(0);
+ /* NOTREACHED */
+ }
+ }
+ break;
+ case STAR:
+ case PLUS: {
+ register char nextch;
+ register int no;
+ register char *save;
+ register int min;
+
+ /*
+ * Lookahead to avoid useless match attempts
+ * when we know what character comes next.
+ */
+ nextch = '\0';
+ if (OP(next) == EXACTLY)
+ nextch = *OPERAND(next);
+ min = (OP(scan) == STAR) ? 0 : 1;
+ save = reginput;
+ no = regrepeat(OPERAND(scan));
+ while (no >= min) {
+ /* If it could work, try it. */
+ if (nextch == '\0' || *reginput == nextch)
+ if (regmatch(next))
+ return(1);
+ /* Couldn't or didn't -- back up. */
+ no--;
+ reginput = save + no;
+ }
+ return(0);
+ }
+ break;
+ case END:
+ return(1); /* Success! */
+ break;
+ default:
+ regerror("memory corruption");
+ return(0);
+ break;
+ }
+
+ scan = next;
+ }
+
+ /*
+ * We get here only if there's trouble -- normally "case END" is
+ * the terminating point.
+ */
+ regerror("corrupted pointers");
+ return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int
+regrepeat(p)
+char *p;
+{
+ register int count = 0;
+ register char *scan;
+ register char *opnd;
+
+ scan = reginput;
+ opnd = OPERAND(p);
+ switch (OP(p)) {
+ case ANY:
+ count = strlen(scan);
+ scan += count;
+ break;
+ case EXACTLY:
+ while (*opnd == *scan) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYOF:
+ while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ case ANYBUT:
+ while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
+ count++;
+ scan++;
+ }
+ break;
+ default: /* Oh dear. Called inappropriately. */
+ regerror("internal foulup");
+ count = 0; /* Best compromise. */
+ break;
+ }
+ reginput = scan;
+
+ return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *
+regnext(p)
+register char *p;
+{
+ register int offset;
+
+ if (p == &regdummy)
+ return(NULL);
+
+ offset = NEXT(p);
+ if (offset == 0)
+ return(NULL);
+
+ if (OP(p) == BACK)
+ return(p-offset);
+ else
+ return(p+offset);
+}
+
+#ifdef DEBUG
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void
+regdump(r)
+regexp *r;
+{
+ register char *s;
+ register char op = EXACTLY; /* Arbitrary non-END op. */
+ register char *next;
+ extern char *strchr();
+
+
+ s = r->program + 1;
+ while (op != END) { /* While that wasn't END last time... */
+ op = OP(s);
+ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */
+ next = regnext(s);
+ if (next == NULL) /* Next ptr. */
+ printf("(0)");
+ else
+ printf("(%d)", (s-r->program)+(next-s));
+ s += 3;
+ if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ /* Literal string, where present. */
+ while (*s != '\0') {
+ putchar(*s);
+ s++;
+ }
+ s++;
+ }
+ putchar('\n');
+ }
+
+ /* Header fields of interest. */
+ if (r->regstart != '\0')
+ printf("start `%c' ", r->regstart);
+ if (r->reganch)
+ printf("anchored ");
+ if (r->regmust != NULL)
+ printf("must have \"%s\"", r->regmust);
+ printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *
+regprop(op)
+char *op;
+{
+ register char *p;
+ static char buf[50];
+
+ (void) strcpy(buf, ":");
+
+ switch (OP(op)) {
+ case BOL:
+ p = "BOL";
+ break;
+ case EOL:
+ p = "EOL";
+ break;
+ case ANY:
+ p = "ANY";
+ break;
+ case ANYOF:
+ p = "ANYOF";
+ break;
+ case ANYBUT:
+ p = "ANYBUT";
+ break;
+ case BRANCH:
+ p = "BRANCH";
+ break;
+ case EXACTLY:
+ p = "EXACTLY";
+ break;
+ case NOTHING:
+ p = "NOTHING";
+ break;
+ case BACK:
+ p = "BACK";
+ break;
+ case END:
+ p = "END";
+ break;
+ case OPEN+1:
+ case OPEN+2:
+ case OPEN+3:
+ case OPEN+4:
+ case OPEN+5:
+ case OPEN+6:
+ case OPEN+7:
+ case OPEN+8:
+ case OPEN+9:
+ sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ p = NULL;
+ break;
+ case CLOSE+1:
+ case CLOSE+2:
+ case CLOSE+3:
+ case CLOSE+4:
+ case CLOSE+5:
+ case CLOSE+6:
+ case CLOSE+7:
+ case CLOSE+8:
+ case CLOSE+9:
+ sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ p = NULL;
+ break;
+ case STAR:
+ p = "STAR";
+ break;
+ case PLUS:
+ p = "PLUS";
+ break;
+ case WORDA:
+ p = "WORDA";
+ break;
+ case WORDZ:
+ p = "WORDZ";
+ break;
+ default:
+ regerror("corrupted opcode");
+ break;
+ }
+ if (p != NULL)
+ (void) strcat(buf, p);
+ return(buf);
+}
+#endif
+
+/*
+ * The following is provided for those people who do not have strcspn() in
+ * their C libraries. They should get off their butts and do something
+ * about it; at least one public-domain implementation of those (highly
+ * useful) string routines has been published on Usenet.
+ */
+#ifdef STRCSPN
+/*
+ * strcspn - find length of initial segment of s1 consisting entirely
+ * of characters not from s2
+ */
+
+static int
+strcspn(s1, s2)
+char *s1;
+char *s2;
+{
+ register char *scan1;
+ register char *scan2;
+ register int count;
+
+ count = 0;
+ for (scan1 = s1; *scan1 != '\0'; scan1++) {
+ for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
+ if (*scan1 == *scan2++)
+ return(count);
+ count++;
+ }
+ return(count);
+}
+#endif
diff --git a/winsup/cygwin/regexp/regsub.c b/winsup/cygwin/regexp/regsub.c
new file mode 100644
index 00000000000..3983cc58bfe
--- /dev/null
+++ b/winsup/cygwin/regexp/regsub.c
@@ -0,0 +1,87 @@
+/*
+ * regsub
+ *
+ * Copyright (c) 1986 by University of Toronto.
+ * Written by Henry Spencer. Not derived from licensed software.
+ *
+ * Permission is granted to anyone to use this software for any
+ * purpose on any computer system, and to redistribute it freely,
+ * subject to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of
+ * this software, no matter how awful, even if they arise
+ * from defects in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either
+ * by explicit claim or by omission.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ */
+
+#if 0
+#ifndef lint
+static char *rcsid = "$Id$";
+#endif /* not lint */
+#endif
+
+#include "regexp.h"
+#include <stdio.h>
+#include <string.h>
+#include "regmagic.h"
+
+#ifndef CHARBITS
+#define UCHARAT(p) ((int)*(unsigned char *)(p))
+#else
+#define UCHARAT(p) ((int)*(p)&CHARBITS)
+#endif
+
+/*
+ - regsub - perform substitutions after a regexp match
+ */
+void
+regsub(prog, source, dest)
+const regexp *prog;
+const char *source;
+char *dest;
+{
+ register char *src;
+ register char *dst;
+ register char c;
+ register int no;
+ register int len;
+
+ if (prog == NULL || source == NULL || dest == NULL) {
+ regerror("NULL parm to regsub");
+ return;
+ }
+ if (UCHARAT(prog->program) != MAGIC) {
+ regerror("damaged regexp fed to regsub");
+ return;
+ }
+
+ src = (char *)source;
+ dst = dest;
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '\\' && '0' <= *src && *src <= '9')
+ no = *src++ - '0';
+ else
+ no = -1;
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '\\' || *src == '&'))
+ c = *src++;
+ *dst++ = c;
+ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
+ len = prog->endp[no] - prog->startp[no];
+ (void) strncpy(dst, prog->startp[no], len);
+ dst += len;
+ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */
+ regerror("damaged match string");
+ return;
+ }
+ }
+ }
+ *dst++ = '\0';
+}
diff --git a/winsup/cygwin/shared.h b/winsup/cygwin/shared.h
new file mode 100644
index 00000000000..036ca8815af
--- /dev/null
+++ b/winsup/cygwin/shared.h
@@ -0,0 +1,552 @@
+/* shared.h: shared info for cygwin
+
+ Copyright 1998, 1999, 2000 Cygnus Solutions.
+
+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. */
+
+/******** Functions declarations for use in methods below ********/
+
+/* Printf type functions */
+extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn));
+extern "C" int __small_sprintf (char *dst, const char *fmt, ...);
+extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap);
+extern "C" int __small_sprintf (char *dst, const char *fmt, ...);
+
+/******** Deletion Queue Class ********/
+
+/* First pass at a file deletion queue structure.
+
+ We can't keep this list in the per-process info, since
+ one process may open a file, and outlive a process which
+ wanted to unlink the file - and the data would go away.
+
+ Perhaps the FILE_FLAG_DELETE_ON_CLOSE would be ok,
+ but brief experimentation didn't get too far.
+*/
+
+#define MAX_DELQUEUES_PENDING 100
+
+class delqueue_list
+{
+ char name[MAX_DELQUEUES_PENDING][MAX_PATH];
+ char inuse[MAX_DELQUEUES_PENDING];
+ int empty;
+
+public:
+ void init ();
+ void queue_file (const char *dosname);
+ void process_queue ();
+};
+
+/******** Process Table ********/
+
+/* Signal constants (have to define them here, unfortunately) */
+
+enum
+{
+ __SIGFLUSH = -2,
+ __SIGSTRACE = -1,
+ __SIGCHILDSTOPPED = 0,
+ __SIGOFFSET = 3
+};
+
+class pinfo
+{
+ public:
+
+ /* If hProcess is set, it's because it came from a
+ CreateProcess call. This means it's process relative
+ to the thing which created the process. That's ok because
+ we only use this handle from the parent. */
+ HANDLE hProcess;
+
+ HANDLE parent_alive;
+
+ /* dwProcessId contains the processid used for sending signals. It
+ * will be reset in a child process when it is capable of receiving
+ * signals.
+ */
+ DWORD dwProcessId;
+
+ /* User information.
+ The information is derived from the GetUserName system call,
+ with the name looked up in /etc/passwd and assigned a default value
+ if not found. This data resides in the shared data area (allowing
+ tasks to store whatever they want here) so it's for informational
+ purposes only. */
+ uid_t uid; /* User ID */
+ gid_t gid; /* Group ID */
+ pid_t pgid; /* Process group ID */
+ pid_t sid; /* Session ID */
+ int ctty; /* Control tty */
+ mode_t umask;
+ char username[MAX_USER_NAME]; /* user's name */
+
+ /* Extendend user information.
+ The information is derived from the internal_getlogin call
+ when on a NT system. */
+ PSID psid; /* user's SID */
+ char sidbuf[40]; /* buffer for user's SID */
+ char logsrv[256]; /* Logon server, may be fully qualified DNS name */
+ char domain[MAX_COMPUTERNAME_LENGTH+1]; /* Logon domain of the user */
+
+ /* Non-zero if process was stopped by a signal. */
+ char stopsig;
+
+ struct sigaction& getsig (int);
+ void copysigs (pinfo *);
+ sigset_t& getsigmask ();
+ void setsigmask (sigset_t);
+ LONG* getsigtodo (int);
+ HANDLE getthread2signal ();
+ void setthread2signal (void *);
+
+ /* Resources used by process. */
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+
+private:
+ struct sigaction sigs[NSIG];
+ sigset_t sig_mask; /* one set for everything to ignore. */
+ LONG _sigtodo[NSIG + __SIGOFFSET];
+#ifdef _MT_SAFE
+ ThreadItem* thread2signal; // NULL means means thread any other means a pthread
+#endif
+
+public:
+
+ /* Pointer to mmap'ed areas for this process. Set up by fork. */
+ void *mmap_ptr;
+
+ /* Used to spawn a child for fork(), among other things. */
+ char progname[MAX_PATH];
+
+ #define PINFO_ZERO ((((pinfo *) NULL)->progname + 1) - ((char *) NULL))
+
+ /* Anything below this point is not zeroed automatically by allocate_pid */
+
+ /* The pid stays the same, while the hProcess moves due to execs. */
+ pid_t pid;
+ /* Parent process id. */
+ pid_t ppid;
+
+ /* Various flags indicating the state of the process. See PID_
+ constants below. */
+ DWORD process_state;
+
+ void record_death (int lock = 1);
+};
+
+#define ISSTATE(p, f) (!!((p)->process_state & f))
+#define NOTSTATE(p, f) (!((p)->process_state & f))
+
+/* Flags associated with process_state */
+enum
+{
+ PID_NOT_IN_USE = 0x0000, // Free entry.
+ PID_IN_USE = 0x0001, // Entry in use.
+ PID_ZOMBIE = 0x0002, // Child exited: no parent wait.
+ PID_STOPPED = 0x0004, // Waiting for SIGCONT.
+ PID_TTYIN = 0x0008, // Waiting for terminal input.
+ PID_TTYOU = 0x0010, // Waiting for terminal output.
+ PID_ORPHANED = 0x0020, // Member of an orphaned process group.
+ PID_ACTIVE = 0x0040, // Pid accepts signals.
+ PID_CYGPARENT = 0x0080, // Set if parent was a cygwin app.
+ PID_SPLIT_HEAP = 0x0100, // Set if the heap has been split,
+ // which means we can't fork again.
+ PID_CLEAR = 0x0200, // Flag that pid should be cleared from parent's
+ // wait list
+ PID_SOCKETS_USED = 0x0400, // Set if process uses Winsock.
+ PID_INITIALIZING = 0x0800, // Set until ready to receive signals.
+ PID_USETTY = 0x1000, // Setting this enables or disables cygwin's
+ // tty support. This is inherited by
+ // all execed or forked processes.
+ PID_REPARENT = 0x2000 // child has execed
+};
+
+#define PSIZE 128
+
+class pinfo_list
+{
+ public:
+ int next_pid;
+ pinfo vec[PSIZE];
+ char lock_info[MAX_PATH + 1];
+ pinfo * operator[] (pid_t x);
+ int size (void) { return PSIZE; }
+ pinfo *allocate_pid (void);
+ void init (void);
+};
+
+void __stdcall pinfo_init (PBYTE);
+pinfo *__stdcall procinfo (int n);
+
+enum
+{
+ PROC_MAGIC = 0xaf04f000,
+ PROC_FORK = PROC_MAGIC + 1,
+ PROC_EXEC = PROC_MAGIC + 2,
+ PROC_SPAWN = PROC_MAGIC + 3,
+ PROC_FORK1 = PROC_MAGIC + 4 // Newer versions provide stack
+ // location information
+};
+
+#define PROC_MAGIC_MASK 0xff00f000
+#define PROC_MAGIC_GENERIC 0xaf00f000
+#define PROC_MAGIC_VER_MASK 0x0ff0000
+
+#define EXEC_MAGIC_SIZE sizeof(child_info)
+class child_info
+{
+public:
+ DWORD zero[1]; // must be zeroed
+ DWORD cb; // size of this record
+ DWORD type; // type of record
+ int cygpid; // cygwin pid of child process
+ HANDLE subproc_ready; // used for synchronization with parent
+ HANDLE shared_h;
+ HANDLE console_h;
+ HANDLE parent_alive; // handle of thread used to track children
+};
+
+class child_info_fork: public child_info
+{
+public:
+ HANDLE forker_finished;// for synchronization with child
+ DWORD stacksize; // size of parent stack
+ void *heaptop;
+ void *heapbase;
+ void *heapptr;
+ jmp_buf jmp; // where child will jump to
+ void *stacktop; // location of top of parent stack
+ void *stackbottom; // location of bottom of parent stack
+};
+
+void __stdcall init_child_info (DWORD, child_info *, int, HANDLE);
+
+extern child_info_fork *child_proc_info;
+
+/* Process info for this process */
+extern pinfo *myself;
+
+/* non-NULL if this process is a child of a cygwin process */
+extern HANDLE parent_alive;
+
+/******** Registry Access ********/
+
+class reg_key
+{
+private:
+
+ HKEY key;
+
+public:
+
+ reg_key (HKEY toplev, REGSAM access, ...);
+ reg_key (REGSAM access, ...);
+ reg_key (REGSAM access = KEY_ALL_ACCESS);
+
+ void *operator new (size_t, void *p) {return p;}
+ void build_reg (HKEY key, REGSAM access, va_list av);
+
+ int error () {return key == (HKEY) INVALID_HANDLE_VALUE;}
+
+ int kill (const char *child);
+
+ HKEY get_key ();
+ int get_int (const char *,int def);
+ int get_string (const char *, char *buf, size_t len, const char *def);
+ int set_string (const char *,const char *);
+ int set_int (const char *, int val);
+ int setone_string (const char *src, const char *name);
+
+ ~reg_key ();
+};
+
+/******** Mount Table ********/
+
+/* Mount table entry */
+
+class mount_item
+{
+public:
+ /* FIXME: Nasty static allocation. Need to have a heap in the shared
+ area [with the user being able to configure at runtime the max size]. */
+
+ /* Win32-style mounted partition source ("C:\foo\bar").
+ native_path[0] == 0 for unused entries. */
+ char native_path[MAX_PATH];
+ int native_pathlen;
+
+ /* POSIX-style mount point ("/foo/bar") */
+ char posix_path[MAX_PATH];
+ int posix_pathlen;
+
+ unsigned flags;
+
+ void init (const char *dev, const char *path, unsigned flags);
+
+ struct mntent *getmntent ();
+};
+
+/* Warning: Decreasing this value will cause cygwin.dll to ignore existing
+ higher numbered registry entries. Don't change this number willy-nilly.
+ What we need is to have a more dynamic allocation scheme, but the current
+ scheme should be satisfactory for a long while yet. */
+#define MAX_MOUNTS 30
+
+class mount_info
+{
+ int posix_sorted[MAX_MOUNTS];
+ int native_sorted[MAX_MOUNTS];
+public:
+ int nmounts;
+ mount_item mount[MAX_MOUNTS];
+
+ /* Strings used by getmntent(). */
+ char mnt_type[20];
+ char mnt_opts[20];
+ char mnt_fsname[MAX_PATH];
+ char mnt_dir[MAX_PATH];
+
+ /* cygdrive_prefix is used as the root of the path automatically
+ prepended to a path when the path has no associated mount.
+ cygdrive_flags are the default flags for the cygdrives. */
+ char cygdrive[MAX_PATH];
+ size_t cygdrive_len;
+ unsigned cygdrive_flags;
+
+ /* Increment when setting up a reg_key if mounts area had to be
+ created so we know when we need to import old mount tables. */
+ int had_to_create_mount_areas;
+
+ void init ();
+ int add_item (const char *dev, const char *path, unsigned flags);
+ int del_item (const char *path, unsigned flags);
+
+ void from_registry ();
+ void from_v1_registry ();
+ int add_reg_mount (const char * native_path, const char * posix_path,
+ unsigned mountflags);
+ int del_reg_mount (const char * posix_path, unsigned mountflags);
+
+ unsigned set_flags_from_win32_path (const char *path);
+ int conv_to_win32_path (const char *src_path, char *win32_path,
+ char *full_win32_path, DWORD &devn, int &unit,
+ unsigned *flags = NULL);
+ int conv_to_posix_path (const char *src_path, char *posix_path,
+ int keep_rel_p);
+ struct mntent *getmntent (int x);
+
+ int write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags);
+
+ void import_v1_mounts ();
+
+private:
+
+ void sort ();
+ void read_mounts (reg_key& r);
+ void read_v1_mounts (reg_key r, unsigned which);
+ void mount_slash ();
+ void to_registry ();
+
+ int cygdrive_win32_path (const char *src, char *dst, int trailing_slash_p);
+ void cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p);
+ void slash_drive_to_win32_path (const char *path, char *buf, int trailing_slash_p);
+ void read_cygdrive_info_from_registry ();
+};
+
+/******** TTY Support ********/
+
+/* tty tables */
+
+#define INP_BUFFER_SIZE 256
+#define OUT_BUFFER_SIZE 256
+#define NTTYS 128
+#define TTY_CONSOLE 0x40000000
+#define tty_attached(p) ((p)->ctty >= 0 && (p)->ctty != TTY_CONSOLE)
+
+/* Input/Output/ioctl events */
+
+#define OUTPUT_DONE_EVENT "cygtty%d.output.done"
+#define IOCTL_REQUEST_EVENT "cygtty%d.ioctl.request"
+#define IOCTL_DONE_EVENT "cygtty%d.ioctl.done"
+#define RESTART_OUTPUT_EVENT "cygtty%d.output.restart"
+#define OUTPUT_MUTEX "cygtty%d.output.mutex"
+#define TTY_SLAVE_ALIVE "cygtty%x.slave_alive"
+#define TTY_MASTER_ALIVE "cygtty%x.master_alive"
+
+#include <sys/termios.h>
+
+enum
+{
+ TTY_INITIALIZED = 1, /* Set if tty is initialized */
+ TTY_RSTCONS = 2 /* Set if console needs to be set to "non-cooked" */
+};
+
+#define TTYISSETF(x) __ISSETF (tc, x, TTY)
+#define TTYSETF(x) __SETF (tc, x, TTY)
+#define TTYCLEARF(x) __CLEARF (tc, x, TTY)
+#define TTYCONDSETF(n, x) __CONDSETF(n, tc, x, TTY)
+
+#ifndef MIN_CTRL_C_SLOP
+#define MIN_CTRL_C_SLOP 50
+#endif
+
+class tty_min
+{
+ pid_t sid; /* Session ID of tty */
+public:
+ DWORD status;
+ pid_t pgid;
+ int OutputStopped;
+ int ntty;
+ DWORD last_ctrl_c; // tick count of last ctrl-c
+
+ tty_min (int t = -1, pid_t s = -1) : sid (s), ntty (t) {}
+ void setntty (int n) {ntty = n;}
+ pid_t getpgid () {return pgid;}
+ void setpgid (int pid) {pgid = pid;}
+ int getsid () {return sid;}
+ void setsid (pid_t tsid) {sid = tsid;}
+ struct termios ti;
+ struct winsize winsize;
+
+ /* ioctl requests buffer */
+ int cmd;
+ union
+ {
+ struct termios termios;
+ struct winsize winsize;
+ int value;
+ pid_t pid;
+ } arg;
+ /* XXX_retval variables holds master's completion codes. Error are stored as
+ * -ERRNO
+ */
+ int ioctl_retval;
+
+ int write_retval;
+};
+
+class fhandler_pty_master;
+
+class tty: public tty_min
+{
+ HANDLE get_event (const char *fmt, BOOL inherit);
+public:
+ HWND hwnd; /* Console window handle tty belongs to */
+
+ DWORD master_pid; /* Win32 PID of tty master process */
+
+ HANDLE from_master, to_slave;
+ HANDLE from_slave, to_master;
+
+ int read_retval;
+ BOOL was_opened; /* True if opened at least once. */
+
+ void init ();
+ HANDLE create_inuse (const char *);
+ BOOL common_init (fhandler_pty_master *);
+ BOOL alive (const char *fmt);
+ BOOL slave_alive ();
+ BOOL master_alive ();
+ HWND gethwnd () {return hwnd;}
+ void sethwnd (HWND wnd) {hwnd = wnd;}
+ int make_pipes (fhandler_pty_master *ptym);
+ HANDLE open_output_mutex (BOOL inherit = FALSE)
+ {
+ char buf[80];
+ __small_sprintf (buf, OUTPUT_MUTEX, ntty);
+ return OpenMutex (MUTEX_ALL_ACCESS, inherit, buf);
+ }
+ BOOL exists ()
+ {
+ HANDLE h = open_output_mutex ();
+ if (h)
+ {
+ CloseHandle (h);
+ return 1;
+ }
+ return slave_alive ();
+ }
+};
+
+class tty_list
+{
+ tty ttys[NTTYS];
+
+public:
+ tty * operator [](int n) {return ttys + n;}
+ int allocate_tty (int n); /* n non zero if allocate a tty, pty otherwise */
+ int connect_tty (int);
+ void terminate ();
+ void init ();
+ tty_min *get_tty (int n);
+};
+
+void __stdcall tty_init ();
+void __stdcall tty_terminate ();
+int __stdcall attach_tty (int);
+void __stdcall create_tty_master (int);
+extern "C" int ttyslot (void);
+
+/******** Shared Info ********/
+/* Data accessible to all tasks */
+
+class shared_info
+{
+ DWORD inited;
+
+public:
+ pinfo_list p;
+
+ /* FIXME: Doesn't work if more than one user on system. */
+ mount_info mount;
+
+ int heap_chunk_in_mb;
+ unsigned heap_chunk_size (void);
+
+ tty_list tty;
+ delqueue_list delqueue;
+ void initialize (void);
+};
+
+/* Various types of security attributes for use in Create* functions. */
+extern SECURITY_ATTRIBUTES sec_none, sec_none_nih, sec_all, sec_all_nih;
+extern SECURITY_ATTRIBUTES *__stdcall sec_user (PVOID sa_buf, PSID sid2 = NULL, BOOL inherit = TRUE);
+extern SECURITY_ATTRIBUTES *__stdcall sec_user_nih (PVOID sa_buf, PSID sid2 = NULL);
+
+extern shared_info *cygwin_shared;
+extern HANDLE cygwin_shared_h;
+extern HANDLE console_shared_h;
+extern int __stdcall set_console_state_for_spawn ();
+
+void __stdcall shared_init (void);
+void __stdcall shared_terminate (void);
+
+/* This is for programs that want to access the shared data. */
+extern "C" class shared_info *cygwin_getshared (void);
+
+char *__stdcall shared_name (const char *, int);
+void *__stdcall open_shared (const char *name, HANDLE &shared_h, DWORD size, void *addr);
+
+struct cygwin_version_info
+{
+ unsigned short api_major;
+ unsigned short api_minor;
+ unsigned short dll_major;
+ unsigned short dll_minor;
+ unsigned short shared_data;
+ unsigned short mount_registry;
+ const char *dll_build_date;
+ const char shared_id[sizeof (CYGWIN_VERSION_DLL_IDENTIFIER) + 64];
+};
+
+extern cygwin_version_info cygwin_version;
+extern const char *cygwin_version_strings;
diff --git a/winsup/cygwin/test.c b/winsup/cygwin/test.c
new file mode 100644
index 00000000000..cd63a3e6884
--- /dev/null
+++ b/winsup/cygwin/test.c
@@ -0,0 +1,165 @@
+/* test.c: misc Cygwin testing code
+
+ Copyright 1996, 1998 Cygnus Solutions.
+
+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. */
+
+#include <stdio.h>
+#include <windows.h>
+
+char a[] ="This is static data";
+
+void
+test1()
+{
+ int depth = 0;
+ while (depth < 5)
+ {
+ int r;
+ printf ("about to fork %d\n", depth);
+
+ r = fork ();
+
+ if (r == 0)
+ {
+ int res;
+ depth++;
+ printf ("************Depth is %d\n", depth);
+ sleep (1);
+ }
+ else
+ {
+ printf ("This is the parent, quitting %d\n", depth);
+ sleep (1);
+ exit (1);
+ }
+ printf ("done loop, depth %d\n", depth);
+ }
+}
+
+#define N 10
+int v[N];
+startup ()
+{
+ int i;
+ for (i = 0; i < N; i++)
+ {
+ int r;
+ fflush (stdout);
+ r = fork ();
+ if (r)
+ {
+ v[i] = r;
+ printf ("started %d, were'id %d\n", v[i], GetCurrentProcessId ());
+ fflush (stdout);
+ }
+ else
+ {
+ /* running the child, sleep a bit and exit. */
+ printf ("the fork said 0, were %d\n", GetCurrentProcessId ());
+ fflush (stdout);
+ sleep (2);
+ printf ("Running, and exiting %d\n", i);
+ fflush (stdout);
+ _exit (i + 0x30);
+ }
+ }
+}
+
+test2()
+{
+ int i;
+ startup ();
+ sleep (1);
+ /* Wait for them one by one */
+ for (i = 0; i < N; i++)
+ {
+ int res;
+
+ waitpid (v[i], &res, 0);
+ printf ("Process %d gave res %x\n", v[i], res);
+ if (res != (0x30 + i) << 8)
+ printf ("***** BAD *** Process %d gave res %x\n", v[i], res);
+ }
+}
+
+test3()
+{
+ int i;
+ startup ();
+ /* Wait for them all at the same time */
+ for (i = 0; i < N; i++)
+ {
+ int res;
+ wait (&res);
+ printf ("Got res %x\n", res);
+ }
+}
+
+test5()
+{
+ char *c = strdup ("HI STEVE");
+ printf ("c is %s\n", c);
+ free (c);
+}
+
+int count;
+
+main (int ac, char **av)
+{
+ int r;
+ int done;
+ int test;
+ fprintf (stderr,"TO STDERR\n");
+ if (ac < 2) {
+ printf ("usage: test <n>\n");
+ exit (2);
+ }
+ test = atoi (av[1]);
+
+ printf ("%d %d Hi steve, about to start fork test %d %d.\n",getpid (), count++, test,
+ GetCurrentProcessId ());
+fflush (stdout);
+ switch (test)
+ {
+ case 1:
+ test1();
+ break;
+ case 2:
+ test2();
+ break;
+ case 3:
+ test3();
+ break;
+ case 4:
+SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), FOREGROUND_RED);
+break;
+ case 5:
+ test5();
+break;
+ }
+
+}
+
+free ()
+{
+ printf ("MY FREE!\n");
+}
+
+char b[100000];
+int i;
+
+malloc (x)
+{
+char *r = b + i;
+i += x;
+return r;
+}
+
+realloc ()
+{
+}
diff --git a/winsup/cygwin/testsuite/README b/winsup/cygwin/testsuite/README
new file mode 100644
index 00000000000..92ede0dc772
--- /dev/null
+++ b/winsup/cygwin/testsuite/README
@@ -0,0 +1,42 @@
+1999-12-23 DJ Delorie <dj@cygnus.com>
+
+Here are some notes about adding and using this testsuite.
+
+First, all the programs are linked with new-libcygwin.a, which is just
+like libcygwin.a, except that it wants new-cygwin1.dll, not
+cygwin1.dll. The testsuite adds the winsup build directory to the
+PATH so that new-cygwin1.dll can be found by windows during testing.
+
+Because we'll probably run into complaints about using two DLLs, we
+run cygrun.exe for each test. All this does is run the test with
+CreateProcess() so that we don't attempt to do the special code for
+when a cygwin program calls another cygwin program, as this might be a
+"multiple cygwins" problem.
+
+Any test that needs to test command line args or redirection needs to
+run such a child program itself, as the testsuite will not do any
+arguments or redirection for it. Same for fork, signals, etc.
+
+The testsuite/winsup.api subdirectory is for testing the API to
+cygwin1.dll ONLY. Create other subdirs under testsuite/ for other
+classes of testing.
+
+Tests in winsup.api/*.c or winsup.api/*/*.c (only one subdirectory
+level is allowed) either compile, run, and exit(0) or they fail.
+Either abort or exit with a non-zero code to indicate failure. Don't
+print anything to the screen if you can avoid it (except for failure
+reasons, of course). One .c file per test, no compile options are
+allowed (we're testing the api, not the compiler).
+
+Tests whose filename begin with "xf-" will be *expected* to fail, and
+will "fail" if they compile, run, and return zero. Note that the
+*only* purpose for adding this feature is to test the testing
+framework itself. All real tests should NOT be named xf-*, and should
+pass for real (compile, run, return 0) if the dll is working
+correctly. Do not use xf-* to "silence" a failure that you know isn't
+going to get fixed for a while; let it just keep failing. There are
+five "sample" tests that demonstrate how the framework works and what
+happens to each condition.
+
+"make check" will only work if you run it *on* an NT machine.
+Cross-checking is not supported.
diff --git a/winsup/cygwin/testsuite/config/default.exp b/winsup/cygwin/testsuite/config/default.exp
new file mode 100644
index 00000000000..ec8ce1448d9
--- /dev/null
+++ b/winsup/cygwin/testsuite/config/default.exp
@@ -0,0 +1,6 @@
+proc winsup_version {} {
+ clone_output "\n[exec grep ^%%% ../new-cygwin1.dll]\n"
+}
+
+proc winsup_exit {} {
+}
diff --git a/winsup/cygwin/testsuite/winsup.api/devzero.c b/winsup/cygwin/testsuite/winsup.api/devzero.c
new file mode 100644
index 00000000000..85a2615b1b9
--- /dev/null
+++ b/winsup/cygwin/testsuite/winsup.api/devzero.c
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+main()
+{
+ int fd, r, w, l;
+ char buf[1024];
+ char *v;
+
+ fd = open("/dev/zero", O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Unable to open /dev/zero for reading\n");
+ perror("The error was");
+ exit(1);
+ }
+
+ l = read(fd, buf, 1024);
+ if (l != 1024)
+ {
+ fprintf(stderr, "Asked to read 1024 bytes, got %d\n", l);
+ exit(1);
+ }
+
+ for (r=0; r<1024; r++)
+ if (buf[r] != 0)
+ {
+ fprintf(stderr, "/dev/zero returned a byte of %02x at offset %d\n",
+ buf[r], r);
+ exit(1);
+ }
+
+ l = lseek(fd, 4096, 0);
+ if (l != 0)
+ {
+ fprintf(stderr, "l == %d\n", l);
+ exit(1);
+ }
+
+ l = close(fd);
+ if (l != 0)
+ {
+ fprintf(stderr, "close: returned %d\n", l);
+ perror("The error was");
+ exit(1);
+ }
+
+ fd = open("/dev/zero", O_WRONLY);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Unable to open /dev/zero for writing\n");
+ perror("The error was");
+ exit(1);
+ }
+
+ l = write(fd, buf, 1024);
+ if (l != 1024)
+ {
+ fprintf(stderr, "Asked to write 1024 bytes, got %d\n", l);
+ exit(1);
+ }
+
+ l = close(fd);
+ if (l != 0)
+ {
+ fprintf(stderr, "close: returned %d\n", l);
+ perror("The error was");
+ exit(1);
+ }
+
+ fd = open("/dev/zero", O_RDWR);
+ v = (char *)mmap(0, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (v == (char *)-1)
+ {
+ fprintf(stderr, "mmap r/w /dev/zero failed\n");
+ perror("The error was");
+ exit(1);
+ }
+
+ for (r=0; r<65536; r++)
+ if (v[r] != 0)
+ {
+ fprintf(stderr, "mmap'd r/w /dev/zero has byte %d at offset %d\n",
+ v[r], r);
+ exit(1);
+ }
+ munmap(v, 65536);
+ close(fd);
+
+ fd = open("/dev/zero", O_RDONLY);
+ v = (char *)mmap(0, 65536, PROT_READ, MAP_SHARED, fd, 0);
+ if (v == (char *)-1)
+ {
+ fprintf(stderr, "mmap /dev/zero r/o failed\n");
+ perror("The error was");
+ exit(1);
+ }
+
+ for (r=0; r<65536; r++)
+ if (v[r] != 0)
+ {
+ fprintf(stderr, "mmap'd r/o /dev/zero has byte %d at offset %d\n",
+ v[r], r);
+ exit(1);
+ }
+ munmap(v, 65536);
+ close(fd);
+
+ exit(0);
+}
diff --git a/winsup/cygwin/testsuite/winsup.api/samples/sample-pass.c b/winsup/cygwin/testsuite/winsup.api/samples/sample-pass.c
new file mode 100644
index 00000000000..893a0b605d1
--- /dev/null
+++ b/winsup/cygwin/testsuite/winsup.api/samples/sample-pass.c
@@ -0,0 +1,4 @@
+main()
+{
+ return 0;
+}
diff --git a/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-fail.c b/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-fail.c
new file mode 100644
index 00000000000..d8beb90be90
--- /dev/null
+++ b/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-fail.c
@@ -0,0 +1,4 @@
+main()
+{
+ return 1;
+}
diff --git a/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-miscompile.c b/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-miscompile.c
new file mode 100644
index 00000000000..bc0d21d2e61
--- /dev/null
+++ b/winsup/cygwin/testsuite/winsup.api/samples/xf-sample-miscompile.c
@@ -0,0 +1 @@
+foo bar grill
diff --git a/winsup/cygwin/testsuite/winsup.api/winsup.exp b/winsup/cygwin/testsuite/winsup.api/winsup.exp
new file mode 100644
index 00000000000..5c85cc00bea
--- /dev/null
+++ b/winsup/cygwin/testsuite/winsup.api/winsup.exp
@@ -0,0 +1,43 @@
+source "site.exp"
+
+if { ! [isnative] } {
+ verbose "skipping winsup.api because it's not native"
+ return
+}
+
+set rv ""
+
+proc ws_spawn {cmd args} {
+ global rv
+ verbose "running $cmd\n"
+ catch [eval "exec $cmd"] rv
+ verbose send "catchCode = $rv\n"
+}
+
+foreach src [glob -nocomplain $srcdir/$subdir/*.c $srcdir/$subdir/*/*.c] {
+ regsub "^$srcdir/$subdir/" $src "" testcase
+ regsub ".c$" $testcase "" base
+ regsub ".*/" $base "" basename
+ regsub "/" $base "-" base
+
+ if { [regexp "^xf-" $basename] } {
+ setup_xfail "*-*-*"
+ } else {
+ clear_xfail
+ }
+
+ ws_spawn "$CC $src $rootme/new-libcygwin.a -o $base.exe"
+ if { $rv != "" } {
+ verbose -log "$rv"
+ fail "$testcase (compile)"
+ } else {
+ ws_spawn "../cygrun ./$base.exe"
+ if { $rv != "" } {
+ verbose -log "$testcase: $rv"
+ fail "$testcase (execute)"
+ } else {
+ pass "$testcase"
+ file delete "$base.exe"
+ }
+ }
+}
diff --git a/winsup/doc/sites.texinfo b/winsup/doc/sites.texinfo
new file mode 100644
index 00000000000..adba7ad55fa
--- /dev/null
+++ b/winsup/doc/sites.texinfo
@@ -0,0 +1,83 @@
+@chapter Cygwin Resources on the Internet
+
+@section FTP Sites
+
+@itemize @bullet
+@item North America:
+@itemize @bullet
+@item Alberta: @file{ftp://ftp.reversion.ca/pub/mirrors/cygwin/}
+@item Arizona: @file{ftp://ftp.ninemoons.com/pub/cygwin/}
+@item California: @file{ftp://ftp.yggdrasil.com/mirrors/site/sourceware.cygnus.com/pub/cygwin/}
+@item California (secondary): @file{ftp://sourceware.cygnus.com/pub/cygwin/}
+@item Kansas: @file{ftp://ftp.the-b.org/pub/cygwin/}
+@item Tennessee: @file{ftp://ftp.sunsite.utk.edu/pub/cygwin/}
+@end itemize
+
+@item Central America:
+@itemize @bullet
+@item Costa Rica: @file{ftp://sunsite.ulatina.ac.cr/cygwin/}
+@end itemize
+
+@item South America:
+@itemize @bullet
+@item Brazil: @file{ftp://ftp.unicamp.br/pub/gnu/=EXTRA=/cygnus/cygwin/}
+@end itemize
+
+@item Africa:
+@itemize @bullet
+@item South Africa: @file{ftp://ftp.sun.ac.za/sites/sourceware.cygnus.com/pub/cygwin/}
+@end itemize
+
+@item Asia:
+@itemize @bullet
+@item Japan: @file{ftp://ring.aist.go.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.etl.go.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.asahi-net.or.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.crl.go.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.astem.or.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.jah.ne.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.saitama-u.ac.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.nacsis.ac.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.exp.fujixerox.co.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.so-net.ne.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://ring.ip-kyoto.ad.jp/archives/pc/gnu-win32/}
+@item Japan: @file{ftp://sysg.kek.jp/cygnus/cygwin/}
+@item Japan: @file{ftp://ftp.u-aizu.ac.jp/pub/gnu/gnu-win32/}
+@item Taiwan: @file{ftp://ftp1.sinica.edu.tw/pub3/CYGNUS/cygwin/}
+@end itemize
+
+@item Australasia:
+@itemize @bullet
+@item Australia: @file{ftp://mirror.aarnet.edu.au/pub/cygwin/}
+@end itemize
+
+@item Europe:
+@itemize @bullet
+@item Austria: @file{ftp://gd.tuwien.ac.at/gnu/cygwin/}
+@item Czech Republic: @file{ftp://sunsite.ms.mff.cuni.cz/MIRRORS/sourceware.cygnus.com/pub/cygwin/}
+@item Denmark: @file{ftp://sunsite.auc.dk/pub/cygwin/}
+@item Finland: @file{ftp://ftp.funet.fi/mirrors/sourceware.cygnus.com/pub/cygwin/}
+@item Germany: @file{ftp://ftp.franken.de/pub/win32/develop/gnuwin32/cygwin32/mirrors/cygnus/}
+@item Greece: @file{ftp://ftp.ntua.gr/pub/pc/cygwin/}
+@item Hungary: @file{ftp://ftp.szrmkk.hu/pub/gnu-win32/ftp.cygnus.com/}
+@item Italy: @file{ftp://ftp.unina.it/pub/Unix/cygnus/cygwin/}
+@item Poland: @file{ftp://sunsite.icm.edu.pl/pub/cygnus/cygwin/}
+@item Slovenia: @file{ftp://sunsite.fri.uni-lj.si/pub/gnu-win32/}
+@item Spain: @file{ftp://ftp.rediris.es/mirror/cygwin}
+@item Sweden: @file{ftp://ftp.sunet.se/pub/lang/cygwin/}
+@item Switzerland: @file{ftp://sunsite.cnlab-switch.ch/mirror/cygwin/}
+@item UK: @file{ftp://sunsite.org.uk/Mirrors/sourceware.cygnus.com/pub/cygwin/}
+@item UK: @file{ftp://ftp.ccp14.dl.ac.uk/ccp14/ftp-mirror/programming/cygnus-gnu-win32/pub/gnu-win32/}
+@end itemize
+@end itemize
+
+@section The Cygwin Project WWW Site
+
+The main WWW page for the Cygwin project is
+@file{http://sourceware.cygnus.com/cygwin/}.
+
+A page containing tool-specific information is
+@file{http://www.cygnus.com/pubs/gnupro/}.
+
+Links to additional documentation are accessible from the main
+web page.
diff --git a/winsup/mingw/dirent.c b/winsup/mingw/dirent.c
new file mode 100644
index 00000000000..00041d76ef5
--- /dev/null
+++ b/winsup/mingw/dirent.c
@@ -0,0 +1,313 @@
+/*
+ * dirent.c
+ *
+ * Derived from DIRLIB.C by Matt J. Weinstein
+ * This note appears in the DIRLIB.H
+ * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89
+ *
+ * Updated by Jeremy Bettis <jeremy@hksys.com>
+ * Significantly revised and rewinddir, seekdir and telldir added by Colin
+ * Peters <colin@fu.is.saga-u.ac.jp>
+ *
+ * $Revision$
+ * $Author$
+ * $Date$
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <io.h>
+#include <direct.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+
+#define SUFFIX "*"
+#define SLASH "\\"
+
+/*
+ * opendir
+ *
+ * Returns a pointer to a DIR structure appropriately filled in to begin
+ * searching a directory.
+ */
+DIR *
+opendir (const char *szPath)
+{
+ DIR *nd;
+ struct _stat statDir;
+
+ errno = 0;
+
+ if (!szPath)
+ {
+ errno = EFAULT;
+ return (DIR *) 0;
+ }
+
+ if (szPath[0] == '\0')
+ {
+ errno = ENOTDIR;
+ return (DIR *) 0;
+ }
+
+ /* Attempt to determine if the given path really is a directory. */
+ if (_stat (szPath, &statDir))
+ {
+ /* Error, stat should have set an error value. */
+ return (DIR *) 0;
+ }
+
+ if (!S_ISDIR (statDir.st_mode))
+ {
+ /* Error, stat reports not a directory. */
+ errno = ENOTDIR;
+ return (DIR *) 0;
+ }
+
+ /* Allocate enough space to store DIR structure and the complete
+ * directory path given. */
+ nd = (DIR *) malloc (sizeof (DIR) + strlen (szPath) + strlen (SLASH) +
+ strlen (SUFFIX));
+
+ if (!nd)
+ {
+ /* Error, out of memory. */
+ errno = ENOMEM;
+ return (DIR *) 0;
+ }
+
+ /* Create the search expression. */
+ strcpy (nd->dd_name, szPath);
+
+ /* Add on a slash if the path does not end with one. */
+ if (nd->dd_name[0] != '\0' &&
+ nd->dd_name[strlen (nd->dd_name) - 1] != '/' &&
+ nd->dd_name[strlen (nd->dd_name) - 1] != '\\')
+ {
+ strcat (nd->dd_name, SLASH);
+ }
+
+ /* Add on the search pattern */
+ strcat (nd->dd_name, SUFFIX);
+
+ /* Initialize handle to -1 so that a premature closedir doesn't try
+ * to call _findclose on it. */
+ nd->dd_handle = -1;
+
+ /* Initialize the status. */
+ nd->dd_stat = 0;
+
+ /* Initialize the dirent structure. ino and reclen are invalid under
+ * Win32, and name simply points at the appropriate part of the
+ * findfirst_t structure. */
+ nd->dd_dir.d_ino = 0;
+ nd->dd_dir.d_reclen = 0;
+ nd->dd_dir.d_namlen = 0;
+ nd->dd_dir.d_name = nd->dd_dta.name;
+
+ return nd;
+}
+
+
+/*
+ * readdir
+ *
+ * Return a pointer to a dirent structure filled with the information on the
+ * next entry in the directory.
+ */
+struct dirent *
+readdir (DIR * dirp)
+{
+ errno = 0;
+
+ /* Check for valid DIR struct. */
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return (struct dirent *) 0;
+ }
+
+ if (dirp->dd_dir.d_name != dirp->dd_dta.name)
+ {
+ /* The structure does not seem to be set up correctly. */
+ errno = EINVAL;
+ return (struct dirent *) 0;
+ }
+
+ if (dirp->dd_stat < 0)
+ {
+ /* We have already returned all files in the directory
+ * (or the structure has an invalid dd_stat). */
+ return (struct dirent *) 0;
+ }
+ else if (dirp->dd_stat == 0)
+ {
+ /* We haven't started the search yet. */
+ /* Start the search */
+ dirp->dd_handle = _findfirst (dirp->dd_name, &(dirp->dd_dta));
+
+ if (dirp->dd_handle == -1)
+ {
+ /* Whoops! Seems there are no files in that
+ * directory. */
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ dirp->dd_stat = 1;
+ }
+ }
+ else
+ {
+ /* Get the next search entry. */
+ if (_findnext (dirp->dd_handle, &(dirp->dd_dta)))
+ {
+ /* We are off the end or otherwise error. */
+ _findclose (dirp->dd_handle);
+ dirp->dd_handle = -1;
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ /* Update the status to indicate the correct
+ * number. */
+ dirp->dd_stat++;
+ }
+ }
+
+ if (dirp->dd_stat > 0)
+ {
+ /* Successfully got an entry. Everything about the file is
+ * already appropriately filled in except the length of the
+ * file name. */
+ dirp->dd_dir.d_namlen = strlen (dirp->dd_dir.d_name);
+ return &dirp->dd_dir;
+ }
+
+ return (struct dirent *) 0;
+}
+
+
+/*
+ * closedir
+ *
+ * Frees up resources allocated by opendir.
+ */
+int
+closedir (DIR * dirp)
+{
+ int rc;
+
+ errno = 0;
+ rc = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (dirp->dd_handle != -1)
+ {
+ rc = _findclose (dirp->dd_handle);
+ }
+
+ /* Delete the dir structure. */
+ free (dirp);
+
+ return rc;
+}
+
+/*
+ * rewinddir
+ *
+ * Return to the beginning of the directory "stream". We simply call findclose
+ * and then reset things like an opendir.
+ */
+void
+rewinddir (DIR * dirp)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return;
+ }
+
+ if (dirp->dd_handle != -1)
+ {
+ _findclose (dirp->dd_handle);
+ }
+
+ dirp->dd_handle = -1;
+ dirp->dd_stat = 0;
+}
+
+/*
+ * telldir
+ *
+ * Returns the "position" in the "directory stream" which can be used with
+ * seekdir to go back to an old entry. We simply return the value in stat.
+ */
+long
+telldir (DIR * dirp)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+ return dirp->dd_stat;
+}
+
+/*
+ * seekdir
+ *
+ * Seek to an entry previously returned by telldir. We rewind the directory
+ * and call readdir repeatedly until either dd_stat is the position number
+ * or -1 (off the end). This is not perfect, in that the directory may
+ * have changed while we weren't looking. But that is probably the case with
+ * any such system.
+ */
+void
+seekdir (DIR * dirp, long lPos)
+{
+ errno = 0;
+
+ if (!dirp)
+ {
+ errno = EFAULT;
+ return;
+ }
+
+ if (lPos < -1)
+ {
+ /* Seeking to an invalid position. */
+ errno = EINVAL;
+ return;
+ }
+ else if (lPos == -1)
+ {
+ /* Seek past end. */
+ if (dirp->dd_handle != -1)
+ {
+ _findclose (dirp->dd_handle);
+ }
+ dirp->dd_handle = -1;
+ dirp->dd_stat = -1;
+ }
+ else
+ {
+ /* Rewind and read forward to the appropriate index. */
+ rewinddir (dirp);
+
+ while ((dirp->dd_stat < lPos) && readdir (dirp))
+ ;
+ }
+}
diff --git a/winsup/mingw/profile/ChangeLog b/winsup/mingw/profile/ChangeLog
new file mode 100644
index 00000000000..d6e94e8c78f
--- /dev/null
+++ b/winsup/mingw/profile/ChangeLog
@@ -0,0 +1,25 @@
+Thu Nov 18 00:20:00 1999 Mumit Khan <khan@xraylith.wisc.edu>
+
+ * profil.c (profile_on): Set the profiler thread priority to
+ be time critical. Thanks to Pascal Obry <pascal_obry@csi.com>.
+
+Sun Nov 7 04:17:27 1999 Mumit Khan <khan@xraylith.wisc.edu>
+
+ * Makefile.in (install): Fix target.
+
+Thu Nov 4 14:06:21 1999 Mumit Khan <khan@xraylith.wisc.edu>
+
+ * Makefile.in: New file.
+ * configure.in: New file.
+ * configure: Generate.
+
+ * gcrt0.c (u_char, u_short, u_int, u_long): typedef for Mingw.
+ * gmon.h (u_char, u_short, u_int, u_long): Likewise.
+ * gmon.c (unistd.h): Include conditionally.
+ (sys/param.h): Likewise.
+ * mcount.c (sys/param.h): Likewise.
+ * profil.c (profile_on): thread id is DWORD, not int.
+
+ * Imported profiling sources from winsup-19991026 snapshot.
+
+
diff --git a/winsup/utils/cygwin.cc b/winsup/utils/cygwin.cc
new file mode 100644
index 00000000000..1fba787fe5b
--- /dev/null
+++ b/winsup/utils/cygwin.cc
@@ -0,0 +1,126 @@
+/* cygwin.cc: general system debugging tool.
+
+ Copyright 1996, 1998 Cygnus Solutions.
+
+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. */
+
+/* This program is intended to be a general tool for debugging cygwin.
+ Possibilities include
+ - dumping various internal data structures
+ - poking various values into system tables
+ - turning on strace'ing for arbitrary tasks
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <windows.h>
+#include "winsup.h"
+
+static char *prog_name;
+
+static void
+usage (FILE *stream, int status)
+{
+ fprintf (stream, "\
+Usage: %s \\\n\
+ [-s|--strace pid mask]\\\n\
+ [-H|--help] [-V|--version]\n\
+",
+ prog_name);
+ exit (status);
+}
+
+static struct option long_options[] =
+{
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'H' },
+ { "strace", required_argument, NULL, 's' },
+ { 0, no_argument, 0, 0 }
+};
+
+struct strace_args
+{
+ int pid;
+ int mask;
+ char *fn;
+};
+
+/* Turn on strace'ing for the indicated pid. */
+
+static void
+set_strace (strace_args *args)
+{
+ shared_info *s = cygwin_getshared ();
+
+ pinfo *p = s->p[args->pid];
+
+ if (!p)
+ {
+ fprintf (stderr, "%s: process %d not found\n", prog_name, args->pid);
+ exit (1);
+ }
+
+ p->strace_mask = args->mask;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int c;
+ int seen_flag_p = 0;
+ int show_version_p = 0;
+ int set_strace_p = 0;
+ strace_args strace_args;
+
+ prog_name = strrchr (argv[0], '/');
+ if (prog_name == NULL)
+ prog_name = strrchr (argv[0], '\\');
+ if (prog_name == NULL)
+ prog_name = argv[0];
+
+ while ((c = getopt_long (argc, argv, "HVs:", long_options, (int *) 0))
+ != EOF)
+ {
+ seen_flag_p = 1;
+
+ switch (c)
+ {
+ case 'H':
+ usage (stdout, 0);
+ break;
+ case 'V':
+ show_version_p = 1;
+ break;
+ case 's':
+ if (optind + 1 > argc)
+ usage (stderr, 1);
+ strace_args.pid = atoi (optarg);
+ if (optind < argc)
+ strace_args.mask = atoi (argv[optind++]);
+ if (optind < argc)
+ strace_args.fn = argv[optind++];
+ set_strace_p = 1;
+ break;
+ default:
+ usage (stderr, 1);
+ break;
+ }
+ }
+
+ if (show_version_p)
+ printf ("CYGWIN version ???\n");
+
+ if (!seen_flag_p || optind != argc)
+ usage (stderr, 1);
+
+ if (set_strace_p)
+ set_strace (&strace_args);
+
+ return 0;
+}
diff --git a/winsup/w32api/README b/winsup/w32api/README
new file mode 100644
index 00000000000..9868968be53
--- /dev/null
+++ b/winsup/w32api/README
@@ -0,0 +1,91 @@
+ Free headers and libraries for the Win32 API
+
+ Written by Anders Norlander
+ Send bug reports and questions to anorland@hem2.passagen.se
+ URL: http://www.acc.umu.se/~anorland/gnu-win32/
+
+* License
+
+ You are free to use, modify and copy this package. No restrictions
+ are imposed on programs or object files compiled with this library.
+
+ You may not restrict the the usage of this library.
+
+ You may distribute this library as part of another package or as a
+ modified package if and only if you do *not* restrict the usage of
+ the portions consisting of this (optionally modified) library.
+
+ If distributed as part of another package, please notify the author
+ of what you are going to do. If distributed as a modified package,
+ this file *must* be included.
+
+ This library 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.
+
+* What is it?
+
+ This is a free set of headers and import libraries for the Win32
+ API. The library differs from the GNU Windows32 library in that I
+ have tried to use a file structure that mirrors Microsoft's. I
+ don't like having *all* definitions in one single header as in the
+ GNU Windows32 library, I want a clean separation between different
+ parts of the API.
+
+ Daniel Guerrero Miralles contributed the DirectX 6.1 import
+ libraries and DirectX GUID definitions.
+
+ See the files NOTES and TODO for what needs to be done.
+
+* Size does matter
+
+ Since the WIN32 API is severely bloated (as most MS products seem to
+ be) the headers are designed to be as compact as possible, while
+ still being readable, in order to minimize parsing time.
+
+ The convention is to omit parameter names for function prototypes,
+ no excessive white space. Struct/union members are indented with tab
+ characters to make them readable. Comment only when necessary.
+
+ If you are contributing a patch please follow the above mentioned
+ convention. Make sure your editor does not convert tabs to spaces.
+
+* What do I need to use it?
+
+ The library is intended for use with egcs 1.1 or later but it is
+ possible to use with some other tools as well (although it is not
+ very useful). LCC-Win32, MSVC and Borland C++ 5.01 or higher may
+ work as well. The import libraries are for GNU tools only.
+
+ The library requires egcs 1.1 or later, since the `#pragma pack'
+ feature is used. Mumit Khan provides egcs patches and binaries for
+ win32 at `http://www.xraylith.wisc.edu/~khan/software/gnu-win32/'.
+
+ If you are going to use C++ COM objects, you will need a version of
+ egcs that recognizes the `comobject' attribute and then define
+ HAVE_COMOBJECT when compiling your program. Antonio Mendes de
+ Oliveira Neto has a prebuilt version at
+ `http://li.facens.br/EGCS-WIN32/english/index.html'. Note that this
+ is very experimental. If you want to use COM objects in C++ but with
+ C interfaces you must define CINTERFACE.
+
+ Objective-C programs cannot use COM functionality because of
+ conflicts between the interface define and the Objective-C
+ @interface directive. There is also a conflict between the windows
+ Obj-C BOOL types. To avoid this conflict you should use WINBOOL in
+ all places where you would use BOOL in a C/C++ windows program. If
+ you include any windows headers *after* `windows.h' you must use the
+ method outlined below:
+
+ /* non-windows includes */
+ #include <objc/objc.h>
+ ...
+ /* windows specific headers */
+ #include <windows.h>
+ #define BOOL WINBOOL
+ #include <commctrl.h>
+ ...
+ #undef BOOL
+ ...
+ /* include other headers */
+
diff --git a/winsup/w32api/include/excpt.h b/winsup/w32api/include/excpt.h
new file mode 100644
index 00000000000..39621a8f605
--- /dev/null
+++ b/winsup/w32api/include/excpt.h
@@ -0,0 +1,17 @@
+#ifndef _EXCPT_H
+#define _EXCPT_H
+
+/* FIXME: This will make some code compile. The programs will most
+ likely crash when an exception is raised, but at least they will
+ compile. */
+#ifdef __GNUC__
+#define __try
+#define __except(x) if (0) /* don't execute handler */
+#define __finally
+
+#define _try __try
+#define _except __except
+#define _finally __finally
+#endif
+
+#endif