coreutils4.5.1 paste.c源码分析

coreutils4.5.1 paste.c源码分析
今天天冷,几天没出门了。把新买的电热取暖器放在电脑房,然后拉上窗帘,开始读源码,学习linus,很好玩。
paste这个命令有啥应用场景呢?先学习使用:
 head -n 100 a1 a2
==> a1 <==
this is
hell owrld

==> a2 <==
this ath
paste a1 a2
this is         this ath
hell owrld
 paste -d: a1 a2
this is :this ath
hell owrld:
paste -s a1 a2
this is         hell owrld
this ath
注意,我在另一个文件夹下新建立了两个文件,然后随便折腾。
可以看到,-s选项,把每个文件变成一行。
-d:以:为分隔符,把两个文件每个文件行为一栏,进行合并。
开始读程序时,有些害怕,毕竟每进入一个新的程序,就象进入一个陌生的场景,生怕其中的怪兽吃了自己,人总是害怕陌生的环境。
昨天睡得香,今天早上就起来看。发现慢慢也看出了眉目。请听我讲。


/* If nonzero, merge subsequent lines of each file rather than
   corresponding lines from each file in parallel. */
static int serial_merge;
这个全局全量,当设置-s选项时,把每个文件变成一行进行汇总。在getopt中处理如下:
  serial_merge = 0;
  delims = default_delims;
  strcpy (delims, "\t");
  strcpy (zero_delims, "\\0");

  while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'd':
      /* Delimiter character(s). */
      if (optarg[0] == '\0')
        optarg = zero_delims;
      delims = optarg;
      break;

    case 's':
      serial_merge++;
      break;
开始设置好变量serial_merge为0,当有-s选项时,值为1,而后面进行处理。

  if (!serial_merge)
    exit_status = paste_parallel (argc - optind, &argv[optind]);
  else
    exit_status = paste_serial (argc - optind, &argv[optind]);
也就是,要达到每个文件变成一行的目的,在子程序paste_serial中实现。
首先,这个子程序很有意思,
static int
paste_serial (int nfiles, char **fnamptr)
也就是说,fnamptr是个字符串数组指针。类似于int main(int argc,char *argv[])
中的,argv,当时我在想,如何进行文件的递进呢?也就是说,当前文件处理完了,开始处理下一文件,是这样的:
  for (; nfiles; nfiles--, fnamptr++)
    {
    }
看到没,nfiles表示文件数量,而fnamptr++表示开始处理下一个文件,太神奇了。我开始总是在找++,后来在循环条件中找到,真厉害。

static int
paste_serial (int nfiles, char **fnamptr)
{
  int errors = 0;        /* 1 if open or read errors occur. */
  register int charnew, charold; /* Current and previous char read. */
  register char *delimptr;    /* Current delimiter char. */
  register FILE *fileptr;    /* Open for reading current file. */

  for (; nfiles; nfiles--, fnamptr++)
    {
      if (STREQ (*fnamptr, "-"))
    {
      have_read_stdin = 1;
      fileptr = stdin;
    }
      else
    {
      fileptr = fopen (*fnamptr, "r");
      if (fileptr == NULL)
        {
          error (0, errno, "%s", *fnamptr);
          errors = 1;
          continue;
        }
    }

      delimptr = delims;    /* Set up for delimiter string. */

      charold = getc (fileptr);
      if (charold != EOF)
    {
      /* `charold' is set up.  Hit it!
         Keep reading characters, stashing them in `charnew';
         output `charold', converting to the appropriate delimiter
         character if needed.  After the EOF, output `charold'
         if it's a newline; otherwise, output it and then a newline. */

      while ((charnew = getc (fileptr)) != EOF)
        {
          /* Process the old character. */
          if (charold == '\n')
        {
          if (*delimptr != EMPTY_DELIM)
            putc (*delimptr, stdout);

          if (++delimptr == delim_end)
            delimptr = delims;
        }
          else
        putc (charold, stdout);

          charold = charnew;
        }

      /* Hit EOF.  Process that last character. */
      putc (charold, stdout);
    }

      if (charold != '\n')
    putc ('\n', stdout);

      if (ferror (fileptr))
    {
      error (0, errno, "%s", *fnamptr);
      errors = 1;
    }
      if (fileptr == stdin)
    clearerr (fileptr);    /* Also clear EOF. */
      else if (fclose (fileptr) == EOF)
    {
      error (0, errno, "%s", *fnamptr);
      errors = 1;
    }
    }
  return errors;
}
这个函数较长,我就用自己的理解把它完成的事说一下。
对每个需要合并的文件进行处理
    对当前文件,判断是否是标准输入,并进行处理。
    对当前文件,把每一个换行符替换为另一字符,直到把文件处理完

这样,这个程序也基本算看完了,其中还有另一个-d选项,没看懂,另个,-s选项中,的处理逻辑还有些难懂,先算看完了。
其实要看懂程序和要看懂到能修改程序是有很大区别的。如果只是满足于大致看懂,应该很快。重点是抓大的逻辑块。建立处理事务的高层抽象。
而如果要能修改程序的话,那要求就更高些。反正,这些程序我可写不出来。
 

猜你喜欢

转载自blog.csdn.net/woshiyilitongdouzi/article/details/85598929