diff options
Diffstat (limited to 'ls.c')
-rw-r--r-- | ls.c | 937 |
1 files changed, 0 insertions, 937 deletions
@@ -1,937 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * tiny-ls.c version 0.1.0: A minimalist 'ls' | ||
4 | * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | * To achieve a small memory footprint, this version of 'ls' doesn't do any | ||
23 | * file sorting, and only has the most essential command line switches | ||
24 | * (i.e., the ones I couldn't live without :-) All features which involve | ||
25 | * linking in substantial chunks of libc can be disabled. | ||
26 | * | ||
27 | * Although I don't really want to add new features to this program to | ||
28 | * keep it small, I *am* interested to receive bug fixes and ways to make | ||
29 | * it more portable. | ||
30 | * | ||
31 | * KNOWN BUGS: | ||
32 | * 1. ls -l of a directory doesn't give "total <blocks>" header | ||
33 | * 2. ls of a symlink to a directory doesn't list directory contents | ||
34 | * 3. hidden files can make column width too large | ||
35 | * | ||
36 | * NON-OPTIMAL BEHAVIOUR: | ||
37 | * 1. autowidth reads directories twice | ||
38 | * 2. if you do a short directory listing without filetype characters | ||
39 | * appended, there's no need to stat each one | ||
40 | * PORTABILITY: | ||
41 | * 1. requires lstat (BSD) - how do you do it without? | ||
42 | */ | ||
43 | |||
44 | enum { | ||
45 | TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ | ||
46 | COLUMN_WIDTH = 14, /* default if AUTOWIDTH not defined */ | ||
47 | COLUMN_GAP = 2, /* includes the file type char */ | ||
48 | }; | ||
49 | |||
50 | |||
51 | /************************************************************************/ | ||
52 | |||
53 | #include <sys/types.h> | ||
54 | #include <sys/stat.h> | ||
55 | #include <stdio.h> | ||
56 | #include <unistd.h> | ||
57 | #include <dirent.h> | ||
58 | #include <errno.h> | ||
59 | #include <stdio.h> | ||
60 | #include <string.h> | ||
61 | #include <stdlib.h> | ||
62 | #include <fcntl.h> | ||
63 | #include <signal.h> | ||
64 | #include <termios.h> | ||
65 | #include <sys/ioctl.h> | ||
66 | #include "busybox.h" | ||
67 | |||
68 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
69 | #include <time.h> | ||
70 | #endif | ||
71 | |||
72 | #ifndef MAJOR | ||
73 | #define MAJOR(dev) (((dev)>>8)&0xff) | ||
74 | #define MINOR(dev) ((dev)&0xff) | ||
75 | #endif | ||
76 | |||
77 | /* what is the overall style of the listing */ | ||
78 | enum { | ||
79 | STYLE_AUTO = 0, | ||
80 | STYLE_LONG = 1, /* one record per line, extended info */ | ||
81 | STYLE_SINGLE = 2, /* one record per line */ | ||
82 | STYLE_COLUMNS = 3 /* fill columns */ | ||
83 | }; | ||
84 | |||
85 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ | ||
86 | /* what file information will be listed */ | ||
87 | #define LIST_INO (1<<0) | ||
88 | #define LIST_BLOCKS (1<<1) | ||
89 | #define LIST_MODEBITS (1<<2) | ||
90 | #define LIST_NLINKS (1<<3) | ||
91 | #define LIST_ID_NAME (1<<4) | ||
92 | #define LIST_ID_NUMERIC (1<<5) | ||
93 | #define LIST_SIZE (1<<6) | ||
94 | #define LIST_DEV (1<<7) | ||
95 | #define LIST_DATE_TIME (1<<8) | ||
96 | #define LIST_FULLTIME (1<<9) | ||
97 | #define LIST_FILENAME (1<<10) | ||
98 | #define LIST_SYMLINK (1<<11) | ||
99 | #define LIST_FILETYPE (1<<12) | ||
100 | #define LIST_EXEC (1<<13) | ||
101 | |||
102 | /* what files will be displayed */ | ||
103 | #define DISP_NORMAL (0) /* show normal filenames */ | ||
104 | #define DISP_DIRNAME (1<<0) /* 2 or more items? label directories */ | ||
105 | #define DISP_HIDDEN (1<<1) /* show filenames starting with . */ | ||
106 | #define DISP_DOT (1<<2) /* show . and .. */ | ||
107 | #define DISP_NOLIST (1<<3) /* show directory as itself, not contents */ | ||
108 | #define DISP_RECURSIVE (1<<4) /* show directory and everything below it */ | ||
109 | #define DISP_ROWS (1<<5) /* print across rows */ | ||
110 | |||
111 | #ifdef BB_FEATURE_LS_SORTFILES | ||
112 | /* how will the files be sorted */ | ||
113 | static const int SORT_FORWARD = 0; /* sort in reverse order */ | ||
114 | static const int SORT_REVERSE = 1; /* sort in reverse order */ | ||
115 | static const int SORT_NAME = 2; /* sort by file name */ | ||
116 | static const int SORT_SIZE = 3; /* sort by file size */ | ||
117 | static const int SORT_ATIME = 4; /* sort by last access time */ | ||
118 | static const int SORT_CTIME = 5; /* sort by last change time */ | ||
119 | static const int SORT_MTIME = 6; /* sort by last modification time */ | ||
120 | static const int SORT_VERSION = 7; /* sort by version */ | ||
121 | static const int SORT_EXT = 8; /* sort by file name extension */ | ||
122 | static const int SORT_DIR = 9; /* sort by file or directory */ | ||
123 | #endif | ||
124 | |||
125 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
126 | /* which of the three times will be used */ | ||
127 | static const int TIME_MOD = 0; | ||
128 | static const int TIME_CHANGE = 1; | ||
129 | static const int TIME_ACCESS = 2; | ||
130 | #endif | ||
131 | |||
132 | #define LIST_SHORT (LIST_FILENAME) | ||
133 | #define LIST_ISHORT (LIST_INO | LIST_FILENAME) | ||
134 | #define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | \ | ||
135 | LIST_SIZE | LIST_DATE_TIME | LIST_FILENAME | \ | ||
136 | LIST_SYMLINK) | ||
137 | #define LIST_ILONG (LIST_INO | LIST_LONG) | ||
138 | |||
139 | static const int SPLIT_DIR = 0; | ||
140 | static const int SPLIT_FILE = 1; | ||
141 | static const int SPLIT_SUBDIR = 2; | ||
142 | |||
143 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) | ||
144 | #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) | ||
145 | #ifdef BB_FEATURE_LS_FILETYPES | ||
146 | #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) | ||
147 | #endif | ||
148 | |||
149 | /* | ||
150 | * a directory entry and its stat info are stored here | ||
151 | */ | ||
152 | struct dnode { /* the basic node */ | ||
153 | char *name; /* the dir entry name */ | ||
154 | char *fullname; /* the dir entry name */ | ||
155 | struct stat dstat; /* the file stat info */ | ||
156 | struct dnode *next; /* point at the next node */ | ||
157 | }; | ||
158 | typedef struct dnode dnode_t; | ||
159 | |||
160 | static struct dnode **list_dir(char *); | ||
161 | static struct dnode **dnalloc(int); | ||
162 | static int list_single(struct dnode *); | ||
163 | |||
164 | static unsigned int disp_opts; | ||
165 | static unsigned int style_fmt; | ||
166 | static unsigned int list_fmt; | ||
167 | #ifdef BB_FEATURE_LS_SORTFILES | ||
168 | static unsigned int sort_opts; | ||
169 | static unsigned int sort_order; | ||
170 | #endif | ||
171 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
172 | static unsigned int time_fmt; | ||
173 | #endif | ||
174 | #ifdef BB_FEATURE_LS_FOLLOWLINKS | ||
175 | static unsigned int follow_links=FALSE; | ||
176 | #endif | ||
177 | |||
178 | static unsigned short column = 0; | ||
179 | #ifdef BB_FEATURE_AUTOWIDTH | ||
180 | static unsigned short terminal_width = TERMINAL_WIDTH; | ||
181 | static unsigned short column_width = COLUMN_WIDTH; | ||
182 | static unsigned short tabstops = COLUMN_GAP; | ||
183 | #else | ||
184 | static unsigned short column_width = COLUMN_WIDTH; | ||
185 | #endif | ||
186 | |||
187 | static int status = EXIT_SUCCESS; | ||
188 | |||
189 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
190 | static unsigned long ls_disp_hr = 0; | ||
191 | #endif | ||
192 | |||
193 | static int my_stat(struct dnode *cur) | ||
194 | { | ||
195 | #ifdef BB_FEATURE_LS_FOLLOWLINKS | ||
196 | if (follow_links == TRUE) { | ||
197 | if (stat(cur->fullname, &cur->dstat)) { | ||
198 | perror_msg("%s", cur->fullname); | ||
199 | status = EXIT_FAILURE; | ||
200 | free(cur->fullname); | ||
201 | free(cur); | ||
202 | return -1; | ||
203 | } | ||
204 | } else | ||
205 | #endif | ||
206 | if (lstat(cur->fullname, &cur->dstat)) { | ||
207 | perror_msg("%s", cur->fullname); | ||
208 | status = EXIT_FAILURE; | ||
209 | free(cur->fullname); | ||
210 | free(cur); | ||
211 | return -1; | ||
212 | } | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static void newline(void) | ||
217 | { | ||
218 | if (column > 0) { | ||
219 | putchar('\n'); | ||
220 | column = 0; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /*----------------------------------------------------------------------*/ | ||
225 | #ifdef BB_FEATURE_LS_FILETYPES | ||
226 | static char append_char(mode_t mode) | ||
227 | { | ||
228 | if ( !(list_fmt & LIST_FILETYPE)) | ||
229 | return '\0'; | ||
230 | if ((list_fmt & LIST_EXEC) && S_ISREG(mode) | ||
231 | && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; | ||
232 | return APPCHAR(mode); | ||
233 | } | ||
234 | #endif | ||
235 | |||
236 | /*----------------------------------------------------------------------*/ | ||
237 | static void nexttabstop( void ) | ||
238 | { | ||
239 | static short nexttab= 0; | ||
240 | int n=0; | ||
241 | |||
242 | if (column > 0) { | ||
243 | n= nexttab - column; | ||
244 | if (n < 1) n= 1; | ||
245 | while (n--) { | ||
246 | putchar(' '); | ||
247 | column++; | ||
248 | } | ||
249 | } | ||
250 | nexttab= column + column_width + COLUMN_GAP; | ||
251 | } | ||
252 | |||
253 | /*----------------------------------------------------------------------*/ | ||
254 | static int is_subdir(struct dnode *dn) | ||
255 | { | ||
256 | return (S_ISDIR(dn->dstat.st_mode) && strcmp(dn->name, ".") != 0 && | ||
257 | strcmp(dn->name, "..") != 0); | ||
258 | } | ||
259 | |||
260 | static int countdirs(struct dnode **dn, int nfiles) | ||
261 | { | ||
262 | int i, dirs; | ||
263 | |||
264 | if (dn==NULL || nfiles < 1) return(0); | ||
265 | dirs= 0; | ||
266 | for (i=0; i<nfiles; i++) { | ||
267 | if (S_ISDIR(dn[i]->dstat.st_mode)) dirs++; | ||
268 | } | ||
269 | return(dirs); | ||
270 | } | ||
271 | |||
272 | static int countsubdirs(struct dnode **dn, int nfiles) | ||
273 | { | ||
274 | int i, subdirs; | ||
275 | |||
276 | if (dn == NULL || nfiles < 1) return 0; | ||
277 | subdirs = 0; | ||
278 | for (i = 0; i < nfiles; i++) | ||
279 | if (is_subdir(dn[i])) | ||
280 | subdirs++; | ||
281 | return subdirs; | ||
282 | } | ||
283 | |||
284 | static int countfiles(struct dnode **dnp) | ||
285 | { | ||
286 | int nfiles; | ||
287 | struct dnode *cur; | ||
288 | |||
289 | if (dnp == NULL) return(0); | ||
290 | nfiles= 0; | ||
291 | for (cur= dnp[0]; cur->next != NULL ; cur= cur->next) nfiles++; | ||
292 | nfiles++; | ||
293 | return(nfiles); | ||
294 | } | ||
295 | |||
296 | /* get memory to hold an array of pointers */ | ||
297 | static struct dnode **dnalloc(int num) | ||
298 | { | ||
299 | struct dnode **p; | ||
300 | |||
301 | if (num < 1) return(NULL); | ||
302 | |||
303 | p= (struct dnode **)xcalloc((size_t)num, (size_t)(sizeof(struct dnode *))); | ||
304 | return(p); | ||
305 | } | ||
306 | |||
307 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
308 | static void dfree(struct dnode **dnp) | ||
309 | { | ||
310 | struct dnode *cur, *next; | ||
311 | |||
312 | if(dnp == NULL) return; | ||
313 | |||
314 | cur=dnp[0]; | ||
315 | while (cur != NULL) { | ||
316 | if (cur->fullname != NULL) free(cur->fullname); /* free the filename */ | ||
317 | next= cur->next; | ||
318 | free(cur); /* free the dnode */ | ||
319 | cur= next; | ||
320 | } | ||
321 | free(dnp); /* free the array holding the dnode pointers */ | ||
322 | } | ||
323 | #endif | ||
324 | |||
325 | static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) | ||
326 | { | ||
327 | int dncnt, i, d; | ||
328 | struct dnode **dnp; | ||
329 | |||
330 | if (dn==NULL || nfiles < 1) return(NULL); | ||
331 | |||
332 | /* count how many dirs and regular files there are */ | ||
333 | if (which == SPLIT_SUBDIR) | ||
334 | dncnt = countsubdirs(dn, nfiles); | ||
335 | else { | ||
336 | dncnt= countdirs(dn, nfiles); /* assume we are looking for dirs */ | ||
337 | if (which == SPLIT_FILE) | ||
338 | dncnt= nfiles - dncnt; /* looking for files */ | ||
339 | } | ||
340 | |||
341 | /* allocate a file array and a dir array */ | ||
342 | dnp= dnalloc(dncnt); | ||
343 | |||
344 | /* copy the entrys into the file or dir array */ | ||
345 | for (d= i=0; i<nfiles; i++) { | ||
346 | if (which == SPLIT_DIR) { | ||
347 | if (S_ISDIR(dn[i]->dstat.st_mode)) { | ||
348 | dnp[d++]= dn[i]; | ||
349 | } /* else skip the file */ | ||
350 | } else if (which == SPLIT_SUBDIR) { | ||
351 | if (is_subdir(dn[i])) { | ||
352 | dnp[d++]= dn[i]; | ||
353 | } /* else skip the file or dir */ | ||
354 | } else { | ||
355 | if (!(S_ISDIR(dn[i]->dstat.st_mode))) { | ||
356 | dnp[d++]= dn[i]; | ||
357 | } /* else skip the dir */ | ||
358 | } | ||
359 | } | ||
360 | return(dnp); | ||
361 | } | ||
362 | |||
363 | /*----------------------------------------------------------------------*/ | ||
364 | #ifdef BB_FEATURE_LS_SORTFILES | ||
365 | static int sortcmp(struct dnode *d1, struct dnode *d2) | ||
366 | { | ||
367 | int cmp, dif; | ||
368 | |||
369 | cmp= 0; | ||
370 | if (sort_opts == SORT_SIZE) { | ||
371 | dif= (int)(d1->dstat.st_size - d2->dstat.st_size); | ||
372 | } else if (sort_opts == SORT_ATIME) { | ||
373 | dif= (int)(d1->dstat.st_atime - d2->dstat.st_atime); | ||
374 | } else if (sort_opts == SORT_CTIME) { | ||
375 | dif= (int)(d1->dstat.st_ctime - d2->dstat.st_ctime); | ||
376 | } else if (sort_opts == SORT_MTIME) { | ||
377 | dif= (int)(d1->dstat.st_mtime - d2->dstat.st_mtime); | ||
378 | } else if (sort_opts == SORT_DIR) { | ||
379 | dif= S_ISDIR(d1->dstat.st_mode) - S_ISDIR(d2->dstat.st_mode); | ||
380 | /* } else if (sort_opts == SORT_VERSION) { */ | ||
381 | /* } else if (sort_opts == SORT_EXT) { */ | ||
382 | } else { /* assume SORT_NAME */ | ||
383 | dif= 0; | ||
384 | } | ||
385 | |||
386 | if (dif > 0) cmp= -1; | ||
387 | if (dif < 0) cmp= 1; | ||
388 | if (dif == 0) { | ||
389 | /* sort by name- may be a tie_breaker for time or size cmp */ | ||
390 | dif= strcmp(d1->name, d2->name); | ||
391 | if (dif > 0) cmp= 1; | ||
392 | if (dif < 0) cmp= -1; | ||
393 | } | ||
394 | |||
395 | if (sort_order == SORT_REVERSE) { | ||
396 | cmp= -1 * cmp; | ||
397 | } | ||
398 | return(cmp); | ||
399 | } | ||
400 | |||
401 | /*----------------------------------------------------------------------*/ | ||
402 | static void shellsort(struct dnode **dn, int size) | ||
403 | { | ||
404 | struct dnode *temp; | ||
405 | int gap, i, j; | ||
406 | |||
407 | /* shell short the array */ | ||
408 | if(dn==NULL || size < 2) return; | ||
409 | |||
410 | for (gap= size/2; gap>0; gap /=2) { | ||
411 | for (i=gap; i<size; i++) { | ||
412 | for (j= i-gap; j>=0; j-=gap) { | ||
413 | if (sortcmp(dn[j], dn[j+gap]) <= 0) | ||
414 | break; | ||
415 | /* they are out of order, swap them */ | ||
416 | temp= dn[j]; | ||
417 | dn[j]= dn[j+gap]; | ||
418 | dn[j+gap]= temp; | ||
419 | } | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | #endif | ||
424 | |||
425 | /*----------------------------------------------------------------------*/ | ||
426 | static void showfiles(struct dnode **dn, int nfiles) | ||
427 | { | ||
428 | int i, ncols, nrows, row, nc; | ||
429 | #ifdef BB_FEATURE_AUTOWIDTH | ||
430 | int len; | ||
431 | #endif | ||
432 | |||
433 | if(dn==NULL || nfiles < 1) return; | ||
434 | |||
435 | #ifdef BB_FEATURE_AUTOWIDTH | ||
436 | /* find the longest file name- use that as the column width */ | ||
437 | column_width= 0; | ||
438 | for (i=0; i<nfiles; i++) { | ||
439 | len= strlen(dn[i]->name) + | ||
440 | ((list_fmt & LIST_INO) ? 8 : 0) + | ||
441 | ((list_fmt & LIST_BLOCKS) ? 5 : 0) | ||
442 | ; | ||
443 | if (column_width < len) | ||
444 | column_width= len; | ||
445 | } | ||
446 | if (column_width >= 6) | ||
447 | ncols = (int)(terminal_width / (column_width + COLUMN_GAP)); | ||
448 | else { | ||
449 | ncols = 1; | ||
450 | column_width = COLUMN_WIDTH; | ||
451 | } | ||
452 | #else | ||
453 | ncols= TERMINAL_WIDTH; | ||
454 | #endif | ||
455 | switch (style_fmt) { | ||
456 | case STYLE_LONG: /* one record per line, extended info */ | ||
457 | case STYLE_SINGLE: /* one record per line */ | ||
458 | ncols= 1; | ||
459 | break; | ||
460 | } | ||
461 | |||
462 | if (ncols > 1) { | ||
463 | nrows = nfiles / ncols; | ||
464 | } else { | ||
465 | nrows = nfiles; | ||
466 | ncols = 1; | ||
467 | } | ||
468 | if ((nrows * ncols) < nfiles) nrows++; /* round up fractionals */ | ||
469 | |||
470 | if (nrows > nfiles) nrows= nfiles; | ||
471 | for (row=0; row<nrows; row++) { | ||
472 | for (nc=0; nc<ncols; nc++) { | ||
473 | /* reach into the array based on the column and row */ | ||
474 | i= (nc * nrows) + row; /* assume display by column */ | ||
475 | if (disp_opts & DISP_ROWS) | ||
476 | i= (row * ncols) + nc; /* display across row */ | ||
477 | if (i < nfiles) { | ||
478 | nexttabstop(); | ||
479 | list_single(dn[i]); | ||
480 | } | ||
481 | } | ||
482 | newline(); | ||
483 | } | ||
484 | } | ||
485 | |||
486 | /*----------------------------------------------------------------------*/ | ||
487 | static void showdirs(struct dnode **dn, int ndirs) | ||
488 | { | ||
489 | int i, nfiles; | ||
490 | struct dnode **subdnp; | ||
491 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
492 | int dndirs; | ||
493 | struct dnode **dnd; | ||
494 | #endif | ||
495 | |||
496 | if (dn==NULL || ndirs < 1) return; | ||
497 | |||
498 | for (i=0; i<ndirs; i++) { | ||
499 | if (disp_opts & (DISP_DIRNAME | DISP_RECURSIVE)) { | ||
500 | printf("\n%s:\n", dn[i]->fullname); | ||
501 | } | ||
502 | subdnp= list_dir(dn[i]->fullname); | ||
503 | nfiles= countfiles(subdnp); | ||
504 | if (nfiles > 0) { | ||
505 | /* list all files at this level */ | ||
506 | #ifdef BB_FEATURE_LS_SORTFILES | ||
507 | shellsort(subdnp, nfiles); | ||
508 | #endif | ||
509 | showfiles(subdnp, nfiles); | ||
510 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
511 | if (disp_opts & DISP_RECURSIVE) { | ||
512 | /* recursive- list the sub-dirs */ | ||
513 | dnd= splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); | ||
514 | dndirs= countsubdirs(subdnp, nfiles); | ||
515 | if (dndirs > 0) { | ||
516 | #ifdef BB_FEATURE_LS_SORTFILES | ||
517 | shellsort(dnd, dndirs); | ||
518 | #endif | ||
519 | showdirs(dnd, dndirs); | ||
520 | free(dnd); /* free the array of dnode pointers to the dirs */ | ||
521 | } | ||
522 | } | ||
523 | dfree(subdnp); /* free the dnodes and the fullname mem */ | ||
524 | #endif | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /*----------------------------------------------------------------------*/ | ||
530 | static struct dnode **list_dir(char *path) | ||
531 | { | ||
532 | struct dnode *dn, *cur, **dnp; | ||
533 | struct dirent *entry; | ||
534 | DIR *dir; | ||
535 | int i, nfiles; | ||
536 | |||
537 | if (path==NULL) return(NULL); | ||
538 | |||
539 | dn= NULL; | ||
540 | nfiles= 0; | ||
541 | dir = opendir(path); | ||
542 | if (dir == NULL) { | ||
543 | perror_msg("%s", path); | ||
544 | status = EXIT_FAILURE; | ||
545 | return(NULL); /* could not open the dir */ | ||
546 | } | ||
547 | while ((entry = readdir(dir)) != NULL) { | ||
548 | /* are we going to list the file- it may be . or .. or a hidden file */ | ||
549 | if ((strcmp(entry->d_name, ".")==0) && !(disp_opts & DISP_DOT)) | ||
550 | continue; | ||
551 | if ((strcmp(entry->d_name, "..")==0) && !(disp_opts & DISP_DOT)) | ||
552 | continue; | ||
553 | if ((entry->d_name[0] == '.') && !(disp_opts & DISP_HIDDEN)) | ||
554 | continue; | ||
555 | cur= (struct dnode *)xmalloc(sizeof(struct dnode)); | ||
556 | cur->fullname = concat_path_file(path, entry->d_name); | ||
557 | cur->name = cur->fullname + | ||
558 | (strlen(cur->fullname) - strlen(entry->d_name)); | ||
559 | if (my_stat(cur)) | ||
560 | continue; | ||
561 | cur->next= dn; | ||
562 | dn= cur; | ||
563 | nfiles++; | ||
564 | } | ||
565 | closedir(dir); | ||
566 | |||
567 | /* now that we know how many files there are | ||
568 | ** allocate memory for an array to hold dnode pointers | ||
569 | */ | ||
570 | if (nfiles < 1) return(NULL); | ||
571 | dnp= dnalloc(nfiles); | ||
572 | for (i=0, cur=dn; i<nfiles; i++) { | ||
573 | dnp[i]= cur; /* save pointer to node in array */ | ||
574 | cur= cur->next; | ||
575 | } | ||
576 | |||
577 | return(dnp); | ||
578 | } | ||
579 | |||
580 | /*----------------------------------------------------------------------*/ | ||
581 | static int list_single(struct dnode *dn) | ||
582 | { | ||
583 | int i; | ||
584 | char scratch[BUFSIZ + 1]; | ||
585 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
586 | char *filetime; | ||
587 | time_t ttime, age; | ||
588 | #endif | ||
589 | #if defined (BB_FEATURE_LS_FILETYPES) | ||
590 | struct stat info; | ||
591 | #endif | ||
592 | #ifdef BB_FEATURE_LS_FILETYPES | ||
593 | char append; | ||
594 | #endif | ||
595 | |||
596 | if (dn==NULL || dn->fullname==NULL) return(0); | ||
597 | |||
598 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
599 | ttime= dn->dstat.st_mtime; /* the default time */ | ||
600 | if (time_fmt & TIME_ACCESS) ttime= dn->dstat.st_atime; | ||
601 | if (time_fmt & TIME_CHANGE) ttime= dn->dstat.st_ctime; | ||
602 | filetime= ctime(&ttime); | ||
603 | #endif | ||
604 | #ifdef BB_FEATURE_LS_FILETYPES | ||
605 | append = append_char(dn->dstat.st_mode); | ||
606 | #endif | ||
607 | |||
608 | for (i=0; i<=31; i++) { | ||
609 | switch (list_fmt & (1<<i)) { | ||
610 | case LIST_INO: | ||
611 | printf("%7ld ", (long int)dn->dstat.st_ino); | ||
612 | column += 8; | ||
613 | break; | ||
614 | case LIST_BLOCKS: | ||
615 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
616 | fprintf(stdout, "%6s ", make_human_readable_str(dn->dstat.st_blocks>>1, | ||
617 | KILOBYTE, (ls_disp_hr==TRUE)? 0: KILOBYTE)); | ||
618 | #else | ||
619 | #if _FILE_OFFSET_BITS == 64 | ||
620 | printf("%4lld ", dn->dstat.st_blocks>>1); | ||
621 | #else | ||
622 | printf("%4ld ", dn->dstat.st_blocks>>1); | ||
623 | #endif | ||
624 | #endif | ||
625 | column += 5; | ||
626 | break; | ||
627 | case LIST_MODEBITS: | ||
628 | printf("%-10s ", (char *)mode_string(dn->dstat.st_mode)); | ||
629 | column += 10; | ||
630 | break; | ||
631 | case LIST_NLINKS: | ||
632 | printf("%4ld ", (long)dn->dstat.st_nlink); | ||
633 | column += 10; | ||
634 | break; | ||
635 | case LIST_ID_NAME: | ||
636 | #ifdef BB_FEATURE_LS_USERNAME | ||
637 | my_getpwuid(scratch, dn->dstat.st_uid); | ||
638 | printf("%-8.8s ", scratch); | ||
639 | my_getgrgid(scratch, dn->dstat.st_gid); | ||
640 | printf("%-8.8s", scratch); | ||
641 | column += 17; | ||
642 | break; | ||
643 | #endif | ||
644 | case LIST_ID_NUMERIC: | ||
645 | printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); | ||
646 | column += 17; | ||
647 | break; | ||
648 | case LIST_SIZE: | ||
649 | case LIST_DEV: | ||
650 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | ||
651 | printf("%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev)); | ||
652 | } else { | ||
653 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
654 | if (ls_disp_hr==TRUE) { | ||
655 | fprintf(stdout, "%8s ", make_human_readable_str(dn->dstat.st_size, 1, 0)); | ||
656 | } else | ||
657 | #endif | ||
658 | { | ||
659 | #if _FILE_OFFSET_BITS == 64 | ||
660 | printf("%9lld ", (long long)dn->dstat.st_size); | ||
661 | #else | ||
662 | printf("%9ld ", dn->dstat.st_size); | ||
663 | #endif | ||
664 | } | ||
665 | } | ||
666 | column += 10; | ||
667 | break; | ||
668 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
669 | case LIST_FULLTIME: | ||
670 | case LIST_DATE_TIME: | ||
671 | if (list_fmt & LIST_FULLTIME) { | ||
672 | printf("%24.24s ", filetime); | ||
673 | column += 25; | ||
674 | break; | ||
675 | } | ||
676 | age = time(NULL) - ttime; | ||
677 | printf("%6.6s ", filetime+4); | ||
678 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | ||
679 | /* hh:mm if less than 6 months old */ | ||
680 | printf("%5.5s ", filetime+11); | ||
681 | } else { | ||
682 | printf(" %4.4s ", filetime+20); | ||
683 | } | ||
684 | column += 13; | ||
685 | break; | ||
686 | #endif | ||
687 | case LIST_FILENAME: | ||
688 | printf("%s", dn->name); | ||
689 | column += strlen(dn->name); | ||
690 | break; | ||
691 | case LIST_SYMLINK: | ||
692 | if (S_ISLNK(dn->dstat.st_mode)) { | ||
693 | char *lpath = xreadlink(dn->fullname); | ||
694 | if (lpath) { | ||
695 | printf(" -> %s", lpath); | ||
696 | #ifdef BB_FEATURE_LS_FILETYPES | ||
697 | if (!stat(dn->fullname, &info)) { | ||
698 | append = append_char(info.st_mode); | ||
699 | } | ||
700 | #endif | ||
701 | column += strlen(lpath) + 4; | ||
702 | free(lpath); | ||
703 | } | ||
704 | } | ||
705 | break; | ||
706 | #ifdef BB_FEATURE_LS_FILETYPES | ||
707 | case LIST_FILETYPE: | ||
708 | if (append != '\0') { | ||
709 | printf("%1c", append); | ||
710 | column++; | ||
711 | } | ||
712 | break; | ||
713 | #endif | ||
714 | } | ||
715 | } | ||
716 | |||
717 | return(0); | ||
718 | } | ||
719 | |||
720 | /*----------------------------------------------------------------------*/ | ||
721 | extern int ls_main(int argc, char **argv) | ||
722 | { | ||
723 | struct dnode **dnf, **dnd; | ||
724 | int dnfiles, dndirs; | ||
725 | struct dnode *dn, *cur, **dnp; | ||
726 | int i, nfiles; | ||
727 | int opt; | ||
728 | int oi, ac; | ||
729 | char **av; | ||
730 | #ifdef BB_FEATURE_AUTOWIDTH | ||
731 | struct winsize win = { 0, 0, 0, 0 }; | ||
732 | #endif | ||
733 | |||
734 | disp_opts= DISP_NORMAL; | ||
735 | style_fmt= STYLE_AUTO; | ||
736 | list_fmt= LIST_SHORT; | ||
737 | #ifdef BB_FEATURE_LS_SORTFILES | ||
738 | sort_opts= SORT_NAME; | ||
739 | sort_order= SORT_FORWARD; | ||
740 | #endif | ||
741 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
742 | time_fmt= TIME_MOD; | ||
743 | #endif | ||
744 | #ifdef BB_FEATURE_AUTOWIDTH | ||
745 | ioctl(fileno(stdout), TIOCGWINSZ, &win); | ||
746 | if (win.ws_row > 4) | ||
747 | column_width = win.ws_row - 2; | ||
748 | if (win.ws_col > 0) | ||
749 | terminal_width = win.ws_col - 1; | ||
750 | #endif | ||
751 | nfiles=0; | ||
752 | |||
753 | /* process options */ | ||
754 | while ((opt = getopt(argc, argv, "1AaCdgilnsx" | ||
755 | #ifdef BB_FEATURE_AUTOWIDTH | ||
756 | "T:w:" | ||
757 | #endif | ||
758 | #ifdef BB_FEATURE_LS_FILETYPES | ||
759 | "Fp" | ||
760 | #endif | ||
761 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
762 | "R" | ||
763 | #endif | ||
764 | #ifdef BB_FEATURE_LS_SORTFILES | ||
765 | "rSvX" | ||
766 | #endif | ||
767 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
768 | "cetu" | ||
769 | #endif | ||
770 | #ifdef BB_FEATURE_LS_FOLLOWLINKS | ||
771 | "L" | ||
772 | #endif | ||
773 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
774 | "h" | ||
775 | #endif | ||
776 | "k")) > 0) { | ||
777 | switch (opt) { | ||
778 | case '1': style_fmt = STYLE_SINGLE; break; | ||
779 | case 'A': disp_opts |= DISP_HIDDEN; break; | ||
780 | case 'a': disp_opts |= DISP_HIDDEN | DISP_DOT; break; | ||
781 | case 'C': style_fmt = STYLE_COLUMNS; break; | ||
782 | case 'd': disp_opts |= DISP_NOLIST; break; | ||
783 | case 'g': /* ignore -- for ftp servers */ break; | ||
784 | case 'i': list_fmt |= LIST_INO; break; | ||
785 | case 'l': | ||
786 | style_fmt = STYLE_LONG; | ||
787 | list_fmt |= LIST_LONG; | ||
788 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
789 | ls_disp_hr = FALSE; | ||
790 | #endif | ||
791 | break; | ||
792 | case 'n': list_fmt |= LIST_ID_NUMERIC; break; | ||
793 | case 's': list_fmt |= LIST_BLOCKS; break; | ||
794 | case 'x': disp_opts = DISP_ROWS; break; | ||
795 | #ifdef BB_FEATURE_LS_FILETYPES | ||
796 | case 'F': list_fmt |= LIST_FILETYPE | LIST_EXEC; break; | ||
797 | case 'p': list_fmt |= LIST_FILETYPE; break; | ||
798 | #endif | ||
799 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
800 | case 'R': disp_opts |= DISP_RECURSIVE; break; | ||
801 | #endif | ||
802 | #ifdef BB_FEATURE_LS_SORTFILES | ||
803 | case 'r': sort_order |= SORT_REVERSE; break; | ||
804 | case 'S': sort_opts= SORT_SIZE; break; | ||
805 | case 'v': sort_opts= SORT_VERSION; break; | ||
806 | case 'X': sort_opts= SORT_EXT; break; | ||
807 | #endif | ||
808 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
809 | case 'e': list_fmt |= LIST_FULLTIME; break; | ||
810 | case 'c': | ||
811 | time_fmt = TIME_CHANGE; | ||
812 | #ifdef BB_FEATURE_LS_SORTFILES | ||
813 | sort_opts= SORT_CTIME; | ||
814 | #endif | ||
815 | break; | ||
816 | case 'u': | ||
817 | time_fmt = TIME_ACCESS; | ||
818 | #ifdef BB_FEATURE_LS_SORTFILES | ||
819 | sort_opts= SORT_ATIME; | ||
820 | #endif | ||
821 | break; | ||
822 | case 't': | ||
823 | #ifdef BB_FEATURE_LS_SORTFILES | ||
824 | sort_opts= SORT_MTIME; | ||
825 | #endif | ||
826 | break; | ||
827 | #endif | ||
828 | #ifdef BB_FEATURE_LS_FOLLOWLINKS | ||
829 | case 'L': follow_links= TRUE; break; | ||
830 | #endif | ||
831 | #ifdef BB_FEATURE_AUTOWIDTH | ||
832 | case 'T': tabstops= atoi(optarg); break; | ||
833 | case 'w': terminal_width= atoi(optarg); break; | ||
834 | #endif | ||
835 | #ifdef BB_FEATURE_HUMAN_READABLE | ||
836 | case 'h': ls_disp_hr = TRUE; break; | ||
837 | #endif | ||
838 | case 'k': break; | ||
839 | default: | ||
840 | goto print_usage_message; | ||
841 | } | ||
842 | } | ||
843 | |||
844 | /* sort out which command line options take precedence */ | ||
845 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
846 | if (disp_opts & DISP_NOLIST) | ||
847 | disp_opts &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ | ||
848 | #endif | ||
849 | #if defined (BB_FEATURE_LS_TIMESTAMPS) && defined (BB_FEATURE_LS_SORTFILES) | ||
850 | if (time_fmt & TIME_CHANGE) sort_opts= SORT_CTIME; | ||
851 | if (time_fmt & TIME_ACCESS) sort_opts= SORT_ATIME; | ||
852 | #endif | ||
853 | if (style_fmt != STYLE_LONG) | ||
854 | list_fmt &= ~LIST_ID_NUMERIC; /* numeric uid only for long list */ | ||
855 | #ifdef BB_FEATURE_LS_USERNAME | ||
856 | if (style_fmt == STYLE_LONG && (list_fmt & LIST_ID_NUMERIC)) | ||
857 | list_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ | ||
858 | #endif | ||
859 | |||
860 | /* choose a display format */ | ||
861 | if (style_fmt == STYLE_AUTO) | ||
862 | style_fmt = isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE; | ||
863 | |||
864 | /* | ||
865 | * when there are no cmd line args we have to supply a default "." arg. | ||
866 | * we will create a second argv array, "av" that will hold either | ||
867 | * our created "." arg, or the real cmd line args. The av array | ||
868 | * just holds the pointers- we don't move the date the pointers | ||
869 | * point to. | ||
870 | */ | ||
871 | ac= argc - optind; /* how many cmd line args are left */ | ||
872 | if (ac < 1) { | ||
873 | av= (char **)xcalloc((size_t)1, (size_t)(sizeof(char *))); | ||
874 | av[0]= xstrdup("."); | ||
875 | ac=1; | ||
876 | } else { | ||
877 | av= (char **)xcalloc((size_t)ac, (size_t)(sizeof(char *))); | ||
878 | for (oi=0 ; oi < ac; oi++) { | ||
879 | av[oi]= argv[optind++]; /* copy pointer to real cmd line arg */ | ||
880 | } | ||
881 | } | ||
882 | |||
883 | /* now, everything is in the av array */ | ||
884 | if (ac > 1) | ||
885 | disp_opts |= DISP_DIRNAME; /* 2 or more items? label directories */ | ||
886 | |||
887 | /* stuff the command line file names into an dnode array */ | ||
888 | dn=NULL; | ||
889 | for (oi=0 ; oi < ac; oi++) { | ||
890 | cur= (struct dnode *)xmalloc(sizeof(struct dnode)); | ||
891 | cur->fullname= xstrdup(av[oi]); | ||
892 | cur->name= cur->fullname; | ||
893 | if (my_stat(cur)) | ||
894 | continue; | ||
895 | cur->next= dn; | ||
896 | dn= cur; | ||
897 | nfiles++; | ||
898 | } | ||
899 | |||
900 | /* now that we know how many files there are | ||
901 | ** allocate memory for an array to hold dnode pointers | ||
902 | */ | ||
903 | dnp= dnalloc(nfiles); | ||
904 | for (i=0, cur=dn; i<nfiles; i++) { | ||
905 | dnp[i]= cur; /* save pointer to node in array */ | ||
906 | cur= cur->next; | ||
907 | } | ||
908 | |||
909 | |||
910 | if (disp_opts & DISP_NOLIST) { | ||
911 | #ifdef BB_FEATURE_LS_SORTFILES | ||
912 | shellsort(dnp, nfiles); | ||
913 | #endif | ||
914 | if (nfiles > 0) showfiles(dnp, nfiles); | ||
915 | } else { | ||
916 | dnd= splitdnarray(dnp, nfiles, SPLIT_DIR); | ||
917 | dnf= splitdnarray(dnp, nfiles, SPLIT_FILE); | ||
918 | dndirs= countdirs(dnp, nfiles); | ||
919 | dnfiles= nfiles - dndirs; | ||
920 | if (dnfiles > 0) { | ||
921 | #ifdef BB_FEATURE_LS_SORTFILES | ||
922 | shellsort(dnf, dnfiles); | ||
923 | #endif | ||
924 | showfiles(dnf, dnfiles); | ||
925 | } | ||
926 | if (dndirs > 0) { | ||
927 | #ifdef BB_FEATURE_LS_SORTFILES | ||
928 | shellsort(dnd, dndirs); | ||
929 | #endif | ||
930 | showdirs(dnd, dndirs); | ||
931 | } | ||
932 | } | ||
933 | return(status); | ||
934 | |||
935 | print_usage_message: | ||
936 | show_usage(); | ||
937 | } | ||