Сначала мы покажем вам, как мы это делали первоначально. Вот (сокращенная версия)
flags2str
из ранней версии
gawk
(3.0.6):
1 /* flags2str --- делает значения флагов удобочитаемыми */
2
3 char *
4 flags2str(flagval)
5 int flagval;
6 {
7 static char buffer[BUFSIZ];
8 char *sp;
9
10 sp = buffer;
11
12 if (flagval & MALLOC) {
13 strcpy(sp, "MALLOC");
14 sp += strlen(sp);
15 }
16 if (flagval & TEMP) {
17 if (sp >= buffer)
18 *sp++ = '|';
19 strcpy(sp, "TEMP");
20 sp += strlen(sp);
21 }
22 if (flagval & PERM) {
23 if (sp != buffer)
24 *sp++ = '|';
25 strcpy(sp, "PERM");
26 sp += strlen(sp);
27 }
/* ...многое
то же самое, опущено для краткости... */
82
83 return buffer;
84 }
(Номера строк даны относительно начала функции.) Результатом является строка, что- то наподобие "
MALLOC | PERM | NUMBER
". Каждый флаг тестируется отдельно, и если он присутствует, действие каждый раз одно и то же: проверка того, что он не в начале буфера и что можно добавить символ '
|
', скопировать строку на место и обновить указатель. Сходные функции существовали для форматирования и отображения других видов флагов в программе.
Этот код является повторяющимся и склонным к ошибкам, и для
gawk
3.1 мы смогли упростить и обобщить его. Вот как
gawk
делает это сейчас. Начиная с этого определения в
awk.h
:
/* для целей отладки */
struct flagtab {
int val; /* Целое значение флага */
const char *name; /* Строковое имя */
};
Эту структуру можно использовать для представления любого набора флагов с соответствующими строковыми значениями. Каждая отдельная группа флагов имеет соответствующую функцию, которая возвращает печатное представление флагов, которые установлены в настоящее время. Из
eval.c
:
/* flags2str --- делает значения флагов удобочитаемыми */
const char *flags2str(int flagval) {
static const struct flagtab values[] = {
{ MALLOC, "MALLOC" },
{ TEMP, "TEMP" },
{ PERM, "PERM" },
{ STRING, "STRING" },
{ STRCUR, "STRCUR" },
{ NUMCUR, "NUMCUR" },
{ NUMBER, "NUMBER" },
{ MAYBE_NUM, "MAYBE_NUM" },
{ ARRAYMAXED, "ARRAYMAXED" },
{ FUNC, "FUNC" },
{ FIELD, "FIELD" },
{ INTLSTR, "INTLSTR" },
{ 0, NULL },
};
return genflags2str(flagval, values);
}
flags2str
определяет массив сопоставлений флагов со строками. По соглашению, значение флага 0 означает конец массива. Код вызывает для осуществления работы
genflags2str
(«общий флаг в строку»).
getflags2str
является процедурой общего назначения, которая преобразует значение флага в строку. Из
eval.c
:
1 /* genflags2str --- общая процедура для преобразования значения флага в строковое представление */
(Номера строк приведены относительно начала функции, а не файла.) Как и в предыдущей версии, идея заключалась в заполнении статического буфера строковыми значениями, такими, как "
MALLOC | PERM | STRING | MAYBE_NUM
", и возвращении адреса этого буфера. Мы вскоре обсудим причины использования статического буфера; сначала давайте исследуем код.
Указатель
sp
отслеживает положение следующего пустого слота в буфере, тогда как
space_left
отслеживает количество оставшегося места; это уберегает нас от переполнения буфера.
Основную часть функции составляет цикл (строка 12), проходящий через массив значений флагов. Когда флаг найден (строка 13), код вычисляет, сколько места требуется строке (строка 18) и проверяет, осталось ли столько места (строки 19–20).
Тест '
sp ! = buffer
' для первого значения флага завершается неудачей, возвращая 0. Для последующих флагов тест дает значение 1. Это говорит нам, что между значениями должен быть вставлен разделительный символ '
|
'. Добавляя результат (1 или 0) к длине строки, мы получаем правильное значение
space_needed
. Тот же тест с той же целью проводится в строке 22 для проверки строк 23 и 24, которые вставляют символ '