3 * KO Myung-Hun <komh@chollian.net>
5 * mirabilos <m@mirbsd.org>
7 * Provided that these terms and disclaimer and all copyright notices
8 * are retained or reproduced in an accompanying document, permission
9 * is granted to deal in this work without restriction, including un-
10 * limited rights to use, publicly perform, distribute, sell, modify,
11 * merge, give away, or sublicence.
13 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14 * the utmost extent permitted by applicable law, neither express nor
15 * implied; without malicious intent or gross negligence. In no event
16 * may a licensor, author or contributor be held liable for indirect,
17 * direct, other damage, loss, or other issues arising in any way out
18 * of dealing in the work, even if advised of the possibility of such
19 * damage or existence of a defect, except proven that it results out
20 * of said person's immediate fault when using the work as intended.
28 #include <klibc/startup.h>
33 __RCSID("$MirOS: src/bin/mksh/os2.c,v 1.2 2017/04/29 22:04:29 tg Exp $");
35 static char *remove_trailing_dots(char *);
36 static int access_stat_ex(int (*)(), const char *, void *);
37 static int test_exec_exist(const char *, char *);
38 static void response(int *, const char ***);
39 static char *make_response_file(char * const *);
40 static void env_slashify(void);
41 static void add_temp(const char *);
42 static void cleanup_temps(void);
43 static void cleanup(void);
45 #define RPUT(x) do { \
46 if (new_argc >= new_alloc) { \
48 if (!(new_argv = realloc(new_argv, \
49 new_alloc * sizeof(char *)))) \
50 goto exit_out_of_memory; \
52 new_argv[new_argc++] = (x); \
53 } while (/* CONSTCOND */ 0)
55 #define KLIBC_ARG_RESPONSE_EXCLUDE \
56 (__KLIBC_ARG_DQUOTE | __KLIBC_ARG_WILDCARD | __KLIBC_ARG_SHELL)
59 response(int *argcp, const char ***argvp)
61 int i, old_argc, new_argc, new_alloc = 0;
62 const char **old_argv, **new_argv;
68 for (i = 1; i < old_argc; ++i)
70 !(old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) &&
71 old_argv[i][0] == '@')
80 for (i = 0; i < old_argc; ++i) {
81 if (i == 0 || !old_argv[i] ||
82 (old_argv[i][-1] & KLIBC_ARG_RESPONSE_EXCLUDE) ||
83 old_argv[i][0] != '@' ||
84 !(f = fopen(old_argv[i] + 1, "rt")))
89 fseek(f, 0, SEEK_END);
91 fseek(f, 0, SEEK_SET);
93 line = malloc(filesize + /* type */ 1 + /* NUL */ 1);
96 fputs("Out of memory while reading response file\n", stderr);
100 line[0] = __KLIBC_ARG_NONZERO | __KLIBC_ARG_RESPONSE;
102 while (fgets(l, (filesize + 1) - (l - (line + 1)), f)) {
106 * if a line ends with a backslash,
107 * concatenate with the next line
109 if (p > l && p[-1] == '\\') {
113 for (p1 = p - 1; p1 >= l &&
128 goto exit_out_of_memory;
138 fputs("Cannot read response file\n", stderr);
154 init_extlibpath(void)
156 const char *vars[] = {
165 for (flag = 0; vars[flag]; flag++) {
166 DosQueryExtLIBPATH(val, flag + 1);
168 setenv(vars[flag], val, 1);
173 * Convert backslashes of environmental variables to forward slahes.
174 * A backslash may be used as an escaped character when doing 'echo'.
175 * This leads to an unexpected behavior.
181 * PATH and TMPDIR are used by OS/2 as well. That is, they may
182 * have backslashes as a directory separator.
183 * BEGINLIBPATH and ENDLIBPATH are special variables on OS/2.
185 const char *var_list[] = {
195 for (var = var_list; *var; var++) {
196 value = getenv(*var);
204 os2_init(int *argcp, const char ***argvp)
206 response(argcp, argvp);
211 if (!isatty(STDIN_FILENO))
212 setmode(STDIN_FILENO, O_BINARY);
213 if (!isatty(STDOUT_FILENO))
214 setmode(STDOUT_FILENO, O_BINARY);
215 if (!isatty(STDERR_FILENO))
216 setmode(STDERR_FILENO, O_BINARY);
222 setextlibpath(const char *name, const char *val)
227 if (!strcmp(name, "BEGINLIBPATH"))
228 flag = BEGIN_LIBPATH;
229 else if (!strcmp(name, "ENDLIBPATH"))
231 else if (!strcmp(name, "LIBPATHSTRICT"))
232 flag = LIBPATHSTRICT;
236 /* convert slashes to backslashes */
237 strdupx(cp, val, ATEMP);
238 for (p = cp; *p; p++) {
243 DosSetExtLIBPATH(cp, flag);
248 /* remove trailing dots */
250 remove_trailing_dots(char *name)
252 char *p = strnul(name);
254 while (--p > name && *p == '.')
257 if (*p != '.' && *p != '/' && *p != '\\' && *p != ':')
263 #define REMOVE_TRAILING_DOTS(name) \
264 remove_trailing_dots(memcpy(alloca(strlen(name) + 1), name, strlen(name) + 1))
266 /* alias of stat() */
267 extern int _std_stat(const char *, struct stat *);
269 /* replacement for stat() of kLIBC which fails if there are trailing dots */
271 stat(const char *name, struct stat *buffer)
273 return (_std_stat(REMOVE_TRAILING_DOTS(name), buffer));
276 /* alias of access() */
277 extern int _std_access(const char *, int);
279 /* replacement for access() of kLIBC which fails if there are trailing dots */
281 access(const char *name, int mode)
284 * On OS/2 kLIBC, X_OK is set only for executable files.
285 * This prevents scripts from being executed.
288 mode = (mode & ~X_OK) | R_OK;
290 return (_std_access(REMOVE_TRAILING_DOTS(name), mode));
293 #define MAX_X_SUFFIX_LEN 4
295 static const char *x_suffix_list[] =
296 { "", ".ksh", ".exe", ".sh", ".cmd", ".com", ".bat", NULL };
298 /* call fn() by appending executable extensions */
300 access_stat_ex(int (*fn)(), const char *name, void *arg)
303 const char **x_suffix;
305 size_t x_namelen = strlen(name) + MAX_X_SUFFIX_LEN + 1;
307 /* otherwise, try to append executable suffixes */
308 x_name = alloc(x_namelen, ATEMP);
310 for (x_suffix = x_suffix_list; rc && *x_suffix; x_suffix++) {
311 strlcpy(x_name, name, x_namelen);
312 strlcat(x_name, *x_suffix, x_namelen);
314 rc = fn(x_name, arg);
317 afree(x_name, ATEMP);
322 /* access()/search_access() version */
324 access_ex(int (*fn)(const char *, int), const char *name, int mode)
326 /*XXX this smells fishy --mirabilos */
327 return (access_stat_ex(fn, name, (void *)mode));
332 stat_ex(const char *name, struct stat *buffer)
334 return (access_stat_ex(stat, name, buffer));
338 test_exec_exist(const char *name, char *real_name)
342 if (stat(name, &sb) < 0 || !S_ISREG(sb.st_mode))
345 /* safe due to calculations in real_exec_name() */
346 memcpy(real_name, name, strlen(name) + 1);
352 real_exec_name(const char *name)
354 char x_name[strlen(name) + MAX_X_SUFFIX_LEN + 1];
355 const char *real_name = name;
357 if (access_stat_ex(test_exec_exist, real_name, x_name) != -1)
359 strdupx(real_name, x_name, ATEMP);
364 /* OS/2 can process a command line up to 32 KiB */
365 #define MAX_CMD_LINE_LEN 32768
367 /* make a response file to pass a very long command line */
369 make_response_file(char * const *argv)
371 char rsp_name_arg[] = "@mksh-rsp-XXXXXX";
372 char *rsp_name = &rsp_name_arg[1];
376 for (i = 0; argv[i]; i++)
377 arg_len += strlen(argv[i]) + 1;
380 * If a length of command line is longer than MAX_CMD_LINE_LEN, then
381 * use a response file. OS/2 cannot process a command line longer
382 * than 32K. Of course, a response file cannot be recognised by a
383 * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But
384 * it cannot accept a command line longer than 32K in itself. So
385 * using a response file in this case, is an acceptable solution.
387 if (arg_len > MAX_CMD_LINE_LEN) {
391 if ((fd = mkstemp(rsp_name)) == -1)
394 /* write all the arguments except a 0th program name */
395 for (i = 1; argv[i]; i++) {
396 write(fd, argv[i], strlen(argv[i]));
402 strdupx(result, rsp_name_arg, ATEMP);
409 /* alias of execve() */
410 extern int _std_execve(const char *, char * const *, char * const *);
412 /* replacement for execve() of kLIBC */
414 execve(const char *name, char * const *argv, char * const *envp)
416 const char *exec_name;
427 * #! /bin/sh : append .exe
428 * extproc sh : search sh.exe in PATH
430 exec_name = search_path(name, path, X_OK, NULL);
437 * kLIBC execve() has problems when executing scripts.
438 * 1. it fails to execute a script if a directory whose name
439 * is same as an interpreter exists in a current directory.
440 * 2. it fails to execute a script not starting with sharpbang.
441 * 3. it fails to execute a batch file if COMSPEC is set to a shell
442 * incompatible with cmd.exe, such as /bin/sh.
443 * And ksh process scripts more well, so let ksh process scripts.
446 if (!(fp = fopen(exec_name, "rb")))
449 if (!errno && fread(sign, 1, sizeof(sign), fp) != sizeof(sign))
452 if (fp && fclose(fp))
456 !((sign[0] == 'M' && sign[1] == 'Z') ||
457 (sign[0] == 'N' && sign[1] == 'E') ||
458 (sign[0] == 'L' && sign[1] == 'X')))
461 if (errno == ENOEXEC)
464 rsp_name_arg = make_response_file(argv);
467 rsp_argv[0] = argv[0];
468 rsp_argv[1] = rsp_name_arg;
474 pid = spawnve(P_NOWAIT, exec_name, argv, envp);
476 afree(rsp_name_arg, ATEMP);
484 /* close all opened handles */
485 for (fd = 0; fd < NUFILE; fd++) {
486 if (fcntl(fd, F_GETFD) == -1)
492 while ((rc = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
497 /* Is this possible? And is this right? */
501 if (WIFSIGNALED(status))
502 _exit(ksh_sigmask(WTERMSIG(status)));
504 _exit(WEXITSTATUS(status));
507 static struct temp *templist = NULL;
510 add_temp(const char *name)
514 tp = alloc(offsetof(struct temp, tffn[0]) + strlen(name) + 1, APERM);
515 memcpy(tp->tffn, name, strlen(name) + 1);
520 /* alias of unlink() */
521 extern int _std_unlink(const char *);
524 * Replacement for unlink() of kLIBC not supporting to remove files used by
528 unlink(const char *name)
532 rc = _std_unlink(name);
533 if (rc == -1 && errno != ENOENT)
543 struct temp **tpnext;
545 for (tpnext = &templist, tp = templist; tp; tp = *tpnext) {
546 if (_std_unlink(tp->tffn) == 0 || errno == ENOENT) {