mirror of https://github.com/t1meshift/os_labs.git
Add 5th lab
parent
fca8d3a057
commit
ea3de372a4
|
@ -16,4 +16,5 @@ endfunction()
|
|||
define_lab(lab2)
|
||||
define_lab(lab3)
|
||||
define_lab(lab4)
|
||||
define_lab(lab5)
|
||||
define_lab(lab7)
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
echo "TODO!"
|
||||
exit 1
|
|
@ -0,0 +1,39 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
# Lab name
|
||||
set(LAB_NAME "lab5")
|
||||
|
||||
# Lab tasks
|
||||
list(APPEND SOURCE_FILES
|
||||
task1.c
|
||||
task2.c
|
||||
mytail.c
|
||||
mystat.c
|
||||
myls.c
|
||||
task6.c
|
||||
)
|
||||
list(APPEND NON_COMPILABLE_SRC
|
||||
.execme
|
||||
)
|
||||
|
||||
### Here goes the template
|
||||
|
||||
project("${LAB_NAME}" C)
|
||||
|
||||
add_custom_target("${LAB_NAME}")
|
||||
|
||||
foreach (file IN LISTS SOURCE_FILES)
|
||||
add_executable("${LAB_NAME}_${file}_run" "${file}")
|
||||
add_dependencies("${LAB_NAME}" "${LAB_NAME}_${file}_run")
|
||||
endforeach ()
|
||||
|
||||
foreach (file IN LISTS NON_COMPILABLE_SRC)
|
||||
add_custom_command(
|
||||
TARGET "${LAB_NAME}" POST_BUILD
|
||||
DEPENDS "${file}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${file}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${file}"
|
||||
)
|
||||
endforeach ()
|
|
@ -0,0 +1,3 @@
|
|||
# Лабораторная работа №5
|
||||
|
||||
## Задание 1
|
|
@ -0,0 +1,190 @@
|
|||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
/*
|
||||
5. Разработать собственную версию (myls) команды ls, которая выводит список файлов в заданной директории.
|
||||
С ключом -l она выводит информацию о каждом файле, включая собственника, группу, разрешения и т.д., получаемые из системного
|
||||
вызова stat().
|
||||
Формат: myls -l directory (или текущую директорию, если параметр не задан)
|
||||
Использовать: stat(), opendir(), readdir(), getcwd(), ...
|
||||
*/
|
||||
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#define STR_EQ(a, b) (!strcmp((a),(b)))
|
||||
#define FATAL(fmt, ...) do {\
|
||||
fprintf(stderr,fmt,##__VA_ARGS__);\
|
||||
exit(1);\
|
||||
} while (0)
|
||||
|
||||
char *path = NULL;
|
||||
bool long_listing = false;
|
||||
bool flag_end = false;
|
||||
|
||||
void cleanup_on_exit() {
|
||||
free(path);
|
||||
}
|
||||
|
||||
void print_entry(char *entry) {
|
||||
if (long_listing) {
|
||||
struct stat fstat;
|
||||
if (lstat(entry, &fstat) == -1) {
|
||||
perror("stat()");
|
||||
FATAL("Couldn't stat %s\n", entry);
|
||||
}
|
||||
|
||||
// Print permissions
|
||||
char entry_type;
|
||||
// setuid, setgid, sticky are not in count
|
||||
const char* perm_sym[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
|
||||
int perm = fstat.st_mode & 0777;
|
||||
struct passwd *pw_data;
|
||||
struct group *gp_data;
|
||||
|
||||
char user_name[33] = "";
|
||||
char group_name[33] = "";
|
||||
char last_mtime[256] = "";
|
||||
|
||||
switch (fstat.st_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
entry_type = '-';
|
||||
break;
|
||||
case S_IFDIR:
|
||||
entry_type = 'd';
|
||||
break;
|
||||
case S_IFLNK:
|
||||
entry_type = 'l';
|
||||
break;
|
||||
case S_IFBLK:
|
||||
entry_type = 'b';
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
entry_type = 's';
|
||||
break;
|
||||
case S_IFCHR:
|
||||
entry_type = 'c';
|
||||
break;
|
||||
#ifdef S_IFIFO
|
||||
case S_IFIFO:
|
||||
entry_type = 'p';
|
||||
break;
|
||||
#endif /* S_IFIFO */
|
||||
default:
|
||||
entry_type = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pw_data = getpwuid(fstat.st_uid)) == NULL) {
|
||||
perror("Could not get owner username");
|
||||
errno = 0;
|
||||
snprintf(user_name, 33, "%d", fstat.st_uid);
|
||||
} else {
|
||||
snprintf(user_name, 33, "%s", pw_data->pw_name);
|
||||
}
|
||||
|
||||
if ((gp_data = getgrgid(fstat.st_gid)) == NULL) {
|
||||
perror("Could not get owner group name");
|
||||
errno = 0;
|
||||
snprintf(group_name, 33, "%d", fstat.st_gid);
|
||||
} else {
|
||||
snprintf(group_name, 33, "%s", gp_data->gr_name);
|
||||
}
|
||||
|
||||
time_t mtime = fstat.st_mtime;
|
||||
struct tm* tm_info = localtime(&mtime);
|
||||
strftime(last_mtime, 256, "%Y-%b-%d %T %Z", tm_info);
|
||||
|
||||
printf("%c%s%s%s\t%4lu\t%s %s\t%6ld\t%s\t",
|
||||
entry_type,
|
||||
perm_sym[(perm & S_IRWXU) >> 6],
|
||||
perm_sym[(perm & S_IRWXG) >> 3],
|
||||
perm_sym[perm & S_IRWXO],
|
||||
fstat.st_nlink,
|
||||
user_name,
|
||||
group_name,
|
||||
fstat.st_size,
|
||||
last_mtime);
|
||||
|
||||
}
|
||||
printf("%s\n", entry);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
atexit(cleanup_on_exit);
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] == '-' && !flag_end) {
|
||||
char *flag = argv[i] + 1;
|
||||
if (STR_EQ(flag, "l")) {
|
||||
long_listing = true;
|
||||
}
|
||||
else if (STR_EQ(flag, "-")) {
|
||||
flag_end = true;
|
||||
}
|
||||
else {
|
||||
FATAL("Usage: %s [-l] [--] [path]\n", argv[0]);
|
||||
}
|
||||
} else {
|
||||
path = strdup(argv[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
/*
|
||||
* As an extension to the POSIX.1-2001 standard, glibc's getcwd()
|
||||
* allocates the buffer dynamically using malloc(3) if buf is NULL. In
|
||||
* this case, the allocated buffer has the length size unless size is
|
||||
* zero, when buf is allocated as big as necessary. The caller should
|
||||
* free(3) the returned buffer.
|
||||
*/
|
||||
if ((path = getcwd(NULL, 0)) == NULL) {
|
||||
perror("getcwd()");
|
||||
FATAL("Couldn't get current working directory\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct stat path_stat;
|
||||
DIR *dir = NULL;
|
||||
if (stat(path, &path_stat) == -1) {
|
||||
perror("stat()");
|
||||
FATAL("Couldn't stat %s\n", path);
|
||||
}
|
||||
|
||||
bool is_dir = S_ISDIR(path_stat.st_mode);
|
||||
|
||||
if (is_dir) {
|
||||
if ((dir = opendir(path)) == NULL) {
|
||||
perror("opendir()");
|
||||
FATAL("Couldn't open directory %s\n", path);
|
||||
}
|
||||
if (chdir(path) == -1) {
|
||||
perror("chdir()");
|
||||
FATAL("Could not chdir to directory %s\n", path);
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
print_entry(ent->d_name);
|
||||
}
|
||||
closedir(dir);
|
||||
} else {
|
||||
print_entry(path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
4. Разработать собственную версию (mystat) команды stat, которая просто осуществляет системный вызов stat(), выводит
|
||||
размер, число задействованных блоков, число ссылок и т.д. Отследить, как меняется число ссылок, когда изменяется
|
||||
содержимое директории.
|
||||
*/
|
||||
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#define FATAL(fmt, ...) do {\
|
||||
fprintf(stderr,fmt,##__VA_ARGS__);\
|
||||
exit(1);\
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
FATAL("Usage: %s <path>\n", argv[0]);
|
||||
}
|
||||
|
||||
char *path = argv[1];
|
||||
struct stat sb;
|
||||
|
||||
if (lstat(path, &sb) == -1) {
|
||||
perror("lstat()");
|
||||
FATAL("Couldn't stat %s\n", path);
|
||||
}
|
||||
|
||||
printf("File: %s\n", path);
|
||||
printf("Size: %ld\tBlocks: %ld\tIO Block: %ld\t", sb.st_size, sb.st_blocks, sb.st_blksize);
|
||||
switch(sb.st_mode & S_IFMT) {
|
||||
case S_IFBLK:
|
||||
puts("block device");
|
||||
break;
|
||||
case S_IFCHR:
|
||||
puts("character device");
|
||||
break;
|
||||
case S_IFDIR:
|
||||
puts("directory");
|
||||
break;
|
||||
case S_IFIFO:
|
||||
puts("FIFO/pipe");
|
||||
break;
|
||||
case S_IFLNK:
|
||||
puts("symlink");
|
||||
break;
|
||||
case S_IFREG:
|
||||
puts("regular file");
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
puts("socket");
|
||||
break;
|
||||
default:
|
||||
puts("unknown?");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Device: [%04x:%04x]\t", major(sb.st_dev), minor(sb.st_dev));
|
||||
printf("Inode: %lu\t", sb.st_ino);
|
||||
printf("Links: %lu\n", sb.st_nlink);
|
||||
|
||||
printf("Mode: 0%o\tUid: %d\tGid: %d\n", sb.st_mode, sb.st_uid, sb.st_gid);
|
||||
printf("Access (atime): %s", ctime(&sb.st_atime));
|
||||
printf("Modify (mtime): %s", ctime(&sb.st_mtime));
|
||||
printf("Change (ctime): %s", ctime(&sb.st_ctime));
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
3. Разработать собственную версию (mytail) команды tail.
|
||||
Формат: mytail -n file
|
||||
Она читает блок из конца файла, просматривает его с конца до заданного количества строк n и печатает эти строки
|
||||
в соответствующем порядке.
|
||||
Использовать: stat(), lseek(), open(), read(), close(), ...
|
||||
*/
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#define FATAL(fmt, ...) do {\
|
||||
fprintf(stderr,fmt,##__VA_ARGS__);\
|
||||
exit(1);\
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const int BUFF_SIZE = 4096;
|
||||
long n = 10;
|
||||
char *filename = NULL;
|
||||
|
||||
// Parse arguments (skip 0th arg)
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if ((argv[i][0] == '-') && (i < argc - 1)) {
|
||||
// -n flag (e.g. -20)
|
||||
n = strtol(argv[i], NULL, 10) * -1;
|
||||
if (n < 1 || errno == ERANGE) {
|
||||
FATAL("Usage: %s [-<number_of_lines>] <file>\n", argv[0]);
|
||||
}
|
||||
} else {
|
||||
filename = argv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filename == NULL) FATAL("No file name specified.\n");
|
||||
|
||||
struct stat sb;
|
||||
int fd;
|
||||
|
||||
if (stat(filename, &sb) == -1) {
|
||||
perror("stat()");
|
||||
FATAL("Couldn't stat %s\n", filename);
|
||||
}
|
||||
if ((fd = open(filename, O_RDONLY)) == -1) {
|
||||
perror("open()");
|
||||
FATAL("Couldn't open %s\n", filename);
|
||||
}
|
||||
|
||||
char *buf = calloc(BUFF_SIZE, 1);
|
||||
int lf_cnt = 0;
|
||||
size_t read_sz = 0;
|
||||
off_t file_offset = MAX(sb.st_size - BUFF_SIZE, 0);
|
||||
bool seek_stop = false;
|
||||
|
||||
lseek(fd, -BUFF_SIZE, SEEK_END);
|
||||
|
||||
do {
|
||||
int data_sz = read(fd, buf, BUFF_SIZE);
|
||||
for (int i = data_sz - 1; i >= 0; --i) {
|
||||
if (buf[i] == '\n') {
|
||||
if (++lf_cnt >= n) {
|
||||
file_offset += i + 1;
|
||||
goto print_lines;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_offset -= BUFF_SIZE;
|
||||
lseek(fd, -BUFF_SIZE, SEEK_CUR);
|
||||
} while ((lf_cnt <= n) && (file_offset > 0));
|
||||
|
||||
print_lines:
|
||||
lseek(fd, file_offset, SEEK_SET);
|
||||
do {
|
||||
read_sz = read(fd, buf, BUFF_SIZE);
|
||||
write(STDOUT_FILENO, buf, read_sz);
|
||||
} while (read_sz == BUFF_SIZE);
|
||||
|
||||
free(buf);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BENCHMARK(name, payload) \
|
||||
do {\
|
||||
struct timeval t0, t1;\
|
||||
gettimeofday(&t0, NULL);\
|
||||
payload \
|
||||
gettimeofday(&t1, NULL);\
|
||||
struct timeval result;\
|
||||
timersub(&t1, &t0, &result);\
|
||||
printf(name ": %ld.%06lds\n", result.tv_sec, result.tv_usec);\
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
1. Используя creat(), write(), fflush(), close(), gettimeofday(), разработать программу, которая
|
||||
открывает файл,
|
||||
записывает туда 300 KB,
|
||||
очищает все записи,
|
||||
закрывает и
|
||||
удаляет файл,
|
||||
а также измеряет и выводит время, затраченное на каждое действие.
|
||||
*/
|
||||
|
||||
int main() {
|
||||
int f = -1;
|
||||
|
||||
BENCHMARK("creat() call",{
|
||||
f = creat("task1.txt", S_IWUSR);
|
||||
});
|
||||
|
||||
if (f == -1) {
|
||||
perror("creat()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bytes_written = -1;
|
||||
const size_t data_size = 307200; // bytes, 300KiB
|
||||
char *random_data = malloc(data_size);
|
||||
|
||||
BENCHMARK("write 300KiB", {
|
||||
bytes_written = write(f, random_data, data_size);
|
||||
});
|
||||
free(random_data);
|
||||
|
||||
if (bytes_written == -1) {
|
||||
perror("write()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BENCHMARK("fsync() call", {
|
||||
fsync(f);
|
||||
});
|
||||
|
||||
BENCHMARK("close() call", {
|
||||
close(f);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BENCHMARK(name, payload) \
|
||||
do {\
|
||||
struct timeval __t0, __t1;\
|
||||
gettimeofday(&__t0, NULL);\
|
||||
payload \
|
||||
gettimeofday(&__t1, NULL);\
|
||||
struct timeval __result;\
|
||||
timersub(&__t1, &__t0, &__result);\
|
||||
printf(name ": %ld.%06lds\n", __result.tv_sec, __result.tv_usec);\
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
2. Разработать программу, которая замеряет время для 300,000 однобайтовых записей с использованием
|
||||
a) напрямую POSIX: creat(), write(), close().
|
||||
b) с использованием библиотеки stdio (напр., fopen(), fwrite(), and fclose()).
|
||||
Сравнить и объяснить результаты.
|
||||
*/
|
||||
|
||||
int main() {
|
||||
BENCHMARK("POSIX API", {
|
||||
int f = -1;
|
||||
BENCHMARK("creat() call",{
|
||||
f = creat("task2_posix.txt", 0644);
|
||||
});
|
||||
|
||||
if (f == -1) {
|
||||
perror("creat()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bytes_written = -1;
|
||||
const size_t data_size = 300000;
|
||||
char *random_data = malloc(data_size);
|
||||
|
||||
BENCHMARK("write() call", {
|
||||
bytes_written = write(f, random_data, data_size);
|
||||
});
|
||||
free(random_data);
|
||||
|
||||
if (bytes_written == -1) {
|
||||
perror("write()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BENCHMARK("close() call", {
|
||||
close(f);
|
||||
});
|
||||
});
|
||||
|
||||
BENCHMARK("stdio.h API",{
|
||||
FILE *f = NULL;
|
||||
BENCHMARK("fopen() call", {
|
||||
f = fopen("task2_stdio.txt", "w");
|
||||
});
|
||||
if (f == NULL) {
|
||||
perror("fopen()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bytes_written = -1;
|
||||
const size_t data_size = 300000;
|
||||
char *random_data = malloc(data_size);
|
||||
BENCHMARK("fwrite() call", {
|
||||
bytes_written = fwrite(random_data, 1, 300000, f);
|
||||
});
|
||||
free(random_data);
|
||||
if (bytes_written != data_size) {
|
||||
perror("fwrite()");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BENCHMARK("fclose() call",{
|
||||
fclose(f);
|
||||
});
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
6. Разработать программу, которая выводит имена каждого файла и директории, начиная с заданной точки в дереве каталогов.
|
||||
a) Без аргументов: сначала текущая директория и ее содержимое, затем поддиректории и т.д. (пока не закончится дерево,
|
||||
root в качестве CWD).
|
||||
b) С одним аргументом (который есть имя директории): все поддерево, начиная с заданной директории.
|
||||
c) А также еще один какой-либо интересный вариант (см. опции для find).
|
||||
*/
|
||||
|
||||
#define USAGE_STRING "Usage: %s [-d <max_depth>] [-L] [--] [path]\n"
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#define STR_EQ(a, b) (!strcmp((a),(b)))
|
||||
#define FATAL(fmt, ...) do {\
|
||||
fprintf(stderr,fmt,##__VA_ARGS__);\
|
||||
exit(1);\
|
||||
} while (0)
|
||||
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
|
||||
|
||||
bool flag_end = false;
|
||||
bool follow_symlinks = false;
|
||||
long max_depth = LONG_MAX;
|
||||
|
||||
void traverse_path(char *path, long depth) {
|
||||
struct stat pstat;
|
||||
DIR *dir = NULL;
|
||||
struct dirent *ent;
|
||||
int stat_status;
|
||||
|
||||
printf("%s\n", path);
|
||||
|
||||
if (depth > max_depth)
|
||||
return;
|
||||
|
||||
if (follow_symlinks) {
|
||||
stat_status = stat(path, &pstat);
|
||||
} else {
|
||||
stat_status = lstat(path, &pstat);
|
||||
}
|
||||
|
||||
if (stat_status == -1) {
|
||||
perror("stat()");
|
||||
FATAL("Couldn't stat %s\n", path);
|
||||
}
|
||||
|
||||
if (!S_ISDIR(pstat.st_mode))
|
||||
return;
|
||||
|
||||
if ((dir = opendir(path)) == NULL) {
|
||||
perror("opendir()");
|
||||
FATAL("Couldn't open directory %s\n", path);
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (STR_EQ(ent->d_name, ".") || STR_EQ(ent->d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char path_next[PATH_MAX];
|
||||
strcpy(path_next, path);
|
||||
|
||||
if (path_next[strlen(path_next) - 1] != '/') {
|
||||
strcat(path_next, "/");
|
||||
}
|
||||
|
||||
strcat(path_next, ent->d_name);
|
||||
|
||||
traverse_path(path_next, depth + 1);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *path = ".";
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] == '-' && !flag_end) {
|
||||
char *flag = argv[i] + 1;
|
||||
if (STR_EQ(flag, "d")) {
|
||||
if (i + 1 < argc) {
|
||||
max_depth = strtol(argv[++i], NULL, 10);
|
||||
if (errno == ERANGE) {
|
||||
WARNING("Max depth level is out of range, %ld is set\n", max_depth);
|
||||
errno = 0;
|
||||
}
|
||||
if (max_depth < 0) {
|
||||
FATAL("Max depth must be non-negative\n");
|
||||
}
|
||||
} else {
|
||||
FATAL(USAGE_STRING, argv[0]);
|
||||
}
|
||||
}
|
||||
else if (STR_EQ(flag, "L")) {
|
||||
follow_symlinks = true;
|
||||
}
|
||||
else if (STR_EQ(flag, "-")) {
|
||||
flag_end = true;
|
||||
}
|
||||
else {
|
||||
FATAL(USAGE_STRING, argv[0]);
|
||||
}
|
||||
} else {
|
||||
path = argv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
traverse_path(path, 1);
|
||||
}
|
Loading…
Reference in New Issue