breakup into multiple files
[unix-history] / usr / src / bin / ls / ls.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Michael Fischbein.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the University of California, Berkeley. The name of the
14 * University may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 */
20
21#ifndef lint
22char copyright[] =
23"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
24 All rights reserved.\n";
25#endif /* not lint */
26
27#ifndef lint
28static char sccsid[] = "@(#)ls.c 5.14 (Berkeley) %G%";
29#endif /* not lint */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <dirent.h>
34#include <strings.h>
35#include <errno.h>
36#include <stdio.h>
37#include "ls.h"
38
39int lstat(), strlen(), prablelen();
40char *emalloc();
41
42int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg;
43
44/* flags */
45int f_listdot; /* list files beginning with . */
46int f_listalldot; /* list . and .. as well */
47int f_modtime; /* use time of last change for time */
48int f_accesstime; /* use time of last access */
49int f_statustime; /* use time of last mode change */
50int f_singlecol; /* use single column output */
51int f_listdir; /* list actual directory, not contents */
52int f_specialdir; /* force params to be directories */
53int f_type; /* add type character for non-regular files */
54int f_group; /* show group ownership of a file */
55int f_inode; /* print inode */
56int f_longform; /* long listing format */
57int f_nonprint; /* show unprintables as ? */
58int f_reversesort; /* reverse whatever sort is used */
59int f_recursive; /* ls subdirectories also */
60int f_size; /* list size in short listing */
61int f_timesort; /* sort by time vice name */
62int f_firsttime = 1; /* to control recursion */
63
64main(argc, argv)
65 int argc;
66 char **argv;
67{
68 extern int optind, stat();
69 int ch;
70 int namecmp(), revnamecmp(), acccmp(), revacccmp();
71 int modcmp(), revmodcmp(), statcmp(), revstatcmp();
72
73 /* set up defaults for terminal/nonterminal stdout */
74 if (isatty(1)) {
75 f_nonprint = 1;
76 lengthfcn = prablelen;
77 } else
78 f_singlecol = 1;
79
80 /* root sees all files automatically */
81 if (!getuid())
82 f_listdot = 1;
83
84 while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) {
85 switch (ch) {
86 case '1':
87 f_singlecol = 1;
88 break;
89 case 'C':
90 f_singlecol = 0;
91 break;
92 case 'F':
93 f_type = 1;
94 break;
95 case 'L':
96 statfcn = stat;
97 break;
98 case 'R':
99 f_recursive = 1;
100 statfcn = lstat;
101 break;
102 case 'a':
103 f_listalldot = 1;
104 /* FALLTHROUGH */
105 case 'A':
106 f_listdot = 1;
107 break;
108 case 'c':
109 f_statustime = 1;
110 f_modtime = f_accesstime = 0;
111 break;
112 case 'S':
113 Sflg++; /* fall into... */
114 case 'd':
115 f_listdir = 1;
116 break;
117 case 'f':
118 f_specialdir = 1;
119 break;
120 case 'g':
121 f_group = 1;
122 break;
123 case 'i':
124 f_inode = 1;
125 break;
126 case 'l':
127 f_longform = 1;
128 f_singlecol = 0;
129 statfcn = lstat;
130 if (!f_accesstime && !f_statustime)
131 f_modtime = 1;
132 break;
133 case 'q':
134 f_nonprint = 1;
135 lengthfcn = prablelen;
136 break;
137 case 'r':
138 f_reversesort = 1;
139 break;
140 case 's':
141 f_size = 1;
142 statfcn = lstat;
143 break;
144 case 't':
145 f_timesort = 1;
146 if (!f_accesstime && !f_statustime)
147 f_modtime = 1;
148 break;
149 case 'u':
150 f_modtime = f_statustime = 0;
151 f_accesstime = 1;
152 break;
153 default:
154 case '?':
155 usage();
156 }
157 }
158 argc -= optind;
159 argv += optind;
160
161 /* -f turns off -l, -t, -s, -r, turns on -a */
162 if (f_specialdir) {
163 f_longform = f_timesort = f_size = f_reversesort =
164 f_recursive = f_type = 0;
165 f_listdot = f_listalldot = 1;
166 }
167
168 /* -d turns off -R */
169 if (f_listdir)
170 f_recursive = 0;
171
172 /* if will need to stat files */
173 needstat = f_longform || f_recursive || f_timesort || f_size ||
174 f_inode || f_type;
175
176 /* select a sort function */
177 if (f_reversesort) {
178 if (!f_timesort)
179 sortfcn = revnamecmp;
180 else if (f_accesstime)
181 sortfcn = revacccmp;
182 else if (f_modtime)
183 sortfcn = revmodcmp;
184 else if (f_statustime)
185 sortfcn = revstatcmp;
186 } else {
187 if (!f_timesort)
188 sortfcn = namecmp;
189 else if (f_accesstime)
190 sortfcn = acccmp;
191 else if (f_modtime)
192 sortfcn = modcmp;
193 else if (f_statustime)
194 sortfcn = statcmp;
195 }
196
197 if (argc)
198 args(argc, argv);
199 else
200 curdir();
201 exit(0);
202}
203
204/**
205 * curdir --
206 * no arguments, list the current directory; this is the
207 * most common case, so it must run labooh.
208 */
209curdir()
210{
211 LS local, *stats;
212 int num;
213 char *names;
214
215 if (statfcn(local.name = ".", &local.lstat)) {
216 (void)fprintf(stderr, "ls: .: %s\n", strerror(errno));
217 exit(1);
218 }
219 if (num = buildstats(&local, &stats, &names))
220 ls(stats, num);
221}
222
223/**
224 * args --
225 * handle arguments; this is separated out since we want to do the
226 * files first, then the directories, plus we must return to the
227 * current directory each time.
228 */
229args(argc, argv)
230 int argc;
231 char **argv;
232{
233 register LS *dstats, *rstats;
234 register int cnt, dircnt, regcnt;
235 struct stat sb;
236 LS *stats;
237 int num;
238 char *names;
239
240 dstats = rstats = NULL;
241 for (dircnt = regcnt = 0; *argv; ++argv) {
242 if (statfcn(*argv, &sb)) {
243 (void)fprintf(stderr, "ls: %s: %s\n",
244 *argv, strerror(errno));
245 if (errno == ENOENT)
246 continue;
247 exit(1);
248 }
249 if (!f_specialdir && !f_listdir && S_ISDIR(sb.st_mode)) {
250 if (!dstats)
251 dstats = (LS *)emalloc((u_int)argc *
252 (sizeof(LS)));
253 dstats[dircnt].name = *argv;
254 dstats[dircnt].lstat = sb;
255 ++dircnt;
256 }
257 else {
258 if (!rstats)
259 rstats = (LS *)emalloc((u_int)argc *
260 (sizeof(LS)));
261 rstats[regcnt].name = *argv;
262 rstats[regcnt].lstat = sb;
263 ++regcnt;
264 }
265 }
266 if (regcnt) {
267 if (f_specialdir) {
268 for (cnt = 0; cnt < regcnt; ++cnt) {
269 if (num = buildstats(rstats++, &stats, &names))
270 ls(stats, num);
271 (void)free((char *)stats);
272 (void)free((char *)names);
273 }
274 } else
275 ls(rstats, regcnt);
276 if (dircnt)
277 (void)putchar('\n');
278 }
279 if (dircnt) {
280 if (dircnt > 1)
281 qsort((char *)dstats, dircnt, sizeof(LS), sortfcn);
282 for (cnt = 0; cnt < dircnt; ++cnt)
283 ls_dir(dstats++, cnt, regcnt || dircnt > 1);
284 }
285#ifdef whybother
286 (void)free((char *)rstats);
287 (void)free((char *)dstats);
288#endif
289}
290
291ls(stats, num)
292 LS *stats;
293 register int num;
294{
295 register char *p;
296 LS *lp;
297
298 if (num > 1 && !f_specialdir)
299 qsort((char *)stats, num, sizeof(LS), sortfcn);
300
301 printdir(stats, num);
302
303 if (f_recursive)
304 for (lp = stats; num--; ++lp) {
305 if (!S_ISDIR(lp->lstat.st_mode))
306 continue;
307 p = lp->name;
308 if (p[0] == '.' && (!p[1] || p[1] == '.' && !p[2]))
309 continue;
310 ls_dir(lp, 1, 1);
311 }
312}
313
314ls_dir(lp, newline, tag)
315 LS *lp;
316 int newline, tag;
317{
318 static char path[MAXPATHLEN + 1];
319 register char *p;
320 LS *stats;
321 int num;
322 char *endofname, *names;
323
324 /* add current name to path */
325 p = index(path, '\0');
326 if (p != path && p[-1] != '/')
327 *p++ = '/';
328 (void)strcpy(p, lp->name);
329 endofname = p;
330
331 if (newline)
332 (void)putchar('\n');
333 if (tag)
334 (void)printf("%s:\n", path);
335
336 if (chdir(lp->name)) {
337 (void)fprintf(stderr, "ls: %s: %s\n",
338 lp->name, strerror(errno));
339 return;
340 }
341 if (num = buildstats(lp, &stats, &names))
342 ls(stats, num);
343
344 *endofname = '\0';
345 (void)free((char *)stats);
346 (void)free((char *)names);
347 if (chdir("..")) {
348 (void)fprintf(stderr, "ls: ..: %s\n", strerror(errno));
349 exit(1);
350 }
351}
352
353static
354buildstats(lp, s_stats, s_names)
355 LS *lp, **s_stats;
356 char **s_names;
357{
358 register int cnt, maxentry;
359 register char *p, *names;
360 struct dirent *entry;
361 LS *stats;
362 DIR *dirp;
363
364 /* make this big so we don't realloc often */
365#define DEFNUM 256
366 maxentry = DEFNUM;
367 *s_stats = stats = (LS *)emalloc((u_int)DEFNUM * sizeof(LS));
368 *s_names = names = emalloc((u_int)lp->lstat.st_size);
369
370 if (!(dirp = opendir(f_specialdir ? lp->name : "."))) {
371 (void)fprintf(stderr, "ls: %s: %s\n",
372 lp->name, strerror(errno));
373 return(0);
374 }
375 for (cnt = 0; entry = readdir(dirp);) {
376 p = entry->d_name;
377 if (p[0] == '.') {
378 if (!f_listdot)
379 continue;
380 if (!f_listalldot && (!p[1] || p[1] == '.' && !p[2]))
381 continue;
382 }
383 if (cnt == maxentry) {
384 maxentry += DEFNUM;
385 if (!(stats = (LS *)realloc((char *)stats,
386 (u_int)maxentry * sizeof(LS))))
387 nomem();
388 }
389 if (needstat && statfcn(entry->d_name, &stats[cnt].lstat)) {
390 (void)fprintf(stderr, "ls: %s: %s\n",
391 entry->d_name, strerror(errno));
392 if (errno == ENOENT)
393 continue;
394 exit(1);
395 }
396 stats[cnt].name = names;
397 bcopy(entry->d_name, names, (int)entry->d_namlen);
398 names += entry->d_namlen;
399 *names++ = '\0';
400 ++cnt;
401 }
402 return(cnt);
403}