diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-09-07 17:24:47 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-09-07 17:24:47 +0000 |
commit | 11c655295c78970d67bc5ee661076743957d8ffb (patch) | |
tree | e5afeaa3728657f54799a54221f43a8f8c73f612 /coreutils/ls.c | |
parent | 44e384034380bde803ec444418532a315e8589e8 (diff) | |
download | busybox-w32-11c655295c78970d67bc5ee661076743957d8ffb.tar.gz busybox-w32-11c655295c78970d67bc5ee661076743957d8ffb.tar.bz2 busybox-w32-11c655295c78970d67bc5ee661076743957d8ffb.zip |
New ls sorting patch, as written by Sterling Huxley, and then updated
by kent robotti. I then updated it to use my_getpwuid and my_getgrgid
(per busybox policy).
-Erik
Diffstat (limited to 'coreutils/ls.c')
-rw-r--r-- | coreutils/ls.c | 1114 |
1 files changed, 642 insertions, 472 deletions
diff --git a/coreutils/ls.c b/coreutils/ls.c index 207c61763..03fed5478 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
@@ -2,7 +2,7 @@ | |||
2 | /* | 2 | /* |
3 | * tiny-ls.c version 0.1.0: A minimalist 'ls' | 3 | * tiny-ls.c version 0.1.0: A minimalist 'ls' |
4 | * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> | 4 | * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
@@ -42,14 +42,14 @@ | |||
42 | */ | 42 | */ |
43 | 43 | ||
44 | #define TERMINAL_WIDTH 80 /* use 79 if your terminal has linefold bug */ | 44 | #define TERMINAL_WIDTH 80 /* use 79 if your terminal has linefold bug */ |
45 | #define COLUMN_WIDTH 14 /* default if AUTOWIDTH not defined */ | 45 | #define COLUMN_WIDTH 14 /* default if AUTOWIDTH not defined */ |
46 | #define COLUMN_GAP 2 /* includes the file type char, if present */ | 46 | #define COLUMN_GAP 2 /* includes the file type char, if present */ |
47 | #define HAS_REWINDDIR | ||
48 | 47 | ||
49 | /************************************************************************/ | 48 | /************************************************************************/ |
50 | 49 | ||
51 | #include "internal.h" | 50 | #include "internal.h" |
52 | # include <sys/types.h> | 51 | #include <sys/types.h> |
52 | #include <sys/stat.h> | ||
53 | #include <stdio.h> | 53 | #include <stdio.h> |
54 | #include <unistd.h> | 54 | #include <unistd.h> |
55 | #include <dirent.h> | 55 | #include <dirent.h> |
@@ -58,590 +58,760 @@ | |||
58 | #ifdef BB_FEATURE_LS_TIMESTAMPS | 58 | #ifdef BB_FEATURE_LS_TIMESTAMPS |
59 | #include <time.h> | 59 | #include <time.h> |
60 | #endif | 60 | #endif |
61 | #include <string.h> | ||
61 | 62 | ||
62 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) | 63 | #ifndef NAJOR |
63 | #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) | ||
64 | #ifdef BB_FEATURE_LS_FILETYPES | ||
65 | #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) | ||
66 | #endif | ||
67 | |||
68 | #define FMT_AUTO 0 | ||
69 | #define FMT_LONG 1 /* one record per line, extended info */ | ||
70 | #define FMT_SINGLE 2 /* one record per line */ | ||
71 | #define FMT_ROWS 3 /* print across rows */ | ||
72 | #define FMT_COLUMNS 3 /* fill columns (same, since we don't sort) */ | ||
73 | |||
74 | #define TIME_MOD 0 | ||
75 | #define TIME_CHANGE 1 | ||
76 | #define TIME_ACCESS 2 | ||
77 | |||
78 | #define DISP_FTYPE 1 /* show character for file type */ | ||
79 | #define DISP_EXEC 2 /* show '*' if regular executable file */ | ||
80 | #define DISP_HIDDEN 4 /* show files starting . (except . and ..) */ | ||
81 | #define DISP_DOT 8 /* show . and .. */ | ||
82 | #define DISP_NUMERIC 16 /* numeric uid and gid */ | ||
83 | #define DISP_FULLTIME 32 /* show extended time display */ | ||
84 | #define DIR_NOLIST 64 /* show directory as itself, not contents */ | ||
85 | #define DISP_DIRNAME 128 /* show directory name (for internal use) */ | ||
86 | #define DISP_RECURSIVE 256 /* Do a recursive listing */ | ||
87 | |||
88 | #ifndef MAJOR | ||
89 | #define MAJOR(dev) (((dev)>>8)&0xff) | 64 | #define MAJOR(dev) (((dev)>>8)&0xff) |
90 | #define MINOR(dev) ((dev)&0xff) | 65 | #define MINOR(dev) ((dev)&0xff) |
91 | #endif | 66 | #endif |
92 | 67 | ||
68 | /* what is the overall style of the listing */ | ||
69 | #define STYLE_AUTO 0 | ||
70 | #define STYLE_LONG 1 /* one record per line, extended info */ | ||
71 | #define STYLE_SINGLE 2 /* one record per line */ | ||
72 | #define STYLE_COLUMNS 3 /* fill columns */ | ||
73 | |||
74 | /* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ | ||
75 | /* what file information will be listed */ | ||
76 | #define LIST_INO (1<<0) | ||
77 | #define LIST_BLOCKS (1<<1) | ||
78 | #define LIST_MODEBITS (1<<2) | ||
79 | #define LIST_NLINKS (1<<3) | ||
80 | #define LIST_ID_NAME (1<<4) | ||
81 | #define LIST_ID_NUMERIC (1<<5) | ||
82 | #define LIST_SIZE (1<<6) | ||
83 | #define LIST_DEV (1<<7) | ||
84 | #define LIST_DATE_TIME (1<<8) | ||
85 | #define LIST_FULLTIME (1<<9) | ||
86 | #define LIST_FILENAME (1<<10) | ||
87 | #define LIST_SYMLINK (1<<11) | ||
88 | #define LIST_FILETYPE (1<<12) | ||
89 | #define LIST_EXEC (1<<13) | ||
90 | |||
91 | /* what files will be displayed */ | ||
92 | #define DISP_NORMAL (0) /* show normal filenames */ | ||
93 | #define DISP_DIRNAME (1<<0) /* 2 or more items? label directories */ | ||
94 | #define DISP_HIDDEN (1<<1) /* show filenames starting with . */ | ||
95 | #define DISP_DOT (1<<2) /* show . and .. */ | ||
96 | #define DISP_NOLIST (1<<3) /* show directory as itself, not contents */ | ||
97 | #define DISP_RECURSIVE (1<<4) /* show directory and everything below it */ | ||
98 | #define DISP_ROWS (1<<5) /* print across rows */ | ||
99 | |||
93 | #ifdef BB_FEATURE_LS_SORTFILES | 100 | #ifdef BB_FEATURE_LS_SORTFILES |
101 | /* how will the files be sorted */ | ||
102 | #define SORT_FORWARD 0 /* sort in reverse order */ | ||
103 | #define SORT_REVERSE 1 /* sort in reverse order */ | ||
104 | #define SORT_NAME 2 /* sort by file name */ | ||
105 | #define SORT_SIZE 3 /* sort by file size */ | ||
106 | #define SORT_ATIME 4 /* sort by last access time */ | ||
107 | #define SORT_CTIME 5 /* sort by last change time */ | ||
108 | #define SORT_MTIME 6 /* sort by last modification time */ | ||
109 | #define SORT_VERSION 7 /* sort by version */ | ||
110 | #define SORT_EXT 8 /* sort by file name extension */ | ||
111 | #define SORT_DIR 9 /* sort by file or directory */ | ||
112 | #endif | ||
113 | |||
114 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
115 | /* which of the three times will be used */ | ||
116 | #define TIME_MOD 0 | ||
117 | #define TIME_CHANGE 1 | ||
118 | #define TIME_ACCESS 2 | ||
119 | #endif | ||
120 | |||
121 | #define LIST_SHORT (LIST_FILENAME) | ||
122 | #define LIST_ISHORT (LIST_INO | LIST_FILENAME) | ||
123 | #define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | \ | ||
124 | LIST_SIZE | LIST_DATE_TIME | LIST_FILENAME | \ | ||
125 | LIST_SYMLINK) | ||
126 | #define LIST_ILONG (LIST_INO | LIST_LONG) | ||
127 | |||
128 | #define SPLIT_DIR 0 | ||
129 | #define SPLIT_FILE 1 | ||
130 | |||
131 | #define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) | ||
132 | #define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) | ||
133 | #ifdef BB_FEATURE_LS_FILETYPES | ||
134 | #define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) | ||
135 | #endif | ||
136 | |||
137 | /* | ||
138 | * a directory entry and its stat info are stored here | ||
139 | */ | ||
94 | struct dnode { /* the basic node */ | 140 | struct dnode { /* the basic node */ |
95 | char *name; /* the dir entry name */ | 141 | char *name; /* the dir entry name */ |
96 | char *fullname; /* the dir entry name */ | 142 | char *fullname; /* the dir entry name */ |
97 | struct stat dstat; /* the file stat info */ | 143 | struct stat dstat; /* the file stat info */ |
144 | struct dnode *next; /* point at the next node */ | ||
98 | }; | 145 | }; |
99 | typedef struct dnode dnode_t; | 146 | typedef struct dnode dnode_t; |
100 | #endif | ||
101 | static unsigned char display_fmt = FMT_AUTO; | ||
102 | static unsigned short opts = 0; | ||
103 | static unsigned short column = 0; | ||
104 | 147 | ||
105 | #ifdef BB_FEATURE_AUTOWIDTH | 148 | struct dnode **list_dir(char *); |
106 | static unsigned short terminal_width = 0; | 149 | struct dnode **dnalloc(int); |
107 | static unsigned short column_width = 0; | 150 | int list_single(struct dnode *); |
108 | static unsigned short toplevel_column_width = 0; | ||
109 | #else | ||
110 | #define terminal_width TERMINAL_WIDTH | ||
111 | #define column_width COLUMN_WIDTH | ||
112 | #endif | ||
113 | 151 | ||
152 | static unsigned int disp_opts= DISP_NORMAL; | ||
153 | static unsigned int style_fmt= STYLE_AUTO ; | ||
154 | static unsigned int list_fmt= LIST_SHORT ; | ||
155 | #ifdef BB_FEATURE_LS_SORTFILES | ||
156 | static unsigned int sort_opts= SORT_FORWARD; | ||
157 | static unsigned int sort_order= SORT_FORWARD; | ||
158 | #endif | ||
114 | #ifdef BB_FEATURE_LS_TIMESTAMPS | 159 | #ifdef BB_FEATURE_LS_TIMESTAMPS |
115 | static unsigned char time_fmt = TIME_MOD; | 160 | static unsigned int time_fmt= TIME_MOD; |
116 | #endif | 161 | #endif |
117 | 162 | ||
118 | #define wr(data,len) fwrite(data, 1, len, stdout) | 163 | static unsigned short column = 0; |
164 | #ifdef BB_FEATURE_AUTOWIDTH | ||
165 | static unsigned short terminal_width = TERMINAL_WIDTH; | ||
166 | static unsigned short column_width = COLUMN_WIDTH; | ||
167 | static unsigned short tabstops = 8; | ||
168 | #else | ||
169 | #define terminal_width TERMINAL_WIDTH | ||
170 | #define column_width COLUMN_WIDTH | ||
171 | #endif | ||
119 | 172 | ||
120 | static void writenum(long val, short minwidth) | 173 | static void newline(void) |
121 | { | 174 | { |
122 | char scratch[128]; | 175 | if (column > 0) { |
123 | 176 | fprintf(stdout, "\n"); | |
124 | char *p = scratch + sizeof(scratch); | 177 | column = 0; |
125 | short len = 0; | 178 | } |
126 | short neg = (val < 0); | ||
127 | |||
128 | if (neg) | ||
129 | val = -val; | ||
130 | do | ||
131 | *--p = (val % 10) + '0', len++, val /= 10; | ||
132 | while (val); | ||
133 | if (neg) | ||
134 | *--p = '-', len++; | ||
135 | while (len < minwidth) | ||
136 | *--p = ' ', len++; | ||
137 | wr(p, len); | ||
138 | column += len; | ||
139 | } | 179 | } |
140 | 180 | ||
141 | static void newline(void) | 181 | /*----------------------------------------------------------------------*/ |
182 | #ifdef BB_FEATURE_LS_FILETYPES | ||
183 | static char append_char(mode_t mode) | ||
142 | { | 184 | { |
143 | if (column > 0) { | 185 | if ( !(list_fmt & LIST_FILETYPE)) |
144 | wr("\n", 1); | 186 | return '\0'; |
145 | column = 0; | 187 | if ((list_fmt & LIST_EXEC) && S_ISREG(mode) |
146 | } | 188 | && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; |
189 | return APPCHAR(mode); | ||
147 | } | 190 | } |
191 | #endif | ||
148 | 192 | ||
149 | static void tab(short col) | 193 | /*----------------------------------------------------------------------*/ |
194 | static void nexttabstop( void ) | ||
150 | { | 195 | { |
151 | static const char spaces[] = " "; | 196 | static short nexttab= 0; |
197 | int n=0; | ||
152 | 198 | ||
153 | #define nspaces ((sizeof spaces)-1) /* null terminator! */ | 199 | if (column > 0) { |
200 | n= nexttab - column; | ||
201 | if (n < 1) n= 1; | ||
202 | while (n--) { | ||
203 | fprintf(stdout, " "); | ||
204 | column++; | ||
205 | } | ||
206 | } | ||
207 | nexttab= column + column_width + COLUMN_GAP ; | ||
208 | } | ||
154 | 209 | ||
155 | short n = col - column; | 210 | /*----------------------------------------------------------------------*/ |
211 | int countdirs(struct dnode **dn, int nfiles) | ||
212 | { | ||
213 | int i, dirs; | ||
156 | 214 | ||
157 | if (n > 0) { | 215 | /* count how many dirs and regular files there are */ |
158 | column = col; | 216 | if (dn==NULL || nfiles < 1) return(0); |
159 | while (n > nspaces) { | 217 | dirs= 0; |
160 | wr(spaces, nspaces); | 218 | for (i=0; i<nfiles; i++) { |
161 | n -= nspaces; | 219 | if (S_ISDIR(dn[i]->dstat.st_mode)) dirs++; |
162 | } | ||
163 | /* must be 1...(sizeof spaces) left */ | ||
164 | wr(spaces, n); | ||
165 | } | 220 | } |
166 | #undef nspaces | 221 | return(dirs); |
167 | } | 222 | } |
168 | 223 | ||
169 | #ifdef BB_FEATURE_LS_FILETYPES | 224 | int countfiles(struct dnode **dnp) |
170 | static char append_char(mode_t mode) | ||
171 | { | 225 | { |
172 | if (!(opts & DISP_FTYPE)) | 226 | int nfiles; |
173 | return '\0'; | 227 | struct dnode *cur; |
174 | if ((opts & DISP_EXEC) && S_ISREG(mode) | 228 | |
175 | && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*'; | 229 | if (dnp == NULL) return(0); |
176 | return APPCHAR(mode); | 230 | nfiles= 0; |
231 | for (cur= dnp[0]; cur->next != NULL ; cur= cur->next) nfiles++; | ||
232 | nfiles++; | ||
233 | return(nfiles); | ||
177 | } | 234 | } |
178 | #endif | ||
179 | 235 | ||
180 | /** | 236 | /* get memory to hold an array of pointers */ |
181 | ** | 237 | struct dnode **dnalloc(int num) |
182 | ** Display a file or directory as a single item | 238 | { |
183 | ** (in either long or short format) | 239 | struct dnode **p; |
184 | ** | 240 | |
185 | **/ | 241 | if (num < 1) return(NULL); |
242 | |||
243 | p= (struct dnode **)xcalloc((size_t)num, (size_t)(sizeof(struct dnode *))); | ||
244 | return(p); | ||
245 | } | ||
186 | 246 | ||
187 | static void list_single(const char *name, struct stat *info, | 247 | void dfree(struct dnode **dnp) |
188 | const char *fullname) | ||
189 | { | 248 | { |
190 | char scratch[BUFSIZ + 1]; | 249 | struct dnode *cur, *next; |
191 | short len = strlen(name); | ||
192 | 250 | ||
193 | #ifdef BB_FEATURE_LS_FILETYPES | 251 | if(dnp == NULL) return; |
194 | char append = append_char(info->st_mode); | ||
195 | #endif | ||
196 | 252 | ||
197 | if (display_fmt == FMT_LONG) { | 253 | cur=dnp[0]; |
198 | mode_t mode = info->st_mode; | 254 | while (cur != NULL) { |
255 | if (cur->fullname != NULL) free(cur->fullname); /* free the filename */ | ||
256 | next= cur->next; | ||
257 | free(cur); /* free the dnode */ | ||
258 | cur= next; | ||
259 | } | ||
260 | free(dnp); /* free the array holding the dnode pointers */ | ||
261 | } | ||
199 | 262 | ||
200 | newline(); | 263 | struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) |
201 | wr(modeString(mode), 10); | 264 | { |
202 | column = 10; | 265 | int dncnt, i, d; |
203 | writenum((long) info->st_nlink, (short) 5); | 266 | struct dnode **dnp; |
204 | fputs(" ", stdout); | ||
205 | #ifdef BB_FEATURE_LS_USERNAME | ||
206 | if (!(opts & DISP_NUMERIC)) { | ||
207 | memset(scratch, 0, sizeof(scratch)); | ||
208 | my_getpwuid(scratch, info->st_uid); | ||
209 | if (*scratch) { | ||
210 | fputs(scratch, stdout); | ||
211 | if (strlen(scratch) <= 8) | ||
212 | wr(" ", 9 - strlen(scratch)); | ||
213 | } else { | ||
214 | writenum((long) info->st_uid, (short) 8); | ||
215 | fputs(" ", stdout); | ||
216 | } | ||
217 | } else | ||
218 | #endif | ||
219 | { | ||
220 | writenum((long) info->st_uid, (short) 8); | ||
221 | fputs(" ", stdout); | ||
222 | } | ||
223 | #ifdef BB_FEATURE_LS_USERNAME | ||
224 | if (!(opts & DISP_NUMERIC)) { | ||
225 | memset(scratch, 0, sizeof(scratch)); | ||
226 | my_getgrgid(scratch, info->st_gid); | ||
227 | if (*scratch) { | ||
228 | fputs(scratch, stdout); | ||
229 | if (strlen(scratch) <= 8) | ||
230 | wr(" ", 8 - strlen(scratch)); | ||
231 | } else | ||
232 | writenum((long) info->st_gid, (short) 8); | ||
233 | } else | ||
234 | #endif | ||
235 | writenum((long) info->st_gid, (short) 8); | ||
236 | //tab(26); | ||
237 | if (S_ISBLK(mode) || S_ISCHR(mode)) { | ||
238 | writenum((long) MAJOR(info->st_rdev), (short) 3); | ||
239 | fputs(", ", stdout); | ||
240 | writenum((long) MINOR(info->st_rdev), (short) 3); | ||
241 | } else | ||
242 | writenum((long) info->st_size, (short) 8); | ||
243 | fputs(" ", stdout); | ||
244 | //tab(32); | ||
245 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
246 | { | ||
247 | time_t cal; | ||
248 | char *string; | ||
249 | 267 | ||
250 | switch (time_fmt) { | 268 | if (dn==NULL || nfiles < 1) return(NULL); |
251 | case TIME_CHANGE: | 269 | |
252 | cal = info->st_ctime; | 270 | /* count how many dirs and regular files there are */ |
253 | break; | 271 | dncnt= countdirs(dn, nfiles); /* assume we are looking for dirs */ |
254 | case TIME_ACCESS: | 272 | if (which != SPLIT_DIR) |
255 | cal = info->st_atime; | 273 | dncnt= nfiles - dncnt; /* looking for files */ |
256 | break; | 274 | |
257 | default: | 275 | /* allocate a file array and a dir array */ |
258 | cal = info->st_mtime; | 276 | dnp= dnalloc(dncnt); |
259 | break; | 277 | |
260 | } | 278 | /* copy the entrys into the file or dir array */ |
261 | string = ctime(&cal); | 279 | for (d= i=0; i<nfiles; i++) { |
262 | if (opts & DISP_FULLTIME) | 280 | if (which == SPLIT_DIR) { |
263 | wr(string, 24); | 281 | if (S_ISDIR(dn[i]->dstat.st_mode)) { |
264 | else { | 282 | dnp[d++]= dn[i]; |
265 | time_t age = time(NULL) - cal; | 283 | } /* else skip the file */ |
266 | 284 | } else { | |
267 | wr(string + 4, 7); /* mmm_dd_ */ | 285 | if (!(S_ISDIR(dn[i]->dstat.st_mode))) { |
268 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) | 286 | dnp[d++]= dn[i]; |
269 | /* hh:mm if less than 6 months old */ | 287 | } /* else skip the dir */ |
270 | wr(string + 11, 5); | ||
271 | else | ||
272 | /* _yyyy otherwise */ | ||
273 | wr(string + 19, 5); | ||
274 | } | ||
275 | wr(" ", 1); | ||
276 | } | ||
277 | #else | ||
278 | fputs("--- -- ----- ", stdout); | ||
279 | #endif | ||
280 | wr(name, len); | ||
281 | if (S_ISLNK(mode)) { | ||
282 | wr(" -> ", 4); | ||
283 | len = readlink(fullname, scratch, sizeof scratch); | ||
284 | if (len > 0) | ||
285 | fwrite(scratch, 1, len, stdout); | ||
286 | #ifdef BB_FEATURE_LS_FILETYPES | ||
287 | /* show type of destination */ | ||
288 | if (opts & DISP_FTYPE) { | ||
289 | if (!stat(fullname, info)) { | ||
290 | append = append_char(info->st_mode); | ||
291 | if (append) | ||
292 | fputc(append, stdout); | ||
293 | } | ||
294 | } | ||
295 | #endif | ||
296 | } | 288 | } |
297 | #ifdef BB_FEATURE_LS_FILETYPES | ||
298 | else if (append) | ||
299 | wr(&append, 1); | ||
300 | #endif | ||
301 | } else { | ||
302 | static short nexttab = 0; | ||
303 | |||
304 | /* sort out column alignment */ | ||
305 | if (column == 0); /* nothing to do */ | ||
306 | else if (display_fmt == FMT_SINGLE) | ||
307 | newline(); | ||
308 | else { | ||
309 | if (nexttab + column_width > terminal_width | ||
310 | #ifndef BB_FEATURE_AUTOWIDTH | ||
311 | || nexttab + len >= terminal_width | ||
312 | #endif | ||
313 | ) | ||
314 | newline(); | ||
315 | else | ||
316 | tab(nexttab); | ||
317 | } | ||
318 | /* work out where next column starts */ | ||
319 | #ifdef BB_FEATURE_AUTOWIDTH | ||
320 | /* we know the calculated width is big enough */ | ||
321 | nexttab = column + column_width + COLUMN_GAP; | ||
322 | #else | ||
323 | /* might cover more than one fixed-width column */ | ||
324 | nexttab = column; | ||
325 | do | ||
326 | nexttab += column_width + COLUMN_GAP; | ||
327 | while (nexttab < (column + len + COLUMN_GAP)); | ||
328 | #endif | ||
329 | /* now write the data */ | ||
330 | wr(name, len); | ||
331 | column = column + len; | ||
332 | #ifdef BB_FEATURE_LS_FILETYPES | ||
333 | if (append) | ||
334 | wr(&append, 1), column++; | ||
335 | #endif | ||
336 | } | 289 | } |
290 | return(dnp); | ||
337 | } | 291 | } |
338 | 292 | ||
293 | /*----------------------------------------------------------------------*/ | ||
339 | #ifdef BB_FEATURE_LS_SORTFILES | 294 | #ifdef BB_FEATURE_LS_SORTFILES |
340 | void shellsort(struct dnode *dn[], int size) | 295 | int sortcmp(struct dnode *d1, struct dnode *d2) |
341 | { | 296 | { |
342 | struct dnode *temp; | 297 | int cmp, dif; |
343 | int gap, i, j; | 298 | |
344 | 299 | cmp= 0; | |
345 | /* shell short the array */ | 300 | if (sort_opts == SORT_SIZE) { |
346 | for (gap= size/2; gap>0; gap /=2) { | 301 | dif= (int)(d1->dstat.st_size - d2->dstat.st_size); |
347 | for (i=gap; i<size; i++) { | 302 | } else if (sort_opts == SORT_ATIME) { |
348 | for (j= i-gap; j>=0; j-=gap) { | 303 | dif= (int)(d1->dstat.st_atime - d2->dstat.st_atime); |
349 | if (strcmp(dn[j]->name, dn[j+gap]->name) <= 0) | 304 | } else if (sort_opts == SORT_CTIME) { |
350 | break; | 305 | dif= (int)(d1->dstat.st_ctime - d2->dstat.st_ctime); |
351 | temp= dn[j]; | 306 | } else if (sort_opts == SORT_MTIME) { |
352 | dn[j]= dn[j+gap]; | 307 | dif= (int)(d1->dstat.st_mtime - d2->dstat.st_mtime); |
353 | dn[j+gap]= temp; | 308 | } else if (sort_opts == SORT_DIR) { |
354 | } | 309 | dif= S_ISDIR(d1->dstat.st_mode) - S_ISDIR(d2->dstat.st_mode); |
355 | } | 310 | /* } else if (sort_opts == SORT_VERSION) { */ |
356 | } | 311 | /* } else if (sort_opts == SORT_EXT) { */ |
312 | } else { /* assume SORT_NAME */ | ||
313 | dif= 0; | ||
314 | } | ||
315 | |||
316 | if (dif > 0) cmp= -1; | ||
317 | if (dif < 0) cmp= 1; | ||
318 | if (dif == 0) { | ||
319 | /* sort by name- may be a tie_breaker for time or size cmp */ | ||
320 | dif= strcmp(d1->name, d2->name); | ||
321 | if (dif > 0) cmp= 1; | ||
322 | if (dif < 0) cmp= -1; | ||
323 | } | ||
324 | |||
325 | if (sort_order == SORT_REVERSE) { | ||
326 | cmp= -1 * cmp; | ||
327 | } | ||
328 | return(cmp); | ||
357 | } | 329 | } |
358 | 330 | ||
359 | void showdnodes(struct dnode *dn[], int nfiles) | 331 | /*----------------------------------------------------------------------*/ |
332 | void shellsort(struct dnode **dn, int size) | ||
360 | { | 333 | { |
361 | int nf, nc; | 334 | struct dnode *temp; |
362 | int ncols, fpc, i; | 335 | int gap, i, j; |
363 | 336 | ||
364 | ncols= (int)(terminal_width / (column_width + COLUMN_GAP)); | 337 | /* shell short the array */ |
365 | /* files per column. The +1 means the last col is shorter than others */ | 338 | if(dn==NULL || size < 2) return; |
366 | fpc= (nfiles / ncols) + 1; | 339 | |
367 | for (nf=0; nf<fpc; nf++) { | 340 | for (gap= size/2; gap>0; gap /=2) { |
368 | for (nc=0; nc<ncols; nc++) { | 341 | for (i=gap; i<size; i++) { |
369 | /* reach into the array based on the column and row */ | 342 | for (j= i-gap; j>=0; j-=gap) { |
370 | i= (nc * fpc) + nf; | 343 | if (sortcmp(dn[j], dn[j+gap]) <= 0) |
371 | if (i >= nfiles) { | 344 | break; |
372 | newline(); | 345 | /* they are out of order, swap them */ |
373 | } else { | 346 | temp= dn[j]; |
374 | list_single(dn[i]->name, &dn[i]->dstat, dn[i]->fullname); | 347 | dn[j]= dn[j+gap]; |
348 | dn[j+gap]= temp; | ||
375 | } | 349 | } |
376 | } | 350 | } |
377 | } | 351 | } |
378 | } | 352 | } |
379 | #endif | 353 | #endif |
380 | 354 | ||
381 | /** | 355 | /*----------------------------------------------------------------------*/ |
382 | ** | 356 | void showfiles(struct dnode **dn, int nfiles) |
383 | ** List the given file or directory, expanding a directory | ||
384 | ** to show its contents if required | ||
385 | ** | ||
386 | **/ | ||
387 | |||
388 | static int list_item(const char *name) | ||
389 | { | 357 | { |
390 | struct stat info; | 358 | int i, ncols, nrows, row, nc; |
391 | DIR *dir; | 359 | #ifdef BB_FEATURE_AUTOWIDTH |
392 | struct dirent *entry; | 360 | int len; |
393 | char fullname[BUFSIZ + 1], *fnend; | ||
394 | #ifdef BB_FEATURE_LS_SORTFILES | ||
395 | int ni=0, nfiles=0; | ||
396 | struct dnode **dnp; | ||
397 | dnode_t *cur; | ||
398 | #endif | 361 | #endif |
399 | 362 | ||
400 | if (lstat(name, &info)) | 363 | if(dn==NULL || nfiles < 1) return; |
401 | goto listerr; | ||
402 | 364 | ||
403 | if (!S_ISDIR(info.st_mode) || (opts & DIR_NOLIST)) { | ||
404 | #ifdef BB_FEATURE_AUTOWIDTH | 365 | #ifdef BB_FEATURE_AUTOWIDTH |
405 | column_width = toplevel_column_width; | 366 | /* find the longest file name- use that as the column width */ |
367 | column_width= 0; | ||
368 | for (i=0; i<nfiles; i++) { | ||
369 | len= strlen(dn[i]->name) + | ||
370 | ((list_fmt & LIST_INO) ? 8 : 0) + | ||
371 | ((list_fmt & LIST_BLOCKS) ? 5 : 0) | ||
372 | ; | ||
373 | if (column_width < len) column_width= len; | ||
374 | } | ||
406 | #endif | 375 | #endif |
407 | list_single(name, &info, name); | 376 | ncols= (int)(terminal_width / (column_width + COLUMN_GAP)); |
408 | return 0; | 377 | switch (style_fmt) { |
378 | case STYLE_LONG: /* one record per line, extended info */ | ||
379 | case STYLE_SINGLE: /* one record per line */ | ||
380 | ncols= 1; | ||
381 | break; | ||
409 | } | 382 | } |
410 | 383 | ||
411 | /* Otherwise, it's a directory we want to list the contents of */ | 384 | nrows= nfiles / ncols; |
385 | if ((nrows * ncols) < nfiles) nrows++; /* round up fractionals */ | ||
412 | 386 | ||
413 | if (opts & DISP_DIRNAME) { /* identify the directory */ | 387 | if (nrows > nfiles) nrows= nfiles; |
414 | if (column) | 388 | for (row=0; row<nrows; row++) { |
415 | wr("\n\n", 2), column = 0; | 389 | for (nc=0; nc<ncols; nc++) { |
416 | wr(name, strlen(name)); | 390 | /* reach into the array based on the column and row */ |
417 | wr(":\n", 2); | 391 | i= (nc * nrows) + row; /* assume display by column */ |
392 | if (disp_opts & DISP_ROWS) | ||
393 | i= (row * ncols) + nc; /* display across row */ | ||
394 | if (i < nfiles) { | ||
395 | nexttabstop(); | ||
396 | list_single(dn[i]); | ||
397 | } | ||
398 | } | ||
399 | newline(); | ||
418 | } | 400 | } |
401 | } | ||
419 | 402 | ||
420 | dir = opendir(name); | 403 | /*----------------------------------------------------------------------*/ |
421 | if (!dir) | 404 | void showdirs(struct dnode **dn, int ndirs) |
422 | goto listerr; | 405 | { |
423 | #ifdef BB_FEATURE_AUTOWIDTH | 406 | int i, nfiles; |
424 | column_width = 0; | 407 | struct dnode **subdnp; |
425 | while ((entry = readdir(dir)) != NULL) { | ||
426 | short w = strlen(entry->d_name); | ||
427 | #ifdef BB_FEATURE_LS_SORTFILES | 408 | #ifdef BB_FEATURE_LS_SORTFILES |
428 | const char *en = entry->d_name; | 409 | int dndirs; |
429 | 410 | struct dnode **dnd; | |
430 | if (en[0] == '.') { | ||
431 | if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ | ||
432 | if (!(opts & DISP_DOT)) | ||
433 | continue; | ||
434 | } else if (!(opts & DISP_HIDDEN)) | ||
435 | continue; | ||
436 | } | ||
437 | nfiles++; /* count how many files there will be */ | ||
438 | #endif | 411 | #endif |
439 | 412 | ||
440 | if (column_width < w) | 413 | if (dn==NULL || ndirs < 1) return; |
441 | column_width = w; | 414 | |
442 | } | 415 | for (i=0; i<ndirs; i++) { |
443 | #ifdef HAS_REWINDDIR | 416 | if (disp_opts & (DISP_DIRNAME | DISP_RECURSIVE)) { |
444 | rewinddir(dir); | 417 | fprintf(stdout, "\n%s:\n", dn[i]->fullname); |
445 | #else | 418 | } |
446 | closedir(dir); | 419 | subdnp= list_dir(dn[i]->fullname); |
447 | dir = opendir(name); | 420 | nfiles= countfiles(subdnp); |
448 | if (!dir) | 421 | if (nfiles > 0) { |
449 | goto listerr; | 422 | /* list all files at this level */ |
450 | #endif | ||
451 | #endif | ||
452 | #ifdef BB_FEATURE_LS_SORTFILES | 423 | #ifdef BB_FEATURE_LS_SORTFILES |
453 | /* now that we know how many files there are | 424 | shellsort(subdnp, nfiles); |
454 | * allocate memory for an array to hold dnode pointers | 425 | #endif |
455 | */ | 426 | showfiles(subdnp, nfiles); |
456 | dnp= (struct dnode **)calloc((size_t)nfiles, (size_t)(sizeof(struct dnode *))); | 427 | #ifdef BB_FEATURE_LS_RECURSIVE |
428 | if (disp_opts & DISP_RECURSIVE) { | ||
429 | /* recursive- list the sub-dirs */ | ||
430 | dnd= splitdnarray(subdnp, nfiles, SPLIT_DIR); | ||
431 | dndirs= countdirs(subdnp, nfiles); | ||
432 | if (dndirs > 0) { | ||
433 | shellsort(dnd, dndirs); | ||
434 | showdirs(dnd, dndirs); | ||
435 | free(dnd); /* free the array of dnode pointers to the dirs */ | ||
436 | } | ||
437 | } | ||
438 | dfree(subdnp); /* free the dnodes and the fullname mem */ | ||
457 | #endif | 439 | #endif |
440 | } | ||
441 | } | ||
442 | } | ||
458 | 443 | ||
459 | /* List the contents */ | 444 | /*----------------------------------------------------------------------*/ |
445 | struct dnode **list_dir(char *path) | ||
446 | { | ||
447 | struct dnode *dn, *cur, **dnp; | ||
448 | struct dirent *entry; | ||
449 | DIR *dir; | ||
450 | char *fnend, fullname[BUFSIZ+1] ; | ||
451 | int i, nfiles; | ||
460 | 452 | ||
461 | strcpy(fullname, name); /* *** ignore '.' by itself */ | 453 | if (path==NULL) return(NULL); |
454 | strcpy(fullname, path); | ||
462 | fnend = fullname + strlen(fullname); | 455 | fnend = fullname + strlen(fullname); |
463 | if (fnend[-1] != '/') | 456 | if (fnend[-1] != '/') { |
464 | *fnend++ = '/'; | 457 | strcat(fullname, "/"); |
458 | fnend++; | ||
459 | } | ||
465 | 460 | ||
461 | dn= NULL; | ||
462 | nfiles= 0; | ||
463 | dir = opendir(fullname); | ||
464 | if (dir == NULL) { | ||
465 | errorMsg("%s: %s\n", fullname, strerror(errno)); | ||
466 | return(NULL); /* could not open the dir */ | ||
467 | } | ||
466 | while ((entry = readdir(dir)) != NULL) { | 468 | while ((entry = readdir(dir)) != NULL) { |
467 | const char *en = entry->d_name; | 469 | /* are we going to list the file- it may be . or .. or a hidden file */ |
468 | |||
469 | if (en[0] == '.') { | ||
470 | if (!en[1] || (en[1] == '.' && !en[2])) { /* . or .. */ | ||
471 | if (!(opts & DISP_DOT)) | ||
472 | continue; | ||
473 | } else if (!(opts & DISP_HIDDEN)) | ||
474 | continue; | ||
475 | } | ||
476 | /* FIXME: avoid stat if not required */ | ||
477 | strcpy(fnend, entry->d_name); | 470 | strcpy(fnend, entry->d_name); |
478 | #ifdef BB_FEATURE_LS_SORTFILES | 471 | if ((strcmp(fnend, ".")==0) && !(disp_opts & DISP_DOT)) continue; |
479 | /* allocate memory for a node and memory for the file name */ | 472 | if ((strcmp(fnend, "..")==0) && !(disp_opts & DISP_DOT)) continue; |
480 | cur= (struct dnode *)malloc(sizeof(struct dnode)); | 473 | if ((fnend[0] == '.') && !(disp_opts & DISP_HIDDEN)) continue; |
481 | cur->fullname= strcpy((char *)malloc(strlen(fullname)+1), fullname); | 474 | cur= (struct dnode *)xmalloc(sizeof(struct dnode)); |
475 | cur->fullname= xstrdup(fullname); | ||
482 | cur->name= cur->fullname + (int)(fnend - fullname) ; | 476 | cur->name= cur->fullname + (int)(fnend - fullname) ; |
483 | lstat(fullname, &cur->dstat); /* get file stat info into node */ | 477 | if (lstat(fullname, &cur->dstat)) { /* get file stat info into node */ |
484 | dnp[ni++]= cur; /* save pointer to node in array */ | 478 | errorMsg("%s: %s\n", fullname, strerror(errno)); |
485 | #else | 479 | free(cur->fullname); |
486 | if (lstat(fullname, &info)) { | 480 | free(cur); |
487 | closedir(dir); | 481 | continue; |
488 | goto listerr; /* (shouldn't fail) */ | ||
489 | } | 482 | } |
490 | list_single(entry->d_name, &info, fullname); | 483 | cur->next= dn; |
491 | #endif | 484 | dn= cur; |
485 | nfiles++; | ||
492 | } | 486 | } |
493 | closedir(dir); | 487 | closedir(dir); |
494 | #ifdef BB_FEATURE_LS_SORTFILES | ||
495 | shellsort(dnp, nfiles); | ||
496 | showdnodes(dnp, nfiles); | ||
497 | #endif | ||
498 | 488 | ||
499 | if (opts & DISP_DIRNAME) { /* separate the directory */ | 489 | /* now that we know how many files there are |
500 | if (column) { | 490 | ** allocate memory for an array to hold dnode pointers |
501 | wr("\n", 1); | 491 | */ |
502 | } | 492 | if (nfiles < 1) return(NULL); |
503 | wr("\n", 1); | 493 | dnp= dnalloc(nfiles); |
504 | column = 0; | 494 | for (i=0, cur=dn; i<nfiles; i++) { |
495 | dnp[i]= cur; /* save pointer to node in array */ | ||
496 | cur= cur->next; | ||
505 | } | 497 | } |
506 | 498 | ||
507 | return 0; | 499 | return(dnp); |
508 | |||
509 | listerr: | ||
510 | newline(); | ||
511 | perror(name); | ||
512 | return 1; | ||
513 | } | 500 | } |
514 | 501 | ||
515 | #ifdef BB_FEATURE_LS_RECURSIVE | 502 | /*----------------------------------------------------------------------*/ |
516 | static int dirAction(const char *fileName, struct stat *statbuf, void* junk) | 503 | int list_single(struct dnode *dn) |
517 | { | 504 | { |
518 | int i; | 505 | int i, len; |
519 | fprintf(stdout, "\n%s:\n", fileName); | 506 | char scratch[BUFSIZ + 1]; |
520 | i = list_item(fileName); | 507 | #ifdef BB_FEATURE_LS_TIMESTAMPS |
521 | newline(); | 508 | char *filetime; |
522 | return (i); | 509 | time_t ttime, age; |
523 | } | 510 | #endif |
511 | #ifdef BB_FEATURE_LS_FILETYPES | ||
512 | struct stat info; | ||
513 | char append; | ||
524 | #endif | 514 | #endif |
525 | 515 | ||
526 | extern int ls_main(int argc, char **argv) | 516 | if (dn==NULL || dn->fullname==NULL) return(0); |
527 | { | ||
528 | int argi = 1, i; | ||
529 | 517 | ||
530 | /* process options */ | 518 | #ifdef BB_FEATURE_LS_TIMESTAMPS |
531 | while (argi < argc && argv[argi][0] == '-') { | 519 | ttime= dn->dstat.st_mtime; /* the default time */ |
532 | const char *p = &argv[argi][1]; | 520 | if (time_fmt & TIME_ACCESS) ttime= dn->dstat.st_atime; |
533 | 521 | if (time_fmt & TIME_CHANGE) ttime= dn->dstat.st_ctime; | |
534 | if (!*p) | 522 | filetime= ctime(&ttime); |
535 | goto print_usage_message; /* "-" by itself not allowed */ | 523 | #endif |
536 | if (*p == '-') { | 524 | #ifdef BB_FEATURE_LS_FILETYPES |
537 | if (!p[1]) { /* "--" forces end of options */ | 525 | append = append_char(dn->dstat.st_mode); |
538 | argi++; | 526 | #endif |
539 | break; | ||
540 | } | ||
541 | /* it's a long option name - we don't support them */ | ||
542 | goto print_usage_message; | ||
543 | } | ||
544 | 527 | ||
545 | while (*p) | 528 | for (i=0; i<=31; i++) { |
546 | switch (*p++) { | 529 | switch (list_fmt & (1<<i)) { |
547 | case 'l': | 530 | case LIST_INO: |
548 | display_fmt = FMT_LONG; | 531 | fprintf(stdout, "%7ld ", dn->dstat.st_ino); |
549 | break; | 532 | column += 8; |
550 | case '1': | ||
551 | display_fmt = FMT_SINGLE; | ||
552 | break; | 533 | break; |
553 | case 'x': | 534 | case LIST_BLOCKS: |
554 | display_fmt = FMT_ROWS; | 535 | fprintf(stdout, "%4ld ", dn->dstat.st_blocks>>1); |
536 | column += 5; | ||
555 | break; | 537 | break; |
556 | case 'C': | 538 | case LIST_MODEBITS: |
557 | display_fmt = FMT_COLUMNS; | 539 | fprintf(stdout, "%10s", (char *)modeString(dn->dstat.st_mode)); |
540 | column += 10; | ||
558 | break; | 541 | break; |
559 | #ifdef BB_FEATURE_LS_FILETYPES | 542 | case LIST_NLINKS: |
560 | case 'p': | 543 | fprintf(stdout, "%4d ", dn->dstat.st_nlink); |
561 | opts |= DISP_FTYPE; | 544 | column += 10; |
562 | break; | 545 | break; |
563 | case 'F': | 546 | case LIST_ID_NAME: |
564 | opts |= DISP_FTYPE | DISP_EXEC; | 547 | #ifdef BB_FEATURE_LS_USERNAME |
548 | { | ||
549 | memset(&info, 0, sizeof(struct stat)); | ||
550 | memset(scratch, 0, sizeof(scratch)); | ||
551 | if (!stat(dn->fullname, &info)) { | ||
552 | my_getpwuid(scratch, info.st_uid); | ||
553 | } | ||
554 | if (*scratch) { | ||
555 | fprintf(stdout, "%-8.8s ", scratch); | ||
556 | } else { | ||
557 | fprintf(stdout, "%-8d ", dn->dstat.st_uid); | ||
558 | } | ||
559 | memset(scratch, 0, sizeof(scratch)); | ||
560 | if (info.st_ctime != 0) { | ||
561 | my_getgrgid(scratch, info.st_gid); | ||
562 | } | ||
563 | if (*scratch) { | ||
564 | fprintf(stdout, "%-8.8s", scratch); | ||
565 | } else { | ||
566 | fprintf(stdout, "%-8d", dn->dstat.st_gid); | ||
567 | } | ||
568 | column += 17; | ||
569 | } | ||
565 | break; | 570 | break; |
566 | #endif | 571 | #endif |
567 | case 'A': | 572 | case LIST_ID_NUMERIC: |
568 | opts |= DISP_HIDDEN; | 573 | fprintf(stdout, "%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); |
569 | break; | 574 | column += 17; |
570 | case 'a': | ||
571 | opts |= DISP_HIDDEN | DISP_DOT; | ||
572 | break; | ||
573 | case 'n': | ||
574 | opts |= DISP_NUMERIC; | ||
575 | break; | 575 | break; |
576 | case 'd': | 576 | case LIST_SIZE: |
577 | opts |= DIR_NOLIST; | 577 | case LIST_DEV: |
578 | if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { | ||
579 | fprintf(stdout, "%4d, %3d ", (int)MAJOR(dn->dstat.st_rdev), (int)MINOR(dn->dstat.st_rdev)); | ||
580 | } else { | ||
581 | fprintf(stdout, "%9ld ", dn->dstat.st_size); | ||
582 | } | ||
583 | column += 10; | ||
578 | break; | 584 | break; |
579 | #ifdef BB_FEATURE_LS_TIMESTAMPS | 585 | #ifdef BB_FEATURE_LS_TIMESTAMPS |
580 | case 'u': | 586 | case LIST_FULLTIME: |
581 | time_fmt = TIME_ACCESS; | 587 | case LIST_DATE_TIME: |
588 | if (list_fmt & LIST_FULLTIME) { | ||
589 | fprintf(stdout, "%24.24s ", filetime); | ||
590 | column += 25; | ||
591 | break; | ||
592 | } | ||
593 | age = time(NULL) - ttime; | ||
594 | fprintf(stdout, "%6.6s ", filetime+4); | ||
595 | if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { | ||
596 | /* hh:mm if less than 6 months old */ | ||
597 | fprintf(stdout, "%5.5s ", filetime+11); | ||
598 | } else { | ||
599 | fprintf(stdout, " %4.4s ", filetime+20); | ||
600 | } | ||
601 | column += 13; | ||
582 | break; | 602 | break; |
583 | case 'c': | 603 | #endif |
584 | time_fmt = TIME_CHANGE; | 604 | case LIST_FILENAME: |
605 | fprintf(stdout, "%s", dn->name); | ||
606 | column += strlen(dn->name); | ||
585 | break; | 607 | break; |
586 | case 'e': | 608 | case LIST_SYMLINK: |
587 | opts |= DISP_FULLTIME; | 609 | if (S_ISLNK(dn->dstat.st_mode)) { |
610 | len= readlink(dn->fullname, scratch, (sizeof scratch)-1); | ||
611 | if (len > 0) { | ||
612 | scratch[len]= '\0'; | ||
613 | fprintf(stdout, " -> %s", scratch); | ||
614 | #ifdef BB_FEATURE_LS_FILETYPES | ||
615 | if (!stat(dn->fullname, &info)) { | ||
616 | append = append_char(info.st_mode); | ||
617 | } | ||
618 | #endif | ||
619 | column += len+4; | ||
620 | } | ||
621 | } | ||
622 | break; | ||
623 | #ifdef BB_FEATURE_LS_FILETYPES | ||
624 | case LIST_FILETYPE: | ||
625 | if (append != '\0') { | ||
626 | fprintf(stdout, "%1c", append); | ||
627 | column++; | ||
628 | } | ||
588 | break; | 629 | break; |
589 | #endif | 630 | #endif |
631 | } | ||
632 | } | ||
633 | |||
634 | return(0); | ||
635 | } | ||
636 | |||
637 | /*----------------------------------------------------------------------*/ | ||
638 | extern int ls_main(int argc, char **argv) | ||
639 | { | ||
640 | struct dnode **dnf, **dnd; | ||
641 | int dnfiles, dndirs; | ||
642 | struct dnode *dn, *cur, **dnp; | ||
643 | int i, nfiles; | ||
644 | int opt; | ||
645 | int oi, ac; | ||
646 | char **av; | ||
647 | |||
648 | disp_opts= DISP_NORMAL; | ||
649 | style_fmt= STYLE_AUTO; | ||
650 | list_fmt= LIST_SHORT; | ||
651 | #ifdef BB_FEATURE_LS_SORTFILES | ||
652 | sort_opts= SORT_NAME; | ||
653 | #endif | ||
654 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
655 | time_fmt= TIME_MOD; | ||
656 | #endif | ||
657 | nfiles=0; | ||
658 | |||
659 | applet_name= argv[0]; | ||
660 | /* process options */ | ||
661 | while ((opt = getopt(argc, argv, "1AaCdgilnsx" | ||
662 | #ifdef BB_FEATURE_AUTOWIDTH | ||
663 | "T:w:" | ||
664 | #endif | ||
665 | #ifdef BB_FEATURE_LS_FILETYPES | ||
666 | "Fp" | ||
667 | #endif | ||
590 | #ifdef BB_FEATURE_LS_RECURSIVE | 668 | #ifdef BB_FEATURE_LS_RECURSIVE |
591 | case 'R': | 669 | "R" |
592 | opts |= DISP_RECURSIVE; | 670 | #endif |
593 | break; | 671 | #ifdef BB_FEATURE_LS_SORTFILES |
672 | "rSvX" | ||
673 | #endif | ||
674 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
675 | "cetu" | ||
676 | #endif | ||
677 | )) > 0) { | ||
678 | switch (opt) { | ||
679 | case '1': style_fmt = STYLE_SINGLE; break; | ||
680 | case 'A': disp_opts |= DISP_HIDDEN; break; | ||
681 | case 'a': disp_opts |= DISP_HIDDEN | DISP_DOT; break; | ||
682 | case 'C': style_fmt = STYLE_COLUMNS; break; | ||
683 | case 'd': disp_opts |= DISP_NOLIST; break; | ||
684 | case 'e': list_fmt |= LIST_FULLTIME; break; | ||
685 | case 'g': /* ignore -- for ftp servers */ break; | ||
686 | case 'i': list_fmt |= LIST_INO; break; | ||
687 | case 'l': style_fmt = STYLE_LONG; list_fmt |= LIST_LONG; break; | ||
688 | case 'n': list_fmt |= LIST_ID_NUMERIC; break; | ||
689 | case 's': list_fmt |= LIST_BLOCKS; break; | ||
690 | case 'x': disp_opts = DISP_ROWS; break; | ||
691 | #ifdef BB_FEATURE_LS_FILETYPES | ||
692 | case 'F': list_fmt |= LIST_FILETYPE | LIST_EXEC; break; | ||
693 | case 'p': list_fmt |= LIST_FILETYPE; break; | ||
694 | #endif | ||
695 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
696 | case 'R': disp_opts |= DISP_RECURSIVE; break; | ||
697 | #endif | ||
698 | #ifdef BB_FEATURE_LS_SORTFILES | ||
699 | case 'r': sort_order |= SORT_REVERSE; break; | ||
700 | case 'S': sort_opts= SORT_SIZE; break; | ||
701 | case 'v': sort_opts= SORT_VERSION; break; | ||
702 | case 'X': sort_opts= SORT_EXT; break; | ||
703 | #endif | ||
704 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
705 | case 'c': time_fmt = TIME_CHANGE; sort_opts= SORT_CTIME; break; | ||
706 | case 't': sort_opts= SORT_MTIME; break; | ||
707 | case 'u': time_fmt = TIME_ACCESS; sort_opts= SORT_ATIME; break; | ||
708 | #endif | ||
709 | #ifdef BB_FEATURE_AUTOWIDTH | ||
710 | case 'T': tabstops= atoi(optarg); break; | ||
711 | case 'w': terminal_width= atoi(optarg); break; | ||
594 | #endif | 712 | #endif |
595 | case 'g': /* ignore -- for ftp servers */ | ||
596 | break; | ||
597 | default: | 713 | default: |
598 | goto print_usage_message; | 714 | goto print_usage_message; |
599 | } | 715 | } |
600 | |||
601 | argi++; | ||
602 | } | 716 | } |
603 | 717 | ||
718 | /* sort out which command line options take precedence */ | ||
719 | #ifdef BB_FEATURE_LS_RECURSIVE | ||
720 | if (disp_opts & DISP_NOLIST) | ||
721 | disp_opts &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ | ||
722 | #endif | ||
723 | #ifdef BB_FEATURE_LS_TIMESTAMPS | ||
724 | if (time_fmt & TIME_CHANGE) sort_opts= SORT_CTIME; | ||
725 | if (time_fmt & TIME_ACCESS) sort_opts= SORT_ATIME; | ||
726 | #endif | ||
727 | if (style_fmt != STYLE_LONG) | ||
728 | list_fmt &= ~LIST_ID_NUMERIC; /* numeric uid only for long list */ | ||
729 | #ifdef BB_FEATURE_LS_USERNAME | ||
730 | if (style_fmt == STYLE_LONG && (list_fmt & LIST_ID_NUMERIC)) | ||
731 | list_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ | ||
732 | #endif | ||
733 | |||
604 | /* choose a display format */ | 734 | /* choose a display format */ |
605 | if (display_fmt == FMT_AUTO) | 735 | if (style_fmt == STYLE_AUTO) |
606 | display_fmt = isatty(fileno(stdout)) ? FMT_COLUMNS : FMT_SINGLE; | 736 | style_fmt = isatty(fileno(stdout)) ? STYLE_COLUMNS : STYLE_SINGLE; |
607 | if (argi < argc - 1) | 737 | |
608 | opts |= DISP_DIRNAME; /* 2 or more items? label directories */ | 738 | /* |
609 | #ifdef BB_FEATURE_AUTOWIDTH | 739 | * when there are no cmd line args we have to supply a default "." arg. |
610 | /* could add a -w option and/or TIOCGWINSZ call */ | 740 | * we will create a second argv array, "av" that will hold either |
611 | if (terminal_width < 1) | 741 | * our created "." arg, or the real cmd line args. The av array |
612 | terminal_width = TERMINAL_WIDTH; | 742 | * just holds the pointers- we don't move the date the pointers |
743 | * point to. | ||
744 | */ | ||
745 | ac= argc - optind; /* how many cmd line args are left */ | ||
746 | if (ac < 1) { | ||
747 | av= (char **)xcalloc((size_t)1, (size_t)(sizeof(char *))); | ||
748 | av[0]= xstrdup("."); | ||
749 | ac=1; | ||
750 | } else { | ||
751 | av= (char **)xcalloc((size_t)ac, (size_t)(sizeof(char *))); | ||
752 | for (oi=0 ; oi < ac; oi++) { | ||
753 | av[oi]= argv[optind++]; /* copy pointer to real cmd line arg */ | ||
754 | } | ||
755 | } | ||
613 | 756 | ||
614 | for (i = argi; i < argc; i++) { | 757 | /* now, everything is in the av array */ |
615 | int len = strlen(argv[i]); | 758 | if (ac > 1) |
759 | disp_opts |= DISP_DIRNAME; /* 2 or more items? label directories */ | ||
760 | |||
761 | /* stuff the command line file names into an dnode array */ | ||
762 | dn=NULL; | ||
763 | for (oi=0 ; oi < ac; oi++) { | ||
764 | cur= (struct dnode *)xmalloc(sizeof(struct dnode)); | ||
765 | cur->fullname= xstrdup(av[oi]); | ||
766 | cur->name= cur->fullname ; | ||
767 | if (lstat(av[oi], &cur->dstat)) { /* get file info into node */ | ||
768 | errorMsg("%s: %s\n", av[oi], strerror(errno)); | ||
769 | free(cur->fullname); | ||
770 | free(cur); | ||
771 | continue; | ||
772 | } | ||
773 | cur->next= dn; | ||
774 | dn= cur; | ||
775 | nfiles++; | ||
776 | } | ||
616 | 777 | ||
617 | if (toplevel_column_width < len) | 778 | /* now that we know how many files there are |
618 | toplevel_column_width = len; | 779 | ** allocate memory for an array to hold dnode pointers |
780 | */ | ||
781 | dnp= dnalloc(nfiles); | ||
782 | for (i=0, cur=dn; i<nfiles; i++) { | ||
783 | dnp[i]= cur; /* save pointer to node in array */ | ||
784 | cur= cur->next; | ||
619 | } | 785 | } |
620 | #endif | ||
621 | 786 | ||
622 | /* process files specified, or current directory if none */ | 787 | |
623 | #ifdef BB_FEATURE_LS_RECURSIVE | 788 | if (disp_opts & DISP_NOLIST) { |
624 | if (opts & DISP_RECURSIVE) { | 789 | #ifdef BB_FEATURE_LS_SORTFILES |
625 | i = 0; | 790 | shellsort(dnp, nfiles); |
626 | if (argi == argc) { | 791 | #endif |
627 | i = recursiveAction(".", TRUE, FALSE, FALSE, NULL, dirAction, NULL); | 792 | if (nfiles > 0) showfiles(dnp, nfiles); |
793 | } else { | ||
794 | dnd= splitdnarray(dnp, nfiles, SPLIT_DIR); | ||
795 | dnf= splitdnarray(dnp, nfiles, SPLIT_FILE); | ||
796 | dndirs= countdirs(dnp, nfiles); | ||
797 | dnfiles= nfiles - dndirs; | ||
798 | if (dnfiles > 0) { | ||
799 | #ifdef BB_FEATURE_LS_SORTFILES | ||
800 | shellsort(dnf, dnfiles); | ||
801 | #endif | ||
802 | showfiles(dnf, dnfiles); | ||
628 | } | 803 | } |
629 | while (argi < argc) { | 804 | if (dndirs > 0) { |
630 | i |= recursiveAction(argv[argi++], TRUE, FALSE, FALSE, NULL, dirAction, NULL); | 805 | #ifdef BB_FEATURE_LS_SORTFILES |
806 | shellsort(dnd, dndirs); | ||
807 | #endif | ||
808 | showdirs(dnd, dndirs); | ||
631 | } | 809 | } |
632 | } else | ||
633 | #endif | ||
634 | { | ||
635 | i = 0; | ||
636 | if (argi == argc) | ||
637 | i = list_item("."); | ||
638 | while (argi < argc) | ||
639 | i |= list_item(argv[argi++]); | ||
640 | newline(); | ||
641 | } | 810 | } |
642 | exit(i); | 811 | |
812 | return(0); | ||
643 | 813 | ||
644 | print_usage_message: | 814 | print_usage_message: |
645 | usage(ls_usage); | 815 | usage(ls_usage); |
646 | exit(FALSE); | 816 | return(FALSE); |
647 | } | 817 | } |