「Unix/Linuxプログラミング 理論と実践」読み進めています。
引き続き3章課題について進んでいきます。
二つ目。lsを拡張して、アラインメントを揃えるように実装しなさい。という課題。
下記のように実装しました。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <sys/types.h> #include <sys/dirent.h> #include <unistd.h> #include <pwd.h> #include <grp.h> #include <uuid/uuid.h> #include <time.h> // // 3.10 // display to be alligned. // to implement this function, make sline buffer for result. // struct sline { char *mode; int link; char *user; char *group; int size; char *datetime; char *file; }; int create_stat_info(struct dirent *entry, struct sline *lentry); int get_digit(int num); static int luser=0, llink=0, lgroup=0, lsize=0; int main(int argc, char* argv[]) { DIR *dp; struct dirent *dent; struct sline *lines; int i=0, csize=1, cline=0; if (1 < argc) { chdir(argv[1]); } if ((dp=opendir(".")) == NULL) { printf("failed to opendir."); exit(-1); } // initialize malloc. lines = malloc(sizeof(struct sline) * csize); while ((dent=readdir(dp)) != NULL) { if (cline+1 > csize) { csize *= 2; lines = realloc(lines, sizeof(struct sline) * csize); } if (create_stat_info(dent, &lines[cline]) != -1) { cline++; } } for(i=0; i<cline; i++) { printf("%s ", lines[i].mode); printf("%*d ", llink+1, lines[i].link); printf("%*s ", luser+1, lines[i].user); printf("%*s ", lgroup+1, lines[i].group); printf("%*d ", lsize+1, lines[i].size); printf("%s ", lines[i].datetime); printf("%s \n", lines[i].file); } closedir(dp); } int create_stat_info(struct dirent *entry, struct sline *lentry) { // init struct stat info; int len; char *smode, *suser, *sgroup, *sfile, *stime; struct sline line; // validation. if (stat(entry->d_name, &info) == -1) { // i dont know yet that how to get symbolic link from 'stat' library function. return -1; // perror("failed"); // exit(-1); } // create mode. smode = malloc(sizeof(char) * 11); strcpy(smode, "----------"); // inode type. if (S_ISDIR(info.st_mode)) { smode[0] = 'd'; } if (S_ISCHR(info.st_mode)) { smode[0] = 'c'; } if (S_ISBLK(info.st_mode)) { smode[0] = 'b'; } // permission. if (info.st_mode & S_IRUSR) { smode[1] = 'r'; } if (info.st_mode & S_ISUID) { smode[1] = 's'; } if (info.st_mode & S_IWUSR) { smode[2] = 'w'; } if (info.st_mode & S_IXUSR) { smode[3] = 'x'; } if (info.st_mode & S_IRGRP) { smode[4] = 'r'; } if (info.st_mode & S_ISGID) { smode[4] = 's'; } if (info.st_mode & S_IWGRP) { smode[5] = 'w'; } if (info.st_mode & S_IXGRP) { smode[6] = 'x'; } if (info.st_mode & S_IROTH) { smode[7] = 'r'; } if (info.st_mode & S_IWOTH) { smode[8] = 'w'; } if (info.st_mode & S_IXOTH) { smode[9] = 'x'; } if (info.st_mode & S_ISVTX) { smode[9] = 't'; } // user struct passwd* user = getpwuid(info.st_uid); len = strlen(user->pw_name); suser = malloc(sizeof(char) * len); strcpy(suser, user->pw_name); if (len > luser) { luser = len; } // group struct group* group = getgrgid(info.st_gid); len = strlen(group->gr_name); sgroup = malloc(sizeof(char) * len); strcpy(sgroup, group->gr_name); if (len > lgroup) { lgroup = len; } // file len = strlen(entry->d_name); sfile = malloc(sizeof(char) * len); strcpy(sfile, entry->d_name); // time struct tm* stm = localtime(&info.st_atimespec.tv_sec); stime = malloc(sizeof(char) * 17); strcpy(stime, " "); sprintf(stime, "%d %2d %2d %02d:%02d", stm->tm_year+1900, stm->tm_mon+1, stm->tm_mday, stm->tm_hour, stm->tm_min); // link len = get_digit(info.st_nlink); if (len > llink) { llink = len; } // size len = get_digit(info.st_size); if (len > lsize) { lsize = len; } line.mode = smode; line.user = suser; line.group = sgroup; line.file = sfile; line.link = info.st_nlink; line.size = info.st_size; line.datetime = stime; *lentry = line; return 1; } int get_digit(int num) { int count = 0; while(1) { count++; if (num < 10) { return count; } num = (int)(num / 10); } return count; }
実行すると下記のように表示される。
目的のアラインメントはなかなかいい感じに表示されている。
$ ./a.out drwxr-xr-x 14 user staff 476 2015 8 11 21:45 . drwxr-xr-x 13 user staff 442 2015 8 11 21:39 .. -rwxr-xr-x 1 user staff 9472 2015 8 11 21:45 a.out -rw-r--r-- 1 user staff 1748 2015 8 11 09:41 cp03.c -rw-r--r-- 1 user staff 3040 2015 8 11 09:41 cp04.c drwxr-xr-x 4 user staff 136 2015 8 10 23:41 dir drwxr-xr-x 4 user staff 136 2015 8 10 23:46 dir2 -rw-r--r-- 1 user staff 295 2015 8 11 09:21 hoge -rw-r--r-- 1 user staff 490 2015 8 11 09:21 ls01.c -rw-r--r-- 1 user staff 2059 2015 8 11 21:28 ls02.c -rw-r--r-- 1 user staff 3941 2015 8 11 21:44 ls03.c -rw-r--r-- 1 user staff 839 2015 8 11 09:31 readtest.c -rw-r--r-- 1 user staff 647 2015 8 11 09:21 statinfo.c -rw-r--r-- 1 user staff 295 2015 8 11 09:32 test.c
今回の実装のポイントとしては各要素のアライメントをどのように実装するか。それにつきる。
はじめはprintfのオプションとしてうまく表示できないか、と考えたが逐次表示していくと、すでに表示したものよりも大きな幅が必要になった場合もはや取り返しがつかない。
例えば1行目に表示したファイルのユーザ名は6文字であったが5行目のユーザ名が10文字であった場合などだ。
ここから言えるのは一点であり、一度すべてのファイル情報をバッファリングする必要がある。
その中で一番大きな幅を持つ要素を各列について計算する。結果としてはとてもシンプルである。
ただこのモデルの欠点としては含めるファイル数が大きくなりすぎるとメモリ上に保持できなくなるということだ。
(最もただの文字列だけでそこまで大きなものはすでに見るきがしないが)
実装の側面からは一行分のデータをそれぞれ各業で分割して捉え、それを構造体として表現してみた。
コメントを残す