ls带来的困惑

    技术2022-05-11  23

    快乐虾

    http://blog.csdn.net/lights_joy/

    lights@hb165.com

     

    本文适用于

    Cygwin checkout-2008-09-28

    vs2008

     

    欢迎转载,但请保留作者信息

     

    继日前在vs2008下编译成功bash-4之后,开始编译linux下的基本应用程序。先瞄准的目标是CoreUtils-7.6,因为这个包里面含有linux的常用命令,包括cp, ls这样再熟悉不过的东西。

    先编译的是ls,洋洋洒洒4000多行的程序,有点出乎意料。不过更出乎意料的是当使用命令:

    ls *.exe

    进行调试的时候,居然发现这个时候传递给main函数的argc值不是期望的2,而是随文件个数变化的值!argv参数里面也不是“*.exe”,而是列出了当前目录下的所有exe文件!

    真让人怀疑当初的C语言是不是白学了。

    cygwin的代码,发现在它初始化的时候有一个相关的处理:

             PWCHAR wline = GetCommandLineW ();

             size_t size = sys_wcstombs (NULL, 0, wline);

             char *line = (char *) alloca (size);

             sys_wcstombs (line, size, wline);

     

             /* Scan the command line and build argv.  Expand wildcards if not

             called from another cygwin process. */

             build_argv (line, __cyg_argv, __cyg_argc,

                  NOTSTATE (myself, PID_CYGPARENT) && allow_glob);

     

             /* Convert argv[0] to posix rules if it's currently blatantly

             win32 style. */

             if ((strchr (__cyg_argv[0], ':')) || (strchr (__cyg_argv[0], '//')))

             {

                  char *new_argv0 = (char *) cyg_malloc (NT_MAX_PATH);

                  cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, __cyg_argv[0],

                       new_argv0, NT_MAX_PATH);

                  __cyg_argv[0] = (char *) cyg_realloc (new_argv0, strlen (new_argv0) + 1);

             }

    这里将根据windows传递进来的命令行构建__argc__argv这两个全局变量,然后将可执行文件的文件名转换为posix路径,比如传递进来的可执行文件名f:/etools/debug/bin/ls.exe,经过转换就变成了/bin/ls,这也是在main函数的argv[0]中看到的字符串。

    取得argv的关键操作由build_argv函数完成,看看:

    /* Build argv, argc from string passed from Windows.  */

     

    static void __stdcall

    build_argv (char *cmd, char **&argv, int &argc, int winshell)

    {

         int argvlen = 0;

         int nesting = 0;       // monitor "nesting" from insert_file

     

         argc = 0;

         argvlen = 0;

         argv = NULL;

     

         /* Scan command line until there is nothing left. */

         while (*cmd)

         {

             /* Ignore spaces */

             if (issep (*cmd))

             {

                  cmd++;

                  continue;

             }

     

             /* Found the beginning of an argument. */

             char *word = cmd;

             char *sawquote = NULL;

             while (*cmd)

             {

                  if (*cmd != '"' && (!winshell || *cmd != '/''))

                       cmd++;        // Skip over this character

                  else

                       /* Skip over characters until the closing quote */

                  {

                       sawquote = cmd;

                       cmd = quoted (cmd, winshell && argc > 0);

                  }

                  if (issep (*cmd))  // End of argument if space

                       break;

             }

             if (*cmd)

                  *cmd++ = '/0';         // Terminate `word'

     

             /* Possibly look for @file construction assuming that this isn't

             the very first argument and the @ wasn't quoted */

             if (argc && sawquote != word && *word == '@')

             {

                  if (++nesting > MAX_AT_FILE_LEVEL)

                       api_fatal ("Too many levels of nesting for %s", word);

                  if (insert_file (word, cmd))

                       continue;          // There's new stuff in cmd now

             }

     

             /* See if we need to allocate more space for argv */

             if (argc >= argvlen)

             {

                  argvlen = argc + 10;

                  argv = (char **) cyg_realloc (argv, (1 + argvlen) * sizeof (argv[0]));

             }

     

             /* Add word to argv file after (optional) wildcard expansion. */

             if (!winshell || !argc || !globify (word, argv, argc, argvlen))

             {

                  debug_printf ("argv[%d] = '%s'", argc, word);

                  argv[argc++] = word;

             }

         }

     

         argv[argc] = NULL;

     

         debug_printf ("argc %d", argc);

    }

    注意这一行:

             /* Add word to argv file after (optional) wildcard expansion. */

             if (!winshell || !argc || !globify (word, argv, argc, argvlen))

    正是通过globify函数,argv得以对*号这样的通配符进行扩展。

    /* Perform a glob on word if it contains wildcard characters.

    Also quote every character between quotes to force cyg_glob to

    treat the characters literally. */

    static int __stdcall

    globify (char *word, char **&argv, int &argc, int &argvlen)

    {

         if (*word != '~' && strpbrk (word, "?*[/"/'(){}") == NULL)

             return 0;

    …………….

    }

    这个函数较长,有兴趣的可以自己看cygwin的源码。

     

     

     


    最新回复(0)