Linux программирование в примерах
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 files42 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); можно было написать ее так, чтобы отмечать ошибки и указывать их в возвращаемом значении
. (Механизм завершения процесса и значение различных кодов завершения обсуждаются в разделе 9.1.5.1 «Определение статуса завершения процесса».)main()Код, работающий с
и функциейstruct stat(строки 31–36 и 50–56), без сомнения, непрозрачен, поскольку мы еще не рассматривали эти функции и не будем рассматривать до следующей главы (Но обратите внимание на использованиеfstat()в строке 50 для получения нижележащего дескриптора файла, связанного с переменнымиfileno().) Идея в основе этого кода заключается в том, чтобы убедиться, что входной и выходной файлы не совпадают. Это предназначено для предотвращения бесконечного роста файла, в случае подобной команды:FILE*$ <b>cat myfile >> myfile</b> /* Добавить копию myfile к себе? */И конечно же, проверка работает:
$ <b>echo hi > myfile</b> /* Создать файл */$ <b>v7cat myfile >> myfile</b> /* Попытка добавить файл к себе */cat: input myfile is outputЕсли вы попробуете это с
, программа продолжит работу, иch04-catбудет расти до тех пор, пока вы не прервете ее. GNU версияmyfileосуществляет эту проверку. Обратите внимание, что что-то вроде этого выходит за рамки контроляcat:cat$ <b>v7cat < myfile > myfile</b>cat: input - is output$ <b>ls -l myfile</b>-rw-r--r-- 1 arnold devel 0 Mar 24 14:17 myfileВ данном случае это слишком поздно, поскольку оболочка урезала файл
(посредством оператораmyfile) еще до того, как>получила возможность исследовать файл! В разделе 5.4.4.2 «Возвращаясь к V7 cat» мы объясним код сcat.struct stat4.5. Произвольный доступ: перемещения внутри файла
До сих пор мы обсуждали последовательный ввод/вывод, при котором данные читаются или записываются с начала файла и продолжаются до его конца. Часто это все, что требуется программе. Однако, возможно осуществление произвольного ввода/вывода; т.е. читать данные из произвольного положения в файле без необходимости предварительного чтения всего, что находится перед этим местом.
Смещение дескриптора файла является положением внутри открытого файла, начиная с которого будет осуществляться следующая операция чтения или записи. Программа устанавливает смещение с помощью системного вызова
:lseek()#include <sys/types.h> /* для off_t; POSIX */#include <unistd.h> /* объявления lseek() и значений whence */off_t lseek(int fd, off_t offset, int whence);Тип
(тип смещения) является знаковым целым, представляющим позиции байтов (смещений от начала) внутри файла. На 32-разрядных системах тип представлен обычно какoff_t. Однако, многие современные системы допускают очень большие файлы, в этом случаеlongможет быть более необычным типом, таким, как C99off_tили какой-нибудь другой расширенный тип.int64_tпринимает три следующих аргумента.lseek()int fdДескриптор открытого файла.
off_t offsetПозиция, в которую нужно переместиться. Интерпретация этого значения зависит от параметра
.whenceможет быть положительным или отрицательным; отрицательные значения перемещают к началу файла; положительные значения перемещают к концу файла.offset