Linux系统ls命令的实现
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <math.h>
11 #include <dirent.h>
12 #include <sys/types.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/stat.h>
16 #include <grp.h>
17 #include <pwd.h>
18 #include <time.h>
19
20 #define FILEMAX 1024
21 #define NAMEMAX 256
22
23 int flag_a = 0;
24 int flag_l = 0;
25 int dir_num = 0;
26 int fg_c, bg_c;
27
28 void size_window(char filename[][NAMEMAX], int cnt, int* row, int* col){
29 struct winsize size;
30 int len[cnt], max = 0, total = 0;
31 memset(len, 0, sizeof(int) * cnt);
32 if (isatty(STDOUT_FILENO) == 0) {
33 exit(1);
34 }
35
36 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) < 0) {
37 perror("ioctl");
38 exit(1);
39 }
40
41 for (int i= 0; i < cnt; i++){
42 len[i] = strlen(filename[i]);
43 if (max < len[i]) max = len[i];
44 total += len[i] + 1;
45 }
46 if (max + 1 >= size.ws_col) {
47 *row = cnt;
48 *col = 1;
49 return ;
50 }
51 if (total <= size.ws_col) {
52 *row = 1;
53 *col = cnt;
54 return ;
55 }
56
57 int try_begin = 0;
58 for (int i = 0, tmp = 0; i < cnt; i++) {
59 tmp += (len[i] + 1);
60 if (tmp >= size.ws_col) {
61 try_begin = i;
62 break;
63 }
64 }
65
66 for (int i = try_begin; ;i--) {
67 int *wide = (int *)malloc(sizeof(int) * i);
68 memset(wide, 0, sizeof(int) * i);
69 *row = (int)ceil(cnt / i);
70 int try_sum = 0;
71 for (int x = 0; x < i; x++) {
72 for (int y = x * *row; y < (x + 1) * *row && y < cnt; y++) {
73 if (wide[x] < len[y]) wide[x] = len[y];
74 }
75 try_sum += (wide[x] + 1);
76 }
77
78 if (try_sum > size.ws_col) continue;
79 if (try_sum <= size.ws_col) {
80 *col = i;
81 break;
82 }
83 }
84 }
85
86 void update_color(mode_t mode) {
87 bg_c = 0;
88 fg_c = 37;
89 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) fg_c = 32;
90 if (mode & 0111) fg_c = 32;
91 switch (mode & S_IFMT) {
92 case S_IFDIR:
93 fg_c = 34;
94 break;
95 case S_IFLNK:
96 fg_c = 36;
97 break;
98 }
99 }
100
101 void show_files(char filename[][NAMEMAX], int cnt, int row, int col) {
102 int wide_file[cnt];
103 struct stat tmp_st;
104 memset(wide_file, 0, sizeof(int) * cnt);
105 for(int i = 0; i < col; i++) {
106 for (int j = (i * row); j < (i + 1) * row && j < cnt; j++) {
107 if (wide_file[i] < strlen(filename[j])) wide_file[i] = strlen(filename[j]) ;
108
109 }
110 }
111
112 for (int i = 0; i < row; i++) {
113 for (int j = i; j < i + (row * col) && j < cnt; j = j + row) {
114 int tmp = j / row;
115 stat(filename[j], &tmp_st);
116 update_color(tmp_st.st_mode);
117 printf("\033[%d;%dm%-*s\033[0m", bg_c, fg_c, wide_file[tmp] + 1, filename[j]);
118 }
119 printf("\n");
120 }
121 }
122
123
124 void mode_to_str(mode_t mode, char *str) {
125 if (S_ISREG(mode)) str[0] = '-';
126 if (S_ISDIR(mode)) str[0] = 'd';
127 if (S_ISCHR(mode)) str[0] = 'c';
128 if (S_ISBLK(mode)) str[0] = 'b';
129 if (S_ISSOCK(mode)) str[0] = 's';
130 if (S_ISLNK(mode)) str[0] = 'l';
131 if (S_ISFIFO(mode)) str[0] = 'p';
132
133 if (mode & S_IRUSR) str[1] = 'r';
134 if (mode & S_IWUSR) str[2] = 'w';
135 if (mode & S_IXUSR) str[3] = 'x';
136 if (mode & S_IRGRP) str[4] = 'r';
137 if (mode & S_IWGRP) str[5] = 'w';
138 if (mode & S_IXGRP) str[6] = 'x';
139
140 if (mode & S_IROTH) str[7] = 'r';
141 if (mode & S_IWOTH) str[8] = 'w';
142 if (mode & S_IXOTH) str[9] = 'x';
143
144 if ((mode & S_IXUSR) && (mode & S_ISUID)) str[3] = 's';
145 update_color(mode);
146 }
147
148 char *uid_to_name(uid_t uid){
149 struct passwd *pw_ptr;
150 static char tmpstr[10] = {0};
151 if ((pw_ptr = getpwuid(uid)) == NULL) {
152 sprintf(tmpstr, "%d", uid);
153 return tmpstr;
154 } else {
155 return pw_ptr->pw_name;
156 }
157 }
158
159 char *gid_to_name(gid_t gid) {
160 struct group *gr_ptr;
161 static char tmpstr[10] = {0};
162 if ((gr_ptr = getgrgid(gid)) == NULL) {
163 sprintf(tmpstr, "%d", gid);
164 return tmpstr;
165 } else {
166 return gr_ptr->gr_name;
167 }
168 }
169
170 void show_info(char *filename, struct stat *info) {
171 char modestr[11] = "----------";
172 mode_to_str(info->st_mode, modestr);
173 printf("%s ", modestr);
174 printf("%4ld ", info->st_nlink);
175 printf("%8s ", uid_to_name(info->st_uid));
176 printf("%8s ", gid_to_name(info->st_gid));
177 printf("%8ld ", info->st_size);
178 printf("%.15s ", 4 + ctime(&info->st_mtime));
179 printf("\033[%d;%dm%s\033[0m ", bg_c, fg_c, filename);
180 if (modestr[0] == 'l') {
181 int cnt;
182 char buf[NAMEMAX] = {0};
183 if (cnt == readlink(filename, buf, 256) < 0) {
184 perror("readlink");
185 }
186 printf("-> \033[%d;%dm%s\033[0m", bg_c, fg_c, buf);
187 }
188 printf("\n");
189 }
190
191 void do_stat(char *filename) {
192 struct stat st;
193 if (stat(filename, &st) < 0) {
194 perror(filename);
195 } else {
196 show_info(filename, &st);
197 }
198 }
199
200 int cmp_name(const void* _a, const void* _b) {
201 char *a = (char *)_a;
202 char *b = (char *)_b;
203 return strcmp(a, b);
204 }
205
206 void do_ls(char *dirname) {
207 DIR *dirp = NULL;
208 struct dirent *direntp;
209 char names[FILEMAX][NAMEMAX] = {0};
210 if ((dirp = opendir(dirname)) == NULL) {
211 if (access(dirname, R_OK) == 0) {
212 if (flag_l == 0) {
213 dir_num--;
214 struct stat tmp_st;
215 stat(dirname, &tmp_st);
216 update_color(tmp_st.st_mode);
217 printf("\033[%d;%dm%s\033[0m\n",bg_c, fg_c, dirname);
218 return;
219 } else {
220 dir_num--;
221 do_stat(dirname);
222 return;
223 }
224 } else {
225 perror(dirname);
226 return ;
227 }
228 } else {
229 if (dir_num)
230 printf("%s:\n", dirname);
231 chdir(dirname);
232 int cnt = 0;
233 while((direntp = readdir(dirp)) != NULL) {
234 if (direntp->d_name[0] == '.' && (flag_a == 0)) continue;
235 strcpy(names[cnt++], direntp->d_name);
236 }
237 qsort(names, cnt, NAMEMAX, cmp_name);
238
239 if (flag_l == 1) {
240 for (int i = 0; i < cnt; i++) {
241 do_stat(names[i]);
242 }
243 } else {
244 int row, col;
245 size_window(names, cnt, &row, &col);
246 show_files(names, cnt, row, col);
247 }
248
249 }
250 }
251
252
253 int main(int argc, char **argv) {
254 int opt;
255 while((opt = getopt(argc, argv, "al")) != -1) {
256 switch (opt) {
257 case 'a':
258 flag_a = 1;
259 break;
260 case 'l':
261 flag_l = 1;
262 break;
263 default:
264 fprintf(stderr, "Usage: %s [-al] [filename]\n", argv[0]);
265 exit(1);
266 }
267 }
268 argc -= (optind - 1);
269 argv += (optind - 1);
270
271 if (argc == 1) {
272 dir_num = 0;
273 do_ls(".");
274 } else {
275 dir_num = argc - 2;
276 while(--argc) {
277 do_ls(*(++argv));
278 }
279 }
280 return 0;
281 }
282