Книга: Linux программирование в примерах

4.4.4. Пример: Unix cat

4.4.4. Пример: Unix cat

Как и было обещано, вот версия cat V7[47]. Она начинается с проверки опций, cat V7 принимает единственную опцию, -u, для осуществления небуферированного вывода.

Общая структура сходна с той, которую мы видели ранее; программа перечисляет файлы, указанные в аргументах командной строки и читает каждый файл, по одному символу за раз, посылая этот символ в стандартный вывод. В отличие от нашей версии, она использует возможности <stdio.h>. Во многих случаях код, использующий стандартную библиотеку ввода/вывода, проще читать и писать, поскольку все проблемы с буферами скрыты библиотекой.

1  /*
2   * Объединение файлов.
3   */
4
5  #include <stdio.h>
6  #include <sys/types.h>
7  #include <sys/stat.h>
8
9  char stdbuf[BUFSIZ];
10
11 main(argc, argv) /* int main(int argc, char **argv) */
12 char **argv;
13 {
14  int fflg = 0;
15  register FILE *fi;
16  register c;
17  int dev, ino = -1;
18  struct stat statb;
19
20  setbuf(stdout, stdbuf);
21  for( ; argc>1 && argv[1][0] == '-'; argc--, argv++) {
22   switch(argv[1][1]) { /* Обработка опций */
23   case 0:
24    break;
25   case 'u':
26    setbuf(stdout, (char*)NULL);
27    continue;
28   }
29   break;
30  }
31  fstat(fileno(stdout), &statb); /* Строки 31-36 объясняются в главе 5 */
32  statb.st_mode &= S_IFMT;
33  if (statb.st_mode != S_IFCHR && statb.st_mode != S_IPBLK) {
34   dev = statb.st_dev;
35   ino = statb.st_ino;
36  }
37  if (argc < 2) {
38   argc = 2;
39   fflg++;
40  }
41  while (--argc > 0) { // Loop over files
42   if (fflg || (*++argv)[0] == '-' && (*argv)[1] == '')
43    fi = stdin;
44   else {
45    if ((fi = fopen(*argv, "r")) == NULL) {
46     fprintf(stderr, "cat: can't open %sn", *argv);
47    continue;
48   }
49  }
50  fstat(fileno(fi), &statb); /* Строки 50-56 объясняются в главе 5 */
51  if (statb.st_dev == dev && statb.st_ino == ino) {
52   fprintf(stderr, "cat: input %s is outputn",
53    fflg ? "-" : *argv);
54   fclose(fi);
55   continue;
56  }
57  while ((c=getc(fi)) != EOF) /* Копировать содержимое в stdout */
58   putchar(с);
59  if (fi != stdin)
60   fclose(fi);
61  }
62  return(0);
63 }

Следует заметить, что программа всегда завершается успешно (строка 62); можно было написать ее так, чтобы отмечать ошибки и указывать их в возвращаемом значении main(). (Механизм завершения процесса и значение различных кодов завершения обсуждаются в разделе 9.1.5.1 «Определение статуса завершения процесса».)

Код, работающий с struct stat и функцией fstat() (строки 31–36 и 50–56), без сомнения, непрозрачен, поскольку мы еще не рассматривали эти функции и не будем рассматривать до следующей главы (Но обратите внимание на использование fileno() в строке 50 для получения нижележащего дескриптора файла, связанного с переменными FILE*.) Идея в основе этого кода заключается в том, чтобы убедиться, что входной и выходной файлы не совпадают. Это предназначено для предотвращения бесконечного роста файла, в случае подобной команды:

$ cat myfile >> myfile /* Добавить копию myfile к себе? */

И конечно же, проверка работает:

$ echo hi > myfile /* Создать файл */
$ v7cat myfile >> myfile /* Попытка добавить файл к себе */
cat: input myfile is output

Если вы попробуете это с ch04-cat, программа продолжит работу, и myfile будет расти до тех пор, пока вы не прервете ее. GNU версия cat осуществляет эту проверку. Обратите внимание, что что-то вроде этого выходит за рамки контроля cat:

$ v7cat < myfile > myfile
cat: input - is output
$ ls -l myfile
-rw-r--r-- 1 arnold devel 0 Mar 24 14:17 myfile

В данном случае это слишком поздно, поскольку оболочка урезала файл myfile (посредством оператора >) еще до того, как cat получила возможность исследовать файл! В разделе 5.4.4.2 «Возвращаясь к V7 cat» мы объясним код с struct stat.

Оглавление книги


Генерация: 0.826. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз