Выбрать главу

Если последний символ не был символом конца строки, это означает, что нам не хватило места, и код выходит (с помощью 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 исключает этот код на платформе Microsoft, очевидно, библиотека <stdio.h> на этих системах автоматически осуществляет это преобразование. Это верно также для других не-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.)

Если число четное, условие '!backshlash' (строка 72) будет истинным. В этом случае конечный символ конца строки замещается байтом NUL, и код выходит из цикла.

С другой стороны, если число нечетно, строка содержит четное число пар обратных слешей (представляющих символы \\, как в С), и конечную комбинацию символов обратного слеша и конца строки.[43] В этом случае, если в буфере остались по крайней мере 80 свободных байтов, программа продолжает чтение в цикле следующей строки (строки 78–81). (Использование магического числа 80 не очень здорово; было бы лучше определить и использовать макроподстановку.)

По достижении строки 83 программе нужно больше места в буфере. Именно здесь вступает в игру динамическое управление памятью. Обратите внимание на комментарий относительно сохранения значения p (строки 83-84); мы обсуждали это ранее в терминах повторной инициализации указателей для динамической памяти. Значение end также устанавливается повторно. Строка 89 изменяет размер памяти.

Обратите внимание, что здесь вызывается функция xrealloc(). Многие программы GNU используют вместо malloc() и realloc() функции-оболочки, которые автоматически выводят сообщение об ошибке и завершают программу, когда стандартные процедуры возвращают NULL. Такая функция-оболочка может выглядеть таким образом:

extern const char *myname; /* установлено в main() */

void *xrealloc(void *ptr, size_t amount) {

 void *p = realloc(ptr, amount);

 if (p == NULL) {

  fprintf(stderr, "%s: out of memory!\n", myname);

  exit(1);

 }

 return p;

}

Таким образом, если функция xrealloc() возвращается, она гарантированно возвращает действительный указатель. (Эта стратегия соответствует принципу «проверки каждого вызова на ошибки», избегая в то же время беспорядка в коде, который происходит при таких проверках с непосредственным использованием стандартных процедур.) Вдобавок, это позволяет эффективно использовать конструкцию 'ptr = xrealloc(ptr, new_size)', против которой мы предостерегали ранее.

Обратите внимание, что не всегда подходит использование такой оболочки. Если вы сами хотите обработать ошибки, не следует использовать оболочку. С другой стороны, если нехватка памяти всегда является фатальной ошибкой, такая оболочка вполне удобна.

97   if (ferror(ebuf->fp))

98    pfatal_with_name(ebuf->floc.filenm);

99

100  /* Если обнаружено несколько строк, возвратить их число.

101     Если не несколько, но _что-то_ нашли, значит, прочитана

102     последняя строка файла без завершающего символа конца

103     строки; вернуть 1. Если ничего не прочитано, это EOF;

104     возвратить -1. */

105  return nlines ? nlines : p == ebuf->bufstart ? -1 : 1;

106 }

В заключение, функция readline() проверяет ошибки ввода/вывода, а затем возвращает описательное значение. Функция pfatal_with_name() (строка 98) не возвращается.[44]

3.2.1.9. Только GLIBC: чтение целых строк: getline() и getdelim()

Теперь, когда вы увидели, как читать строки произвольной длины, вы можете сделать вздох облегчения, что вам не нужно самим писать такую функцию. GLIBC предоставляет вам для этого две функции:

#define _GNU_SOURCE 1 /* GLIBC */

#include <stdio.h>

#include <sys/types.h> /* для ssize_t */

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);

Определение константы _GNU_SOURCE вводит объявления функций getline() и getdelim(). В противном случае они неявно объявлены как возвращающие int. Для объявления возвращаемого типа ssize_t нужен файл <sys/types.h>. (ssize_t является «знаковым size_t». Он предназначен для такого же использования, что и size_t, но в местах, где может понадобиться использование также и отрицательных значений.)

вернуться

43

Этот код несет с собой аромат практического опыта, не удивительно было узнать, что более ранние версии просто проверяли наличие обратного слеша перед символом конца строки, пока кто-то не пожаловался, что он не работает, когда в конце строки есть несколько обратных слешей — Примеч. автора.

вернуться

44

Эта функция завершает выполнение программы — Примеч. науч. ред.