Нам был нужен способ воспроизведения проблемы на своей машине разработки, система с неудачей находилась в стороне за девять часовых поясов, а интерактивный запуск GDB через Атлантический океан мучителен. Мы воспроизвели проблему, заставив
optimal_bufsize
проверять значение специальной переменной окружения
AWKBUFSIZE
. Когда ее значение равно
"exact"
,
optimal_bufsize
всегда возвращает размер файла, каким бы он ни был. Если значением
AWKBUFSIZE
является какое-нибудь целое число, функция возвращает это число. В противном
случае, функция возвращается к прежнему алгоритму. Это дает нам возможность запускать тесты, не требуя постоянной перекомпиляции
gawk
. Например,
$ AWKBUFSIZE=42 make check
Это запускает тестовый набор
gawk
с использованием размера буфера в 42 байта. (Тестовый набор проходит.) Вот модифицированная версия
optimal_bufsize
:
1 /* optimal_bufsize --- определение оптимального размера буфера */
2
3 /*
4 * В целях отладки усовершенствуйте это следующим образом:
5 *
6 * Всегда используйте stat для файла, буфер stat используется кодом
7 * более высокого уровня.
8 * if (AWKBUFSIZE == "exact")
9 * return the file size
10 * else if (AWKBUFSIZE == число)
11 * всегда возвращать это число
12 * else
13 * if размер < default_blocksize
14 * return размер
15 * else
16 * return default_blocksize
17 * end if
18 * end if
19 *
20 * Приходится повозиться, чтобы иметь дело с AWKBUFSIZE лишь
21 * однажды, при первом вызове этой процедуры, а не при каждом
22 * ее вызове. Производительность, знаете ли.
23 */
24
25 size_t
26 optimal_bufsize(fd, stb)
27 int fd;
28 struct stat *stb;
29 {
30 char *val;
31 static size_t env_val = 0;
32 static short first = TRUE;
33 static short exact = FALSE;
34
35 /* обнулить все члены, на случай, если ОС их не использует. */
36 memset(stb, '\0', sizeof(struct stat));
37
38 /* всегда использовать stat на случай, если stb используется кодом более высокого уровня */
39 if (fstat(fd, stb) == -1)
40 fatal("can't stat fd %d (%s)", fd, strerror(errno));
41
42 if (first) {
43 first = FALSE;
44
45 if ((val = getenv("AWKBUFSIZE")) != NULL) {
46 if (strcmp(val, "exact") == 0)
47 exact = TRUE;
48 else if (ISDIGIT(*val)) {
49 for (; *val && ISDIGIT(*val); val++)
50 env_val = (env_val * 10) + *val - '0';
51
52 return env_val;
53 }
54 }
55 } else if (!exact && env_val > 0)
56 return env_val;
57 /* else
58
обрабатывать дальше */
59
60 /*
61 * System V.n, n < 4, не имеет в структуре stat размера системного
62 * блока файла. Поэтому нам нужно осуществить разумную догадку.
63 * Мы используем BUFSIZ из stdio, поскольку именно это имелось
76 return stb->st_size; /* использовать размер файла*/
77
78 return DEFBLKSIZE;
79 }
Комментарий в строках 3–23 объясняет алгоритм. Поскольку поиск переменных окружения может быть затратным и его нужно осуществить лишь однажды, функция использует для сбора соответствующих сведений в первый раз несколько статических переменных.
Строки 42–54 выполняются лишь при первом вызове функции. Строка 43 обеспечивает это условие, устанавливая в
first
значение
false
. Строки 45–54 обрабатывают переменную окружения, разыскивая либо строку
"exact"
, либо число. В последнем случае оно преобразуется из строкового значения в десятичное, сохраняясь в
env_val
. (Возможно, нам следовало бы использовать здесь
strtoul
; в свое время это не пришло нам на ум.)
Строка 55 выполняется каждый раз, кроме первого. Если было представлено числовое значение, условие будет истинным, и возвращается это значение (строка 56). В противном случае, исполнение переходит к оставшейся части функции.