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
Если вы попробуете это с