diff --git a/Build.sh b/Build.sh index e8c0314..b489693 100644 --- a/Build.sh +++ b/Build.sh @@ -1,5 +1,5 @@ #!/bin/sh -srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.711 2017/04/02 14:14:03 tg Exp $' +srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.712 2017/04/02 15:00:39 tg Exp $' #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014, 2015, 2016, 2017 @@ -862,11 +862,11 @@ OpenBSD) OS/2) HAVE_TERMIOS_H=0 HAVE_MKNOD=0 # setmode() incompatible - oswarn="; it is currently being ported, get it from" - oswarn="$oswarn${nl}https://github.com/komh/mksh-os2 in the meanwhile" + oswarn="; it is being ported" check_categories="$check_categories nosymlink" : "${CC=gcc}" : "${SIZE=: size}" + SRCS="$SRCS os2.c" add_cppflags -DMKSH_UNEMPLOYED add_cppflags -DMKSH_NOPROSPECTOFWORK add_cppflags -DMKSH_NO_LIMITS diff --git a/edit.c b/edit.c index a3d60bd..3b333d9 100644 --- a/edit.c +++ b/edit.c @@ -28,7 +28,7 @@ #ifndef MKSH_NO_CMDLINE_EDITING -__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.313 2017/03/11 22:49:54 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.314 2017/04/02 15:00:40 tg Exp $"); /* * in later versions we might use libtermcap for this, but since external @@ -145,6 +145,9 @@ x_read(char *buf) static int x_getc(void) { +#ifdef __OS2__ + return (_read_kbd(0, 1, 0)); +#else char c; ssize_t n; @@ -166,6 +169,7 @@ x_getc(void) x_mode(true); } return ((n == 1) ? (int)(unsigned char)c : -1); +#endif } static void diff --git a/eval.c b/eval.c index 6d4b94e..375458a 100644 --- a/eval.c +++ b/eval.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.199 2017/03/26 00:10:23 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.200 2017/04/02 15:00:41 tg Exp $"); /* * string expansion @@ -879,10 +879,30 @@ expand( c = '\n'; --newlines; } else { - while ((c = shf_getc(x.u.shf)) == 0 || c == '\n') + while ((c = shf_getc(x.u.shf)) == 0 || +#ifdef MKSH_WITH_TEXTMODE + c == '\r' || +#endif + c == '\n') { +#ifdef MKSH_WITH_TEXTMODE + if (c == '\r') { + c = shf_getc(x.u.shf); + switch (c) { + case '\n': + break; + default: + shf_ungetc(c, x.u.shf); + /* FALLTHROUGH */ + case -1: + c = '\r'; + break; + } + } +#endif if (c == '\n') /* save newlines */ newlines++; + } if (newlines && c != -1) { shf_ungetc(c, x.u.shf); c = '\n'; diff --git a/exec.c b/exec.c index 41273df..817168b 100644 --- a/exec.c +++ b/exec.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.192 2017/04/02 13:08:06 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.193 2017/04/02 15:00:42 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" @@ -889,6 +889,9 @@ scriptexec(struct op *tp, const char **ap) unsigned short m; ssize_t n; +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + setmode(fd, O_TEXT); +#endif /* read first couple of octets from file */ n = read(fd, buf, sizeof(buf) - 1); close(fd); @@ -944,6 +947,17 @@ scriptexec(struct op *tp, const char **ap) if (*cp) *tp->args-- = (char *)cp; } +#ifdef __OS2__ + /* + * Search shell/interpreter name without directory in PATH + * if specified path does not exist + */ + if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) { + cp = search_path(_getname(sh), path, X_OK, NULL); + if (cp) + sh = cp; + } +#endif goto nomagic; noshebang: m = buf[0] << 8 | buf[1]; @@ -964,6 +978,19 @@ scriptexec(struct op *tp, const char **ap) buf[4] == 'Z') || (m == /* 7zip */ 0x377A) || (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) errorf("%s: not executable: magic %04X", tp->str, m); +#ifdef __OS2__ + cp = _getext(tp->str); + if (cp && (!stricmp(cp, ".cmd") || !stricmp(cp, ".bat"))) { + /* execute .cmd and .bat with OS2_SHELL, usually CMD.EXE */ + sh = getenv("OS2_SHELL"); + *tp->args-- = "/c"; + /* convert slahes to backslashes */ + for (cp = tp->str; *cp; cp++) { + if (*cp == '/') + *cp = '\\'; + } + } +#endif nomagic: ; } @@ -1267,6 +1294,13 @@ search_access(const char *fn, int mode) return (0); } +#ifdef __OS2__ +/* check if path is something we want to find, adding executable extensions */ +#define search_access(fn, mode) access_ex((search_access), (fn), (mode)) +#else +#define search_access(fn, mode) (search_access)((fn), (mode)) +#endif + /* * search for command with PATH */ @@ -1288,7 +1322,11 @@ search_path(const char *name, const char *lpath, search_path_ok: if (errnop) *errnop = 0; +#ifndef __OS2__ return (name); +#else + return (real_exec_name(name)); +#endif } goto search_path_err; } @@ -1305,6 +1343,10 @@ search_path(const char *name, const char *lpath, XcheckN(xs, xp, p - sp); memcpy(xp, sp, p - sp); xp += p - sp; +#ifdef __OS2__ + if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1])) + xp--; +#endif *xp++ = '/'; } sp = p; diff --git a/expr.c b/expr.c index edc22aa..df12734 100644 --- a/expr.c +++ b/expr.c @@ -23,7 +23,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.91 2017/03/26 00:10:23 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.92 2017/04/02 15:00:42 tg Exp $"); #define EXPRTOK_DEFNS #include "exprtok.h" @@ -857,6 +857,9 @@ utf_wctomb(char *dst, unsigned int wc) int ksh_access(const char *fn, int mode) { +#ifdef __OS2__ + return (access_ex(access, fn, mode)); +#else int rv; struct stat sb; @@ -866,6 +869,7 @@ ksh_access(const char *fn, int mode) rv = -1; return (rv); +#endif } #ifndef MIRBSD_BOOTFLOPPY diff --git a/funcs.c b/funcs.c index 8471c4f..d277e61 100644 --- a/funcs.c +++ b/funcs.c @@ -38,7 +38,7 @@ #endif #endif -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.331 2017/03/22 00:20:41 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.332 2017/04/02 15:00:42 tg Exp $"); #if HAVE_KILLPG /* @@ -1968,6 +1968,10 @@ c_read(const char **wp) #define c_read_opts "Aad:N:n:prst:u," #else #define c_read_opts "Aad:N:n:prsu," +#endif +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + int saved_mode; + int saved_errno; #endif while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1) @@ -2097,7 +2101,15 @@ c_read(const char **wp) } #endif +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + saved_mode = setmode(fd, O_TEXT); +#endif if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) { +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + saved_errno = errno; + setmode(fd, saved_mode); + errno = saved_errno; +#endif if (errno == EINTR) { /* check whether the signal would normally kill */ if (!fatal_trap_check()) { @@ -2112,6 +2124,9 @@ c_read(const char **wp) rv = 2; goto c_read_out; } +#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) + setmode(fd, saved_mode); +#endif switch (readmode) { case READALL: @@ -2828,7 +2843,7 @@ c_exec(const char **wp MKSH_A_UNUSED) return (0); } -#if HAVE_MKNOD +#if HAVE_MKNOD && !defined(__OS2__) int c_mknod(const char **wp) { @@ -3085,6 +3100,14 @@ test_isop(Test_meta meta, const char *s) return (TO_NONOP); } +#ifdef __OS2__ +#define test_access(name, mode) access_ex(access, (name), (mode)) +#define test_stat(name, buffer) stat_ex((name), (buffer)) +#else +#define test_access(name, mode) access((name), (mode)) +#define test_stat(name, buffer) stat((name), (buffer)) +#endif + int test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, bool do_eval) @@ -3150,12 +3173,12 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, /* -r */ case TO_FILRD: /* LINTED use of access */ - return (access(opnd1, R_OK) == 0); + return (test_access(opnd1, R_OK) == 0); /* -w */ case TO_FILWR: /* LINTED use of access */ - return (access(opnd1, W_OK) == 0); + return (test_access(opnd1, W_OK) == 0); /* -x */ case TO_FILEX: @@ -3165,11 +3188,11 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, case TO_FILAXST: /* -e */ case TO_FILEXST: - return (stat(opnd1, &b1) == 0); + return (test_stat(opnd1, &b1) == 0); /* -r */ case TO_FILREG: - return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode)); + return (test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode)); /* -d */ case TO_FILID: diff --git a/main.c b/main.c index 27d7f5a..d01292b 100644 --- a/main.c +++ b/main.c @@ -34,7 +34,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.328 2017/03/19 22:31:27 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.329 2017/04/02 15:00:43 tg Exp $"); extern char **environ; @@ -195,6 +195,8 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) for (i = 0; i < 3; ++i) if (!isatty(i)) setmode(i, O_BINARY); + + os2_init(&argc, &argv); #endif /* do things like getpgrp() et al. */ @@ -467,7 +469,9 @@ main_init(int argc, const char *argv[], Source **sp, struct block **lp) * to search for it. This changes the behaviour of a * simple "mksh foo", but can't be helped. */ - s->file = search_path(argv[argi++], path, X_OK, NULL); + s->file = argv[argi++]; + if (search_access(s->file, X_OK) != 0) + s->file = search_path(s->file, path, X_OK, NULL); if (!s->file || !*s->file) s->file = argv[argi - 1]; #else @@ -1461,6 +1465,10 @@ openpipe(int *pv) pv[1] = savefd(lpv[1]); if (pv[1] != lpv[1]) close(lpv[1]); +#ifdef __OS2__ + setmode(pv[0], O_BINARY); + setmode(pv[1], O_BINARY); +#endif } void diff --git a/os2.c b/os2.c new file mode 100644 index 0000000..5d39630 --- /dev/null +++ b/os2.c @@ -0,0 +1,557 @@ +/*- + * Copyright (c) 2015 + * KO Myung-Hun + * + * Provided that these terms and disclaimer and all copyright notices + * are retained or reproduced in an accompanying document, permission + * is granted to deal in this work without restriction, including un- + * limited rights to use, publicly perform, distribute, sell, modify, + * merge, give away, or sublicence. + * + * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to + * the utmost extent permitted by applicable law, neither express nor + * implied; without malicious intent or gross negligence. In no event + * may a licensor, author or contributor be held liable for indirect, + * direct, other damage, loss, or other issues arising in any way out + * of dealing in the work, even if advised of the possibility of such + * damage or existence of a defect, except proven that it results out + * of said person's immediate fault when using the work as intended. + */ + +#define INCL_DOS +#include + +#include "sh.h" + +#include +#include +#include +#include + +__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.1 2017/04/02 15:00:44 tg Exp $"); + +static char *remove_trailing_dots(char *); +static int access_stat_ex(int (*)(), const char *, void *); +static int test_exec_exist(const char *, char *); +static void response(int *, const char ***); +static char *make_response_file(char * const *); +static void env_slashify(void); +static void add_temp(const char *); +static void cleanup_temps(void); +static void cleanup(void); + +#define RPUT(x) do { \ + if (new_argc >= new_alloc) { \ + new_alloc += 20; \ + if (!(new_argv = realloc(new_argv, \ + new_alloc * sizeof(char *)))) \ + goto exit_out_of_memory; \ + } \ + new_argv[new_argc++] = (x); \ +} while (/* CONSTCOND */ 0) + +#define KLIBC_ARG_RESPONSE_EXCLUDE \ + (__KLIBC_ARG_DQUOTE | __KLIBC_ARG_WILDCARD | __KLIBC_ARG_SHELL) + +static void +response(int *argcp, const char ***argvp) +{ + int i, old_argc, new_argc, new_alloc = 0; + const char **old_argv, **new_argv; + char *line, *l, *p; + FILE *f; + + old_argc = *argcp; + old_argv = *argvp; + for (i = 1; i < old_argc; ++i) + if (old_argv[i] && + !(old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) && + old_argv[i][0] == '@') + break; + + if (i >= old_argc) + /* do nothing */ + return; + + new_argv = NULL; + new_argc = 0; + for (i = 0; i < old_argc; ++i) { + if (i == 0 || !old_argv[i] || + (old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) || + old_argv[i][0] != '@' || + !(f = fopen(old_argv[i] + 1, "rt"))) + RPUT(old_argv[i]); + else { + long filesize; + + fseek(f, 0, SEEK_END); + filesize = ftell(f); + fseek(f, 0, SEEK_SET); + + line = malloc(filesize + /* type */ 1 + /* NUL */ 1); + if (!line) { + exit_out_of_memory: + fputs("Out of memory while reading response file\n", stderr); + exit(255); + } + + line[0] = __KLIBC_ARG_NONZERO | __KLIBC_ARG_RESPONSE; + l = line + 1; + while (fgets(l, (filesize + 1) - (l - (line + 1)), f)) { + p = strchr(l, '\n'); + if (p) { + /* + * if a line ends with a backslash, + * concatenate with the next line + */ + if (p > l && p[-1] == '\\') { + char *p1; + int count = 0; + + for (p1 = p - 1; p1 >= l && + *p1 == '\\'; p1--) + count++; + + if (count & 1) { + l = p + 1; + + continue; + } + } + + *p = 0; + } + p = strdup(line); + if (!p) + goto exit_out_of_memory; + + RPUT(p + 1); + + l = line + 1; + } + + free(line); + + if (ferror(f)) { + fputs("Cannot read response file\n", stderr); + exit(255); + } + + fclose(f); + } + } + + RPUT(NULL); + --new_argc; + + *argcp = new_argc; + *argvp = new_argv; +} + +static void +init_extlibpath(void) +{ + const char *vars[] = { + "BEGINLIBPATH", + "ENDLIBPATH", + "LIBPATHSTRICT", + NULL + }; + char val[512]; + int flag; + + for (flag = 0; vars[flag]; flag++) { + DosQueryExtLIBPATH(val, flag + 1); + if (val[0]) + setenv(vars[flag], val, 1); + } +} + +/* + * Convert backslashes of environmental variables to forward slahes. + * A backslash may be used as an escaped character when doing 'echo'. + * This leads to an unexpected behavior. + */ +static void +env_slashify(void) +{ + /* + * PATH and TMPDIR are used by OS/2 as well. That is, they may + * have backslashes as a directory separator. + * BEGINLIBPATH and ENDLIBPATH are special variables on OS/2. + */ + const char *var_list[] = { + "PATH", + "TMPDIR", + "BEGINLIBPATH", + "ENDLIBPATH", + NULL + }; + const char **var; + char *value; + + for (var = var_list; *var; var++) { + value = getenv(*var); + + if (value) + _fnslashify(value); + } +} + +void +os2_init(int *argcp, const char ***argvp) +{ + response(argcp, argvp); + + init_extlibpath(); + env_slashify(); + + if (!isatty(STDIN_FILENO)) + setmode(STDIN_FILENO, O_BINARY); + if (!isatty(STDOUT_FILENO)) + setmode(STDOUT_FILENO, O_BINARY); + if (!isatty(STDERR_FILENO)) + setmode(STDERR_FILENO, O_BINARY); + + atexit(cleanup); +} + +void +setextlibpath(const char *name, const char *val) +{ + int flag; + char *p, *cp; + + if (!strcmp(name, "BEGINLIBPATH")) + flag = BEGIN_LIBPATH; + else if (!strcmp(name, "ENDLIBPATH")) + flag = END_LIBPATH; + else if (!strcmp(name, "LIBPATHSTRICT")) + flag = LIBPATHSTRICT; + else + return; + + /* convert slashes to backslashes */ + strdupx(cp, val, ATEMP); + for (p = cp; *p; p++) { + if (*p == '/') + *p = '\\'; + } + + DosSetExtLIBPATH(cp, flag); + + afree(cp, ATEMP); +} + +/* remove trailing dots */ +static char * +remove_trailing_dots(char *name) +{ + char *p; + + for (p = name + strlen(name); --p > name && *p == '.'; ) + /* nothing */; + + if (*p != '.' && *p != '/' && *p != '\\' && *p != ':') + p[1] = '\0'; + + return (name); +} + +#define REMOVE_TRAILING_DOTS(name) \ + remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1)) + +/* alias of stat() */ +extern int _std_stat(const char *, struct stat *); + +/* replacement for stat() of kLIBC which fails if there are trailing dots */ +int +stat(const char *name, struct stat *buffer) +{ + return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer)); +} + +/* alias of access() */ +extern int _std_access(const char *, int); + +/* replacement for access() of kLIBC which fails if there are trailing dots */ +int +access(const char *name, int mode) +{ + /* + * On OS/2 kLIBC, X_OK is set only for executable files. + * This prevents scripts from being executed. + */ + if (mode & X_OK) + mode = (mode & ~X_OK) | R_OK; + + return (_std_access(REMOVE_TRAILING_DOTS(name), mode)); +} + +#define MAX_X_SUFFIX_LEN 4 + +static const char *x_suffix_list[] = + { "", ".ksh", ".exe", ".sh", ".cmd", ".com", ".bat", NULL }; + +/* call fn() by appending executable extensions */ +static int +access_stat_ex(int (*fn)(), const char *name, void *arg) +{ + char *x_name; + const char **x_suffix; + int rc = -1; + size_t x_namelen = strlen(name) + MAX_X_SUFFIX_LEN + 1; + + /* otherwise, try to append executable suffixes */ + x_name = alloc(x_namelen, ATEMP); + + for (x_suffix = x_suffix_list; rc && *x_suffix; x_suffix++) { + strlcpy(x_name, name, x_namelen); + strlcat(x_name, *x_suffix, x_namelen); + + rc = fn(x_name, arg); + } + + afree(x_name, ATEMP); + + return (rc); +} + +/* access()/search_access() version */ +int +access_ex(int (*fn)(const char *, int), const char *name, int mode) +{ + /*XXX this smells fishy --mirabilos */ + return (access_stat_ex(fn, name, (void *)mode)); +} + +/* stat() version */ +int +stat_ex(const char *name, struct stat *buffer) +{ + return (access_stat_ex(stat, name, buffer)); +} + +static int +test_exec_exist(const char *name, char *real_name) +{ + struct stat sb; + + if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode)) + return (-1); + + /* safe due to calculations in real_exec_name() */ + memcpy(real_name, name, strlen(name) + 1); + + return (0); +} + +const char * +real_exec_name(const char *name) +{ + char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1]; + const char *real_name = name; + + if (access_stat_ex(test_exec_exist, real_name, x_name) != -1) + /*XXX memory leak */ + strdupx(real_name, x_name, ATEMP); + + return (real_name); +} + +/* OS/2 can process a command line up to 32 KiB */ +#define MAX_CMD_LINE_LEN 32768 + +/* make a response file to pass a very long command line */ +static char * +make_response_file(char * const *argv) +{ + char rsp_name_arg[] = "@mksh-rsp-XXXXXX"; + char *rsp_name = &rsp_name_arg[1]; + int arg_len = 0; + int i; + + for (i = 0; argv[i]; i++) + arg_len += strlen(argv[i]) + 1; + + /* + * If a length of command line is longer than MAX_CMD_LINE_LEN, then + * use a response file. OS/2 cannot process a command line longer + * than 32K. Of course, a response file cannot be recognised by a + * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But + * it cannot accept a command line longer than 32K in itself. So + * using a response file in this case, is an acceptable solution. + */ + if (arg_len > MAX_CMD_LINE_LEN) { + int fd; + char *result; + + if ((fd = mkstemp(rsp_name)) == -1) + return (NULL); + + /* write all the arguments except a 0th program name */ + for (i = 1; argv[i]; i++) { + write(fd, argv[i], strlen(argv[i])); + write(fd, "\n", 1); + } + + close(fd); + add_temp(rsp_name); + strdupx(result, rsp_name_arg, ATEMP); + return (result); + } + + return (NULL); +} + +/* alias of execve() */ +extern int _std_execve(const char *, char * const *, char * const *); + +/* replacement for execve() of kLIBC */ +int +execve(const char *name, char * const *argv, char * const *envp) +{ + const char *exec_name; + FILE *fp; + char sign[2]; + char *rsp_argv[3]; + char *rsp_name_arg; + int pid; + int status; + int fd; + int rc; + + /* + * #! /bin/sh : append .exe + * extproc sh : search sh.exe in PATH + */ + exec_name = search_path(name, path, X_OK, NULL); + if (!exec_name) { + errno = ENOENT; + return (-1); + } + + /*- + * kLIBC execve() has problems when executing scripts. + * 1. it fails to execute a script if a directory whose name + * is same as an interpreter exists in a current directory. + * 2. it fails to execute a script not starting with sharpbang. + * 3. it fails to execute a batch file if COMSPEC is set to a shell + * incompatible with cmd.exe, such as /bin/sh. + * And ksh process scripts more well, so let ksh process scripts. + */ + errno = 0; + if (!(fp = fopen(exec_name, "rb"))) + errno = ENOEXEC; + + if (!errno && fread(sign, 1, sizeof(sign), fp) != sizeof(sign)) + errno = ENOEXEC; + + if (fp && fclose(fp)) + errno = ENOEXEC; + + if (!errno && + !((sign[0] == 'M' && sign[1] == 'Z') || + (sign[0] == 'N' && sign[1] == 'E') || + (sign[0] == 'L' && sign[1] == 'X'))) + errno = ENOEXEC; + + if (errno == ENOEXEC) + return (-1); + + rsp_name_arg = make_response_file(argv); + + if (rsp_name_arg) { + rsp_argv[0] = argv[0]; + rsp_argv[1] = rsp_name_arg; + rsp_argv[2] = NULL; + + argv = rsp_argv; + } + + pid = spawnve(P_NOWAIT, exec_name, argv, envp); + + afree(rsp_name_arg, ATEMP); + + if (pid == -1) { + cleanup_temps(); + + return (-1); + } + + /* close all opened handles */ + for (fd = 0; fd < NUFILE; fd++) { + if (fcntl(fd, F_GETFD) == -1) + continue; + + close(fd); + } + + while ((rc = waitpid(pid, &status, 0)) < 0 && errno == EINTR) + /* nothing */; + + cleanup_temps(); + + /* Is this possible? And is this right? */ + if (rc == -1) + return (-1); + + if (WIFSIGNALED(status)) + _exit(ksh_sigmask(WTERMSIG(status))); + + _exit(WEXITSTATUS(status)); +} + +static struct temp *templist = NULL; + +static void +add_temp(const char *name) +{ + struct temp *tp; + + tp = alloc(offsetof(struct temp, tffn[0]) + strlen(name) + 1, APERM); + memcpy(tp->tffn, name, strlen(name) + 1); + tp->next = templist; + templist = tp; +} + +/* alias of unlink() */ +extern int _std_unlink(const char *); + +/* + * Replacement for unlink() of kLIBC not supporting to remove files used by + * another processes. + */ +int +unlink(const char *name) +{ + int rc; + + rc = _std_unlink(name); + if (rc == -1 && errno != ENOENT) + add_temp(name); + + return (rc); +} + +static void +cleanup_temps(void) +{ + struct temp *tp; + struct temp **tpnext; + + for (tpnext = &templist, tp = templist; tp; tp = *tpnext) { + if (_std_unlink(tp->tffn) == 0 || errno == ENOENT) { + *tpnext = tp->next; + afree(tp, APERM); + } else { + tpnext = &tp->next; + } + } +} + +static void +cleanup(void) +{ + cleanup_temps(); +} diff --git a/sh.h b/sh.h index 952f660..7cb47c7 100644 --- a/sh.h +++ b/sh.h @@ -175,7 +175,7 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.800 2017/04/02 14:14:08 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.801 2017/04/02 15:00:44 tg Exp $"); #endif #define MKSH_VERSION "R54 2017/03/25" @@ -2273,6 +2273,14 @@ char *strdup_i(const char *, Area *); char *strndup_i(const char *, size_t, Area *); #endif int unbksl(bool, int (*)(void), void (*)(int)); +#ifdef __OS2__ +/* os2.c */ +void os2_init(int *, const char ***); +void setextlibpath(const char *, const char *); +int access_ex(int (*)(const char *, int), const char *, int); +int stat_ex(const char *, struct stat *); +const char *real_exec_name(const char *); +#endif /* shf.c */ struct shf *shf_open(const char *, int, int, int); struct shf *shf_fdopen(int, int, struct shf *); @@ -2448,11 +2456,14 @@ extern int tty_init_fd(void); /* initialise tty_fd, tty_devtty */ char mksh_cdirsep_c = (c); \ (mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\'); \ }) -/* - * I've seen mksh_sdirsep(s) and mksh_vdirsep(s) but need to think - * more about the OS/2 port (and, possibly, toy with it) before I - * can merge this upstream, but good job so far @komh, thanks! - */ +#define mksh_sdirsep(s) __extension__({ \ + const char *mksh_sdirsep_s = (s); \ + ((char *)((ksh_isalphx(mksh_sdirsep_s[0]) && \ + mksh_sdirsep_s[1] == ':' && \ + !mksh_cdirsep(mksh_sdirsep_s[2])) ? \ + (mksh_sdirsep_s + 1) : strpbrk(mksh_sdirsep_s, "/\\"))); \ +}) +#define mksh_vdirsep(s) (mksh_sdirsep((s)) != NULL) #else #define mksh_abspath(s) ((s)[0] == '/') #define mksh_cdirsep(c) ((c) == '/') diff --git a/shf.c b/shf.c index ace0ab6..85c7c79 100644 --- a/shf.c +++ b/shf.c @@ -25,7 +25,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.76 2016/07/25 00:04:47 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.77 2017/04/02 15:00:45 tg Exp $"); /* flags to shf_emptybuf() */ #define EB_READSW 0x01 /* about to switch to reading */ @@ -518,7 +518,23 @@ shf_getse(char *buf, ssize_t bsize, struct shf *shf) shf->rnleft -= ncopy; buf += ncopy; bsize -= ncopy; +#ifdef MKSH_WITH_TEXTMODE + if (end && buf > orig_buf + 1 && buf[-2] == '\r') { + buf--; + bsize++; + buf[-1] = '\n'; + } +#endif } while (!end && bsize); +#ifdef MKSH_WITH_TEXTMODE + if (!bsize && buf[-1] == '\r') { + int c = shf_getc(shf); + if (c == '\n') + buf[-1] = '\n'; + else if (c != -1) + shf_ungetc(c, shf); + } +#endif *buf = '\0'; return (buf); }