Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
Если ни одно из этих условий не отмечается, размер вычисляется либо в соответствии с размером в struct stat, либо в соответствии с числом блоков диска (строки 73–75) Это решение основывается на переменной apparent_size, которая установлена при использовании опции --apparent-size.
78 if (first_call)
79 {
80 n_alloc = info->level + 10; /* Allocate arrays */
81 sum_ent = XCALLOC(uintmax_t, n_alloc); /* to hold sums */
82 sum_subdir = XCALLOC(uintmax_t, n_alloc);
83 }
84 else
85 {
86 /* FIXME: Стыдно, что нам нужно приводить к типу size_t для избежания
87 предупреждений gcc о 'сравнении между знаковым и беззнаковым'.
88 Возможно, неизбежно, при условии, что члены структуры FTW
89 имеют тип 'int' (исторически), так как мне нужно, чтобы переменные
90 вроде n_alloc и prev_level имели осмысленные типы. */
91 if (n_alloc <= (size_t)info->level)
92 {
93 n_alloc = info->level * 2; /* Удвоить сумму */
94 sum_ent = XREALLOC(sum_ent, uintmax_t, realloc); /* И выделить повторно */
95 sum_subdir = XREALLOC(sum_subdir, uintmax_t, n_alloc);
96 }
97 }
98
99 size_to_print = size;
Строки 78–97 управляют динамической памятью, используемой для хранения статистики о размере файла, first_call является статической переменной (строка 12), которая равна true при первом вызове process_file(). В этом случае вызывается calloc() (через упаковывающий макрос в строках 81–82; это обсуждалось в разделе 3.2.1.8 «Пример чтение строк произвольной длины»). Остальную часть времени first_call равно false, и используется realloc() (снова через упаковывающий макрос, строки 91–96).
Строка 99 заносит значение size в size_to_print; эта переменная может обновляться в зависимости от того, должна ли она включать размеры дочерних элементов. Хотя size могла бы использоваться повторно, отдельная переменная упрощает чтение кода.
101 if (!first_call)
102 {
103 if ((size_t)info->level == prev_level)
104 {
105 /* Обычно самый частый случай. Ничего не делать. */
106 }
107 else if ((size_t)info->level > prev_level)
108 {
109 /* Нисхождение по иерархии.
110 Очистить аккумуляторы для *всех* уровней между prev_level
111 и текущим. Глубина может значительно меняться,
112 например, от 1 до 10. */
113 int i;
114 for (i = prev_level +1; i <= info->level; i++)
115 sum_ent[i] = sum_subdir[i] = 0;
116 }
117 else /* info->level < prev_level */
118 {
119 /* Восхождение по иерархии.
120 nftw обрабатывает каталог лишь после всех элементов,
121 в которых был обработан каталог. Когда глубина уменьшается,
122 передать суммы от детей (prev_level) родителям.
123 Здесь текущий уровень всегда меньше, чем
124 предыдущий. */
125 assert (<size_t) info->level == prev_level - 1);
126 size_to_print += sum_ent[prev_level];
127 if (!opt_separate_dirs)
128 size_to_print += sum_subdir[prev_level];
129 sum_subdir[info->level] += (sum_ent[prev_level]
130 + sum_subdir[prev_level]);
131 }
132 }
Строки 101–132 сравнивают текущий уровень с предыдущим. Возможны три случая.
Уровни те же самые
В этом случае нет необходимости беспокоиться о статистике дочерних элементов. (Строки 103–106.)
Текущий уровень выше предыдущего
В этом случае мы спустились по иерархии, и статистику нужно восстановить (строки 107–116). Термин «аккумулятор» в комментарии подходящий: каждый элемент аккумулирует общее дисковое пространство, использованное на этом уровне. (На заре вычислительной техники регистры центрального процессора часто назывались «аккумуляторами».)
Текущий уровень ниже предыдущего
В этом случае мы завершили обработку всех дочерних элементов каталога и только что вернулись обратно в родительский каталог (строки 117–131). Код обновляет суммы, включая size_to_print.
134 prev_level = info->level; /* Установить статические переменные */
135 first_call = 0;
136
137 /* Включить элемент каталога в общую сумму для содержащего
138 каталога, если не указана --separate-dirs (-S). */
139 if (!(opt_separate_dirs && IS_FTW_DIR_TYPE(file_type)))
140 sum_ent[info->level] += size;
141
142 /* Даже если каталог нельзя прочесть или перейти в него,
143 включить его размер в общую сумму, ... */
144 tot_size += size;
145
146 /* ...но не выводить для него итог, поскольку без размера(-ов)
147 потенциальных элементов, это может сильно запутывать. */
148 if (file_type == FTW_DNR || file_type == FTW_DCH)
149 return 0;
150
151 /* Если мы не считаем элемент, например, потому что это прямая
152 ссылка на файл, который уже посчитан (и --count-links), не
153 выводить для него строку. */
154 if (!print)
155 return 0;
Строки 134–135 устанавливают статические переменные prev_level и first_call таким образом, что они содержат правильные значения для последующего вызова process_file(), гарантируя, что весь предыдущий код работает правильно.
Строки 137–144 выверяют статистику на основе опций и типа файла. Комментарии и код достаточно просты. Строки 146–155 сразу завершают функцию, если сведения не должны выводиться.
157 /* FIXME: Это выглядит подозрительно годным для упрощения. */
158 if ((IS_FTW_DIR_TYPE(file_type) &&
159 (info->level <= max_depth || info->level == 0))
160 || <(opt_all && info->level <= max_depth) || info->level == 0))
161 {
162 print_only_size(size_to_print);
163 fputc('t', stdout);
164 if (arg_length)
165 {
166 /* Вывести имя файла, но без суффикса каталога '.' или '/.'
167 который мы, возможно, добавили в main. */
168 /* Вывести все до добавленной нами части. */
169 fwrite(file, arg_length, 1, stdout);
170 /* Вывести все после добавленного нами. */
171 fputs(file + arg_length + suffix_length
172 + (file[arg_length + suffix_length] == '/'), stdout);
173 }
174 else
175 {
176 fputs(file, stdout);
177 }
178 fputc('n', stdout);
179 fflush(stdout);
180 }
181
182 return 0;
183 }
Условие в строках 158–160 сбивает с толку, и комментарий в строке 157 указывает на это. Условие утверждает: «Если (1a) файл является каталогом и (1b) уровень меньше максимального для вывода (переменные — -max-depth и max_depth) или нулевой, или (2a) должны быть выведены все файлы и уровень меньше, чем максимальный для вывода, или (2b) уровень нулевой», тогда вывести файл. (Версия du после 5.0 использует в этом случае несколько менее запутанное условие.)
Строки 162–179 осуществляют вывод. Строки 162–163 выводят размер и символ TAB Строки 164–173 обрабатывают специальный случай. Это объяснено далее в du.c, в строках файла 524–529:
524 /* При разыменовании лишь аргументов командной строки мы
525 используем флаг nftw FTW_PHYS, поэтому символическая ссылка
526 на каталог, указанная в командной строке, в норме не
527 разыменовывается. Для решения этого мы идем на издержки,
528 сначала добавляя '/.' (или '.'), а затем удаляем их каждый раз
529 при выводе имени производного файла или каталога. */
В этом случае arg_length равен true, поэтому строки 164–173 должны вывести первоначальное имя, а не измененное В противном случае строки 174–177 могут вывести имя как есть.
Фу! Куча кода. Мы находим, что это верхний уровень спектра сложности, по крайней мере, насколько это может быть просто представлено в книге данного содержания. Однако, он демонстрирует, что код из реальной жизни часто бывает сложным. Лучшим способом справиться с этой сложностью является ясное именование переменных и подробные комментарии du.с в этом отношении хорош; мы довольно легко смогли извлечь и изучить код без необходимости показывать все 735 строк программы!
8.6. Изменение корневого каталога: chroot()
Текущий рабочий каталог, установленный с помощью chdir() (см. раздел 8.4.1 «Изменение каталога — chdir() и fchdir()»), является атрибутом процесса, таким же, как набор открытых файлов. Он также наследуется новыми процессами.
Менее известным является то, что у каждого процесса есть также текущий корневой каталог. Это именно на этот каталог ссылается имя пути /. В большинстве случаев корневые каталоги процесса и системы идентичны. Однако, суперпользователь может изменить корневой каталог с помощью (как вы догадались) системного вызова chroot():