1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #if HAVE_UNISTD_H
8 #   include <unistd.h>
9 #endif
10 
11 #if HAVE_DIRENT_H
12 #   include <dirent.h>
13 #   define direct dirent
14 #else
15 #   include <sys/dir.h>
16 #   if !HAVE_OPENDIR
17        static int dirfile = -1;
18        static struct direct direntry;
19 #      define DIR int
20 #      define opendir(name)     ((dirfile = open (name, 0)) >= 0 ? &dirfile : 0)
21 #      define closedir(f)       (close (dirfile), dirfile = -1)
22 #      define readdir(f)        (read (dirfile, &direntry, sizeof (direntry)) ==\
23 					sizeof (direntry) ? &direntry : 0)
24 #      undef dirfd
25 #      define dirfd(f)          dirfile
26 #   endif
27 #endif
28 
29 #if !HAVE_DIRFD && !defined (dirfd)
30 #   define dirfd(f)             ((f)->dd_fd)
31 #endif
32 
33 #include "dir.h"
34 #include "deco.h"
35 
36 #define QUANT 128
37 
38 #ifndef MAXNAMLEN
39 #   define MAXNAMLEN    255
40 #endif
41 
42 #ifndef S_IFLNK
43 #   define lstat stat
44 #endif
45 
46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
47 
48 static struct dir *compdir;
49 
50 static int comptype (const struct file *f);
51 static void chdirup (char *name);
52 static char *prepend (char *p, char *str);
53 static char *getcwdname (char *pathname);
54 static void retag (struct dir *d, struct dir *od);
55 static int fgetinfo (char *info, int maxlen, char *rinfo, int rmaxlen,
56 	long *date, FILE *fd);
57 
58 /*
59  *      Compare files. Used in call to qsort.
60  *      Return -1, 0, 1 iff a is less, equal or greater than b.
61  */
compfile(const void * arg1,const void * arg2)62 int compfile (const void *arg1, const void *arg2)
63 {
64 	const struct file *a = arg1;
65 	const struct file *b = arg2;
66 	register atype, btype;
67 	int rez;
68 
69 	if (compdir->revsort) {
70 		const struct file *t;
71 
72 		t = a;
73 		a = b;
74 		b = t;
75 	}
76 	if (compdir->typesort) {
77 		atype = comptype (a);
78 		btype = comptype (b);
79 		if (atype != btype)
80 			return (atype<btype ? -1 : 1);
81 	}
82 	switch (compdir->sort) {
83 	case SORTEXT:
84 		atype = a->name[0] != '.';
85 		btype = b->name[0] != '.';
86 		if (atype != btype)
87 			return (atype<btype ? -1 : 1);
88 		if ((rez = strcmp (extension (a->name), extension (b->name))))
89 			return (rez);
90 	case SORTNAME:
91 		return (strcmp (a->name, b->name));
92 	case SORTTIME:
93 		return (a->mtime < b->mtime ? -1 :
94 			a->mtime > b->mtime ? 1 : 0);
95 	case SORTSIZE:
96 		return (a->size < b->size ? -1 :
97 			a->size > b->size ? 1 : 0);
98 	}
99 	return (0);
100 }
101 
102 /*
103  *      Return integer number, used for comparing file types.
104  */
comptype(const struct file * f)105 static int comptype (const struct file *f)
106 {
107 	switch (f->mode & S_IFMT) {
108 	case S_IFDIR:
109 		if (f->name[0] == '.') {
110 			if (! f->name[1])
111 				return (-1);
112 			if (f->name[1] == '.' && ! f->name[2])
113 				return (-2);
114 		}
115 		if (! f->execable)
116 			return (1);
117 		return (0);
118 	case S_IFCHR:
119 		return (100);
120 	case S_IFBLK:
121 		return (110);
122 #ifdef S_IFIFO
123 	case S_IFIFO:
124 		return (120);
125 #endif
126 #ifdef S_IFNAM
127 	case S_IFNAM:
128 		return (130);
129 #endif
130 #ifdef S_IFSOCK
131 	case S_IFSOCK:
132 		return (140);
133 #endif
134 #ifdef S_IFLNK
135 	case S_IFLNK:
136 		return (150);
137 #endif
138 	case S_IFREG:
139 		return (500);
140 	default:
141 		return (900);
142 	}
143 }
144 
chdirup(char * name)145 static void chdirup (char *name)
146 {
147 	register char *s, *p;
148 
149 	for (;;) {
150 		if (chdir (name) < 0)
151 			error ("Cannot chdir to %s", name);
152 		else if (access (".", 4) < 0)
153 			error ("Cannot read %s", name);
154 		else
155 			break;
156 		if (name[0]=='/' && name[1]==0) {       /* root directory */
157 			error ("DECO cannot work on this system -- goodbye!");
158 			quitdeco ();
159 		}
160 		/* cut last directory name */
161 		for (p=s=name; *s; ++s)
162 			if (*s == '/')
163 				p = s;
164 		*p = 0;
165 		if (! *name) {
166 			name [0] = '/';
167 			name [1] = 0;
168 		}
169 	}
170 }
171 
prepend(char * p,char * str)172 static char *prepend (char *p, char *str)
173 {
174 	int len = strlen (str);
175 	memcpy (p-len, str, len);
176 	return (p-len);
177 }
178 
179 /*
180  * Get current directory name.  Pathname should be at least
181  * MAXPATHLEN long.  Returns 0 on error, and puts message into pathname.
182  * Compatible with BSD getwd().
183  */
getcwdname(char * pathname)184 static char *getcwdname (char *pathname)
185 {
186 	char upname [MAXPATHLEN+MAXNAMLEN];
187 	char myname [MAXPATHLEN];
188 	char *upptr, *myptr;
189 	struct stat root, up, cur, f;
190 	register struct direct *rec;
191 	DIR *dir;
192 
193 	myptr = &myname[MAXPATHLEN-1];
194 	upptr = &upname[MAXPATHLEN-1];
195 	*myptr = *upptr = 0;
196 	upptr = prepend (upptr, "./");
197 	stat ("/", &root);
198 	stat (upptr, &cur);
199 	while (cur.st_dev != root.st_dev || cur.st_ino != root.st_ino) {
200 		upptr = prepend (upptr, "../");
201 		dir = opendir (upptr);
202 		if (! dir) {
203 			strcpy (pathname, "Cannot read `..'");
204 			return (0);
205 		}
206 		fstat (dirfd (dir), &up);
207 		for (;;) {
208 			if (! (rec = readdir (dir))) {
209 				strcpy (pathname, "Cannot find path");
210 				return (0);
211 			}
212 			if (cur.st_dev == up.st_dev) {
213 				if (cur.st_ino == rec->d_ino)
214 					break;
215 				continue;
216 			}
217 			if (rec->d_name[0]=='.' && (!rec->d_name[1] ||
218 			   (rec->d_name[1]=='.' && rec->d_name[2]==0)))
219 				continue;
220 			strcpy (&upname[MAXPATHLEN-1], rec->d_name);
221 			lstat (upptr, &f);
222 			upname[MAXPATHLEN-1] = 0;
223 			if (cur.st_ino == f.st_ino && cur.st_dev == f.st_dev)
224 				break;
225 		}
226 		myptr = prepend (myptr, rec->d_name);
227 		*--myptr = '/';
228 		closedir (dir);
229 		cur = up;
230 	}
231 	strcpy (pathname, *myptr ? myptr : "/");
232 	return (pathname);
233 }
234 
235 /*
236  *      Set up new directory.
237  *      struct dir *d   - directory structure
238  *      char *dirname   - name of directory.
239  *                      If 0 then use d->cwd.
240  */
setdir(struct dir * d,char * dirname)241 int setdir (struct dir *d, char *dirname)
242 {
243 	DIR *f;
244 	struct direct *recp;
245 	register struct file *p;
246 	struct dir olddir;
247 	struct stat st;
248 
249 	/* change directory */
250 	if (dirname)
251 		if (chdir (dirname) < 0)
252 			error ("Cannot chdir to %s", dirname);
253 		else
254 			getcwdname (d->cwd);
255 	chdirup (d->cwd);
256 	d->shortcwd = strtail (d->cwd, '/', 35);
257 	stat (d->cwd, &st);
258 	d->dev = st.st_dev;
259 	d->ino = st.st_ino;
260 
261 	f = opendir (".");
262 	if (! f) {
263 		error ("Cannot read '.' -- good bye !");
264 		quitdeco ();
265 	}
266 
267 	if (! dirname) {
268 		/* save directory structure, then use it for retagging */
269 		olddir = *d;
270 		d->catsz = 0;
271 		d->cat = 0;
272 	}
273 
274 	if (! d->cat) {
275 		d->catsz = QUANT;
276 		d->cat = (struct file*) calloc (d->catsz, sizeof (struct file));
277 	} else
278 		for (p=d->cat; p<d->cat+d->num; ++p) {
279 			if (p->info)
280 				free (p->info);
281 			if (p->rinfo)
282 				free (p->rinfo);
283 			p->info = p->rinfo = 0;
284 		}
285 
286 	d->num = 0;
287 	d->nfiles = 0;
288 	d->tfiles = 0;
289 	d->nbytes = 0;
290 	d->mfiles = 0;
291 	d->mbytes = 0;
292 	while ((recp = readdir (f))) {
293 		if (! recp->d_ino)
294 			continue;
295 		mcheck (d->cat, struct file*, d->catsz, QUANT, d->num);
296 		p = &d->cat[d->num];
297 		lstat (recp->d_name, &st);
298 #ifdef S_IFLNK
299 		p->link = ((st.st_mode & S_IFMT) == (unsigned) S_IFLNK);
300 		if (p->link)
301 			stat (recp->d_name, &st);
302 #else
303 		p->link = 0;
304 #endif
305 		/* skip .. in root directory */
306 		if (recp->d_name[0] == '.' && recp->d_name[1] == '.' &&
307 		    recp->d_name[2] == 0 && st.st_ino == d->ino &&
308 		    st.st_dev == d->dev)
309 			continue;
310 
311 		/* skip all hidden files except parent dir entry */
312 		if (! showhidden && recp->d_name[0] == '.' &&
313 		    (recp->d_name[1] != '.' || recp->d_name[2]))
314 			continue;
315 
316 		/* skip names not matched with pattern */
317 		if ((st.st_mode & S_IFMT) == (unsigned) S_IFREG) {
318 			if (! showhidden && recp->d_name[0] == '.')
319 				continue;
320 			++d->nfiles;
321 			d->nbytes += st.st_size;
322 			if (d->pattern [0] && ! match (recp->d_name, d->pattern))
323 				continue;
324 			++d->mfiles;
325 			d->mbytes += st.st_size;
326 		}
327 		p->mode  = st.st_mode;
328 		p->mtime = st.st_mtime;
329 		p->atime = st.st_atime;
330 		p->ctime = st.st_ctime;
331 		p->size  = st.st_size;
332 		p->nlink = st.st_nlink;
333 		p->dev   = st.st_dev;
334 #if HAVE_ST_RDEV
335 		p->rdev  = st.st_rdev;
336 #else
337 		p->rdev  = 0;
338 #endif
339 		p->ino   = st.st_ino;
340 		p->uid   = st.st_uid;
341 		p->gid   = st.st_gid;
342 		strncpy (p->name, recp->d_name, sizeof(p->name)-1);
343 		p->name [sizeof(p->name)-1] = 0;
344 		if (uid == 0)
345 			p->execable = ((p->mode & 0111) != 0);
346 		else if (uid == p->uid)
347 			p->execable = p->mode >> 6 & 1;
348 		else if (mygroup (p->gid))
349 			p->execable = p->mode >> 3 & 1;
350 		else
351 			p->execable = p->mode & 1;
352 		p->tag = 0;
353 		p->info = 0;
354 		p->rinfo = 0;
355 		p->infodate = 0;
356 		p->infoflag = 0;
357 		p->dateflag = 0;
358 		++d->num;
359 	}
360 
361 	d->topfile = d->curfile = 0;
362 
363 	if (d->sort != SORTSKIP) {
364 	compdir = d;
365 	qsort ((char*)d->cat, (unsigned) d->num, sizeof (d->cat[0]), compfile);
366 	}
367 
368 	if (! dirname) {
369 		/* retag files, restore curfile */
370 		retag (d, &olddir);
371 		free (olddir.cat);
372 		counttag (d);
373 	}
374 	closedir (f);
375 	return (1);
376 }
377 
filesize(struct file * f)378 char *filesize (struct file *f)
379 {
380 	static char buf [9];
381 
382 	switch (f->mode & S_IFMT) {
383 	case S_IFDIR:
384 		return ("   <DIR>");
385 	case S_IFCHR:
386 		sprintf (buf, " %3d,%3d", f->rdev>>8&0377, f->rdev&0377);
387 		return (buf);
388 	case S_IFBLK:
389 		sprintf (buf, " %3d,%3d", f->rdev>>8&0377, f->rdev&0377);
390 		return (buf);
391 #ifdef S_IFIFO
392 	case S_IFIFO:
393 		return ("  <FIFO>");
394 #endif
395 #ifdef S_IFNAM
396 	case S_IFNAM:
397 		return ("  <NAME>");
398 #endif
399 #ifdef S_IFSOCK
400 	case S_IFSOCK:
401 		return ("  <SOCK>");
402 #endif
403 #ifdef S_IFLNK
404 	case S_IFLNK:
405 		return ("  <LINK>");
406 #endif
407 	case S_IFREG:
408 		sprintf (buf, "%8ld", (long) f->size);
409 		return (buf);
410 	default:
411 		return ("     ???");
412 	}
413 }
414 
filemode(int m)415 char *filemode (int m)
416 {
417 	static char buf [10];
418 	register char *p = buf;
419 
420 	*p++ = m & S_IREAD ? 'r' : '-';
421 	*p++ = m & S_IWRITE ? 'w' : '-';
422 	*p++ = m & S_IEXEC ?
423 		(m & S_ISUID ? 's' : 'x') :
424 		(m & S_ISUID ? 'S' : '-');
425 	*p++ = m & S_IREAD >> 3 ? 'r' : '-';
426 	*p++ = m & S_IWRITE >> 3 ? 'w' : '-';
427 	*p++ = m & S_IEXEC >> 3 ?
428 		(m & S_ISGID ? 's' : 'x') :
429 		(m & S_ISGID ? 'S' : '-');
430 	*p++ = m & S_IREAD >> 6 ? 'r' : '-';
431 	*p++ = m & S_IWRITE >> 6 ? 'w' : '-';
432 	*p++ = m & S_IEXEC >> 6 ?
433 		(m & S_ISVTX ? 't' : 'x') :
434 		(m & S_ISVTX ? 'T' : '-');
435 	*p = 0;
436 	return (buf);
437 }
438 
filedate(long d)439 char *filedate (long d)
440 {
441 	static char buf [10];
442 	register char *p = buf;
443 
444 	*p++ = d / 100000 + '0';
445 	*p++ = d / 10000 % 10 + '0';
446 	*p++ = '.';
447 	*p++ = d / 1000 % 10 + '0';
448 	*p++ = d / 100 % 10 + '0';
449 	*p++ = '.';
450 	*p++ = d / 10 % 10 + '0';
451 	*p++ = d % 10 + '0';
452 	return (buf);
453 }
454 
filetype(struct file * f)455 int filetype (struct file *f)
456 {
457 	switch (f->mode & S_IFMT) {
458 	case S_IFDIR:
459 		if (! f->execable)
460 			return ('&');
461 		return ('/');
462 	case S_IFCHR:
463 		return ('$');
464 	case S_IFBLK:
465 		return ('#');
466 #ifdef S_IFIFO
467 	case S_IFIFO:
468 		return ('=');
469 #endif
470 #ifdef S_IFNAM
471 	case S_IFNAM:
472 		return ('@');
473 #endif
474 #ifdef S_IFSOCK
475 	case S_IFSOCK:
476 		return ('!');
477 #endif
478 #ifdef S_IFLNK
479 	case S_IFLNK:
480 		return ('~');
481 #endif
482 	case S_IFREG:
483 		if (f->execable)
484 			return ('*');
485 		return (' ');
486 	default:
487 		return ('?');
488 	}
489 }
490 
exist(char * name)491 int exist (char *name)
492 {
493 	struct stat st;
494 
495 	if (stat (name, &st) < 0)
496 		return (0);
497 	switch (st.st_mode & S_IFMT) {
498 	case S_IFDIR:
499 		return ('d');
500 	case S_IFREG:
501 		return ('f');
502 	default:
503 		return ('?');
504 	}
505 }
506 
modtime(char * name)507 long modtime (char *name)
508 {
509 	struct stat st;
510 
511 	if (stat (name, &st) < 0)
512 		return (0);
513 	return (st.st_mtime);
514 }
515 
retag(struct dir * d,struct dir * od)516 static void retag (struct dir *d, struct dir *od)
517 {
518 	register struct file *f, *of;
519 	struct file *end, *oend, *ocur, *otop;
520 	int cmp;
521 
522 	end = d->cat + d->num;
523 	oend = od->cat + od->num;
524 	ocur = od->cat + od->curfile;
525 	otop = od->cat + od->topfile;
526 	d->curfile = -1;
527 	compdir = d;
528 	for (f=d->cat, of=od->cat; of<oend; ++of) {
529 		while ((cmp = compfile (f, of)) < 0)
530 			if (++f >= end)
531 				goto breakloop;
532 		if (of == ocur)
533 			d->curfile = f - d->cat;
534 		if (of == otop)
535 			d->topfile = f - d->cat;
536 		if (cmp == 0)
537 			f->tag = of->tag;
538 	}
539 breakloop:
540 	if (d->curfile < 0) {
541 		d->curfile = d->num - 1;
542 		d->topfile = d->curfile - PAGELEN (d) + 1;
543 	} else if (d->topfile > d->curfile || d->topfile + PAGELEN (d) <= d->curfile)
544 		d->topfile = d->curfile - PAGELEN (d) + 1;
545 	while (d->topfile + PAGELEN (d) - H > d->curfile)
546 		d->topfile -= H;
547 	if (d->topfile < 0)
548 		d->topfile = 0;
549 }
550 
counttag(struct dir * d)551 void counttag (struct dir *d)
552 {
553 	register struct file *f;
554 
555 	/* compute number of files and bytes */
556 	d->tfiles = 0;
557 	d->tbytes = 0;
558 	for (f=d->cat; f<d->cat+d->num; ++f)
559 		if ((f->mode & S_IFMT) == (unsigned) S_IFREG && f->tag) {
560 			++d->tfiles;
561 			d->tbytes += f->size;
562 		}
563 }
564 
findfile(struct dir * d,char * name)565 void findfile (struct dir *d, char *name)
566 {
567 	/* set current file by name */
568 	register struct file *f;
569 
570 	for (f=d->cat; f<d->cat+d->num; ++f)
571 		if (! strncmp (f->name, name, NAMESZ-1)) {
572 			d->curfile = f - d->cat;
573 			break;
574 		}
575 	while (d->topfile > d->curfile)
576 		d->topfile -= H;
577 	if (d->topfile < 0)
578 		d->topfile = 0;
579 	while (d->topfile + PAGELEN (d) <= d->curfile)
580 		cur->topfile += H;
581 }
582 
setinfo(struct file * f,struct dir * d)583 void setinfo (struct file *f, struct dir *d)
584 {
585 	char info[512], rinfo[512];
586 	FILE *fd;
587 
588 	f->infoflag = 1;
589 	f->infodate = 0;
590 
591 	/* No info for . and .. */
592 	if (f->name[0] == '.' && (f->name[1] == 0 ||
593 	   (f->name[1] == '.' && f->name[2] == 0)))
594 		return;
595 
596 	sprintf (info, "%s/.info/%s", d->cwd, f->name);
597 	fd = fopen (info, "r");
598 	if (fd) {
599 		fgetinfo (info, sizeof (info), rinfo, sizeof (rinfo),
600 			&f->infodate, fd);
601 		if (*info) {
602 			if (strlen (info) > 45)
603 				info[45] = 0;
604 			f->info = mdup (info);
605 		}
606 		if (*rinfo) {
607 			if (strlen (rinfo) > 45)
608 				rinfo[45] = 0;
609 			f->rinfo = mdup (rinfo);
610 		}
611 		fclose (fd);
612 	}
613 	if (f->infodate)
614 		f->dateflag = 1;
615 	else {
616 		struct tm *t = localtime (&f->mtime);
617 
618 		f->infodate = t->tm_year % 100 * 10000 +
619 			(t->tm_mon + 1) * 100 + t->tm_mday;
620 		f->dateflag = 0;
621 	}
622 }
623 
624 /*
625  * Get multistring from the file.
626  * The continuation lines are indented by spaces.
627  */
fgetinfo(char * info,int maxlen,char * rinfo,int rmaxlen,long * date,FILE * fd)628 static int fgetinfo (char *info, int maxlen, char *rinfo, int rmaxlen,
629 	long *date, FILE *fd)
630 {
631 	char *p;
632 	int n;
633 
634 	*info = *rinfo = 0;
635 	*date = 0;
636 again:
637 	do n = getc (fd);
638 	while (n == ' ' || n == '\t');
639 	if (n < 0)
640 		return (0);
641 	ungetc (n, fd);
642 
643 	fgets (info, maxlen, fd);
644 	if (ISDIGIT (info[0]) && ISDIGIT (info[1]) &&
645 	    ISDIGIT (info[2]) && ISDIGIT (info[3]) &&
646 	    ISDIGIT (info[4]) && ISDIGIT (info[5])) {
647 		char *q = info;
648 
649 		*date = strtol (info, 0, 0);
650 		for (p=info+6; *p==' ' || *p=='\t'; ++p)
651 			continue;
652 		while (*p)
653 			*q++ = *p++;
654 		*q = 0;
655 	}
656 	p = info;
657 	while (maxlen > 0) {
658 		n = strlen (p);
659 		maxlen -= n;
660 		p += n;
661 
662 		while (--n >= 0 && (p[-1] == '\n' || p[-1] == '\15')) {
663 			*--p = 0;
664 			++maxlen;
665 		}
666 
667 		n = getc (fd);
668 		if (n < 0)
669 			break;
670 		if (n != ' ' && n != '\t') {
671 			char buf[9];
672 
673 			ungetc (n, fd);
674 			if (n != 'R' || ! fgets (buf, 9, fd) ||
675 			    strncmp (buf, "Russian:", 8) != 0)
676 				break;
677 			info = rinfo;
678 			maxlen = rmaxlen;
679 			goto again;
680 		}
681 
682 		do n = getc (fd);
683 		while (n == ' ' || n == '\t');
684 		if (n < 0)
685 			break;
686 		ungetc (n, fd);
687 		*p++ = ' ';
688 		--maxlen;
689 
690 		fgets (p, maxlen, fd);
691 	}
692 	return (1);
693 }
694