Текст книги "Linux программирование в примерах"
Автор книги: Арнольд Роббинс
Жанры:
Программирование
,сообщить о нарушении
Текущая страница: 14 (всего у книги 55 страниц)
cat
В разделе 4.4.4 «Пример: Unix cat» мы обещали вернуться к программе V7 cat
, чтобы посмотреть, как она использует системный вызов stat()
. Первая группа строк, использовавшая ее, была такой:
31 fstat(fileno(stdout), &statb);
32 statb.st_mode &= S_IFMT;
33 if (statb.st_mode != S_IFCHR && statb.st_mode != S_IFBLK) {
34 dev = statb.st_dev;
35 ino = statb.st_ino;
36 }
Этот код теперь должен иметь смысл. В строке 31 вызывается fstat()
для стандартного вывода, чтобы заполнить структуру statb
. Строка 32 отбрасывает всю информацию в statb.st_mode
за исключением типа файла, используя логическое AND с маской S_IFMT
. Строка 33 проверяет, что используемый для стандартного вывода файл не является файлом устройства. В таком случае программа сохраняет номера устройства и индекса в dev
и ino
. Эти значения затем проверяются для каждого входного файла в строках 50–56.
50 fstat(fileno(fi), &statb);
51 if (statb.st_dev == dev && statb.st_ino == ino) {
52 fprintf(stderr, "cat: input %s is outputn",
53 ffig ? "-" : *argv);
54 fclose(fi);
55 continue;
56 }
Если значения st_dev
и st_ino
входного файла совпадают с соответствующими значениями выходного файла, cat
выдает сообщение и продолжает со следующего файла, указанного в командной строке.
Проверка сделана безусловно, хотя dev
и ino
устанавливаются, лишь если вывод не является файлом устройства. Это срабатывает нормально из-за того, как эти переменные объявлены:
int dev, ino = -1;
Поскольку ino
инициализирован значением (-1), ни один действительный номер индекса не будет ему соответствовать[60]60
Это утверждение было верно для V7, на современных системах больше нет таких гарантий – Примеч. автора.
[Закрыть]. То, что dev
не инициализирован так, является небрежным, но не представляет проблемы, поскольку тест в строке 51 требует, чтобы были равными значения как устройства, так и индекса. (Хороший компилятор выдаст предупреждение, что dev
используется без инициализации: 'gcc -Wall
' сделает это.)
Обратите также внимание, что ни один вызов fstat()
не проверяется на ошибки. Это также небрежность, хотя не такая большая, маловероятно, что fstat()
завершится неудачей с действительным дескриптором файла
Проверка того, что входной файл не равен выходному файлу, осуществляется лишь для файлов, не являющихся устройствами. Это дает возможность использовать cat
для копирования ввода из файлов устройств в самих себя, как в случае с терминалами:
$ tty /* Вывести имя устройства текущего терминала */
/dev/pts/3
$ cat /dev/pts/3 > /dev/pts/3 /* Копировать ввод от клавиатуры на экран */
this is a line of text /* Набираемое в строке */
this is a line of text /* cat это повторяет */
В общем, символические ссылки ведут себя подобно прямым ссылкам; файловые операции, такие, как open()
и stat()
, применяются к указываемому файлу вместо самой символической ссылки. Однако, бывают моменты, когда в самом деле необходимо работать с символической ссылкой вместо файла, на которую она указывает.
По этой причине существует системный вызов lstat()
. Он действует точно также, как stat()
, но если проверяемый файл окажется символической ссылкой, возвращаемые сведения относятся к символической ссылке, а не к указываемому файлу. А именно:
• S_ISLNK(sbuf.st_mode)
будет true
.
• sbuf.st_size
содержит число байтов в имени указываемого файла.
Мы уже видели, что системный вызов symlink()
создает символическую ссылку. Но если дана существующая символическая ссылка, как можно получить имя файла, на которую она указывает? (Очевидно, ls
может получить это имя; поэтому мы должны быть способны это сделать.)
Открывание ссылки с помощью open()
для чтения ее с использованием read()
не будет работать, open()
следует по ссылке на указываемый файл. Таким образом, символические ссылки сделали необходимым дополнительный системный вызов, который называется readlink()
:
#include
int readlink(const char *path, char *buf, size_t bufsiz);
readlink()
помещает содержимое символической ссылки, на имя которой указывает path
, в буфер, на который указывает buf
. Копируется не более bufsiz
символов. Возвращаемое значение равно числу символов, помещенных в buf
, либо -1, если возникла ошибка, readlink()
не вставляет завершающий нулевой байт.
Обратите внимание, что если буфер, переданный readlink()
, слишком маленький, информация будет потеряна; полное имя указываемого файла будет недоступно. Чтобы использовать readlink()
должным образом, вы должны делать следующее:
1. Используйте lstat()
, чтобы убедиться, что это символическая ссылка.
2. Убедитесь, что ваш буфер для содержимого символической ссылки составляет по крайней мере 'sbuf.st_size + 1
' байтов; '+ 1
' нужно для завершающего нулевого байта, чтобы сделать буфер годной к употреблению строкой С.
3. Вызовите readlink()
. Не мешает проверить, что возвращенное значение равно sbuf.st_size
.
4. Добавьте '