OS2/emx/syscall/__spawnve
プログラム起動をつかさどるシステムコール。exec 系と spawn 系の関数はすべて、このシステムコールを呼び出すことで実装されている(emx/src/lib/process 内のライブラリソース参照)。
実行部分のソースは emx/src/os2/process.c、メインの処理は do_spawn()。
究極的には DosExecPgm もしくは DosStartSession のいずれかの API が呼び出される。 ただし、コマンドライン引数は API に渡される前に若干の前処理が行われる。
static LONG spawn_args (char *arg_buf, ULONG arg_buf_size, const char *arg_ptr, ULONG argc, ULONG mode32, UCHAR session, const char *prog_name) { size_t len, size; const char *src, *s, *base; int i, quote, bs, method; base = (const char *)_getname (prog_name); method = 0; if (stricmp (base, "cmd.exe") == 0 || stricmp (base, "4os2.exe") == 0) method = 1; src = arg_ptr; size = 0; if (argc > 0) { ++src; /* skip flags byte */ len = strlen (src) + 1; if (!session) ADDSTR (src, len); src += len; } for (i = 1; i < argc; ++i) { if (i > 1) ADDCHR (' '); ++src; /* skip flags byte */ quote = FALSE; if (*src == 0) quote = TRUE; else if (opt_quote || (mode32 & P_QUOTE)) { if (src[0] == '@' && src[1] != 0) quote = TRUE; else for (s = src; *s != 0; ++s) if (*s == '?' || *s == '*') { quote = TRUE; break; } } if (!quote) for (s = src; *s != 0; ++s) if (*s == ' ' || *s == '\t' || (*s == '"' && method == 1)) { quote = TRUE; break; } if (quote) ADDCHR ('"'); bs = 0; while (*src != 0) { if (*src == '"' && method == 0) { ADDCHRS ('\\', bs+1); bs = 0; } else if (*src == '\\' && method == 0) ++bs; else bs = 0; ADDCHR (*src); ++src; } if (quote) { ADDCHRS ('\\', bs); bs = 0; ADDCHR ('"'); } ++src; } ADDCHR (0); if (!session && argc > 0 && (mode32 & P_TILDE)) { ADDCHR ('~'); src = arg_ptr; for (i = 0; i < argc; ++i) { ++src; /* skip flags byte */ if (*src == 0 || *src == '~') ADDCHR ('~'); len = strlen (src) + 1; ADDSTR (src, len); src += len; } } ADDCHR (0); return size; }
…ちなみにこれ、ランタイムライブラリじゃなくて emx カーネル中のコード(do_spawn から呼び出される)なので emx アプリからはほとんど回避不能だったりなんかして。
問題点としては、
- 引数に対するクゥオーティングやエスケープを禁止することができない。あらかじめユーザ側で前処理を行った引数を渡すと、たぶん子プロセスには期待通りの引数が伝わらない。
- ソース見る限り DBCS 文字列は考慮されていないようだ。シフト JIS 文字列を食わせるとバックスラッシュのエスケープが余分にはいりそう…もっとも子プロセス側も emx アプリなら辻褄が合っちゃいそうな気もするけど。
- cmd と 4os2 だけは特別扱いなので、余計なクゥオーティング/エスケーピングをしたくない場合は cmd.exe 経由で実行するという手もあるが、これはこれでコマンドライン文字列の整形が厄介だ。