if( match(basename, find_PATTERN))
printf("Level#%02d %s\n", level, fullname);
if( !strcmp( basename, "core")){
printf("Найден дамп %s, поиск прекращен.\n", fullname);
return FAILURE;
}
return SUCCESS;
}
void find (char *root, char *pattern){
find_PATTERN = pattern;
walktree(root, find_check, NULL, find_check);
}
А. Богатырев, 1992-95 - 198 - Си в UNIX
/* -------------------------------------------------------------- */
#ifndef TREEONLY
void main(int argc, char *argv[]){
#ifdef FIND
if(argc != 3){ fprintf(stderr, "Arg count\n"); exit(1); }
find(argv[1], argv[2]);
#else
# ifdef RM_REC
for(argv++; *argv; argv++)
recrmdir(*argv);
# else
du( argc == 1 ? "." : argv[1] );
printf( "%ld килобайт в %ld файлах.\n", size, nfiles );
printf( "%ld каталогов.\n", ndirs );
# endif
#endif
exit(0);
}
#endif /*TREEONLY*/
6.1.6. Используя предыдущий алгоритм, напишите программу рекурсивного копирования
поддерева каталогов в другое место. Для создания новых каталогов используйте систем-
ный вызов
mkdir(имя_каталога, коды_доступа);
6.1.7. Используя тот же алгоритм, напишите программу удаления каталога, которая уда-
ляет все файлы в нем и, рекурсивно, все его подкаталоги. Таким образом, удаляется
дерево каталогов. В UNIX подобную операцию выполняет команда
rm -r имя_каталога_корня_дерева
6.1.8. Используя все тот же алгоритм обхода, напишите аналог команды find, который
будет позволять:
- находить все файлы, чьи имена удовлетворяют заданному шаблону (используйте функ-
цию match() из главы "Текстовая обработка");
- находить все выполняемые файлы: обычные файлы S_IFREG, у которых
(st.st_mode & 0111) != 0
Как уже ясно, следует пользоваться вызовом stat для проверки каждого файла.
6.2. Время в UNIX.
6.2.1. Напишите функцию, переводящую год, месяц, день, часы, минуты и секунды в
число секунд, прошедшее до указанного момента с 00 часов 00 минут 00 секунд 1 Января
1970 года. Внимание: результат должен иметь тип long (точнее time_t).
Эта функция облегчит вам сравнение двух моментов времени, заданных в общеприня-
том "человеческом" формате, поскольку сравнить два long числа гораздо проще, чем
сравнивать по очереди годы, затем, если они равны - месяцы, если месяцы равны - даты,
и.т.д.; а также облегчит измерение интервала между двумя событиями - он вычисляется
просто как разность двух чисел. В системе UNIX время обрабатывается и хранится
именно в виде числа секунд; в частности текущее астрономическое время можно узнать
системным вызовом
#include
#include
time_t t = time(NULL); /* time(&t); */
Функция
struct tm *tm = localtime( &t );
А. Богатырев, 1992-95 - 199 - Си в UNIX
разлагает число секунд на отдельные составляющие, содержащиеся в int-полях структуры:
tm_year год (надо прибавлять 1900)
tm_yday день в году 0..365
tm_mon номер месяца 0..11 (0 - Январь)
tm_mday дата месяца 1..31
tm_wday день недели 0..6 (0 - Воскресенье)
tm_hour часы 0..23
tm_min минуты 0..59
tm_sec секунды 0..59
Номера месяца и дня недели начинаются с нуля, чтобы вы могли использовать их в
качестве индексов:
char *months[] = { "Январь", "Февраль", ..., "Декабрь" };
printf( "%s\n", months[ tm->tm_mon ] );
Пример использования этих функций есть в приложении.
Установить время в системе может суперпользователь вызовом
stime(&t);
6.2.2. Напишите функцию печати текущего времени в формате ЧЧ:ММ:СС ДД-МЕС-ГГ.
Используйте системный вызов time() и функцию localtime().
Существует стандартная функция ctime(), которая печатает время в формате:
/* Mon Mar 25 18:56:36 1991 */
#include
#include
main(){ /* команда date */
time_t t = time(NULL);
char *s = ctime(&t);
printf("%s", s);
}
Обратите внимание, что строка s уже содержит на конце символ '\n'.
6.2.3. Структура stat, заполняемая системным вызовом stat(), кроме прочих полей
содержит поля типа time_t st_ctime, st_mtime и st_atime - время последнего изменения
содержимого I-узла файла, время последнего изменения файла и время последнего доступа
к файлу.
- Поле st_ctime изменяется (устанавливается равным текущему астрономическому вре-
мени) при применении к файлу вызовов creat, chmod, chown, link, unlink, mknod,
utime[*], write (т.к. изменяется длина файла); Это поле следует рассматривать как
время модификации прав доступа к файлу;
- st_mtime - write, creat, mknod, utime; Это поле следует рассматривать как время
модификации содержимого файла (данных);
- st_atime - read, creat, mknod, utime; Это поле следует рассматривать как время
чтения содержимого файла (данных).
Модифицируйте функцию typeOf(), чтобы она печатала еще и эти даты.
____________________
[*] Время модификации файла можно изменить на текущее астрономическое время и не
производя записи в файл. Для этого используется вызов
utime(имяФайла, NULL);
Он используется для взаимодействия с программой make - в команде touch. Изменить
время можно только своему файлу.
А. Богатырев, 1992-95 - 200 - Си в UNIX
6.2.4. Напишите аналог команды ls -tm, выдающей список имен файлов текущего ката-
лога, отсортированный по убыванию поля st_mtime, то есть недавно модифицированные
файлы выдаются первыми. Для каждого прочитанного из каталога имени надо сделать
stat; имена файлов и времена следует сохранить в массиве структур, а затем отсортиро-
вать его.
6.2.5. Напишите аналогичную программу, сортирующую файлы в порядке возрастания их
размера (st_size).
6.2.6. Напишите аналог команды ls -l, выдающий имена файлов каталога и их коды дос-
тупа в формате rwxrw-r--. Для получения кодов доступа используйте вызов stat
stat( имяФайла, &st);
кодыДоступа = st.st_mode & 0777;
Для изменения кодов доступа используется вызов
chmod(имя_файла, новые_коды);
Можно изменять коды доступа, соответствующие битовой маске
0777 | S_ISUID | S_ISGID | S_ISVTX
(смотри ). Тип файла (см. функцию typeOf) не может быть изменен. Изме-
нить коды доступа к файлу может только его владелец.
Печатайте еще номер I-узла файла: поле d_ino каталога либо поле st_ino структуры
stat.
6.2.7. Вот программа, которая каждые 2 секунды проверяет - не изменилось ли содержи-
мое текущего каталога:
#include
#include
extern char *ctime();
main(){
time_t last; struct stat st;
for( stat(".", &st), last=st.st_mtime; ; sleep(2)){
stat(".", &st);
if(last != st.st_mtime){
last = st.st_mtime;
printf("Был создан или удален какой-то файл: %s",
ctime(&last));
}
}
}
Модифицируйте ее, чтобы она сообщала какое имя (имена) было удалено или создано (для
этого надо при запуске программы прочитать и запомнить содержимое каталога, а при
обнаружении модификации - перечитать каталог и сравнить его с прежним содержимым).
6.2.8. Напишите по аналогии программу, которая выдает сообщение, если указанный вами
файл был кем-то прочитан, записан или удален. Вам следует отслеживать изменение полей
st_atime, st_mtime и значение stat() < 0 соответственно. Если файл удален - программа
завершается.
6.2.9. Современные UNIX-машины имеют встроенные таймеры (как правило несколько) с
довольно высоким разрешением. Некоторые из них могут использоваться как "будильники"
с обратным отсчетом времени: в таймер загружается некоторое значение; таймер ведет
обратный отсчет, уменьшая загруженный счетчик; как только это время истекает - посы-
лается сигнал процессу, загрузившему таймер.
А. Богатырев, 1992-95 - 201 - Си в UNIX
Вот как, к примеру, выглядит функция задержки в микросекундах (миллионных долях
секунды). Примечание: эту функцию не следует использовать вперемежку с функциями
sleep и alarm (смотри статью про них ниже, в главе про сигналы).
#include
#include
#include
void do_nothing() {}
/* Задержка на usec миллионных долей секунды (микросекунд) */
void usleep(unsigned int usec) {
struct itimerval new, old;
/* struct itimerval содержит поля:
struct timeval it_interval;
struct timeval it_value;
Где struct timeval содержит поля:
long tv_sec; -- число целых секунд
long tv_usec; -- число микросекунд
*/
struct sigaction new_vec, old_vec;
if (usec == 0) return;
/* Поле tv_sec содержит число целых секунд.
Поле tv_usec содержит число микросекунд.
it_value - это время, через которое В ПЕРВЫЙ раз
таймер "прозвонит",
то есть пошлет нашему процессу
сигнал SIGALRM.
Время, равное нулю, немедленно остановит таймер.
it_interval - это интервал времени, который будет загружаться
в таймер после каждого "звонка"
(но не в первый раз).
Время, равное нулю, остановит таймер
после его первого "звонка".
*/
new.it_interval.tv_sec = 0;
new.it_interval.tv_usec = 0;
new.it_value.tv_sec = usec / 1000000;
new.it_value.tv_usec = usec % 1000000;
А. Богатырев, 1992-95 - 202 - Си в UNIX
/* Сохраняем прежнюю реакцию на сигнал SIGALRM в old_vec,
заносим в качестве новой реакции do_nothing()
*/
new_vec.sa_handler = do_nothing;
sigemptyset(&new_vec.sa_mask);
new_vec.sa_flags = 0;
sighold(SIGALRM);
sigaction(SIGALRM, &new_vec, &old_vec);
/* Загрузка интервального таймера значением new, начало отсчета.
* Прежнее значение спасти в old.
* Вместо &old можно также NULL - не спасать.
*/
setitimer(ITIMER_REAL, &new, &old);
/* Ждать прихода сигнала SIGALRM */
sigpause(SIGALRM);
/* Восстановить реакцию на SIGALRM */
sigaction(SIGALRM, &old_vec, (struct sigaction *) 0);
sigrelse(SIGALRM);
/* Восстановить прежние параметры таймера */
setitimer(ITIMER_REAL, &old, (struct itimerval *) 0);
}
6.2.10. Второй пример использования таймера - это таймер, отсчитывающий текущее
время суток (а также дату). Чтобы получить значение этого таймера используется вызов
функции gettimeofday
#include
void main(){
struct timeval timenow;
gettimeofday(&timenow, NULL);
printf("%u sec, %u msec\n",
timenow.tv_sec,
timenow.tv_usec
);
printf("%s", ctime(&timenow.tv_sec));
exit(0);
}
Поле tv_sec содержит число секунд, прошедшее с полуночи 1 января 1970 года до данного