Функция fgets()
(строка 23) принимает указатель на буфер, количество байтов для прочтения и переменную FILE*
для файла, из которого осуществляется чтение. Она читает на один байт меньше указанного, чтобы можно было завершить буфер символом '\0
'. Эта функция подходит, поскольку она позволяет избежать переполнения буфера. Она прекращает чтение, когда встречается с символами конца строки или конца файла; если это символ новой строки, он помещается в буфер. Функция возвращает NULL
при неудаче или значение указателя первого аргумента при успешном завершении.
В этом случае аргументами являются указатель на свободную область буфера, размер оставшейся части буфера и указатель FILE
для чтения.
Комментарии в строках 32–36 очевидны; если встречается нулевой байт, программа выводит сообщение об ошибке и представляет вывод как пустую строку. После компенсирования нулевого байта (строки 30–41) код продолжает работу.
43 /* Обойти только что прочитанный текст. */
44 p += len;
45
46 /* Если последний символ - не конец строки, она не поместилась
47 целиком в буфер. Увеличить буфер и попытаться снова. */
48 if (p[-1] != '\n')
49 goto more_buffer;
50
51 /* Мы получили новую строку, увеличить число строк. */
52 ++nlines;
Строки 43–52 увеличивают указатель на участок буфера за только что прочитанными данными. Затем код проверяет, является ли последний прочитанный символ символом конца строки. Конструкция p[-1]
(строка 48) проверяет символ перед p, также как p[0]
является текущим символом, а p[1]
— следующим. Сначала это кажется странным, но если вы переведете это на язык математики указателей, *(p-1)
, это приобретет больший смысл, а индексированная форма, возможно, проще для чтения.
Если последний символ не был символом конца строки, это означает, что нам не хватило места, и код выходит (с помощью goto
) для увеличения размера буфера (строка 49). В противном случае увеличивается число строк.
54 #if !defined(WINDOWS32) && !defined(__MSDOS__)
55 /* Проверить, что строка завершилась CRLF; если так,
56 игнорировать CR. */
57 if ((p - start) > 1 && p[-2] == '\r')
58 {
59 --p;
60 p[-1] = '\n';
61 }
62 #endif
Строки 54–62 обрабатывают вводимые строки, следующие соглашению Microsoft по завершению строк комбинацией символов возврата каретки и перевода строки (CR-LF), а не просто символом перевода строки (новой строки), который является соглашением Linux/Unix. Обратите внимание, что #ifdef
на этих системах автоматически осуществляет это преобразование. Это верно также для других не-Unix систем, поддерживающих стандартный С.
64 backslash = 0;
65 for (p2 = p - 2; p2 >= start; --p2)
66 {
67 if (*p2 != '\\')
68 break;
69 backslash = !backslash;
70 }
71
72 if (!backslash)
73 {
74 p[-1] = '\0';
75 break;
76 }
77
78 /* Это была комбинация обратный слеш/новая строка. Если есть
79 место, прочесть еще одну строку. */
80 if (end - p >= 80)
81 continue;
82
83 /* В конце буфера нужно больше места, поэтому выделить еще.
84 Позаботиться о сохранении текущего смещения в p. */
85 more_buffer:
86 {
87 unsigned long off = p - start;
88 ebuf->size *= 2;
89 start = ebuf->buffer=ebuf->bufstart=(char*)xrealloc(start,
90 ebuf->size);
91 p = start + off;
92 end = start + ebuf->size;
93 *p = '\0';
94 }
95 }
До сих пор мы имели дело с механизмом получения в буфер по крайней мере одной полной строки. Следующий участок обрабатывает случай строки с продолжением. Хотя он должен гарантировать, что конечный символ обратного слеша не является частью нескольких обратных слешей в конце строки. Код проверяет, является ли общее число таких символов четным или нечетным путем простого переключения переменной backslash
из 0 в 1 и обратно. (Строки 64–70.)