unix_programming3章_2

「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文字であった場合などだ。

ここから言えるのは一点であり、一度すべてのファイル情報をバッファリングする必要がある。
その中で一番大きな幅を持つ要素を各列について計算する。結果としてはとてもシンプルである。

ただこのモデルの欠点としては含めるファイル数が大きくなりすぎるとメモリ上に保持できなくなるということだ。
(最もただの文字列だけでそこまで大きなものはすでに見るきがしないが)

実装の側面からは一行分のデータをそれぞれ各業で分割して捉え、それを構造体として表現してみた。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です