breakup into multiple files
[unix-history] / usr / src / bin / ls / ls.c
CommitLineData
bcf1365c 1/*
9ffe97fe
KB
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.
bcf1365c 19 */
9ffe97fe
KB
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
b3f77fd1 28static char sccsid[] = "@(#)ls.c 5.14 (Berkeley) %G%";
9ffe97fe
KB
29#endif /* not lint */
30
2fd18a0a 31#include <sys/param.h>
9ffe97fe 32#include <sys/stat.h>
2fd18a0a 33#include <dirent.h>
9ffe97fe 34#include <strings.h>
9ffe97fe
KB
35#include <errno.h>
36#include <stdio.h>
b3f77fd1 37#include "ls.h"
2fd18a0a 38
b3f77fd1
KB
39int lstat(), strlen(), prablelen();
40char *emalloc();
bcf1365c 41
2fd18a0a 42int qflg, Aflg, Cflg, Fflg, Lflg, Rflg, Sflg;
547b0031 43
9ffe97fe 44/* flags */
b3f77fd1
KB
45int f_listdot; /* list files beginning with . */
46int f_listalldot; /* list . and .. as well */
9ffe97fe
KB
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 */
b3f77fd1 53int f_type; /* add type character for non-regular files */
9ffe97fe
KB
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 */
9ffe97fe 62int f_firsttime = 1; /* to control recursion */
2fd18a0a 63
9ffe97fe
KB
64main(argc, argv)
65 int argc;
66 char **argv;
547b0031 67{
9ffe97fe
KB
68 extern int optind, stat();
69 int ch;
70 int namecmp(), revnamecmp(), acccmp(), revacccmp();
71 int modcmp(), revmodcmp(), statcmp(), revstatcmp();
2fd18a0a 72
9ffe97fe 73 /* set up defaults for terminal/nonterminal stdout */
547b0031 74 if (isatty(1)) {
9ffe97fe 75 f_nonprint = 1;
2fd18a0a 76 lengthfcn = prablelen;
9ffe97fe
KB
77 } else
78 f_singlecol = 1;
2fd18a0a 79
9ffe97fe
KB
80 /* root sees all files automatically */
81 if (!getuid())
b3f77fd1 82 f_listdot = 1;
9ffe97fe
KB
83
84 while ((ch = getopt(argc, argv, "1ACFLRacdfgilqrstu")) != EOF) {
85 switch (ch) {
86 case '1':
87 f_singlecol = 1;
2fd18a0a 88 break;
9ffe97fe
KB
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':
b3f77fd1 103 f_listalldot = 1;
9ffe97fe 104 /* FALLTHROUGH */
2fd18a0a 105 case 'A':
b3f77fd1 106 f_listdot = 1;
2fd18a0a 107 break;
547b0031 108 case 'c':
9ffe97fe
KB
109 f_statustime = 1;
110 f_modtime = f_accesstime = 0;
2fd18a0a 111 break;
1c52c7b1
JB
112 case 'S':
113 Sflg++; /* fall into... */
547b0031 114 case 'd':
9ffe97fe 115 f_listdir = 1;
2fd18a0a 116 break;
ec0c22c1 117 case 'f':
9ffe97fe 118 f_specialdir = 1;
2fd18a0a 119 break;
547b0031 120 case 'g':
9ffe97fe 121 f_group = 1;
2fd18a0a 122 break;
ec0c22c1 123 case 'i':
9ffe97fe 124 f_inode = 1;
2fd18a0a 125 break;
547b0031 126 case 'l':
9ffe97fe 127 f_longform = 1;
b3f77fd1 128 f_singlecol = 0;
2fd18a0a 129 statfcn = lstat;
9ffe97fe
KB
130 if (!f_accesstime && !f_statustime)
131 f_modtime = 1;
2fd18a0a 132 break;
ec0c22c1 133 case 'q':
9ffe97fe 134 f_nonprint = 1;
2fd18a0a
KB
135 lengthfcn = prablelen;
136 break;
547b0031 137 case 'r':
9ffe97fe 138 f_reversesort = 1;
2fd18a0a 139 break;
ec0c22c1 140 case 's':
9ffe97fe 141 f_size = 1;
2fd18a0a
KB
142 statfcn = lstat;
143 break;
547b0031 144 case 't':
9ffe97fe
KB
145 f_timesort = 1;
146 if (!f_accesstime && !f_statustime)
147 f_modtime = 1;
2fd18a0a 148 break;
547b0031 149 case 'u':
9ffe97fe
KB
150 f_modtime = f_statustime = 0;
151 f_accesstime = 1;
2fd18a0a 152 break;
ec0c22c1 153 default:
2fd18a0a 154 case '?':
9ffe97fe 155 usage();
2955376b 156 }
9ffe97fe 157 }
b3f77fd1
KB
158 argc -= optind;
159 argv += optind;
9ffe97fe
KB
160
161 /* -f turns off -l, -t, -s, -r, turns on -a */
162 if (f_specialdir) {
b3f77fd1
KB
163 f_longform = f_timesort = f_size = f_reversesort =
164 f_recursive = f_type = 0;
165 f_listdot = f_listalldot = 1;
9ffe97fe
KB
166 }
167
b3f77fd1
KB
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
9ffe97fe
KB
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;
2fd18a0a 186 } else {
9ffe97fe
KB
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
b3f77fd1
KB
197 if (argc)
198 args(argc, argv);
199 else
200 curdir();
9ffe97fe 201 exit(0);
547b0031
BJ
202}
203
b3f77fd1
KB
204/**
205 * curdir --
206 * no arguments, list the current directory; this is the
207 * most common case, so it must run labooh.
2fd18a0a 208 */
b3f77fd1 209curdir()
547b0031 210{
b3f77fd1
KB
211 LS local, *stats;
212 int num;
213 char *names;
9ffe97fe 214
b3f77fd1
KB
215 if (statfcn(local.name = ".", &local.lstat)) {
216 (void)fprintf(stderr, "ls: .: %s\n", strerror(errno));
217 exit(1);
2fd18a0a 218 }
b3f77fd1
KB
219 if (num = buildstats(&local, &stats, &names))
220 ls(stats, num);
2fd18a0a
KB
221}
222
b3f77fd1
KB
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.
2fd18a0a 228 */
b3f77fd1
KB
229args(argc, argv)
230 int argc;
231 char **argv;
2fd18a0a 232{
b3f77fd1
KB
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);
9ffe97fe 248 }
b3f77fd1
KB
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;
9ffe97fe 264 }
9ffe97fe 265 }
b3f77fd1
KB
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);
9ffe97fe 273 }
b3f77fd1
KB
274 } else
275 ls(rstats, regcnt);
276 if (dircnt)
277 (void)putchar('\n');
547b0031 278 }
b3f77fd1
KB
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);
547b0031 284 }
b3f77fd1
KB
285#ifdef whybother
286 (void)free((char *)rstats);
287 (void)free((char *)dstats);
288#endif
547b0031
BJ
289}
290
b3f77fd1
KB
291ls(stats, num)
292 LS *stats;
293 register int num;
547b0031 294{
b3f77fd1
KB
295 register char *p;
296 LS *lp;
9ffe97fe 297
b3f77fd1
KB
298 if (num > 1 && !f_specialdir)
299 qsort((char *)stats, num, sizeof(LS), sortfcn);
9ffe97fe 300
b3f77fd1 301 printdir(stats, num);
547b0031 302
b3f77fd1
KB
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 }
547b0031
BJ
312}
313
b3f77fd1
KB
314ls_dir(lp, newline, tag)
315 LS *lp;
316 int newline, tag;
2fd18a0a 317{
b3f77fd1
KB
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;
2fd18a0a 340 }
b3f77fd1
KB
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);
2fd18a0a 350 }
547b0031
BJ
351}
352
b3f77fd1
KB
353static
354buildstats(lp, s_stats, s_names)
355 LS *lp, **s_stats;
356 char **s_names;
547b0031 357{
b3f77fd1
KB
358 register int cnt, maxentry;
359 register char *p, *names;
360 struct dirent *entry;
361 LS *stats;
362 DIR *dirp;
547b0031 363
b3f77fd1
KB
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);
9ffe97fe 369
b3f77fd1
KB
370 if (!(dirp = opendir(f_specialdir ? lp->name : "."))) {
371 (void)fprintf(stderr, "ls: %s: %s\n",
372 lp->name, strerror(errno));
373 return(0);
9ffe97fe 374 }
b3f77fd1
KB
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;
2fd18a0a 401 }
b3f77fd1 402 return(cnt);
547b0031 403}