OS2/emx/syscall/__spawnve

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 経由で実行するという手もあるが、これはこれでコマンドライン文字列の整形が厄介だ。