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