morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base
Log | Files | Refs

ls.c (6170B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <dirent.h>
      3 #include <grp.h>
      4 #include <pwd.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/stat.h>
      9 #include <time.h>
     10 #include <unistd.h>
     11 
     12 #include "util.h"
     13 
     14 typedef struct {
     15 	char *name;
     16 	mode_t mode, tmode;
     17 	nlink_t nlink;
     18 	uid_t uid;
     19 	gid_t gid;
     20 	off_t size;
     21 	time_t t;
     22 	ino_t ino;
     23 } Entry;
     24 
     25 static int entcmp(const void *, const void *);
     26 static void ls(Entry *);
     27 static void lsdir(const char *);
     28 static void mkent(Entry *, char *, int, int);
     29 static void output(Entry *);
     30 
     31 static int aflag = 0;
     32 static int cflag = 0;
     33 static int dflag = 0;
     34 static int Fflag = 0;
     35 static int Hflag = 0;
     36 static int hflag = 0;
     37 static int iflag = 0;
     38 static int Lflag = 0;
     39 static int lflag = 0;
     40 static int rflag = 0;
     41 static int tflag = 0;
     42 static int Uflag = 0;
     43 static int first = 1;
     44 static int many;
     45 
     46 static void
     47 usage(void)
     48 {
     49 	eprintf("usage: %s [-1acdFHhiLlrtU] [file ...]\n", argv0);
     50 }
     51 
     52 int
     53 main(int argc, char *argv[])
     54 {
     55 	int i;
     56 	Entry *ents;
     57 
     58 	ARGBEGIN {
     59 	case '1':
     60 		/* ignore */
     61 		break;
     62 	case 'a':
     63 		aflag = 1;
     64 		break;
     65 	case 'c':
     66 		cflag = 1;
     67 		break;
     68 	case 'd':
     69 		dflag = 1;
     70 		break;
     71 	case 'F':
     72 		Fflag = 1;
     73 		break;
     74 	case 'H':
     75 		Hflag = 1;
     76 		break;
     77 	case 'h':
     78 		hflag = 1;
     79 		break;
     80 	case 'i':
     81 		iflag = 1;
     82 		break;
     83 	case 'L':
     84 		Lflag = 1;
     85 		break;
     86 	case 'l':
     87 		lflag = 1;
     88 		break;
     89 	case 'r':
     90 		rflag = 1;
     91 		break;
     92 	case 't':
     93 		tflag = 1;
     94 		break;
     95 	case 'U':
     96 		Uflag = 1;
     97 		break;
     98 	default:
     99 		usage();
    100 	} ARGEND;
    101 
    102 	many = (argc > 1);
    103 	if (argc == 0)
    104 		*--argv = ".", argc++;
    105 
    106 	ents = emalloc(argc * sizeof(*ents));
    107 
    108 	for (i = 0; i < argc; i++)
    109 		mkent(&ents[i], argv[i], 1, Hflag || Lflag);
    110 	qsort(ents, argc, sizeof *ents, entcmp);
    111 	for (i = 0; i < argc; i++)
    112 		ls(&ents[rflag ? argc-i-1 : i]);
    113 
    114 	return 0;
    115 }
    116 
    117 static int
    118 entcmp(const void *va, const void *vb)
    119 {
    120 	const Entry *a = va, *b = vb;
    121 
    122 	if (tflag)
    123 		return b->t - a->t;
    124 	else
    125 		return strcmp(a->name, b->name);
    126 }
    127 
    128 static void
    129 ls(Entry *ent)
    130 {
    131 	if ((S_ISDIR(ent->mode) || (S_ISLNK(ent->mode) && S_ISDIR(ent->tmode) && !Fflag && !lflag)) && !dflag) {
    132 		lsdir(ent->name);
    133 	} else {
    134 		output(ent);
    135 	}
    136 }
    137 
    138 static void
    139 lsdir(const char *path)
    140 {
    141 	char *cwd, *p;
    142 	long i, n = 0;
    143 	struct dirent *d;
    144 	DIR *dp;
    145 	Entry ent, *ents = NULL;
    146 	size_t sz;
    147 
    148 	cwd = agetcwd();
    149 	if (!(dp = opendir(path)))
    150 		eprintf("opendir %s:", path);
    151 	if (chdir(path) < 0)
    152 		eprintf("chdir %s:", path);
    153 
    154 	if (many) {
    155 		if (!first)
    156 			putchar('\n');
    157 		printf("%s:\n", path);
    158 		first = 0;
    159 	}
    160 
    161 	while ((d = readdir(dp))) {
    162 		if (d->d_name[0] == '.' && !aflag)
    163 			continue;
    164 		if (Uflag){
    165 			mkent(&ent, d->d_name, Fflag || lflag || iflag, Lflag);
    166 			output(&ent);
    167 		} else {
    168 			ents = erealloc(ents, ++n * sizeof *ents);
    169 			p = emalloc((sz = strlen(d->d_name)+1));
    170 			memcpy(p, d->d_name, sz);
    171 			mkent(&ents[n-1], p, tflag || Fflag || lflag || iflag, Lflag);
    172 		}
    173 	}
    174 	closedir(dp);
    175 	if (!Uflag){
    176 		qsort(ents, n, sizeof *ents, entcmp);
    177 		for (i = 0; i < n; i++) {
    178 			output(&ents[rflag ? n-i-1 : i]);
    179 			free(ents[rflag ? n-i-1 : i].name);
    180 		}
    181 	}
    182 	if (chdir(cwd) < 0)
    183 		eprintf("chdir %s:", cwd);
    184 	free(ents);
    185 	free(cwd);
    186 }
    187 
    188 static void
    189 mkent(Entry *ent, char *path, int dostat, int follow)
    190 {
    191 	struct stat st;
    192 
    193 	ent->name   = path;
    194 	if (!dostat)
    195 		return;
    196 	if ((follow ? stat : lstat)(path, &st) < 0)
    197 		eprintf("%s %s:", follow ? "stat" : "lstat", path);
    198 	ent->mode   = st.st_mode;
    199 	ent->nlink  = st.st_nlink;
    200 	ent->uid    = st.st_uid;
    201 	ent->gid    = st.st_gid;
    202 	ent->size   = st.st_size;
    203 	ent->t      = cflag ? st.st_ctime : st.st_mtime;
    204 	ent->ino    = st.st_ino;
    205 	if (S_ISLNK(ent->mode))
    206 		ent->tmode = stat(path, &st) == 0 ? st.st_mode : 0;
    207 }
    208 
    209 static char *
    210 indicator(mode_t mode)
    211 {
    212 	if (!Fflag)
    213 		return "";
    214 
    215 	if (S_ISLNK(mode))
    216 		return "@";
    217 	else if (S_ISDIR(mode))
    218 		return "/";
    219 	else if (S_ISFIFO(mode))
    220 		return "|";
    221 	else if (S_ISSOCK(mode))
    222 		return "=";
    223 	else if (mode & S_IXUSR ||
    224 		 mode & S_IXGRP ||
    225 		 mode & S_IXOTH)
    226 		return "*";
    227 	else
    228 		return "";
    229 }
    230 
    231 static void
    232 output(Entry *ent)
    233 {
    234 	char buf[BUFSIZ], *fmt;
    235 	char mode[] = "----------";
    236 	ssize_t len;
    237 	struct group *gr;
    238 	struct passwd *pw;
    239 	char pwname[_SC_LOGIN_NAME_MAX];
    240 	char grname[_SC_LOGIN_NAME_MAX];
    241 
    242 	if (iflag)
    243 		printf("%lu ", (unsigned long)ent->ino);
    244 	if (!lflag) {
    245 		printf("%s%s\n", ent->name, indicator(ent->mode));
    246 		return;
    247 	}
    248 	if (S_ISREG(ent->mode))
    249 		mode[0] = '-';
    250 	else if (S_ISBLK(ent->mode))
    251 		mode[0] = 'b';
    252 	else if (S_ISCHR(ent->mode))
    253 		mode[0] = 'c';
    254 	else if (S_ISDIR(ent->mode))
    255 		mode[0] = 'd';
    256 	else if (S_ISFIFO(ent->mode))
    257 		mode[0] = 'p';
    258 	else if (S_ISLNK(ent->mode))
    259 		mode[0] = 'l';
    260 	else if (S_ISSOCK(ent->mode))
    261 		mode[0] = 's';
    262 	else
    263 		mode[0] = '?';
    264 
    265 	if (ent->mode & S_IRUSR) mode[1] = 'r';
    266 	if (ent->mode & S_IWUSR) mode[2] = 'w';
    267 	if (ent->mode & S_IXUSR) mode[3] = 'x';
    268 	if (ent->mode & S_IRGRP) mode[4] = 'r';
    269 	if (ent->mode & S_IWGRP) mode[5] = 'w';
    270 	if (ent->mode & S_IXGRP) mode[6] = 'x';
    271 	if (ent->mode & S_IROTH) mode[7] = 'r';
    272 	if (ent->mode & S_IWOTH) mode[8] = 'w';
    273 	if (ent->mode & S_IXOTH) mode[9] = 'x';
    274 
    275 	if (ent->mode & S_ISUID) mode[3] = (mode[3] == 'x') ? 's' : 'S';
    276 	if (ent->mode & S_ISGID) mode[6] = (mode[6] == 'x') ? 's' : 'S';
    277 	if (ent->mode & S_ISVTX) mode[9] = (mode[9] == 'x') ? 't' : 'T';
    278 
    279 	pw = getpwuid(ent->uid);
    280 	if (pw)
    281 		snprintf(pwname, sizeof(pwname), "%s", pw->pw_name);
    282 	else
    283 		snprintf(pwname, sizeof(pwname), "%d", ent->uid);
    284 
    285 	gr = getgrgid(ent->gid);
    286 	if (gr)
    287 		snprintf(grname, sizeof(grname), "%s", gr->gr_name);
    288 	else
    289 		snprintf(grname, sizeof(grname), "%d", ent->gid);
    290 
    291 	if (time(NULL) > ent->t + (180*24*60*60)) /* 6 months ago? */
    292 		fmt = "%b %d  %Y";
    293 	else
    294 		fmt = "%b %d %H:%M";
    295 
    296 	strftime(buf, sizeof buf, fmt, localtime(&ent->t));
    297 	printf("%s %4ld %-8.8s %-8.8s ", mode, (long)ent->nlink, pwname, grname);
    298 	if (hflag)
    299 		printf("%10s ", humansize((unsigned long)ent->size));
    300 	else
    301 		printf("%10lu ", (unsigned long)ent->size);
    302 	printf("%s %s%s", buf, ent->name, indicator(ent->mode));
    303 	if (S_ISLNK(ent->mode)) {
    304 		if ((len = readlink(ent->name, buf, sizeof buf - 1)) < 0)
    305 			eprintf("readlink %s:", ent->name);
    306 		buf[len] = '\0';
    307 		printf(" -> %s%s", buf, indicator(ent->tmode));
    308 	}
    309 	putchar('\n');
    310 }