diff --git a/chapter_8.unix_interface/8_5.finfo/Makefile b/chapter_8.unix_interface/8_5.finfo/Makefile new file mode 100644 index 0000000..f1c13e2 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/Makefile @@ -0,0 +1,3 @@ +BINARY=finfo + +include ../../Exercise.mk diff --git a/chapter_8.unix_interface/8_5.finfo/main.c b/chapter_8.unix_interface/8_5.finfo/main.c new file mode 100644 index 0000000..29bdb74 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/main.c @@ -0,0 +1,92 @@ +# define _DEFAULT_SOURCE + +# include <stdio.h> +# include <string.h> +# include <sys/types.h> /* typedefs */ +# include <sys/stat.h> /* structure returned by stat */ +# include <dirent.h> +# include <time.h> + +/* + you can't read a directory like a file in modern system. so no manual "dirent.h" + */ + +# define MAX_PATH 1024 + +/* dirwalk: apply fcn to all files in dir */ +void dirwalk (char *dir, void (*fcn)(char *)) +{ + char name[MAX_PATH]; + struct dirent *dp; + DIR *dfd; + + if ((dfd = opendir(dir)) == NULL) { + fprintf(stderr, "dirwalk: can't open %s\n", dir); + return; + } + while ((dp = readdir(dfd)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; /* skip self and parent */ + if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) + fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name); + else { + sprintf(name, "%s/%s", dir, dp->d_name); + (*fcn)(name); + } + } + closedir(dfd); +} + +/* finfo: print the name of file "name" */ +void finfo (char *name) +{ + struct stat stbuf; + char *ftype, fbits[4] = "---", fmode[10] = "---------", mtime[64]; + + if (stat(name, &stbuf) == -1) { + fprintf(stderr, "finfo: can't access %s\n", name); + return; + } + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + dirwalk(name, finfo); + + if (S_ISREG (stbuf.st_mode)) ftype = "FILE"; + else if (S_ISDIR (stbuf.st_mode)) ftype = " DIR"; + else if (S_ISCHR (stbuf.st_mode)) ftype = "CDEV"; + else if (S_ISBLK (stbuf.st_mode)) ftype = "BDEV"; + else if (S_ISFIFO(stbuf.st_mode)) ftype = "PIPE"; + else if (S_ISLNK (stbuf.st_mode)) ftype = "LINK"; + else if (S_ISSOCK(stbuf.st_mode)) ftype = "SOCK"; + else ftype = "(\?\?)"; + + if (stbuf.st_mode & S_ISUID) fbits[0] = 'u'; + if (stbuf.st_mode & S_ISGID) fbits[1] = 'g'; + if (stbuf.st_mode & S_ISVTX) fbits[2] = 's'; + + if (stbuf.st_mode & S_IRUSR) fmode[0] = 'r'; + if (stbuf.st_mode & S_IWUSR) fmode[1] = 'w'; + if (stbuf.st_mode & S_IXUSR) fmode[2] = 'x'; + if (stbuf.st_mode & S_IRGRP) fmode[3] = 'r'; + if (stbuf.st_mode & S_IWGRP) fmode[4] = 'w'; + if (stbuf.st_mode & S_IXGRP) fmode[5] = 'x'; + if (stbuf.st_mode & S_IROTH) fmode[6] = 'r'; + if (stbuf.st_mode & S_IWOTH) fmode[7] = 'w'; + if (stbuf.st_mode & S_IXOTH) fmode[8] = 'x'; + + strftime(mtime, sizeof(mtime), "%Y/%m/%d %H:%M:%S", localtime(&stbuf.st_mtim.tv_sec)); + + printf("%4s %4d:%4d %3s %9s %s %8ld %2lu %s\n", + ftype, stbuf.st_uid, stbuf.st_gid, fbits, fmode, mtime, + stbuf.st_size, stbuf.st_nlink, name); +} + +/* print file name */ +int main (int argc, char **argv) +{ + if (argc == 1) /* default: current directory */ + finfo("."); + else + while (--argc > 0) + finfo(*++argv); + return 0; +} diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs new file mode 100644 index 0000000..77fbafa --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.targs @@ -0,0 +1 @@ +no_file.txt diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr new file mode 100644 index 0000000..cd74c2a --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.terr @@ -0,0 +1 @@ +finfo: can't access no_file.txt diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tin b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tin new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tout b/chapter_8.unix_interface/8_5.finfo/tests/0_no_file.tout new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs b/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs new file mode 100644 index 0000000..af03a6e --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/1_file.targs @@ -0,0 +1 @@ +tests/testdir/Makefile diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.terr b/chapter_8.unix_interface/8_5.finfo/tests/1_file.terr new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.tin b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tin new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout new file mode 100644 index 0000000..99e81e5 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/1_file.tout @@ -0,0 +1 @@ +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07 40 1 tests/testdir/Makefile diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs new file mode 100644 index 0000000..0b8db3c --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.targs @@ -0,0 +1 @@ +tests/testdir/ diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.terr b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.terr new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tin b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tin new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout new file mode 100644 index 0000000..15a1b39 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/2_directory.tout @@ -0,0 +1,7 @@ +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07 1506 1 tests/testdir//dirent.c +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:48:28 2741 1 tests/testdir//main.c +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:39:07 40 1 tests/testdir//Makefile +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:48:28 2741 1 tests/testdir//mainlink.c +FILE 1000:1000 --- rw-r--r-- 2024/08/28 15:45:37 0 1 tests/testdir//maindir/another_file + DIR 1000:1000 --- rwxr-xr-x 2024/08/28 15:45:37 24 1 tests/testdir//maindir + DIR 1000:1000 --- rwxr-xr-x 2024/08/28 15:40:17 78 1 tests/testdir/ diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile b/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile new file mode 100644 index 0000000..f1c13e2 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/Makefile @@ -0,0 +1,3 @@ +BINARY=finfo + +include ../../Exercise.mk diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c new file mode 100644 index 0000000..7c40845 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/dirent.c @@ -0,0 +1,64 @@ +# define _XOPEN_SOURCE + +# include <error.h> +# include <errno.h> + +# include <stdlib.h> +# include <unistd.h> +# include <string.h> +# include <fcntl.h> /* flags for read and write */ +# include <sys/types.h> /* typedefs */ +# include <sys/stat.h> /* structure returned by stat */ +# include <sys/dir.h> /* local directory structure */ +# include "dirent.h" + + +# ifndef DIRSIZ +# define DIRSIZ 14 +# endif + +/* opendir: open a directory for readdir calls */ +KNR_DIR *knr_opendir (char *dirname) +{ + int fd; + struct stat stbuf; + KNR_DIR *dp; + if ((fd = open(dirname, O_RDONLY, 0)) == -1 + || fstat(fd, &stbuf) == -1 + || (stbuf.st_mode & S_IFMT) != S_IFDIR + || (dp = (KNR_DIR *) malloc(sizeof(KNR_DIR))) == NULL) + return NULL; + dp->fd = fd; + return dp; +} + +/* closedir: close directory opened by opendir */ +void knr_closedir (KNR_DIR *dp) +{ + if (dp) { + close(dp->fd); + free(dp); + } +} + +/* readdir: read directory entries in sequence */ +KNR_Dirent *knr_readdir (KNR_DIR *dp) +{ + struct direct dirbuf; /* local directory structure */ + static KNR_Dirent d; /* return: portable structure */ + + ssize_t r; + + while ((r = read(dp->fd, (char *) &dirbuf, sizeof(dirbuf))) == sizeof(dirbuf)) { + if (dirbuf.d_ino == 0) /* slot not in use */ + continue; + d.ino = dirbuf.d_ino; + strncpy(d.name, dirbuf.d_name, DIRSIZ); + d.name[DIRSIZ] = '\0'; /* ensure termination */ + return &d; + } + if (r == -1) + error(-1, errno, "reading dir \"%s\"", dp->d.name); + return NULL; +} + diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c new file mode 100644 index 0000000..29bdb74 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/main.c @@ -0,0 +1,92 @@ +# define _DEFAULT_SOURCE + +# include <stdio.h> +# include <string.h> +# include <sys/types.h> /* typedefs */ +# include <sys/stat.h> /* structure returned by stat */ +# include <dirent.h> +# include <time.h> + +/* + you can't read a directory like a file in modern system. so no manual "dirent.h" + */ + +# define MAX_PATH 1024 + +/* dirwalk: apply fcn to all files in dir */ +void dirwalk (char *dir, void (*fcn)(char *)) +{ + char name[MAX_PATH]; + struct dirent *dp; + DIR *dfd; + + if ((dfd = opendir(dir)) == NULL) { + fprintf(stderr, "dirwalk: can't open %s\n", dir); + return; + } + while ((dp = readdir(dfd)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; /* skip self and parent */ + if (strlen(dir)+strlen(dp->d_name)+2 > sizeof(name)) + fprintf(stderr, "dirwalk: name %s %s too long\n", dir, dp->d_name); + else { + sprintf(name, "%s/%s", dir, dp->d_name); + (*fcn)(name); + } + } + closedir(dfd); +} + +/* finfo: print the name of file "name" */ +void finfo (char *name) +{ + struct stat stbuf; + char *ftype, fbits[4] = "---", fmode[10] = "---------", mtime[64]; + + if (stat(name, &stbuf) == -1) { + fprintf(stderr, "finfo: can't access %s\n", name); + return; + } + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) + dirwalk(name, finfo); + + if (S_ISREG (stbuf.st_mode)) ftype = "FILE"; + else if (S_ISDIR (stbuf.st_mode)) ftype = " DIR"; + else if (S_ISCHR (stbuf.st_mode)) ftype = "CDEV"; + else if (S_ISBLK (stbuf.st_mode)) ftype = "BDEV"; + else if (S_ISFIFO(stbuf.st_mode)) ftype = "PIPE"; + else if (S_ISLNK (stbuf.st_mode)) ftype = "LINK"; + else if (S_ISSOCK(stbuf.st_mode)) ftype = "SOCK"; + else ftype = "(\?\?)"; + + if (stbuf.st_mode & S_ISUID) fbits[0] = 'u'; + if (stbuf.st_mode & S_ISGID) fbits[1] = 'g'; + if (stbuf.st_mode & S_ISVTX) fbits[2] = 's'; + + if (stbuf.st_mode & S_IRUSR) fmode[0] = 'r'; + if (stbuf.st_mode & S_IWUSR) fmode[1] = 'w'; + if (stbuf.st_mode & S_IXUSR) fmode[2] = 'x'; + if (stbuf.st_mode & S_IRGRP) fmode[3] = 'r'; + if (stbuf.st_mode & S_IWGRP) fmode[4] = 'w'; + if (stbuf.st_mode & S_IXGRP) fmode[5] = 'x'; + if (stbuf.st_mode & S_IROTH) fmode[6] = 'r'; + if (stbuf.st_mode & S_IWOTH) fmode[7] = 'w'; + if (stbuf.st_mode & S_IXOTH) fmode[8] = 'x'; + + strftime(mtime, sizeof(mtime), "%Y/%m/%d %H:%M:%S", localtime(&stbuf.st_mtim.tv_sec)); + + printf("%4s %4d:%4d %3s %9s %s %8ld %2lu %s\n", + ftype, stbuf.st_uid, stbuf.st_gid, fbits, fmode, mtime, + stbuf.st_size, stbuf.st_nlink, name); +} + +/* print file name */ +int main (int argc, char **argv) +{ + if (argc == 1) /* default: current directory */ + finfo("."); + else + while (--argc > 0) + finfo(*++argv); + return 0; +} diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/maindir/another_file b/chapter_8.unix_interface/8_5.finfo/tests/testdir/maindir/another_file new file mode 100644 index 0000000..e69de29 diff --git a/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c b/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c new file mode 120000 index 0000000..8a03e94 --- /dev/null +++ b/chapter_8.unix_interface/8_5.finfo/tests/testdir/mainlink.c @@ -0,0 +1 @@ +main.c \ No newline at end of file