#include <stdarg.h>
#include <float.h>
#include <shellapi.h>
+#include <process.h>
#define perl
#include "celib_defs.h"
#include "cewin32_defs.h"
#include "cecrt_defs.h"
+#define GetCurrentDirectoryW XCEGetCurrentDirectoryW
+
#ifdef PALM_SIZE
#include "stdio-palmsize.h"
#endif
# define getlogin g_getlogin
#endif
+static void get_shell(void);
+static long tokenize(const char *str, char **dest, char ***destv);
+static int do_spawn2(pTHX_ char *cmd, int exectype);
+static BOOL has_shell_metachars(char *ptr);
static long filetime_to_clock(PFILETIME ft);
static BOOL filetime_from_time(PFILETIME ft, time_t t);
static char * get_emd_part(SV **leading, char *trailing, ...);
+static void remove_dead_process(long deceased);
+static long find_pid(int pid);
+static char * qualified_path(const char *cmd);
static char * win32_get_xlib(const char *pl, const char *xlib,
const char *libname);
+#ifdef USE_ITHREADS
+static void remove_dead_pseudo_process(long child);
+static long find_pseudo_pid(int pid);
+#endif
+
+int _fmode = O_TEXT; /* celib do not provide _fmode, so we define it here */
+
START_EXTERN_C
HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
char w32_module_name[MAX_PATH+1];
return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME);
}
+static BOOL
+has_shell_metachars(char *ptr)
+{
+ int inquote = 0;
+ char quote = '\0';
+
+ /*
+ * Scan string looking for redirection (< or >) or pipe
+ * characters (|) that are not in a quoted string.
+ * Shell variable interpolation (%VAR%) can also happen inside strings.
+ */
+ while (*ptr) {
+ switch(*ptr) {
+ case '%':
+ return TRUE;
+ case '\'':
+ case '\"':
+ if (inquote) {
+ if (quote == *ptr) {
+ inquote = 0;
+ quote = '\0';
+ }
+ }
+ else {
+ quote = *ptr;
+ inquote++;
+ }
+ break;
+ case '>':
+ case '<':
+ case '|':
+ if (!inquote)
+ return TRUE;
+ default:
+ break;
+ }
+ ++ptr;
+ }
+ return FALSE;
+}
+
#if !defined(PERL_IMPLICIT_SYS)
/* since the current process environment is being updated in util.c
* the library functions will get the correct environment
DllExport int
win32_getpid(void)
{
- return xcegetpid();
+ int pid;
+#ifdef USE_ITHREADS
+ dTHX;
+ if (w32_pseudo_id)
+ return -((int)w32_pseudo_id);
+#endif
+ pid = xcegetpid();
+ return pid;
}
-bool
-Perl_do_exec(pTHX_ char *cmd)
+/* Tokenize a string. Words are null-separated, and the list
+ * ends with a doubled null. Any character (except null and
+ * including backslash) may be escaped by preceding it with a
+ * backslash (the backslash will be stripped).
+ * Returns number of words in result buffer.
+ */
+static long
+tokenize(const char *str, char **dest, char ***destv)
{
- Perl_croak_nocontext("exec() unimplemented on this platform");
- return FALSE;
+ char *retstart = Nullch;
+ char **retvstart = 0;
+ int items = -1;
+ if (str) {
+ dTHX;
+ int slen = strlen(str);
+ register char *ret;
+ register char **retv;
+ New(1307, ret, slen+2, char);
+ New(1308, retv, (slen+3)/2, char*);
+
+ retstart = ret;
+ retvstart = retv;
+ *retv = ret;
+ items = 0;
+ while (*str) {
+ *ret = *str++;
+ if (*ret == '\\' && *str)
+ *ret = *str++;
+ else if (*ret == ' ') {
+ while (*str == ' ')
+ str++;
+ if (ret == retstart)
+ ret--;
+ else {
+ *ret = '\0';
+ ++items;
+ if (*str)
+ *++retv = ret+1;
+ }
+ }
+ else if (!*str)
+ ++items;
+ ret++;
+ }
+ retvstart[items] = Nullch;
+ *ret++ = '\0';
+ *ret = '\0';
+ }
+ *dest = retstart;
+ *destv = retvstart;
+ return items;
}
DllExport int
return FALSE;
}
+static void
+get_shell(void)
+{
+ dTHX;
+ if (!w32_perlshell_tokens) {
+ /* we don't use COMSPEC here for two reasons:
+ * 1. the same reason perl on UNIX doesn't use SHELL--rampant and
+ * uncontrolled unportability of the ensuing scripts.
+ * 2. PERL5SHELL could be set to a shell that may not be fit for
+ * interactive use (which is what most programs look in COMSPEC
+ * for).
+ */
+ const char* defaultshell = (IsWinNT()
+ ? "cmd.exe /x/d/c" : "command.com /c");
+ const char *usershell = PerlEnv_getenv("PERL5SHELL");
+ w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
+ &w32_perlshell_tokens,
+ &w32_perlshell_vec);
+ }
+}
-DllExport char ***
-win32_environ(void)
+int
+Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
{
- return (&(environ));
+ Perl_croak(aTHX_ PL_no_func, "aspawn");
+ return -1;
+}
+
+/* returns pointer to the next unquoted space or the end of the string */
+static char*
+find_next_space(const char *s)
+{
+ bool in_quotes = FALSE;
+ while (*s) {
+ /* ignore doubled backslashes, or backslash+quote */
+ if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
+ s += 2;
+ }
+ /* keep track of when we're within quotes */
+ else if (*s == '"') {
+ s++;
+ in_quotes = !in_quotes;
+ }
+ /* break it up only at spaces that aren't in quotes */
+ else if (!in_quotes && isSPACE(*s))
+ return (char*)s;
+ else
+ s++;
+ }
+ return (char*)s;
+}
+
+#if 1
+static int
+do_spawn2(pTHX_ char *cmd, int exectype)
+{
+ char **a;
+ char *s;
+ char **argv;
+ int status = -1;
+ BOOL needToTry = TRUE;
+ char *cmd2;
+
+ /* Save an extra exec if possible. See if there are shell
+ * metacharacters in it */
+ if (!has_shell_metachars(cmd)) {
+ New(1301,argv, strlen(cmd) / 2 + 2, char*);
+ New(1302,cmd2, strlen(cmd) + 1, char);
+ strcpy(cmd2, cmd);
+ a = argv;
+ for (s = cmd2; *s;) {
+ while (*s && isSPACE(*s))
+ s++;
+ if (*s)
+ *(a++) = s;
+ s = find_next_space(s);
+ if (*s)
+ *s++ = '\0';
+ }
+ *a = Nullch;
+ if (argv[0]) {
+ switch (exectype) {
+ case EXECF_SPAWN:
+ status = win32_spawnvp(P_WAIT, argv[0],
+ (const char* const*)argv);
+ break;
+ case EXECF_SPAWN_NOWAIT:
+ status = win32_spawnvp(P_NOWAIT, argv[0],
+ (const char* const*)argv);
+ break;
+ case EXECF_EXEC:
+ status = win32_execvp(argv[0], (const char* const*)argv);
+ break;
+ }
+ if (status != -1 || errno == 0)
+ needToTry = FALSE;
+ }
+ Safefree(argv);
+ Safefree(cmd2);
+ }
+ if (needToTry) {
+ char **argv;
+ int i = -1;
+ get_shell();
+ New(1306, argv, w32_perlshell_items + 2, char*);
+ while (++i < w32_perlshell_items)
+ argv[i] = w32_perlshell_vec[i];
+ argv[i++] = cmd;
+ argv[i] = Nullch;
+ switch (exectype) {
+ case EXECF_SPAWN:
+ status = win32_spawnvp(P_WAIT, argv[0],
+ (const char* const*)argv);
+ break;
+ case EXECF_SPAWN_NOWAIT:
+ status = win32_spawnvp(P_NOWAIT, argv[0],
+ (const char* const*)argv);
+ break;
+ case EXECF_EXEC:
+ status = win32_execvp(argv[0], (const char* const*)argv);
+ break;
+ }
+ cmd = argv[0];
+ Safefree(argv);
+ }
+ if (exectype == EXECF_SPAWN_NOWAIT) {
+ if (IsWin95())
+ PL_statusvalue = -1; /* >16bits hint for pp_system() */
+ }
+ else {
+ if (status < 0) {
+ if (ckWARN(WARN_EXEC))
+ Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't %s \"%s\": %s",
+ (exectype == EXECF_EXEC ? "exec" : "spawn"),
+ cmd, strerror(errno));
+ status = 255 * 256;
+ }
+ else
+ status *= 256;
+ PL_statusvalue = status;
+ }
+ return (status);
+}
+
+int
+Perl_do_spawn(pTHX_ char *cmd)
+{
+ return do_spawn2(aTHX_ cmd, EXECF_SPAWN);
+}
+
+int
+Perl_do_spawn_nowait(pTHX_ char *cmd)
+{
+ return do_spawn2(aTHX_ cmd, EXECF_SPAWN_NOWAIT);
+}
+
+bool
+Perl_do_exec(pTHX_ char *cmd)
+{
+ do_spawn2(aTHX_ cmd, EXECF_EXEC);
+ return FALSE;
+}
+
+/* The idea here is to read all the directory names into a string table
+ * (separated by nulls) and when one of the other dir functions is called
+ * return the pointer to the current file name.
+ */
+DllExport DIR *
+win32_opendir(char *filename)
+{
+ dTHX;
+ DIR *dirp;
+ long len;
+ long idx;
+ char scanname[MAX_PATH+3];
+ Stat_t sbuf;
+ WIN32_FIND_DATAA aFindData;
+ WIN32_FIND_DATAW wFindData;
+ HANDLE fh;
+ char buffer[MAX_PATH*2];
+ WCHAR wbuffer[MAX_PATH+1];
+ char* ptr;
+
+ len = strlen(filename);
+ if (len > MAX_PATH)
+ return NULL;
+
+ /* check to see if filename is a directory */
+ if (win32_stat(filename, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
+ return NULL;
+
+ /* Get us a DIR structure */
+ Newz(1303, dirp, 1, DIR);
+
+ /* Create the search pattern */
+ strcpy(scanname, filename);
+
+ /* bare drive name means look in cwd for drive */
+ if (len == 2 && isALPHA(scanname[0]) && scanname[1] == ':') {
+ scanname[len++] = '.';
+ scanname[len++] = '/';
+ }
+ else if (scanname[len-1] != '/' && scanname[len-1] != '\\') {
+ scanname[len++] = '/';
+ }
+ scanname[len++] = '*';
+ scanname[len] = '\0';
+
+ /* do the FindFirstFile call */
+ fh = FindFirstFile(PerlDir_mapA(scanname), &aFindData);
+ dirp->handle = fh;
+ if (fh == INVALID_HANDLE_VALUE) {
+ DWORD err = GetLastError();
+ /* FindFirstFile() fails on empty drives! */
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ return dirp;
+ case ERROR_NO_MORE_FILES:
+ case ERROR_PATH_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ errno = ENOMEM;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ Safefree(dirp);
+ return NULL;
+ }
+
+ /* now allocate the first part of the string table for
+ * the filenames that we find.
+ */
+ ptr = aFindData.cFileName;
+ idx = strlen(ptr)+1;
+ if (idx < 256)
+ dirp->size = 128;
+ else
+ dirp->size = idx;
+ New(1304, dirp->start, dirp->size, char);
+ strcpy(dirp->start, ptr);
+ dirp->nfiles++;
+ dirp->end = dirp->curr = dirp->start;
+ dirp->end += idx;
+ return dirp;
+}
+
+
+/* Readdir just returns the current string pointer and bumps the
+ * string pointer to the nDllExport entry.
+ */
+DllExport struct direct *
+win32_readdir(DIR *dirp)
+{
+ long len;
+
+ if (dirp->curr) {
+ /* first set up the structure to return */
+ len = strlen(dirp->curr);
+ strcpy(dirp->dirstr.d_name, dirp->curr);
+ dirp->dirstr.d_namlen = len;
+
+ /* Fake an inode */
+ dirp->dirstr.d_ino = dirp->curr - dirp->start;
+
+ /* Now set up for the next call to readdir */
+ dirp->curr += len + 1;
+ if (dirp->curr >= dirp->end) {
+ dTHX;
+ char* ptr;
+ BOOL res;
+ WIN32_FIND_DATAW wFindData;
+ WIN32_FIND_DATAA aFindData;
+ char buffer[MAX_PATH*2];
+
+ /* finding the next file that matches the wildcard
+ * (which should be all of them in this directory!).
+ */
+ res = FindNextFile(dirp->handle, &aFindData);
+ if (res)
+ ptr = aFindData.cFileName;
+ if (res) {
+ long endpos = dirp->end - dirp->start;
+ long newsize = endpos + strlen(ptr) + 1;
+ /* bump the string table size by enough for the
+ * new name and its null terminator */
+ while (newsize > dirp->size) {
+ long curpos = dirp->curr - dirp->start;
+ dirp->size *= 2;
+ Renew(dirp->start, dirp->size, char);
+ dirp->curr = dirp->start + curpos;
+ }
+ strcpy(dirp->start + endpos, ptr);
+ dirp->end = dirp->start + newsize;
+ dirp->nfiles++;
+ }
+ else
+ dirp->curr = NULL;
+ }
+ return &(dirp->dirstr);
+ }
+ else
+ return NULL;
+}
+
+/* Telldir returns the current string pointer position */
+DllExport long
+win32_telldir(DIR *dirp)
+{
+ return (dirp->curr - dirp->start);
+}
+
+
+/* Seekdir moves the string pointer to a previously saved position
+ * (returned by telldir).
+ */
+DllExport void
+win32_seekdir(DIR *dirp, long loc)
+{
+ dirp->curr = dirp->start + loc;
+}
+
+/* Rewinddir resets the string pointer to the start */
+DllExport void
+win32_rewinddir(DIR *dirp)
+{
+ dirp->curr = dirp->start;
+}
+
+/* free the memory allocated by opendir */
+DllExport int
+win32_closedir(DIR *dirp)
+{
+ dTHX;
+ if (dirp->handle != INVALID_HANDLE_VALUE)
+ FindClose(dirp->handle);
+ Safefree(dirp->start);
+ Safefree(dirp);
+ return 1;
}
+#else
+/////!!!!!!!!!!! return here and do right stuff!!!!
+
DllExport DIR *
win32_opendir(char *filename)
{
closedir(dirp);
return 0;
}
+#endif // 1
DllExport int
win32_kill(int pid, int sig)
return -1;
}
-DllExport unsigned int
-win32_sleep(unsigned int t)
-{
- return xcesleep(t);
-}
-
DllExport int
win32_stat(const char *path, struct stat *sbuf)
{
return 0;
}
+/* Timing related stuff */
+
+int
+do_raise(pTHX_ int sig)
+{
+ if (sig < SIG_SIZE) {
+ Sighandler_t handler = w32_sighandler[sig];
+ if (handler == SIG_IGN) {
+ return 0;
+ }
+ else if (handler != SIG_DFL) {
+ (*handler)(sig);
+ return 0;
+ }
+ else {
+ /* Choose correct default behaviour */
+ switch (sig) {
+#ifdef SIGCLD
+ case SIGCLD:
+#endif
+#ifdef SIGCHLD
+ case SIGCHLD:
+#endif
+ case 0:
+ return 0;
+ case SIGTERM:
+ default:
+ break;
+ }
+ }
+ }
+ /* Tell caller to exit thread/process as approriate */
+ return 1;
+}
+
void
sig_terminate(pTHX_ int sig)
{
- Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
+ Perl_warn(aTHX_ "Terminating on signal SIG%s(%d)\n",PL_sig_name[sig], sig);
/* exit() seems to be safe, my_exit() or die() is a problem in ^C
thread
*/
exit(sig);
}
-void
DllExport int
win32_async_check(pTHX)
{
goto Raise;
break;
#endif
+
/* We use WM_USER to fake kill() with other signals */
case WM_USER: {
sig = msg.wParam;
return (ticks > 0) ? ticks : 0;
}
-/* Timing related stuff */
+static UINT timerid = 0;
-int
-do_raise(pTHX_ int sig)
+static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
{
- if (sig < SIG_SIZE) {
- Sighandler_t handler = w32_sighandler[sig];
- if (handler == SIG_IGN) {
- return 0;
- }
- else if (handler != SIG_DFL) {
- (*handler)(sig);
- return 0;
- }
- else {
- /* Choose correct default behaviour */
- switch (sig) {
-#ifdef SIGCLD
- case SIGCLD:
-#endif
-#ifdef SIGCHLD
- case SIGCHLD:
-#endif
- case 0:
- return 0;
- case SIGTERM:
- default:
- break;
- }
- }
- }
- /* Tell caller to exit thread/process as approriate */
- return 1;
+ dTHX;
+ KillTimer(NULL,timerid);
+ timerid=0;
+ sighandler(14);
}
-/* Timing related stuff */
+DllExport unsigned int
+win32_sleep(unsigned int t)
+{
+ return xcesleep(t);
+}
-int
-do_raise(pTHX_ int sig)
-{
- if (sig < SIG_SIZE) {
- Sighandler_t handler = w32_sighandler[sig];
- if (handler == SIG_IGN) {
- return 0;
- }
- else if (handler != SIG_DFL) {
- (*handler)(sig);
- return 0;
- }
- else {
- /* Choose correct default behaviour */
- switch (sig) {
-#ifdef SIGCLD
- case SIGCLD:
-#endif
-#ifdef SIGCHLD
- case SIGCHLD:
-#endif
- case 0:
- return 0;
- case SIGTERM:
- default:
- break;
- }
- }
- }
- /* Tell caller to exit thread/process as approriate */
- return 1;
-}
-
-static UINT timerid = 0;
-
-static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
-{
- dTHX;
- KillTimer(NULL,timerid);
- timerid=0;
- sighandler(14);
-}
-
-DllExport unsigned int
-win32_alarm(unsigned int sec)
+DllExport unsigned int
+win32_alarm(unsigned int sec)
{
/*
* the 'obvious' implentation is SetTimer() with a callback
#endif
}
-/* C doesn't like repeat struct definitions */
-
-#if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-/*
- * Control structure for lowio file handles
- */
-typedef struct {
- long osfhnd; /* underlying OS file HANDLE */
- char osfile; /* attributes of file (e.g., open in text mode?) */
- char pipech; /* one char buffer for handles opened on pipes */
- int lockinitflag;
- CRITICAL_SECTION lock;
-} ioinfo;
-
-
-/*
- * Array of arrays of control structures for lowio files.
- */
-EXTERN_C _CRTIMP ioinfo* __pioinfo[];
-
-/*
- * Definition of IOINFO_L2E, the log base 2 of the number of elements in each
- * array of ioinfo structs.
- */
-#define IOINFO_L2E 5
-
-/*
- * Definition of IOINFO_ARRAY_ELTS, the number of elements in ioinfo array
- */
-#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
-
-/*
- * Access macros for getting at an ioinfo struct and its fields from a
- * file handle
- */
-#define _pioinfo(i) (__pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - 1)))
-#define _osfhnd(i) (_pioinfo(i)->osfhnd)
-#define _osfile(i) (_pioinfo(i)->osfile)
-#define _pipech(i) (_pioinfo(i)->pipech)
-
-#endif
/*
* redirected io subsystem for all XS modules
return (&errno);
}
+DllExport char ***
+win32_environ(void)
+{
+ return (&(environ));
+}
+
/* the rest are the remapped stdio routines */
DllExport FILE *
win32_stderr(void)
*/
DllExport char *
-win32_strerror(int e)
+win32_strerror(int e)
{
return xcestrerror(e);
}
}
DllExport int
-win32_fseek(FILE *pf,long offset,int origin)
+win32_fseek(FILE *pf, Off_t offset,int origin)
{
return fseek(pf, offset, origin);
}
}
DllExport long
-win32_lseek(int fd, long offset, int origin)
+win32_lseek(int fd, Off_t offset, int origin)
{
return xcelseek(fd, offset, origin);
}
return xcechmod(path, mode);
}
-DllExport void
-win32_perror(const char *str)
-{
- xceperror(str);
-}
-
-DllExport void
-win32_setbuf(FILE *pf, char *buf)
-{
- dTHX;
- Perl_croak(aTHX_ PL_no_func, "setbuf");
-}
-
-DllExport int
-win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
+static char *
+create_command_line(char *cname, STRLEN clen, const char * const *args)
{
- return setvbuf(pf, buf, type, size);
-}
+ dTHX;
+ int index, argc;
+ char *cmd, *ptr;
+ const char *arg;
+ STRLEN len = 0;
+ bool bat_file = FALSE;
+ bool cmd_shell = FALSE;
+ bool dumb_shell = FALSE;
+ bool extra_quotes = FALSE;
+ bool quote_next = FALSE;
-DllExport int
-win32_flushall(void)
-{
- return flushall();
-}
+ if (!cname)
+ cname = (char*)args[0];
-DllExport int
-win32_fcloseall(void)
-{
- return fcloseall();
-}
+ /* The NT cmd.exe shell has the following peculiarity that needs to be
+ * worked around. It strips a leading and trailing dquote when any
+ * of the following is true:
+ * 1. the /S switch was used
+ * 2. there are more than two dquotes
+ * 3. there is a special character from this set: &<>()@^|
+ * 4. no whitespace characters within the two dquotes
+ * 5. string between two dquotes isn't an executable file
+ * To work around this, we always add a leading and trailing dquote
+ * to the string, if the first argument is either "cmd.exe" or "cmd",
+ * and there were at least two or more arguments passed to cmd.exe
+ * (not including switches).
+ * XXX the above rules (from "cmd /?") don't seem to be applied
+ * always, making for the convolutions below :-(
+ */
+ if (cname) {
+ if (!clen)
+ clen = strlen(cname);
-DllExport char*
-win32_fgets(char *s, int n, FILE *pf)
-{
- return fgets(s, n, pf);
-}
+ if (clen > 4
+ && (stricmp(&cname[clen-4], ".bat") == 0
+ || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
+ {
+ bat_file = TRUE;
+ len += 3;
+ }
+ else {
+ char *exe = strrchr(cname, '/');
+ char *exe2 = strrchr(cname, '\\');
+ if (exe2 > exe)
+ exe = exe2;
+ if (exe)
+ ++exe;
+ else
+ exe = cname;
+ if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
+ cmd_shell = TRUE;
+ len += 3;
+ }
+ else if (stricmp(exe, "command.com") == 0
+ || stricmp(exe, "command") == 0)
+ {
+ dumb_shell = TRUE;
+ }
+ }
+ }
-DllExport char*
-win32_gets(char *s)
-{
- return gets(s);
-}
+ DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
+ for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
+ STRLEN curlen = strlen(arg);
+ if (!(arg[0] == '"' && arg[curlen-1] == '"'))
+ len += 2; /* assume quoting needed (worst case) */
+ len += curlen + 1;
+ DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
+ }
+ DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
-DllExport int
-win32_fgetc(FILE *pf)
-{
- return fgetc(pf);
-}
+ argc = index;
+ New(1310, cmd, len, char);
+ ptr = cmd;
-DllExport int
-win32_putc(int c, FILE *pf)
-{
- return putc(c,pf);
-}
+ if (bat_file) {
+ *ptr++ = '"';
+ extra_quotes = TRUE;
+ }
-DllExport int
-win32_puts(const char *s)
-{
- return puts(s);
-}
+ for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
+ bool do_quote = 0;
+ STRLEN curlen = strlen(arg);
-DllExport int
-win32_getchar(void)
-{
- return getchar();
-}
+ /* we want to protect empty arguments and ones with spaces with
+ * dquotes, but only if they aren't already there */
+ if (!dumb_shell) {
+ if (!curlen) {
+ do_quote = 1;
+ }
+ else if (quote_next) {
+ /* see if it really is multiple arguments pretending to
+ * be one and force a set of quotes around it */
+ if (*find_next_space(arg))
+ do_quote = 1;
+ }
+ else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
+ STRLEN i = 0;
+ while (i < curlen) {
+ if (isSPACE(arg[i])) {
+ do_quote = 1;
+ }
+ else if (arg[i] == '"') {
+ do_quote = 0;
+ break;
+ }
+ i++;
+ }
+ }
+ }
-DllExport int
-win32_putchar(int c)
-{
- return putchar(c);
-}
+ if (do_quote)
+ *ptr++ = '"';
-#ifdef MYMALLOC
+ strcpy(ptr, arg);
+ ptr += curlen;
-#ifndef USE_PERL_SBRK
+ if (do_quote)
+ *ptr++ = '"';
-static char *committed = NULL;
-static char *base = NULL;
-static char *reserved = NULL;
-static char *brk = NULL;
-static DWORD pagesize = 0;
-static DWORD allocsize = 0;
-
-void *
-sbrk(int need)
-{
- void *result;
- if (!pagesize)
- {SYSTEM_INFO info;
- GetSystemInfo(&info);
- /* Pretend page size is larger so we don't perpetually
- * call the OS to commit just one page ...
- */
- pagesize = info.dwPageSize << 3;
- allocsize = info.dwAllocationGranularity;
- }
- /* This scheme fails eventually if request for contiguous
- * block is denied so reserve big blocks - this is only
- * address space not memory ...
- */
- if (brk+need >= reserved)
- {
- DWORD size = 64*1024*1024;
- char *addr;
- if (committed && reserved && committed < reserved)
- {
- /* Commit last of previous chunk cannot span allocations */
- addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
- if (addr)
- committed = reserved;
- }
- /* Reserve some (more) space
- * Note this is a little sneaky, 1st call passes NULL as reserved
- * so lets system choose where we start, subsequent calls pass
- * the old end address so ask for a contiguous block
- */
- addr = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
- if (addr)
- {
- reserved = addr+size;
- if (!base)
- base = addr;
- if (!committed)
- committed = base;
- if (!brk)
- brk = committed;
- }
- else
- {
- return (void *) -1;
- }
- }
- result = brk;
- brk += need;
- if (brk > committed)
- {
- DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
- char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
- if (addr)
- {
- committed += size;
- }
- else
- return (void *) -1;
- }
- return result;
-}
-
-#endif
-#endif
-
-DllExport void*
-win32_malloc(size_t size)
-{
- return malloc(size);
-}
-
-DllExport void*
-win32_calloc(size_t numitems, size_t size)
-{
- return calloc(numitems,size);
-}
-
-DllExport void*
-win32_realloc(void *block, size_t size)
-{
- return realloc(block,size);
-}
-
-DllExport void
-win32_free(void *block)
-{
- free(block);
-}
-
-/* returns pointer to the next unquoted space or the end of the string */
-static char*
-find_next_space(const char *s)
-{
- bool in_quotes = FALSE;
- while (*s) {
- /* ignore doubled backslashes, or backslash+quote */
- if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) {
- s += 2;
- }
- /* keep track of when we're within quotes */
- else if (*s == '"') {
- s++;
- in_quotes = !in_quotes;
- }
- /* break it up only at spaces that aren't in quotes */
- else if (!in_quotes && isSPACE(*s))
- return (char*)s;
- else
- s++;
- }
- return (char*)s;
-}
-
-static char *
-create_command_line(char *cname, STRLEN clen, const char * const *args)
-{
- dTHX;
- int index, argc;
- char *cmd, *ptr;
- const char *arg;
- STRLEN len = 0;
- bool bat_file = FALSE;
- bool cmd_shell = FALSE;
- bool dumb_shell = FALSE;
- bool extra_quotes = FALSE;
- bool quote_next = FALSE;
-
- if (!cname)
- cname = (char*)args[0];
-
- /* The NT cmd.exe shell has the following peculiarity that needs to be
- * worked around. It strips a leading and trailing dquote when any
- * of the following is true:
- * 1. the /S switch was used
- * 2. there are more than two dquotes
- * 3. there is a special character from this set: &<>()@^|
- * 4. no whitespace characters within the two dquotes
- * 5. string between two dquotes isn't an executable file
- * To work around this, we always add a leading and trailing dquote
- * to the string, if the first argument is either "cmd.exe" or "cmd",
- * and there were at least two or more arguments passed to cmd.exe
- * (not including switches).
- * XXX the above rules (from "cmd /?") don't seem to be applied
- * always, making for the convolutions below :-(
- */
- if (cname) {
- if (!clen)
- clen = strlen(cname);
-
- if (clen > 4
- && (stricmp(&cname[clen-4], ".bat") == 0
- || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0)))
- {
- bat_file = TRUE;
- len += 3;
- }
- else {
- char *exe = strrchr(cname, '/');
- char *exe2 = strrchr(cname, '\\');
- if (exe2 > exe)
- exe = exe2;
- if (exe)
- ++exe;
- else
- exe = cname;
- if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) {
- cmd_shell = TRUE;
- len += 3;
- }
- else if (stricmp(exe, "command.com") == 0
- || stricmp(exe, "command") == 0)
- {
- dumb_shell = TRUE;
- }
- }
- }
-
- DEBUG_p(PerlIO_printf(Perl_debug_log, "Args "));
- for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
- STRLEN curlen = strlen(arg);
- if (!(arg[0] == '"' && arg[curlen-1] == '"'))
- len += 2; /* assume quoting needed (worst case) */
- len += curlen + 1;
- DEBUG_p(PerlIO_printf(Perl_debug_log, "[%s]",arg));
- }
- DEBUG_p(PerlIO_printf(Perl_debug_log, "\n"));
-
- argc = index;
- New(1310, cmd, len, char);
- ptr = cmd;
-
- if (bat_file) {
- *ptr++ = '"';
- extra_quotes = TRUE;
- }
-
- for (index = 0; (arg = (char*)args[index]) != NULL; ++index) {
- bool do_quote = 0;
- STRLEN curlen = strlen(arg);
-
- /* we want to protect empty arguments and ones with spaces with
- * dquotes, but only if they aren't already there */
- if (!dumb_shell) {
- if (!curlen) {
- do_quote = 1;
- }
- else if (quote_next) {
- /* see if it really is multiple arguments pretending to
- * be one and force a set of quotes around it */
- if (*find_next_space(arg))
- do_quote = 1;
- }
- else if (!(arg[0] == '"' && curlen > 1 && arg[curlen-1] == '"')) {
- STRLEN i = 0;
- while (i < curlen) {
- if (isSPACE(arg[i])) {
- do_quote = 1;
- }
- else if (arg[i] == '"') {
- do_quote = 0;
- break;
- }
- i++;
- }
- }
- }
-
- if (do_quote)
- *ptr++ = '"';
-
- strcpy(ptr, arg);
- ptr += curlen;
-
- if (do_quote)
- *ptr++ = '"';
-
- if (args[index+1])
- *ptr++ = ' ';
+ if (args[index+1])
+ *ptr++ = ' ';
if (!extra_quotes
&& cmd_shell
* and win32_spawnvp() could also be avoided.
*/
-#define P_WAIT 0
-#define P_NOWAIT 1
DllExport int
win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
{
&StartupInfo,
&ProcessInformation))
{
- /* initial NULL argument to CreateProcess() does a PATH
- * search, but it always first looks in the directory
- * where the current process was started, which behavior
- * is undesirable for backward compatibility. So we
- * jump through our own hoops by picking out the path
- * we really want it to use. */
- if (!fullcmd) {
- fullcmd = qualified_path(cname);
- if (fullcmd) {
- if (cname != cmdname)
- Safefree(cname);
- cname = fullcmd;
- DEBUG_p(PerlIO_printf(Perl_debug_log,
- "Retrying [%s] with same args\n",
- cname));
- goto RETRY;
- }
- }
- errno = ENOENT;
- ret = -1;
- goto RETVAL;
+ /* initial NULL argument to CreateProcess() does a PATH
+ * search, but it always first looks in the directory
+ * where the current process was started, which behavior
+ * is undesirable for backward compatibility. So we
+ * jump through our own hoops by picking out the path
+ * we really want it to use. */
+ if (!fullcmd) {
+ fullcmd = qualified_path(cname);
+ if (fullcmd) {
+ if (cname != cmdname)
+ Safefree(cname);
+ cname = fullcmd;
+ DEBUG_p(PerlIO_printf(Perl_debug_log,
+ "Retrying [%s] with same args\n",
+ cname));
+ goto RETRY;
+ }
+ }
+ errno = ENOENT;
+ ret = -1;
+ goto RETVAL;
+ }
+
+ if (mode == P_NOWAIT) {
+ /* asynchronous spawn -- store handle, return PID */
+ ret = (int)ProcessInformation.dwProcessId;
+ if (IsWin95() && ret < 0)
+ ret = -ret;
+
+ w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
+ w32_child_pids[w32_num_children] = (DWORD)ret;
+ ++w32_num_children;
+ }
+ else {
+ DWORD status;
+ win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
+ /* FIXME: if msgwait returned due to message perhaps forward the
+ "signal" to the process
+ */
+ GetExitCodeProcess(ProcessInformation.hProcess, &status);
+ ret = (int)status;
+ CloseHandle(ProcessInformation.hProcess);
+ }
+
+ CloseHandle(ProcessInformation.hThread);
+
+RETVAL:
+ PerlEnv_free_childenv(env);
+ PerlEnv_free_childdir(dir);
+ Safefree(cmd);
+ if (cname != cmdname)
+ Safefree(cname);
+ return ret;
+#endif
+}
+
+DllExport int
+win32_execv(const char *cmdname, const char *const *argv)
+{
+ dTHX;
+ Perl_croak(aTHX_ PL_no_func, "execv");
+ return -1;
+}
+
+DllExport int
+win32_execvp(const char *cmdname, const char *const *argv)
+{
+ dTHX;
+ Perl_croak(aTHX_ PL_no_func, "execvp");
+ return -1;
+}
+
+DllExport void
+win32_perror(const char *str)
+{
+ xceperror(str);
+}
+
+DllExport void
+win32_setbuf(FILE *pf, char *buf)
+{
+ dTHX;
+ Perl_croak(aTHX_ PL_no_func, "setbuf");
+}
+
+DllExport int
+win32_setvbuf(FILE *pf, char *buf, int type, size_t size)
+{
+ return setvbuf(pf, buf, type, size);
+}
+
+DllExport int
+win32_flushall(void)
+{
+ return flushall();
+}
+
+DllExport int
+win32_fcloseall(void)
+{
+ return fcloseall();
+}
+
+DllExport char*
+win32_fgets(char *s, int n, FILE *pf)
+{
+ return fgets(s, n, pf);
+}
+
+DllExport char*
+win32_gets(char *s)
+{
+ return gets(s);
+}
+
+DllExport int
+win32_fgetc(FILE *pf)
+{
+ return fgetc(pf);
+}
+
+DllExport int
+win32_putc(int c, FILE *pf)
+{
+ return putc(c,pf);
+}
+
+DllExport int
+win32_puts(const char *s)
+{
+ return puts(s);
+}
+
+DllExport int
+win32_getchar(void)
+{
+ return getchar();
+}
+
+DllExport int
+win32_putchar(int c)
+{
+ return putchar(c);
+}
+
+#ifdef MYMALLOC
+
+#ifndef USE_PERL_SBRK
+
+static char *committed = NULL;
+static char *base = NULL;
+static char *reserved = NULL;
+static char *brk = NULL;
+static DWORD pagesize = 0;
+static DWORD allocsize = 0;
+
+void *
+sbrk(int need)
+{
+ void *result;
+ if (!pagesize)
+ {SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ /* Pretend page size is larger so we don't perpetually
+ * call the OS to commit just one page ...
+ */
+ pagesize = info.dwPageSize << 3;
+ allocsize = info.dwAllocationGranularity;
+ }
+ /* This scheme fails eventually if request for contiguous
+ * block is denied so reserve big blocks - this is only
+ * address space not memory ...
+ */
+ if (brk+need >= reserved)
+ {
+ DWORD size = 64*1024*1024;
+ char *addr;
+ if (committed && reserved && committed < reserved)
+ {
+ /* Commit last of previous chunk cannot span allocations */
+ addr = (char *) VirtualAlloc(committed,reserved-committed,MEM_COMMIT,PAGE_READWRITE);
+ if (addr)
+ committed = reserved;
+ }
+ /* Reserve some (more) space
+ * Note this is a little sneaky, 1st call passes NULL as reserved
+ * so lets system choose where we start, subsequent calls pass
+ * the old end address so ask for a contiguous block
+ */
+ addr = (char *) VirtualAlloc(reserved,size,MEM_RESERVE,PAGE_NOACCESS);
+ if (addr)
+ {
+ reserved = addr+size;
+ if (!base)
+ base = addr;
+ if (!committed)
+ committed = base;
+ if (!brk)
+ brk = committed;
+ }
+ else
+ {
+ return (void *) -1;
+ }
+ }
+ result = brk;
+ brk += need;
+ if (brk > committed)
+ {
+ DWORD size = ((brk-committed + pagesize -1)/pagesize) * pagesize;
+ char *addr = (char *) VirtualAlloc(committed,size,MEM_COMMIT,PAGE_READWRITE);
+ if (addr)
+ {
+ committed += size;
}
+ else
+ return (void *) -1;
+ }
+ return result;
+}
- if (mode == P_NOWAIT) {
- /* asynchronous spawn -- store handle, return PID */
- ret = (int)ProcessInformation.dwProcessId;
- if (IsWin95() && ret < 0)
- ret = -ret;
+#endif
+#endif
- w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
- w32_child_pids[w32_num_children] = (DWORD)ret;
- ++w32_num_children;
- }
- else {
- DWORD status;
- win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
- /* FIXME: if msgwait returned due to message perhaps forward the
- "signal" to the process
- */
- GetExitCodeProcess(ProcessInformation.hProcess, &status);
- ret = (int)status;
- CloseHandle(ProcessInformation.hProcess);
- }
+DllExport void*
+win32_malloc(size_t size)
+{
+ return malloc(size);
+}
- CloseHandle(ProcessInformation.hThread);
+DllExport void*
+win32_calloc(size_t numitems, size_t size)
+{
+ return calloc(numitems,size);
+}
-RETVAL:
- PerlEnv_free_childenv(env);
- PerlEnv_free_childdir(dir);
- Safefree(cmd);
- if (cname != cmdname)
- Safefree(cname);
- return ret;
-#endif
+DllExport void*
+win32_realloc(void *block, size_t size)
+{
+ return realloc(block,size);
}
-DllExport int
-win32_execv(const char *cmdname, const char *const *argv)
+DllExport void
+win32_free(void *block)
{
- dTHX;
- Perl_croak(aTHX_ PL_no_func, "execv");
- return -1;
+ free(block);
}
-DllExport int
-win32_execvp(const char *cmdname, const char *const *argv)
+int
+win32_open_osfhandle(intptr_t osfhandle, int flags)
{
- dTHX;
- Perl_croak(aTHX_ PL_no_func, "execvp");
- return -1;
+ int fh;
+ char fileflags=0; /* _osfile flags */
+
+ Perl_croak_nocontext("win32_open_osfhandle() TBD on this platform");
+ return 0;
+}
+
+int
+win32_get_osfhandle(int fd)
+{
+ int fh;
+ char fileflags=0; /* _osfile flags */
+
+ Perl_croak_nocontext("win32_get_osfhandle() TBD on this platform");
+ return 0;
+}
+
+FILE *
+win32_fdupopen(FILE *pf)
+{
+ FILE* pfdup;
+ fpos_t pos;
+ char mode[3];
+ int fileno = win32_dup(win32_fileno(pf));
+ int fmode = palm_fgetmode(pfdup);
+
+ fprintf(stderr,"DEBUG for win32_fdupopen()\n");
+
+ /* open the file in the same mode */
+ if(fmode & O_RDONLY) {
+ mode[0] = 'r';
+ mode[1] = 0;
+ }
+ else if(fmode & O_APPEND) {
+ mode[0] = 'a';
+ mode[1] = 0;
+ }
+ else if(fmode & O_RDWR) {
+ mode[0] = 'r';
+ mode[1] = '+';
+ mode[2] = 0;
+ }
+
+ /* it appears that the binmode is attached to the
+ * file descriptor so binmode files will be handled
+ * correctly
+ */
+ pfdup = win32_fdopen(fileno, mode);
+
+ /* move the file pointer to the same position */
+ if (!fgetpos(pf, &pos)) {
+ fsetpos(pfdup, &pos);
+ }
+ return pfdup;
}
DllExport void*
}
int
-Perl_do_spawn(pTHX_ char *cmd)
-{
- return do_spawn(aTHX_ cmd);
-}
-
-int
-Perl_do_aspawn(pTHX_ SV *really, SV **mark, SV **sp)
-{
- Perl_croak(aTHX_ PL_no_func, "aspawn");
- return -1;
-}
-
-int
wce_reopen_stdout(char *fname)
{
if(xcefreopen(fname, "w", stdout) == NULL)
return _isnan(d);
}
-int
-win32_open_osfhandle(intptr_t osfhandle, int flags)
-{
- int fh;
- char fileflags=0; /* _osfile flags */
-
- Perl_croak_nocontext("win32_open_osfhandle() TBD on this platform");
- return 0;
-}
-int
-win32_get_osfhandle(int fd)
+DllExport PerlIO*
+win32_popenlist(const char *mode, IV narg, SV **args)
{
- int fh;
- char fileflags=0; /* _osfile flags */
-
- Perl_croak_nocontext("win32_get_osfhandle() TBD on this platform");
- return 0;
+ dTHX;
+ Perl_croak(aTHX_ "List form of pipe open not implemented");
+ return NULL;
}
/*
DllExport PerlIO*
win32_popen(const char *command, const char *mode)
{
- Perl_croak_nocontext("win32_popen() TBD on this platform");
-}
+#ifdef USE_RTL_POPEN
+ return _popen(command, mode);
+#else
+ dTHX;
+ int p[2];
+ int parent, child;
+ int stdfd, oldfd;
+ int ourmode;
+ int childpid;
+ DWORD nhandle;
+ HANDLE old_h;
+ int lock_held = 0;
+
+ /* establish which ends read and write */
+ if (strchr(mode,'w')) {
+ stdfd = 0; /* stdin */
+ parent = 1;
+ child = 0;
+ nhandle = STD_INPUT_HANDLE;
+ }
+ else if (strchr(mode,'r')) {
+ stdfd = 1; /* stdout */
+ parent = 0;
+ child = 1;
+ nhandle = STD_OUTPUT_HANDLE;
+ }
+ else
+ return NULL;
-DllExport PerlIO*
-win32_popenlist(const char *mode, IV narg, SV **args)
-{
- dTHX;
- Perl_croak(aTHX_ "List form of pipe open not implemented");
- return NULL;
+ /* set the correct mode */
+ if (strchr(mode,'b'))
+ ourmode = O_BINARY;
+ else if (strchr(mode,'t'))
+ ourmode = O_TEXT;
+ else
+ ourmode = _fmode & (O_TEXT | O_BINARY);
+
+ /* the child doesn't inherit handles */
+ ourmode |= O_NOINHERIT;
+
+ if (win32_pipe(p, 512, ourmode) == -1)
+ return NULL;
+
+ /* save current stdfd */
+ if ((oldfd = win32_dup(stdfd)) == -1)
+ goto cleanup;
+
+ /* save the old std handle (this needs to happen before the
+ * dup2(), since that might call SetStdHandle() too) */
+ OP_REFCNT_LOCK;
+ lock_held = 1;
+ old_h = GetStdHandle(nhandle);
+
+ /* make stdfd go to child end of pipe (implicitly closes stdfd) */
+ /* stdfd will be inherited by the child */
+ if (win32_dup2(p[child], stdfd) == -1)
+ goto cleanup;
+
+ /* close the child end in parent */
+ win32_close(p[child]);
+
+ /* set the new std handle (in case dup2() above didn't) */
+ SetStdHandle(nhandle, (HANDLE)_get_osfhandle(stdfd));
+
+ /* start the child */
+ {
+ dTHX;
+ if ((childpid = do_spawn_nowait((char*)command)) == -1)
+ goto cleanup;
+
+ /* revert stdfd to whatever it was before */
+ if (win32_dup2(oldfd, stdfd) == -1)
+ goto cleanup;
+
+ /* restore the old std handle (this needs to happen after the
+ * dup2(), since that might call SetStdHandle() too */
+ if (lock_held) {
+ SetStdHandle(nhandle, old_h);
+ OP_REFCNT_UNLOCK;
+ lock_held = 0;
+ }
+
+ /* close saved handle */
+ win32_close(oldfd);
+
+ LOCK_FDPID_MUTEX;
+ sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
+ UNLOCK_FDPID_MUTEX;
+
+ /* set process id so that it can be returned by perl's open() */
+ PL_forkprocess = childpid;
+ }
+
+ /* we have an fd, return a file stream */
+ return (PerlIO_fdopen(p[parent], (char *)mode));
+
+cleanup:
+ /* we don't need to check for errors here */
+ win32_close(p[0]);
+ win32_close(p[1]);
+ if (lock_held) {
+ SetStdHandle(nhandle, old_h);
+ OP_REFCNT_UNLOCK;
+ lock_held = 0;
+ }
+ if (oldfd != -1) {
+ win32_dup2(oldfd, stdfd);
+ win32_close(oldfd);
+ }
+ return (NULL);
+
+#endif /* USE_RTL_POPEN */
}
/*
#endif /* USE_RTL_POPEN */
}
-FILE *
-win32_fdupopen(FILE *pf)
-{
- FILE* pfdup;
- fpos_t pos;
- char mode[3];
- int fileno = win32_dup(win32_fileno(pf));
-
- XCEMessageBoxA(NULL, "NEED TO IMPLEMENT a place in .../wince/wince.c(win32_fdupopen)", "Perl(developer)", 0);
- Perl_croak_nocontext("win32_fdupopen() TBD on this platform");
-
-#if 0
- /* open the file in the same mode */
- if((pf)->_flag & _IOREAD) {
- mode[0] = 'r';
- mode[1] = 0;
- }
- else if((pf)->_flag & _IOWRT) {
- mode[0] = 'a';
- mode[1] = 0;
- }
- else if((pf)->_flag & _IORW) {
- mode[0] = 'r';
- mode[1] = '+';
- mode[2] = 0;
- }
-
- /* it appears that the binmode is attached to the
- * file descriptor so binmode files will be handled
- * correctly
- */
- pfdup = win32_fdopen(fileno, mode);
-
- /* move the file pointer to the same position */
- if (!fgetpos(pf, &pos)) {
- fsetpos(pfdup, &pos);
- }
-#endif
- return pfdup;
-}
-
#ifdef HAVE_INTERP_INTERN