気づけば(その9)まで来てしまいました。今回でtailっぽいことはできるようになりましたので、一応完成です。でも、まだまだ機能を追加していくので、タイトルを変えて続けると思います。
さて、本物版tailですが、特殊なオプション指定ができますよね。
tail -行数
このようにハイフンに続いて数字を指定する方法。まあ、普段から使ってるので、普通の方法と言えますが。
使ってみます。
takk@deb9:~/tmp$ seq 10 >a takk@deb9:~/tmp$ tail -3 a 8 9 10 takk@deb9:~/tmp$
普段使っていて気づかなかったのですが、使い方に制限があります。
作りを簡単にするような制限に思えます。-行数オプションの指定方法を間違えると、このようなエラーになります。
takk@deb9:~/tmp$ seq 10 >a takk@deb9:~/tmp$ seq 21 30 >b takk@deb9:~/tmp$ tail a -3 tail: 無効なコンテキストでオプションが使用されました -- 3 takk@deb9:~/tmp$ tail -v -3 a tail: 無効なコンテキストでオプションが使用されました -- 3 takk@deb9:~/tmp$ tail -3 -v a tail: 無効なコンテキストでオプションが使用されました -- 3 takk@deb9:~/tmp$ tail -3 a b tail: 無効なコンテキストでオプションが使用されました -- 3 takk@deb9:~/tmp$
では、-行数 オプション処理を考えてみます。
getoptだけでは、-だけのオプションは処理できないので、getoptでオプション解析する前に、-行数 オプション解析を行います。-行数 オプションの判定は、atoiで変換した値が0より大きな数値になるかどうかで、判定しています。
ネストが深くならないようにgoto抜けしてます。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <getopt.h> //getopt
4
5 int main(int argc, char* argv[])
6 {
7 int optch;
8 int tmp;
9
10
11 if(argc==3 && *argv[1]=='-'){
12 tmp = atoi(argv[1]+1);
13 if(tmp > 0)
14 goto getopt_skip;
15 }
16
17 while ((optch = getopt(argc, argv, "n:")) != -1){
18 switch (optch) {
19 case 'n':
20 tmp = atoi(optarg);
21 break;
22 default:
23 printf("parameter error\n");
24 return 1;
25 }
26 }
27 getopt_skip:
28
29 printf("-n = %d\n",tmp);
30 return 0;
31 }
gccでビルド後、確認してみます。
takk@deb9:~/tmp$ ./a.out -5 a -n = 5 takk@deb9:~/tmp$ ./a.out -n6 a -n = 6 takk@deb9:~/tmp$ ./a.out -5 a b ./a.out: invalid option -- '5' parameter error takk@deb9:~/tmp$ ./a.out -n6 a b -n = 6 takk@deb9:~/tmp$
上手く解析できてそうなので、この判定方法で作っていきます。
さて、前回までのプログラムに、オプション解析と複数ファイル処理、また、行単位、Byte単位の抽出機能すべてを盛り込んだプログラムがこれです。
takk@deb9:~/tmp$ cat -n t.c
1 #include <stdio.h>
2 #include <sys/types.h> //stat
3 #include <sys/stat.h> //stat
4 #include <unistd.h> //stat
5 #include <getopt.h> //getopt
6 #include <stdlib.h> //atoi
7 #include <fcntl.h> //open
8
9 int get_tailsize(int fd,int filesize,int req_lines)
10 {
11 char buf[10],*bufp;
12 int bsize=sizeof(buf);
13 int i,size,bno,this_size,lf_num=0;
14
15 for(bno=0; bno <= filesize/bsize; bno++){
16 size = bsize*(bno+1);
17 size = filesize < size ? filesize : size;
18 lseek(fd, -size, SEEK_END);
19
20 size = size % bsize;
21 size = size > 0 ? size : bsize;
22 bufp = buf;
23 while(1){
24 this_size = read(fd, bufp, size - (bufp-buf));
25 if(this_size == 0)break;
26 bufp += this_size;
27 }
28
29 for(i = 0; i < size; i++){
30 if(buf[size-i-1] == 0x0a){
31 if(!((bno == 0) && (i==0))){
32 lf_num++;
33 }
34 if(lf_num == req_lines){
35 return bsize*bno+i;
36 }
37 }
38 }
39 }
40 return filesize;
41 }
42
43 void out_taillines(int fd,int size)
44 {
45 char buf[10],*bufp;
46 int bsize=sizeof(buf);
47 int read_size,this_size;
48
49 lseek(fd, -size, SEEK_END);
50
51 while(1){
52 bufp = buf;
53 read_size = bsize <= size ? bsize : size % bsize;
54
55 this_size = read(fd, buf, read_size);
56 size -= this_size;
57 while(this_size--){
58 putchar((int)*bufp++);
59 }
60 if(size <= 0)
61 break;
62 }
63 }
64
65 int main(int argc, char* argv[])
66 {
67 int fd;
68 char *fname;
69 struct stat fs;
70 int optch;
71 int qflag = 0;
72 int req_lines = 10;
73 int csize = 0;
74 int size;
75
76 if(argc==3 && *argv[1]=='-'){
77 req_lines = atoi(argv[1]+1);
78 if(req_lines > 0){
79 argc-=2;
80 argv+=2;
81 goto getopt_skip;
82 }
83 }
84
85 while ((optch = getopt(argc, argv, "n:c:q")) != -1){
86 switch (optch) {
87 case 'n':
88 req_lines = atoi(optarg);
89 break;
90 case 'c':
91 csize = atoi(optarg);
92 break;
93 case 'q':
94 qflag = 1;
95 break;
96 default:
97 printf("parameter error\n");
98 return 1;
99 }
100 }
101
102 argv += optind;
103 argc -= optind;
104
105 getopt_skip:
106 if(argc==1)
107 qflag = 1;
108
109 while(argc--){
110 fname = *argv++;
111
112 stat(fname, &fs);
113
114 if((fd=open(fname, O_RDONLY)) != -1){
115 if(csize)
116 size = fs.st_size < csize ? fs.st_size : csize;
117 else
118 size = get_tailsize(fd, fs.st_size, req_lines);
119
120 if(!qflag)
121 printf("<== %s ==>\n",fname);
122
123 out_taillines(fd, size);
124
125 close(fd);
126 }else{
127 perror("ERROR");
128 }
129
130 }
131
132 return 0;
133 }
takk@deb9:~/tmp$
一応ファイル末尾抽出する処理はできていると思うので、完成版となりますが、全133行とコンパクトにできてます。
使ってみます。末尾3行抽出。
takk@deb9:~/tmp$ gcc t.c takk@deb9:~/tmp$ ./a.out -3 a 8 9 10
オプション間違いの場合。
takk@deb9:~/tmp$ ./a.out -3 a b ./a.out: invalid option -- '3' parameter error takk@deb9:~/tmp$
複数ファイルを抽出。
takk@deb9:~/tmp$ ./a.out -n3 a b <== a ==> 8 9 10 <== b ==> 28 29 30 takk@deb9:~/tmp$
Byte単位で抽出。
takk@deb9:~/tmp$ ./a.out -qc3 a b | od -tx1 0000000 31 30 0a 33 30 0a 0000006 takk@deb9:~/tmp$
動作も今のところ問題なさそう。
本物tailのソースを読んで、答え合わせをしたいところですが、そろそろtailにも飽きてきたので、一旦終了。
アニメでも見ながら、次の学習課題を探そうかと思います。


コメント