Commit | Line | Data |
---|---|---|
31cef89c BJ |
1 | #define UCB /* Controls output format for -F */ |
2 | /* #define UCB_PWHASH /* If have hashed password file */ | |
3 | ||
1217e91a BJ |
4 | /* |
5 | * ls - list file or directory | |
6 | * | |
7 | * Modified by Bill Joy UCB May/August 1977 | |
31cef89c BJ |
8 | * Modified by Dave Presotto BTL Feb/80 |
9 | * Modified by Bill Joy and Mark Horton Summer 1980 | |
1217e91a | 10 | * |
31cef89c | 11 | * this version of ls is designed for graphic terminals and to |
1217e91a BJ |
12 | * list directories with lots of files in them compactly. |
13 | * It supports three variants for listings: | |
14 | * | |
15 | * 1) Columnar output. | |
16 | * 2) Stream output. | |
17 | * 3) Old one per line format. | |
18 | * | |
19 | * Columnar output is the default. | |
20 | * If, however, the standard output is not a teletype, the default | |
21 | * is one-per-line. | |
22 | * | |
23 | * With columnar output, the items are sorted down the columns. | |
24 | * We use columns only for a directory we are interpreting. | |
25 | * Thus, in particular, we do not use columns for | |
26 | * | |
27 | * ls /usr/bin/p* | |
28 | * | |
29 | * This version of ls also prints non-printing characters as '?' if | |
30 | * the standard output is a teletype. | |
31 | * | |
32 | * Flags relating to these and other new features are: | |
33 | * | |
34 | * -m force stream output. | |
35 | * | |
36 | * -1 force one entry per line, e.g. to a teletype | |
37 | * | |
38 | * -q force non-printings to be '?'s, e.g. to a file | |
39 | * | |
31cef89c | 40 | * -C force columnar output, e.g. into a file |
1217e91a BJ |
41 | * |
42 | * -n like -l, but user/group id's in decimal rather than | |
43 | * looking in /etc/passwd to save time | |
31cef89c BJ |
44 | * |
45 | * -F turns on the "flagging" of executables and directories | |
46 | * | |
47 | * -R causes ls to recurse through the branches of the subtree | |
48 | * ala find | |
1217e91a BJ |
49 | */ |
50 | ||
51 | #include <sys/param.h> | |
52 | #include <sys/stat.h> | |
53 | #include <sys/dir.h> | |
54 | #include <stdio.h> | |
55 | #include <ctype.h> | |
31cef89c BJ |
56 | #include <pwd.h> |
57 | #include <grp.h> | |
58 | #include <utmp.h> | |
1217e91a | 59 | |
31cef89c BJ |
60 | struct utmp utmp; |
61 | #define NMAX (sizeof utmp.ut_name) | |
1217e91a | 62 | |
31cef89c BJ |
63 | #define MAXFILEWIDTH 14 |
64 | #define NFILES 1024 | |
1217e91a BJ |
65 | FILE *pwdf, *dirf; |
66 | ||
67 | struct lbuf { | |
68 | union { | |
69 | char lname[15]; | |
70 | char *namep; | |
71 | } ln; | |
72 | char ltype; | |
31cef89c | 73 | ino_t lnum; |
1217e91a BJ |
74 | short lflags; |
75 | short lnl; | |
76 | short luid; | |
77 | short lgid; | |
78 | long lsize; | |
79 | long lmtime; | |
80 | }; | |
81 | ||
31cef89c BJ |
82 | struct dchain { |
83 | char *dc_name; /* the path name */ | |
84 | struct dchain *dc_next; /* the next directory on the chain */ | |
85 | }; | |
86 | ||
87 | struct dchain *dfirst; /* the start of the directory chain */ | |
88 | struct dchain *cdfirst; /* the start of the current directory chain */ | |
89 | struct dchain *dtemp; /* temporary used when linking */ | |
90 | char *curdir; /* the current directory */ | |
91 | ||
92 | int aflg, bflg, dflg, lflg, sflg, tflg, uflg, iflg, fflg, gflg, cflg; | |
93 | int Aflg, nflg, qflg, Fflg, Rflg, across, Cflg; | |
1217e91a | 94 | int nopad; |
31cef89c | 95 | int tabflg; |
1217e91a BJ |
96 | int rflg = 1; |
97 | long year; | |
98 | int flags; | |
1217e91a BJ |
99 | long tblocks; |
100 | int statreq; | |
31cef89c | 101 | int xtraent; /* for those switches which print out a total */ |
1217e91a BJ |
102 | struct lbuf *flist[NFILES]; |
103 | struct lbuf **lastp = flist; | |
104 | struct lbuf **firstp = flist; | |
105 | char *dotp = "."; | |
106 | ||
107 | char *makename(); | |
108 | struct lbuf *gstat(); | |
109 | char *ctime(); | |
110 | long nblock(); | |
31cef89c | 111 | char *getname(); |
1217e91a | 112 | |
31cef89c BJ |
113 | #define ISARG 0100000 |
114 | int colwidth; | |
115 | int filewidth; | |
116 | int fixedwidth; | |
1217e91a BJ |
117 | int outcol; |
118 | ||
119 | char obuf[BUFSIZ]; | |
120 | ||
121 | main(argc, argv) | |
31cef89c | 122 | int argc; |
1217e91a BJ |
123 | char *argv[]; |
124 | { | |
31cef89c BJ |
125 | #include <sgtty.h> |
126 | ||
127 | int i, width; | |
128 | register struct lbuf *ep; | |
1217e91a BJ |
129 | register struct lbuf **slastp; |
130 | struct lbuf **epp; | |
131 | struct lbuf lb; | |
132 | char *t; | |
133 | char *cp; | |
134 | int compar(); | |
31cef89c | 135 | struct sgttyb sgbuf; |
1217e91a | 136 | |
31cef89c BJ |
137 | Fflg = 0; |
138 | tabflg = 0; | |
1217e91a BJ |
139 | Aflg = getuid() == 0; |
140 | setbuf(stdout, obuf); | |
31cef89c | 141 | lb.lmtime = time((long *) 0); |
1217e91a | 142 | year = lb.lmtime - 6L*30L*24L*60L*60L; /* 6 months ago */ |
31cef89c BJ |
143 | qflg = gtty(1, &sgbuf) == 0; |
144 | ||
145 | /* guarantee at least on column width */ | |
146 | fixedwidth = 2; | |
147 | ||
1217e91a BJ |
148 | /* |
149 | * If the standard output is not a teletype, | |
150 | * then we default to one-per-line format | |
151 | * otherwise decide between stream and | |
152 | * columnar based on our name. | |
153 | */ | |
154 | if (qflg) { | |
31cef89c BJ |
155 | Cflg = 1; |
156 | if ((sgbuf.sg_flags & XTABS) == 0) | |
157 | tabflg++; | |
1217e91a BJ |
158 | for (cp = argv[0]; cp[0] && cp[1]; cp++) |
159 | continue; | |
160 | /* | |
31cef89c BJ |
161 | * Certain kinds of links (l, ll, lr, lf, lx) cause some |
162 | * various options to be turned on. | |
1217e91a | 163 | */ |
31cef89c BJ |
164 | switch (cp[0]) { |
165 | case 'l': | |
166 | if (cp[-1] == 'l') { | |
167 | /* ll => -l */ | |
168 | lflg = 1; | |
169 | statreq++; | |
170 | xtraent++; | |
171 | } else { | |
172 | /* l => -m */ | |
173 | nopad = 1; | |
174 | Cflg = 0; | |
175 | } | |
176 | break; | |
177 | case 'x': /* lx => -x */ | |
1217e91a | 178 | across = 1; |
31cef89c BJ |
179 | break; |
180 | case 'f': /* lf => -F */ | |
181 | Fflg = 1; | |
182 | break; | |
183 | case 'r': /* lr => -R */ | |
184 | Rflg = 1; | |
185 | break; | |
186 | } | |
187 | } else { | |
188 | tabflg++; | |
1217e91a | 189 | } |
31cef89c BJ |
190 | |
191 | while (--argc > 0 && *argv[1] == '-') { | |
1217e91a BJ |
192 | argv++; |
193 | while (*++*argv) switch (**argv) { | |
194 | /* | |
31cef89c | 195 | * C - force columnar output |
1217e91a | 196 | */ |
31cef89c BJ |
197 | case 'C': |
198 | Cflg = 1; | |
1217e91a BJ |
199 | nopad = 0; |
200 | continue; | |
201 | /* | |
202 | * m - force stream output | |
203 | */ | |
204 | case 'm': | |
31cef89c | 205 | Cflg = 0; |
1217e91a BJ |
206 | nopad = 1; |
207 | continue; | |
208 | /* | |
209 | * x - force sort across | |
210 | */ | |
211 | case 'x': | |
212 | across = 1; | |
213 | nopad = 0; | |
31cef89c | 214 | Cflg = 1; |
1217e91a BJ |
215 | continue; |
216 | /* | |
217 | * q - force ?'s in output | |
218 | */ | |
219 | case 'q': | |
220 | qflg = 1; | |
31cef89c BJ |
221 | bflg = 0; |
222 | continue; | |
223 | /* | |
224 | * b - force octal value in output | |
225 | */ | |
226 | case 'b': | |
227 | bflg = 1; | |
228 | qflg = 0; | |
1217e91a BJ |
229 | continue; |
230 | /* | |
231 | * 1 - force 1/line in output | |
232 | */ | |
233 | case '1': | |
31cef89c | 234 | Cflg = 0; |
1217e91a BJ |
235 | nopad = 0; |
236 | continue; | |
237 | /* STANDARD FLAGS */ | |
238 | case 'a': | |
239 | aflg++; | |
240 | continue; | |
241 | ||
242 | case 'A': | |
243 | Aflg = !Aflg; | |
244 | continue; | |
245 | ||
31cef89c BJ |
246 | case 'c': |
247 | cflg++; | |
248 | continue; | |
249 | ||
1217e91a | 250 | case 's': |
31cef89c | 251 | fixedwidth += 5; |
1217e91a BJ |
252 | sflg++; |
253 | statreq++; | |
31cef89c | 254 | xtraent++; |
1217e91a BJ |
255 | continue; |
256 | ||
257 | case 'd': | |
258 | dflg++; | |
259 | continue; | |
260 | ||
261 | /* | |
262 | * n - don't look in password file | |
263 | */ | |
264 | case 'n': | |
265 | nflg++; | |
266 | case 'l': | |
267 | lflg++; | |
268 | statreq++; | |
31cef89c | 269 | xtraent++; |
1217e91a BJ |
270 | continue; |
271 | ||
272 | case 'r': | |
273 | rflg = -1; | |
274 | continue; | |
275 | ||
276 | case 't': | |
277 | tflg++; | |
278 | statreq++; | |
279 | continue; | |
280 | ||
281 | case 'u': | |
282 | uflg++; | |
283 | continue; | |
284 | ||
285 | case 'i': | |
31cef89c | 286 | fixedwidth += 6; |
1217e91a BJ |
287 | iflg++; |
288 | continue; | |
289 | ||
290 | case 'f': | |
291 | fflg++; | |
292 | continue; | |
293 | ||
294 | case 'g': | |
295 | gflg++; | |
296 | continue; | |
297 | ||
31cef89c BJ |
298 | case 'F': |
299 | Fflg++; | |
300 | continue; | |
301 | ||
302 | case 'R': | |
303 | Rflg++; | |
1217e91a | 304 | continue; |
31cef89c BJ |
305 | |
306 | default: | |
307 | fprintf (stderr, "usage: ls [-1ACFRabcdfgilmnqrstux] [files]\n"); | |
308 | exit(1); | |
1217e91a | 309 | } |
1217e91a | 310 | } |
31cef89c BJ |
311 | if (Fflg) |
312 | #ifdef UCB | |
313 | fixedwidth++; | |
314 | #else | |
315 | fixedwidth += 2; | |
316 | #endif | |
1217e91a BJ |
317 | if (fflg) { |
318 | aflg++; | |
319 | lflg = 0; | |
320 | sflg = 0; | |
321 | tflg = 0; | |
322 | statreq = 0; | |
31cef89c | 323 | xtraent = 0; |
1217e91a BJ |
324 | } |
325 | if(lflg) { | |
31cef89c | 326 | Cflg = 0; |
1217e91a BJ |
327 | t = "/etc/passwd"; |
328 | if (gflg) | |
329 | t = "/etc/group"; | |
330 | nopad = 0; | |
31cef89c | 331 | fixedwidth = 70; |
1217e91a BJ |
332 | pwdf = fopen(t, "r"); |
333 | } | |
334 | if (argc==0) { | |
335 | argc++; | |
336 | argv = &dotp - 1; | |
337 | } | |
338 | for (i=0; i < argc; i++) { | |
31cef89c BJ |
339 | argv++; |
340 | if (Cflg) { | |
341 | width = strlen (*argv); | |
342 | if (width > filewidth) | |
343 | filewidth = width; | |
344 | } | |
345 | if ((ep = gstat(*argv, 1))==NULL) | |
1217e91a BJ |
346 | continue; |
347 | ep->ln.namep = *argv; | |
348 | ep->lflags |= ISARG; | |
349 | } | |
31cef89c BJ |
350 | if (!Cflg) |
351 | filewidth = MAXFILEWIDTH; | |
352 | else | |
353 | colwidth = fixedwidth + filewidth; | |
1217e91a BJ |
354 | qsort(firstp, lastp - firstp, sizeof *lastp, compar); |
355 | slastp = lastp; | |
31cef89c | 356 | /* For each argument user typed */ |
1217e91a BJ |
357 | for (epp=firstp; epp<slastp; epp++) { |
358 | ep = *epp; | |
31cef89c BJ |
359 | if (ep->ltype=='d' && dflg==0 || fflg) |
360 | pdirectory(ep->ln.namep, (argc>1), slastp); | |
361 | else | |
1217e91a | 362 | pentry(ep); |
31cef89c BJ |
363 | |
364 | /* -R: print subdirectories found */ | |
365 | while (dfirst || cdfirst) { | |
366 | /* Place direct subdirs on front in right order */ | |
367 | while (cdfirst) { | |
368 | /* reverse cdfirst onto front of dfirst */ | |
369 | dtemp = cdfirst; | |
370 | cdfirst = cdfirst -> dc_next; | |
371 | dtemp -> dc_next = dfirst; | |
372 | dfirst = dtemp; | |
373 | } | |
374 | /* take off first dir on dfirst & print it */ | |
375 | dtemp = dfirst; | |
376 | dfirst = dfirst->dc_next; | |
377 | pdirectory (dtemp->dc_name, 1, firstp); | |
378 | cfree (dtemp->dc_name); | |
379 | cfree (dtemp); | |
380 | } | |
1217e91a BJ |
381 | } |
382 | if (outcol) | |
383 | putc('\n', stdout); | |
384 | fflush(stdout); | |
385 | } | |
386 | ||
31cef89c BJ |
387 | /* |
388 | * pdirectory: print the directory name, labelling it if title is | |
389 | * nonzero, using lp as the place to start reading in the dir. | |
390 | */ | |
391 | pdirectory (name, title, lp) | |
392 | char *name; | |
393 | int title; | |
394 | struct lbuf **lp; | |
395 | { | |
396 | register struct dchain *dp; | |
397 | register struct lbuf *ap; | |
398 | register char *pname; | |
399 | struct lbuf **app; | |
400 | ||
401 | filewidth = 0; | |
402 | curdir = name; | |
403 | if (title) | |
404 | printf("\n%s:\n", name); | |
405 | lastp = lp; | |
406 | readdir(name); | |
407 | if (!Cflg) | |
408 | filewidth = MAXFILEWIDTH; | |
409 | colwidth = fixedwidth + filewidth; | |
410 | #ifdef notdef | |
411 | /* Taken out because it appears this is done below in pem. */ | |
412 | if (tabflg) { | |
413 | if (colwidth <= 8) | |
414 | colwidth = 8; | |
415 | else | |
416 | if (colwidth <= 16) | |
417 | colwidth = 16; | |
418 | } | |
419 | #endif | |
420 | if (fflg==0) | |
421 | qsort(lp,lastp - lp,sizeof *lastp,compar); | |
422 | if (Rflg) for (app=lastp-1; app>=lp; app--) { | |
423 | ap = *app; | |
424 | if (ap->ltype == 'd' && strcmp(ap->ln.lname, ".") && | |
425 | strcmp(ap->ln.lname, "..")) { | |
426 | dp = (struct dchain *) calloc(1, sizeof(struct dchain)); | |
427 | pname = makename (curdir, ap->ln.lname); | |
428 | dp->dc_name = (char *) calloc(1, strlen(pname)+1); | |
429 | strcpy(dp->dc_name, pname); | |
430 | dp -> dc_next = dfirst; | |
431 | dfirst = dp; | |
432 | } | |
433 | } | |
434 | if (lflg || sflg) | |
435 | printf("total %D", tblocks); | |
436 | pem(lp, lastp); | |
437 | newline(); | |
438 | } | |
439 | ||
440 | /* | |
441 | * pem: print 'em. Print a list of files (e.g. a directory) bounded | |
442 | * by slp and lp. | |
443 | */ | |
1217e91a BJ |
444 | pem(slp, lp) |
445 | register struct lbuf **slp, **lp; | |
446 | { | |
447 | int ncols, nrows, row, col; | |
448 | register struct lbuf **ep; | |
449 | ||
31cef89c BJ |
450 | if (tabflg) { |
451 | if (colwidth <= 9) | |
452 | colwidth = 8; | |
453 | else | |
454 | if (colwidth <= 17) | |
455 | colwidth = 16; | |
456 | } | |
1217e91a | 457 | ncols = 80 / colwidth; |
31cef89c | 458 | if (ncols == 1 || Cflg == 0) { |
1217e91a BJ |
459 | for (ep = slp; ep < lp; ep++) |
460 | pentry(*ep); | |
461 | return; | |
462 | } | |
463 | if (across) { | |
464 | for (ep = slp; ep < lp; ep++) | |
465 | pentry(*ep); | |
466 | return; | |
467 | } | |
31cef89c | 468 | if (xtraent) |
1217e91a BJ |
469 | slp--; |
470 | nrows = (lp - slp - 1) / ncols + 1; | |
471 | for (row = 0; row < nrows; row++) { | |
31cef89c | 472 | col = row == 0 && xtraent; |
1217e91a BJ |
473 | for (; col < ncols; col++) { |
474 | ep = slp + (nrows * col) + row; | |
475 | if (ep < lp) | |
476 | pentry(*ep); | |
477 | } | |
478 | if (outcol) | |
479 | printf("\n"); | |
480 | } | |
481 | } | |
482 | ||
31cef89c BJ |
483 | /* |
484 | * pputchar: like putchar but knows how to handle control chars. | |
485 | * CAUTION: if you make ctrl chars print in ^x notation, or any | |
486 | * other notation which is wider than one character, the column | |
487 | * nature of things (such as files with 14 letter names) will be | |
488 | * messed up. Weigh this carefully! | |
489 | */ | |
1217e91a BJ |
490 | pputchar(c) |
491 | char c; | |
492 | { | |
31cef89c | 493 | char cc; |
1217e91a BJ |
494 | |
495 | switch (c) { | |
496 | case '\t': | |
497 | outcol = (outcol + 8) &~ 7; | |
498 | break; | |
499 | case '\n': | |
500 | outcol = 0; | |
501 | break; | |
502 | default: | |
31cef89c BJ |
503 | if (c < ' ' || c >= 0177) { |
504 | if (qflg) | |
505 | c = '?'; | |
506 | else if (bflg) { | |
507 | outcol += 3; | |
508 | putc ('\\', stdout); | |
509 | cc = '0' + (c>>6 & 07); | |
510 | putc (cc, stdout); | |
511 | cc = '0' + (c>>3 & 07); | |
512 | putc (cc, stdout); | |
513 | c = '0' + (c & 07); | |
514 | } | |
515 | } | |
1217e91a BJ |
516 | outcol++; |
517 | break; | |
518 | } | |
519 | putc(c, stdout); | |
520 | } | |
521 | ||
522 | newline() | |
523 | { | |
524 | if (outcol) | |
525 | putc('\n', stdout); | |
526 | outcol = 0; | |
527 | } | |
528 | ||
31cef89c BJ |
529 | /* |
530 | * column: get to the beginning of the next column. | |
531 | */ | |
1217e91a BJ |
532 | column() |
533 | { | |
534 | ||
535 | if (outcol == 0) | |
536 | return; | |
537 | if (nopad) { | |
538 | putc(',', stdout); | |
539 | outcol++; | |
540 | if (outcol + colwidth + 2 > 80) { | |
541 | putc('\n', stdout); | |
542 | outcol = 0; | |
543 | return; | |
544 | } | |
545 | putc(' ', stdout); | |
546 | outcol++; | |
547 | return; | |
548 | } | |
31cef89c | 549 | if (Cflg == 0) { |
1217e91a BJ |
550 | putc('\n', stdout); |
551 | return; | |
552 | } | |
553 | if ((outcol / colwidth + 2) * colwidth > 80) { | |
554 | putc('\n', stdout); | |
555 | outcol = 0; | |
556 | return; | |
557 | } | |
31cef89c BJ |
558 | if (tabflg && (colwidth <= 16)) { |
559 | if (colwidth > 8) | |
560 | if ((outcol % 16) < 8) { | |
561 | outcol += 8 - (outcol % 8); | |
562 | putc ('\t', stdout); | |
563 | } | |
564 | outcol += 8 - (outcol % 8); | |
565 | putc ('\t', stdout); | |
566 | return; | |
567 | } | |
1217e91a BJ |
568 | do { |
569 | outcol++; | |
570 | putc(' ', stdout); | |
571 | } while (outcol % colwidth); | |
572 | } | |
573 | ||
574 | ||
31cef89c BJ |
575 | /* |
576 | * nblock: the number of 512 byte blocks a size byte file takes up. | |
577 | * (Note: the number stays 512 no matter what BUFSIZ or the filesystem uses.) | |
578 | */ | |
1217e91a BJ |
579 | long |
580 | nblock(size) | |
581 | long size; | |
582 | { | |
583 | return((size+511)>>9); | |
584 | } | |
585 | ||
31cef89c BJ |
586 | /* |
587 | * This code handles the rwx- business. | |
588 | * You figure it out. | |
589 | */ | |
1217e91a BJ |
590 | int m1[] = { 1, S_IREAD>>0, 'r', '-' }; |
591 | int m2[] = { 1, S_IWRITE>>0, 'w', '-' }; | |
592 | int m3[] = { 2, S_ISUID, 's', S_IEXEC>>0, 'x', '-' }; | |
593 | int m4[] = { 1, S_IREAD>>3, 'r', '-' }; | |
594 | int m5[] = { 1, S_IWRITE>>3, 'w', '-' }; | |
595 | int m6[] = { 2, S_ISGID, 's', S_IEXEC>>3, 'x', '-' }; | |
596 | int m7[] = { 1, S_IREAD>>6, 'r', '-' }; | |
597 | int m8[] = { 1, S_IWRITE>>6, 'w', '-' }; | |
598 | int m9[] = { 2, S_ISVTX, 't', S_IEXEC>>6, 'x', '-' }; | |
599 | ||
600 | int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9}; | |
601 | ||
602 | pmode(aflag) | |
603 | { | |
604 | register int **mp; | |
605 | ||
606 | flags = aflag; | |
607 | for (mp = &m[0]; mp < &m[sizeof(m)/sizeof(m[0])];) | |
608 | select(*mp++); | |
609 | } | |
610 | ||
611 | select(pairp) | |
612 | register int *pairp; | |
613 | { | |
614 | register int n; | |
615 | ||
616 | n = *pairp++; | |
617 | while (--n>=0 && (flags&*pairp++)==0) | |
618 | pairp++; | |
619 | pputchar(*pairp); | |
620 | } | |
621 | ||
31cef89c BJ |
622 | /* |
623 | * returns cat(dir, "/", file), unless dir ends in /, when it doesn't // | |
624 | */ | |
1217e91a BJ |
625 | char * |
626 | makename(dir, file) | |
627 | char *dir, *file; | |
628 | { | |
629 | static char dfile[100]; | |
630 | register char *dp, *fp; | |
631 | register int i; | |
632 | ||
633 | dp = dfile; | |
634 | fp = dir; | |
635 | while (*fp) | |
636 | *dp++ = *fp++; | |
31cef89c | 637 | if (*(dp-1) != '/') |
1217e91a BJ |
638 | *dp++ = '/'; |
639 | fp = file; | |
640 | for (i=0; i<DIRSIZ; i++) | |
641 | *dp++ = *fp++; | |
642 | *dp = 0; | |
643 | return(dfile); | |
644 | } | |
645 | ||
31cef89c BJ |
646 | /* |
647 | * readdir: read in the directory whose name is dir, | |
648 | * starting at lastp. | |
649 | */ | |
1217e91a BJ |
650 | readdir(dir) |
651 | char *dir; | |
652 | { | |
653 | static struct direct dentry; | |
31cef89c | 654 | register int j, width; |
1217e91a BJ |
655 | register struct lbuf *ep; |
656 | ||
657 | if ((dirf = fopen(dir, "r")) == NULL) { | |
658 | printf("%s unreadable\n", dir); | |
659 | return; | |
660 | } | |
661 | tblocks = 0; | |
662 | for(;;) { | |
663 | if (fread(&dentry, sizeof(dentry), 1, dirf) != 1) | |
664 | break; | |
665 | if (dentry.d_ino==0 || | |
666 | aflg==0 && dentry.d_name[0]=='.' && ( | |
667 | !Aflg || | |
668 | dentry.d_name[1]=='\0' | |
669 | || dentry.d_name[1]=='.' && dentry.d_name[2]=='\0')) | |
670 | continue; | |
31cef89c BJ |
671 | if (Cflg) { |
672 | width = strlen (dentry.d_name); | |
673 | if (width > filewidth) | |
674 | filewidth = width; | |
675 | } | |
676 | ep = gstat(makename(dir, dentry.d_name), Fflg || Rflg); | |
1217e91a BJ |
677 | if (ep==NULL) |
678 | continue; | |
679 | if (ep->lnum != -1) | |
680 | ep->lnum = dentry.d_ino; | |
681 | for (j=0; j<DIRSIZ; j++) | |
682 | ep->ln.lname[j] = dentry.d_name[j]; | |
683 | } | |
684 | fclose(dirf); | |
685 | } | |
686 | ||
31cef89c BJ |
687 | /* |
688 | * stat the given file and return an lbuf containing it. | |
689 | * argfl is nonzero if a stat is required because the file is | |
690 | * an argument, rather than having been found in a directory. | |
691 | */ | |
1217e91a BJ |
692 | struct lbuf * |
693 | gstat(file, argfl) | |
694 | char *file; | |
695 | { | |
696 | struct stat statb; | |
697 | register struct lbuf *rep; | |
698 | static int nomocore; | |
699 | ||
700 | if (nomocore) | |
701 | return(NULL); | |
702 | rep = (struct lbuf *)malloc(sizeof(struct lbuf)); | |
703 | if (rep==NULL) { | |
704 | fprintf(stderr, "ls: out of memory\n"); | |
705 | nomocore = 1; | |
706 | return(NULL); | |
707 | } | |
708 | if (lastp >= &flist[NFILES]) { | |
709 | static int msg; | |
710 | lastp--; | |
711 | if (msg==0) { | |
712 | fprintf(stderr, "ls: too many files\n"); | |
713 | msg++; | |
714 | } | |
715 | } | |
716 | *lastp++ = rep; | |
717 | rep->lflags = 0; | |
718 | rep->lnum = 0; | |
719 | rep->ltype = '-'; | |
720 | if (argfl || statreq) { | |
721 | if (stat(file, &statb)<0) { | |
722 | printf("%s not found\n", file); | |
723 | statb.st_ino = -1; | |
724 | statb.st_size = 0; | |
725 | statb.st_mode = 0; | |
726 | if (argfl) { | |
727 | lastp--; | |
728 | return(0); | |
729 | } | |
730 | } | |
731 | rep->lnum = statb.st_ino; | |
732 | rep->lsize = statb.st_size; | |
733 | switch(statb.st_mode&S_IFMT) { | |
734 | ||
735 | case S_IFDIR: | |
736 | rep->ltype = 'd'; | |
737 | break; | |
738 | ||
739 | case S_IFBLK: | |
740 | rep->ltype = 'b'; | |
741 | rep->lsize = statb.st_rdev; | |
742 | break; | |
743 | ||
744 | case S_IFCHR: | |
745 | rep->ltype = 'c'; | |
746 | rep->lsize = statb.st_rdev; | |
747 | break; | |
31cef89c BJ |
748 | |
749 | case S_IFMPB: | |
750 | rep->ltype = 'M'; | |
751 | rep->lsize = statb.st_rdev; | |
752 | break; | |
753 | ||
754 | case S_IFMPC: | |
755 | rep->ltype = 'm'; | |
756 | rep->lsize = statb.st_rdev; | |
757 | break; | |
1217e91a BJ |
758 | } |
759 | rep->lflags = statb.st_mode & ~S_IFMT; | |
760 | rep->luid = statb.st_uid; | |
761 | rep->lgid = statb.st_gid; | |
762 | rep->lnl = statb.st_nlink; | |
763 | if(uflg) | |
764 | rep->lmtime = statb.st_atime; | |
765 | else if (cflg) | |
766 | rep->lmtime = statb.st_ctime; | |
767 | else | |
768 | rep->lmtime = statb.st_mtime; | |
769 | tblocks += nblock(statb.st_size); | |
770 | } | |
771 | return(rep); | |
772 | } | |
773 | ||
31cef89c BJ |
774 | /* |
775 | * decide whether to print pp1 before or after pp2, based on their | |
776 | * names, various times, and the r flag. | |
777 | */ | |
1217e91a BJ |
778 | compar(pp1, pp2) |
779 | struct lbuf **pp1, **pp2; | |
780 | { | |
781 | register struct lbuf *p1, *p2; | |
782 | ||
783 | p1 = *pp1; | |
784 | p2 = *pp2; | |
785 | if (dflg==0) { | |
786 | if (p1->lflags&ISARG && p1->ltype=='d') { | |
787 | if (!(p2->lflags&ISARG && p2->ltype=='d')) | |
788 | return(1); | |
789 | } else { | |
790 | if (p2->lflags&ISARG && p2->ltype=='d') | |
791 | return(-1); | |
792 | } | |
793 | } | |
794 | if (tflg) { | |
795 | if(p2->lmtime == p1->lmtime) | |
796 | return(0); | |
797 | if(p2->lmtime > p1->lmtime) | |
798 | return(rflg); | |
799 | return(-rflg); | |
800 | } | |
801 | return(rflg * strcmp(p1->lflags&ISARG? p1->ln.namep: p1->ln.lname, | |
802 | p2->lflags&ISARG? p2->ln.namep: p2->ln.lname)); | |
803 | } | |
31cef89c BJ |
804 | |
805 | /* | |
806 | * print the entry pointed at by ap | |
807 | */ | |
1217e91a BJ |
808 | pentry(ap) |
809 | struct lbuf *ap; | |
810 | { | |
811 | struct { char dminor, dmajor;}; | |
1217e91a BJ |
812 | register struct lbuf *p; |
813 | register char *cp; | |
31cef89c BJ |
814 | char fname[100]; |
815 | char *pname; | |
816 | struct passwd *getpwuid(); | |
817 | struct passwd *pwptr; | |
818 | struct group *getgrgid(); | |
819 | struct group *grptr; | |
1217e91a | 820 | |
31cef89c | 821 | fname[0] = 0; |
1217e91a BJ |
822 | p = ap; |
823 | if (p->lnum == -1) | |
824 | return; | |
825 | column(); | |
826 | if (iflg) | |
827 | if (nopad && !lflg) | |
828 | printf("%d ", p->lnum); | |
829 | else | |
830 | printf("%5d ", p->lnum); | |
831 | if (sflg) | |
4b9ccde7 C |
832 | switch (p->ltype) { |
833 | ||
834 | case 'b': | |
835 | case 'c': | |
836 | case 'm': | |
837 | case 'M': | |
838 | if (nopad && !lflg) | |
839 | printf("%D ", 0); | |
840 | else | |
841 | printf("%4D ", 0); | |
842 | break; | |
843 | ||
844 | default: | |
845 | if (nopad && !lflg) | |
846 | printf("%D ", nblock(p->lsize)); | |
847 | else | |
848 | printf("%4D ", nblock(p->lsize)); | |
849 | break; | |
850 | } | |
1217e91a BJ |
851 | if (lflg) { |
852 | pputchar(p->ltype); | |
853 | pmode(p->lflags); | |
854 | printf("%2d ", p->lnl); | |
31cef89c BJ |
855 | if(gflg) { |
856 | grptr = getgrgid(p->lgid); | |
857 | if (nflg == 0 && grptr != 0) | |
858 | printf("%-8.8s", grptr->gr_name); | |
859 | else | |
860 | printf("%-8d", p->lgid); | |
861 | } else { | |
862 | #ifndef UCB_PWHASH | |
863 | char *name; | |
864 | if (nflg == 0 && (name = getname(p->luid))) { | |
865 | printf("%-8.8s", name); | |
866 | } | |
867 | #else | |
868 | pwptr = getpwuid(p->luid); | |
869 | if (nflg == 0 && pwptr != 0) | |
870 | printf("%-8.8s", pwptr->pw_name); | |
871 | #endif | |
872 | else | |
873 | printf("%-8d", p->luid); | |
874 | } | |
875 | switch (p->ltype) { | |
876 | ||
877 | case 'b': | |
878 | case 'c': | |
879 | case 'm': | |
880 | case 'M': | |
881 | printf("%3d,%3d", | |
882 | major((int)p->lsize), minor((int)p->lsize)); | |
883 | break; | |
884 | default: | |
1217e91a | 885 | printf("%7ld", p->lsize); |
31cef89c | 886 | } |
1217e91a BJ |
887 | cp = ctime(&p->lmtime); |
888 | if(p->lmtime < year) | |
889 | printf(" %-7.7s %-4.4s ", cp+4, cp+20); else | |
890 | printf(" %-12.12s ", cp+4); | |
891 | } | |
31cef89c BJ |
892 | #ifndef UCB |
893 | if (Fflg) { | |
894 | if (p->ltype == 'd') | |
895 | strcat (fname, "["); | |
896 | else if (p->lflags & 0111) | |
897 | strcat (fname, "*"); | |
898 | else if (!nopad) | |
899 | strcat (fname, " "); | |
900 | } | |
901 | #endif | |
902 | if (p->lflags & ISARG) | |
903 | strncat (fname, p->ln.namep, 98); | |
1217e91a | 904 | else |
31cef89c BJ |
905 | strncat (fname, p->ln.lname, 14); |
906 | #ifndef UCB | |
907 | if (Fflg) { | |
908 | if (p->ltype == 'd') | |
909 | strcat (fname, "]"); | |
910 | else if (!nopad) | |
911 | strcat (fname, " "); | |
912 | } | |
913 | #else | |
914 | if (Fflg) { | |
915 | if (p->ltype == 'd') | |
916 | strcat (fname, "/"); | |
917 | else if (p->lflags & 0111) | |
918 | strcat (fname, "*"); | |
919 | else if (!nopad) | |
920 | strcat (fname, " "); | |
921 | } | |
922 | #endif | |
923 | printf ("%s", fname); | |
924 | free(ap); | |
1217e91a | 925 | } |
31cef89c | 926 | |
1217e91a | 927 | /* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/ |
31cef89c | 928 | |
1217e91a | 929 | #include "varargs.h" |
31cef89c BJ |
930 | |
931 | /* | |
932 | * This version of printf is compatible with the Version 7 C | |
1217e91a BJ |
933 | * printf. The differences are only minor except that this |
934 | * printf assumes it is to print through pputchar. Version 7 | |
935 | * printf is more general (and is much larger) and includes | |
936 | * provisions for floating point. | |
937 | */ | |
1217e91a | 938 | |
31cef89c BJ |
939 | #define MAXOCT 11 /* Maximum octal digits in a long */ |
940 | #define MAXINT 32767 /* largest normal length positive integer */ | |
1217e91a | 941 | #define BIG 1000000000 /* largest power of 10 less than an unsigned long */ |
31cef89c | 942 | #define MAXDIGS 10 /* number of digits in BIG */ |
1217e91a BJ |
943 | |
944 | static int width, sign, fill; | |
945 | ||
946 | char *b_dconv(); | |
947 | ||
948 | printf(va_alist) | |
949 | va_dcl | |
950 | { | |
951 | va_list ap; | |
952 | register char *fmt; | |
953 | char fcode; | |
954 | int prec; | |
955 | int length,mask1,nbits,n; | |
956 | long int mask2, num; | |
957 | register char *bptr; | |
958 | char *ptr; | |
959 | char buf[134]; | |
960 | ||
961 | va_start(ap); | |
962 | fmt = va_arg(ap,char *); | |
963 | for (;;) { | |
964 | /* process format string first */ | |
965 | while ((fcode = *fmt++)!='%') { | |
966 | /* ordinary (non-%) character */ | |
967 | if (fcode=='\0') | |
968 | return; | |
969 | pputchar(fcode); | |
970 | } | |
971 | /* length modifier: -1 for h, 1 for l, 0 for none */ | |
972 | length = 0; | |
973 | /* check for a leading - sign */ | |
974 | sign = 0; | |
975 | if (*fmt == '-') { | |
976 | sign++; | |
977 | fmt++; | |
978 | } | |
979 | /* a '0' may follow the - sign */ | |
980 | /* this is the requested fill character */ | |
981 | fill = 1; | |
982 | if (*fmt == '0') { | |
983 | fill--; | |
984 | fmt++; | |
985 | } | |
986 | ||
987 | /* Now comes a digit string which may be a '*' */ | |
988 | if (*fmt == '*') { | |
989 | width = va_arg(ap, int); | |
990 | if (width < 0) { | |
991 | width = -width; | |
992 | sign = !sign; | |
993 | } | |
994 | fmt++; | |
995 | } | |
996 | else { | |
997 | width = 0; | |
998 | while (*fmt>='0' && *fmt<='9') | |
999 | width = width * 10 + (*fmt++ - '0'); | |
1000 | } | |
1001 | ||
1002 | /* maybe a decimal point followed by more digits (or '*') */ | |
1003 | if (*fmt=='.') { | |
1004 | if (*++fmt == '*') { | |
1005 | prec = va_arg(ap, int); | |
1006 | fmt++; | |
1007 | } | |
1008 | else { | |
1009 | prec = 0; | |
1010 | while (*fmt>='0' && *fmt<='9') | |
1011 | prec = prec * 10 + (*fmt++ - '0'); | |
1012 | } | |
1013 | } | |
1014 | else | |
1015 | prec = -1; | |
1016 | ||
1017 | /* | |
1018 | * At this point, "sign" is nonzero if there was | |
1019 | * a sign, "fill" is 0 if there was a leading | |
1020 | * zero and 1 otherwise, "width" and "prec" | |
1021 | * contain numbers corresponding to the digit | |
1022 | * strings before and after the decimal point, | |
1023 | * respectively, and "fmt" addresses the next | |
1024 | * character after the whole mess. If there was | |
1025 | * no decimal point, "prec" will be -1. | |
1026 | */ | |
1027 | switch (*fmt) { | |
1028 | case 'L': | |
1029 | case 'l': | |
1030 | length = 2; | |
1031 | /* no break!! */ | |
1032 | case 'h': | |
1033 | case 'H': | |
1034 | length--; | |
1035 | fmt++; | |
1036 | break; | |
1037 | } | |
1038 | ||
1039 | /* | |
1040 | * At exit from the following switch, we will | |
1041 | * emit the characters starting at "bptr" and | |
1042 | * ending at "ptr"-1, unless fcode is '\0'. | |
1043 | */ | |
1044 | switch (fcode = *fmt++) { | |
1045 | /* process characters and strings first */ | |
1046 | case 'c': | |
1047 | buf[0] = va_arg(ap, int); | |
1048 | ptr = bptr = &buf[0]; | |
1049 | if (buf[0] != '\0') | |
1050 | ptr++; | |
1051 | break; | |
1052 | case 's': | |
1053 | bptr = va_arg(ap,char *); | |
1054 | if (bptr==0) | |
1055 | bptr = "(null pointer)"; | |
1056 | if (prec < 0) | |
1057 | prec = MAXINT; | |
1058 | for (n=0; *bptr++ && n < prec; n++) ; | |
1059 | ptr = --bptr; | |
1060 | bptr -= n; | |
1061 | break; | |
1062 | case 'O': | |
1063 | length = 1; | |
1064 | fcode = 'o'; | |
1065 | /* no break */ | |
1066 | case 'o': | |
1067 | case 'X': | |
1068 | case 'x': | |
1069 | if (length > 0) | |
1070 | num = va_arg(ap,long); | |
1071 | else | |
1072 | num = (unsigned)va_arg(ap,int); | |
1073 | if (fcode=='o') { | |
1074 | mask1 = 0x7; | |
1075 | mask2 = 0x1fffffffL; | |
1076 | nbits = 3; | |
1077 | } | |
1078 | else { | |
1079 | mask1 = 0xf; | |
1080 | mask2 = 0x0fffffffL; | |
1081 | nbits = 4; | |
1082 | } | |
1083 | n = (num!=0); | |
1084 | bptr = buf + MAXOCT + 3; | |
1085 | /* shift and mask for speed */ | |
1086 | do | |
1087 | if (((int) num & mask1) < 10) | |
31cef89c | 1088 | *--bptr = ((int) num & mask1) + 060; |
1217e91a BJ |
1089 | else |
1090 | *--bptr = ((int) num & mask1) + 0127; | |
1091 | while (num = (num >> nbits) & mask2); | |
1092 | ||
1093 | if (fcode=='o') { | |
1094 | if (n) | |
1095 | *--bptr = '0'; | |
1096 | } | |
1097 | else | |
1098 | if (!sign && fill <= 0) { | |
1099 | pputchar('0'); | |
1100 | pputchar(fcode); | |
1101 | width -= 2; | |
1102 | } | |
1103 | else { | |
1104 | *--bptr = fcode; | |
1105 | *--bptr = '0'; | |
1106 | } | |
1107 | ptr = buf + MAXOCT + 3; | |
1108 | break; | |
1109 | case 'D': | |
1110 | case 'U': | |
1111 | case 'I': | |
1112 | length = 1; | |
31cef89c BJ |
1113 | |
1114 | fcode = fcode + 'a' - 'A'; | |
1217e91a BJ |
1115 | /* no break */ |
1116 | case 'd': | |
1117 | case 'i': | |
1118 | case 'u': | |
1119 | if (length > 0) | |
1120 | num = va_arg(ap,long); | |
1121 | else { | |
1122 | n = va_arg(ap,int); | |
1123 | if (fcode=='u') | |
1124 | num = (unsigned) n; | |
1125 | else | |
1126 | num = (long) n; | |
1127 | } | |
1128 | if (n = (fcode != 'u' && num < 0)) | |
1129 | num = -num; | |
1130 | /* now convert to digits */ | |
1131 | bptr = b_dconv(num, buf); | |
1132 | if (n) | |
1133 | *--bptr = '-'; | |
1134 | if (fill == 0) | |
1135 | fill = -1; | |
1136 | ptr = buf + MAXDIGS + 1; | |
1137 | break; | |
1138 | default: | |
1139 | /* not a control character, | |
1140 | * print it. | |
1141 | */ | |
1142 | ptr = bptr = &fcode; | |
1143 | ptr++; | |
1144 | break; | |
1145 | } | |
1146 | if (fcode != '\0') | |
1147 | b_emit(bptr,ptr); | |
1148 | } | |
1149 | va_end(ap); | |
1150 | } | |
1151 | ||
1152 | /* b_dconv converts the unsigned long integer "value" to | |
1153 | * printable decimal and places it in "buffer", right-justified. | |
1154 | * The value returned is the address of the first non-zero character, | |
1155 | * or the address of the last character if all are zero. | |
1156 | * The result is NOT null terminated, and is MAXDIGS characters long, | |
1157 | * starting at buffer[1] (to allow for insertion of a sign). | |
1158 | * | |
1159 | * This program assumes it is running on 2's complement machine | |
1160 | * with reasonable overflow treatment. | |
1161 | */ | |
1162 | char * | |
1163 | b_dconv(value, buffer) | |
1164 | long value; | |
1165 | char *buffer; | |
1166 | { | |
1167 | register char *bp; | |
1168 | register int svalue; | |
1169 | int n; | |
1170 | long lval; | |
1171 | ||
1172 | bp = buffer; | |
1173 | ||
1174 | /* zero is a special case */ | |
1175 | if (value == 0) { | |
1176 | bp += MAXDIGS; | |
1177 | *bp = '0'; | |
1178 | return(bp); | |
1179 | } | |
1180 | ||
1181 | /* develop the leading digit of the value in "n" */ | |
1182 | n = 0; | |
1183 | while (value < 0) { | |
1184 | value -= BIG; /* will eventually underflow */ | |
1185 | n++; | |
1186 | } | |
1187 | while ((lval = value - BIG) >= 0) { | |
1188 | value = lval; | |
1189 | n++; | |
1190 | } | |
1191 | ||
1192 | /* stash it in buffer[1] to allow for a sign */ | |
1193 | bp[1] = n + '0'; | |
1194 | /* | |
1195 | * Now develop the rest of the digits. Since speed counts here, | |
1196 | * we do it in two loops. The first gets "value" down until it | |
1197 | * is no larger than MAXINT. The second one uses integer divides | |
1198 | * rather than long divides to speed it up. | |
1199 | */ | |
1200 | bp += MAXDIGS + 1; | |
1201 | while (value > MAXINT) { | |
1202 | *--bp = (int)(value % 10) + '0'; | |
1203 | value /= 10; | |
1204 | } | |
1205 | ||
1206 | /* cannot lose precision */ | |
1207 | svalue = value; | |
1208 | while (svalue > 0) { | |
1209 | *--bp = (svalue % 10) + '0'; | |
1210 | svalue /= 10; | |
1211 | } | |
1212 | ||
1213 | /* fill in intermediate zeroes if needed */ | |
1214 | if (buffer[1] != '0') { | |
1215 | while (bp > buffer + 2) | |
1216 | *--bp = '0'; | |
1217 | --bp; | |
1218 | } | |
1219 | return(bp); | |
1220 | } | |
1221 | ||
1222 | /* | |
1223 | * This program sends string "s" to pputchar. The character after | |
1224 | * the end of "s" is given by "send". This allows the size of the | |
1225 | * field to be computed; it is stored in "alen". "width" contains the | |
1226 | * user specified length. If width<alen, the width will be taken to | |
1227 | * be alen. "sign" is zero if the string is to be right-justified | |
1228 | * in the field, nonzero if it is to be left-justified. "fill" is | |
1229 | * 0 if the string is to be padded with '0', positive if it is to be | |
1230 | * padded with ' ', and negative if an initial '-' should appear before | |
1231 | * any padding in right-justification (to avoid printing "-3" as | |
1232 | * "000-3" where "-0003" was intended). | |
1233 | */ | |
1234 | b_emit(s, send) | |
1235 | register char *s; | |
1236 | char *send; | |
1237 | { | |
1238 | char cfill; | |
1239 | register int alen; | |
1240 | int npad; | |
1241 | ||
1242 | alen = send - s; | |
1243 | if (alen > width) | |
1244 | width = alen; | |
1245 | cfill = fill>0? ' ': '0'; | |
1246 | ||
1247 | /* we may want to print a leading '-' before anything */ | |
1248 | if (*s == '-' && fill < 0) { | |
1249 | pputchar(*s++); | |
1250 | alen--; | |
1251 | width--; | |
1252 | } | |
1253 | npad = width - alen; | |
1254 | ||
1255 | /* emit any leading pad characters */ | |
1256 | if (!sign) | |
1257 | while (--npad >= 0) | |
1258 | pputchar(cfill); | |
1259 | ||
1260 | /* emit the string itself */ | |
1261 | while (--alen >= 0) | |
1262 | pputchar(*s++); | |
1263 | ||
1264 | /* emit trailing pad characters */ | |
1265 | if (sign) | |
1266 | while (--npad >= 0) | |
1267 | pputchar(cfill); | |
1268 | } | |
31cef89c BJ |
1269 | |
1270 | #ifndef UCB_PWHASH | |
1271 | #define NUID 2048 | |
1272 | ||
1273 | char names[NUID][NMAX+1]; | |
1274 | ||
1275 | char * | |
1276 | getname(uid) | |
1277 | { | |
1278 | register struct passwd *pw; | |
1279 | static init; | |
1280 | struct passwd *getpwent(); | |
1281 | ||
1282 | if (uid >= 0 && uid < NUID && names[uid][0]) | |
1283 | return (&names[uid][0]); | |
1284 | if (init == 2) | |
1285 | return (0); | |
1286 | if (init == 0) | |
1287 | setpwent(), init = 1; | |
1288 | while (pw = getpwent()) { | |
1289 | if (pw->pw_uid < 0 || pw->pw_uid >= NUID) | |
1290 | continue; | |
1291 | if (names[pw->pw_uid][0]) | |
1292 | continue; | |
1293 | strncpy(names[pw->pw_uid], pw->pw_name, NMAX); | |
1294 | if (pw->pw_uid == uid) | |
1295 | return (&names[uid][0]); | |
1296 | } | |
1297 | init = 2; | |
1298 | endpwent(); | |
1299 | return (0); | |
1300 | } | |
1301 | #endif |