1 #ifndef lint
2 static char *copyright =
3 	"LC - Copyright University of Waterloo, 1978,1985,1987";
4 #endif /* lint */
5 /*
6  * lc [directory ...]
7  */
8 
9 #include <sys/types.h>
10 #include <sys/param.h>
11 #include <sys/stat.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <dirent.h>
17 typedef struct dirent DirType;
18 
19 #ifndef MAXPATHLEN
20 #	define	MAXPATHLEN	1024			/* For older systems */
21 #endif
22 
23 		/* this assumes that S_IFMT is a mask of contiguous 1s */
24 #define	IFMT_OFFSET	12			/* may be system-dependent */
25 #define	IFMT		(S_IFMT >> IFMT_OFFSET)
26 
27 #define	MODEX(m)	(((m)>>IFMT_OFFSET)&IFMT) /* IFMT to table index */
28 
29 #define	NMALLOC		100		/* how much to malloc in growlist() */
30 #define COLUMNWIDTH	15		/* number of characters in a column */
31 #define INDENT		"    "		/* blanks at beginning of a column */
32 
33 static struct filelist {
34 	char **names;		/* pointer to array of names */
35 	int size;		/* how many elements */
36 	int end;		/* next element to use */
37 	int flag;
38 	char *title;
39 }
40 	dlist = {(char **) NULL, 0, 0, 0, "Directories"},
41 	flist = {(char **) NULL, 0, 0, 0, "Files"},
42 	plist = {(char **) NULL, 0, 0, 0, "Pipes"},
43 	blist = {(char **) NULL, 0, 0, 0, "Block Spec. Files"},
44 	clist = {(char **) NULL, 0, 0, 0, "Char. Spec. Files"},
45 	mpclist = {(char **) NULL, 0, 0, 0, "MPX Char. Files"},
46 	mpblist = {(char **) NULL, 0, 0, 0, "MPX Block Files"},
47 	symlist = {(char **) NULL, 0, 0, 0, "Unsatisfied Symbolic Links"},
48 	soclist = {(char **) NULL, 0, 0, 0, "Sockets"},
49 				/* flag is always on for this list */
50 	goklist = {(char **) NULL, 0, 0, 1, "**GOK"};
51 
52 static struct filelist *printlist[] = {
53 	&dlist, &flist, &plist, &blist, &clist, &mpclist, &mpblist, &symlist,
54 	&soclist, &goklist, 0
55 };
56 
57 static struct filelist *listtype[IFMT+1];
58 
59 static struct xinit {
60 	int	x_flag;
61 	struct	filelist	*x_list;
62 } xinit[] ={
63 #ifdef S_IPIPE
64 	S_IPIPE,	&plist,
65 #endif
66 #ifdef S_IFPIP
67 	S_IFPIP,	&plist,
68 #endif
69 #ifdef S_IFIFO
70 	S_IFIFO,	&plist,
71 #endif
72 	S_IFREG,	&flist,
73 	S_IFDIR,	&dlist,
74 	S_IFCHR,	&clist,
75 	S_IFBLK,	&blist,
76 #ifdef S_IFMPC
77 	S_IFMPC,	&mpclist,
78 #endif
79 #ifdef S_IFMPB
80 	S_IFMPB,	&mpblist,
81 #endif
82 #ifdef S_IFLNK
83 	S_IFLNK,	&symlist,
84 #endif
85 #ifdef S_IFSOCK
86 	S_IFSOCK,	&soclist,
87 #endif
88 	-1,
89 };
90 
91 static int nfl;		/* flag; this is not the first line of output */
92 static int errcall;	/* flag; last line of output was error message */
93 static int notfirst;	/* flag; means not the first thing in this directory */
94 static int nflg;	/* flag; no output - just want exit status */
95 static int ncols = 5;	/* number of columns */
96 static int linewidth;
97 static int manyflg;
98 static int anyfound;
99 static int all  = 1;
100 static char pathbuf[MAXPATHLEN];
101 static char *fnptr = pathbuf;
102 static int aflg;
103 
104 
105 	static void
warn(name)106 warn(name)
107 	char *name;
108 {
109 	if(nfl && !errcall)
110 		putchar('\n');
111 	fprintf(stdout, "%s:  %s\n", name, strerror(errno));
112 	fflush(stdout);
113 	errcall=nfl=1;
114 	errno=0;
115 }
116 
117 	static void
growlist(list)118 growlist( list )
119 	struct filelist *list;
120 {
121 	register int oldsize, newsize;
122 	register char **newlist;
123 
124 	oldsize = list->size * sizeof (char *);
125 	newsize = oldsize + (NMALLOC * sizeof (char *));
126 
127 	newlist = (char **) malloc( newsize );
128 
129 	if (oldsize != 0) {
130 		memcpy( newlist, list->names, oldsize );
131 		free( (char *)list->names );
132 	}
133 
134 	list->names = newlist;
135 	list->size += NMALLOC;
136 }
137 
138 	static void
build(list,name)139 build(list, name)
140 	struct filelist *list;
141 	char *name;
142 {
143 	register int n;
144 
145 	if(list->flag == 0)
146 		return;
147 	anyfound++;
148 	if(nflg)
149 		return;
150 	if (list->end >= list->size)
151 		growlist(list);
152 	n = strlen( name ) + 1;
153 
154 	(list->names)[list->end++] = strncpy( malloc( n ), name, n );
155 }
156 
157 	static void
release(list)158 release( list )
159 	struct filelist *list;
160 {
161 	register char **p;
162 	register int i;
163 
164 	for (p = list->names, i = 0; i < list->end; i++)
165 		free( p[i] );
166 
167 	list->end = 0;
168 }
169 
170 	static void
print(list)171 print(list)
172 	struct filelist *list;
173 {
174 	register int cursor, i;
175 	register char **p;
176 
177 	if(notfirst++)
178 		putchar('\n');
179 	if(ncols!=1) {
180 		if (manyflg)
181 			fputs(INDENT, stdout);
182 		printf("%s:\n", list->title);
183 	}
184 	cursor = 0;
185 	for(p = list->names, i = 0; i < list->end; i++) {
186 		int len;
187 		int posused;	/* positions used by blanks and filename */
188 		register char *fname;
189 
190 		fname = p[i];
191 		len = strlen(fname);
192 		posused = len + ((cursor == 0) ? 0 : COLUMNWIDTH - cursor%COLUMNWIDTH);
193 		if ((cursor + posused) > linewidth) {
194 			printf("\n");
195 			cursor = 0;
196 			posused = len;
197 		}
198 		if (manyflg && (cursor == 0)) fputs(INDENT, stdout);
199 		printf("%*s", posused, fname);
200 		cursor += posused;
201 	}
202 	if(cursor != 0)
203 		 printf("\n");
204 	errcall = 0;
205 	if (manyflg)
206 		release(list);
207 }
208 
209 	static int
pstrcmp(pa,pb)210 pstrcmp( pa, pb )
211 	char *pa, *pb;
212 {
213 	auto char *a = *(char**)pa;
214 	auto char *b = *(char**)pb;
215 
216 	return (strcmp( a, b ));
217 }
218 
219 	static void
listdir(path)220 listdir(path)
221 	char *path;
222 {
223 	register char *p;
224 	DirType *d;
225 	struct filelist **ppr, *pr;
226 	DIR *u;
227 	char *dirname;
228 	struct stat ibuf;
229 
230 	if (!manyflg) {
231 		if (chdir(path) < 0) {
232 			warn(path);
233 			return;
234 		}
235 		dirname = ".";
236 	} else
237 		dirname = path;
238 	if (stat(dirname, &ibuf) < 0) {
239 		warn(path);
240 		return;
241 	}
242 	if ((ibuf.st_mode&S_IFMT) != S_IFDIR) {
243 		if(!manyflg){
244 			errno=ENOTDIR;
245 			warn(path);
246 		}
247 		return;
248 	}
249 	if ((u = opendir(dirname)) == NULL) {
250 		warn(path);
251 		return;
252 	}
253 	if(manyflg) {
254 		if(nfl++)
255 			putchar('\n');
256 		printf("%s:\n", path);
257 	}
258 	while((d = readdir(u)) != NULL) {
259 		if (!aflg) {	/* skip "." and ".." */
260 			p = d->d_name;
261 			if(*p++ == '.' && (*p == '\0'
262 			    || *p++ == '.' && *p == '\0'))
263 				continue;
264 		}
265 		if ((fnptr + strlen(d->d_name)) >= &pathbuf[MAXPATHLEN]) {
266 			errno=ENAMETOOLONG;
267 			warn(pathbuf);
268 /*
269 			err("%s: Component `%s' makes pathname > %d bytes.\n",
270 				pathbuf, d->d_name, MAXPATHLEN);
271 */
272 			continue;
273 		}
274 		(void)strcpy(fnptr, d->d_name);
275 		if(stat(pathbuf, &ibuf) < 0 && lstat(pathbuf, &ibuf) < 0) {
276 			warn(pathbuf);
277 			continue;
278 		}
279 		build(listtype[MODEX(ibuf.st_mode)], d->d_name);
280 	}
281 
282 	closedir(u);
283 	notfirst = 0;
284 	for (ppr = printlist; 0 != (pr = *ppr); ppr++)
285 		if(pr->names && pr->end != 0) {
286 			qsort( (char *)pr->names, pr->end,
287 					sizeof(char *), pstrcmp );
288 			print(pr);
289 		}
290 	fflush(stdout);
291 }
292 
293 
294 	int /*GOTO*/
main(argc,argv)295 main(argc, argv)
296 	char **argv;
297 {
298 	register char *path, *p, *q;
299 	struct xinit *xp;
300 	int i;
301 
302 	for (i = 0 ; i < IFMT ; i++)
303 		listtype[i] = &goklist;
304 	for (xp = xinit; xp->x_flag != -1; xp++)
305 		listtype[MODEX(xp->x_flag)] = xp->x_list;
306 #ifdef TIOCGWINSZ
307 	if (isatty(1)) {
308 		struct winsize win;
309 		if (ioctl(1, TIOCGWINSZ, &win) != -1) {
310 			ncols = (win.ws_col == 0 ? 5 : (win.ws_col / COLUMNWIDTH));
311 			ncols = ncols ? ncols : 1;	/* can't have 0 columns */
312 		}
313 	}
314 #endif /* TIOGCWINSZ */
315 	if(argc >= 2 && argv[1][0] == '-') {
316 		argv++;
317 		argc--;
318 		for(p = &argv[0][1]; *p; p++)
319 			switch(*p) {
320 			case 'a':
321 				aflg++;
322 				break;
323 
324 			case 'n':
325 				nflg++;
326 				break;
327 
328 			case '1':
329 				ncols=1;
330 				break;
331 
332 			default:
333 				switch(*p) {
334 				case 'f':
335 					flist.flag++;
336 					break;
337 
338 				case 'd':
339 					dlist.flag++;
340 					break;
341 
342 				case 'b':
343 					blist.flag++;
344 					break;
345 
346 				case 'c':
347 					clist.flag++;
348 					break;
349 
350 				case 'B':
351 					mpblist.flag++;
352 					break;
353 
354 				case 'C':
355 					mpclist.flag++;
356 					break;
357 
358 				case 's':
359 					blist.flag++;
360 					clist.flag++;
361 					mpblist.flag++;
362 					mpclist.flag++;
363 					break;
364 
365 				case 'p':
366 					plist.flag++;
367 					break;
368 
369 				default:
370 					fprintf(stderr, "Unknown flag: %c\n",
371 						*p);
372 					continue;
373 				}
374 				all = 0;
375 			}
376 	}
377 
378 	if (all)
379 		flist.flag = dlist.flag = blist.flag = clist.flag
380 		    = mpblist.flag = mpclist.flag
381 		    = symlist.flag = soclist.flag
382 		    = plist.flag = 1;
383 
384 	linewidth = ncols * COLUMNWIDTH;
385 
386 	if(argc < 3) {
387 		path = argc == 1 ? "." : argv[1];
388 		listdir(path);
389 	} else {
390 		manyflg++;
391 		while(--argc) {
392 			path = p = *++argv;
393 			if(strlen(path) >= MAXPATHLEN) {/* = for '/' */
394 				errno=ENAMETOOLONG;
395 				warn(path);
396 				continue;
397 			}
398 			q = pathbuf;
399 			while (0 != (*q++ = *p++));
400 			q[-1] = '/';
401 			fnptr = q;
402 			listdir(path);
403 		}
404 	}
405 	exit(anyfound==0);
406 }
407