Linux программирование в примерах | страница 83



>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] == '\0')

>43    fi = stdin;

>44   else {

>45    if ((fi = fopen(*argv, "r")) == NULL) {

>46     fprintf(stderr, "cat: can't open %s\n", *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 output\n",

>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

Если вы попробуете это с