9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

ls.c (5449B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 
      5 #define dirbuf p9dirbuf	/* avoid conflict on sun */
      6 
      7 typedef struct NDir NDir;
      8 struct NDir
      9 {
     10 	Dir *d;
     11 	char	*prefix;
     12 };
     13 
     14 int	errs = 0;
     15 int	dflag;
     16 int	lflag;
     17 int	mflag;
     18 int	nflag;
     19 int	pflag;
     20 int	qflag;
     21 int	Qflag;
     22 int	rflag;
     23 int	sflag;
     24 int	tflag;
     25 int	uflag;
     26 int	Fflag;
     27 int	ndirbuf;
     28 int	ndir;
     29 NDir*	dirbuf;
     30 int	ls(char*, int);
     31 int	compar(NDir*, NDir*);
     32 char*	asciitime(long);
     33 char*	darwx(long);
     34 void	rwx(long, char*);
     35 void	growto(long);
     36 void	dowidths(Dir*);
     37 void	format(Dir*, char*);
     38 void	output(void);
     39 ulong	clk;
     40 int	swidth;			/* max width of -s size */
     41 int	qwidth;			/* max width of -q version */
     42 int	vwidth;			/* max width of dev */
     43 int	uwidth;			/* max width of userid */
     44 int	mwidth;			/* max width of muid */
     45 int	glwidth;		/* max width of groupid and length */
     46 Biobuf	bin;
     47 
     48 void
     49 main(int argc, char *argv[])
     50 {
     51 	int i;
     52 
     53 	Binit(&bin, 1, OWRITE);
     54 	ARGBEGIN{
     55 	case 'F':	Fflag++; break;
     56 	case 'd':	dflag++; break;
     57 	case 'l':	lflag++; break;
     58 	case 'm':	mflag++; break;
     59 	case 'n':	nflag++; break;
     60 	case 'p':	pflag++; break;
     61 	case 'q':	qflag++; break;
     62 	case 'Q':	Qflag++; break;
     63 	case 'r':	rflag++; break;
     64 	case 's':	sflag++; break;
     65 	case 't':	tflag++; break;
     66 	case 'u':	uflag++; break;
     67 	default:	fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
     68 			exits("usage");
     69 	}ARGEND
     70 
     71 	doquote = needsrcquote;
     72 	quotefmtinstall();
     73 	fmtinstall('M', dirmodefmt);
     74 
     75 	if(lflag)
     76 		clk = time(0);
     77 	if(argc == 0)
     78 		errs = ls(".", 0);
     79 	else for(i=0; i<argc; i++)
     80 		errs |= ls(argv[i], 1);
     81 	output();
     82 	exits(errs? "errors" : 0);
     83 }
     84 
     85 int
     86 ls(char *s, int multi)
     87 {
     88 	int fd;
     89 	long i, n;
     90 	char *p;
     91 	Dir *db;
     92 
     93 	for(;;) {
     94 		p = utfrrune(s, '/');
     95 		if(p == 0 || p[1] != 0 || p == s)
     96 			break;
     97 		*p = 0;
     98 	}
     99 	db = dirstat(s);
    100 	if(db == nil){
    101     error:
    102 		fprint(2, "ls: %s: %r\n", s);
    103 		return 1;
    104 	}
    105 	if(db->qid.type&QTDIR && dflag==0){
    106 		free(db);
    107 		db = nil;
    108 		output();
    109 		fd = open(s, OREAD);
    110 		if(fd == -1)
    111 			goto error;
    112 		n = dirreadall(fd, &db);
    113 		if(n < 0)
    114 			goto error;
    115 		growto(ndir+n);
    116 		for(i=0; i<n; i++){
    117 			dirbuf[ndir+i].d = db+i;
    118 			dirbuf[ndir+i].prefix = multi? s : 0;
    119 		}
    120 		ndir += n;
    121 		close(fd);
    122 		output();
    123 	}else{
    124 		growto(ndir+1);
    125 		dirbuf[ndir].d = db;
    126 		dirbuf[ndir].prefix = 0;
    127 		p = utfrrune(s, '/');
    128 		if(p){
    129 			dirbuf[ndir].prefix = s;
    130 			*p = 0;
    131 			/* restore original name; don't use result of stat */
    132 			dirbuf[ndir].d->name = strdup(p+1);
    133 		}
    134 		ndir++;
    135 	}
    136 	return 0;
    137 }
    138 
    139 void
    140 output(void)
    141 {
    142 	int i;
    143 	char buf[4096];
    144 	char *s;
    145 
    146 	if(!nflag)
    147 		qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
    148 	for(i=0; i<ndir; i++)
    149 		dowidths(dirbuf[i].d);
    150 	for(i=0; i<ndir; i++) {
    151 		if(!pflag && (s = dirbuf[i].prefix)) {
    152 			if(strcmp(s, "/") ==0)	/* / is a special case */
    153 				s = "";
    154 			sprint(buf, "%s/%s", s, dirbuf[i].d->name);
    155 			format(dirbuf[i].d, buf);
    156 		} else
    157 			format(dirbuf[i].d, dirbuf[i].d->name);
    158 	}
    159 	ndir = 0;
    160 	Bflush(&bin);
    161 }
    162 
    163 void
    164 dowidths(Dir *db)
    165 {
    166 	char buf[256];
    167 	int n;
    168 
    169 	if(sflag) {
    170 		n = sprint(buf, "%llud", (db->length+1023)/1024);
    171 		if(n > swidth)
    172 			swidth = n;
    173 	}
    174 	if(qflag) {
    175 		n = sprint(buf, "%lud", db->qid.vers);
    176 		if(n > qwidth)
    177 			qwidth = n;
    178 	}
    179 	if(mflag) {
    180 		n = snprint(buf, sizeof buf, "[%s]", db->muid);
    181 		if(n > mwidth)
    182 			mwidth = n;
    183 	}
    184 	if(lflag) {
    185 		n = sprint(buf, "%ud", db->dev);
    186 		if(n > vwidth)
    187 			vwidth = n;
    188 		n = strlen(db->uid);
    189 		if(n > uwidth)
    190 			uwidth = n;
    191 		n = sprint(buf, "%llud", db->length);
    192 		n += strlen(db->gid);
    193 		if(n > glwidth)
    194 			glwidth = n;
    195 	}
    196 }
    197 
    198 char*
    199 fileflag(Dir *db)
    200 {
    201 	if(Fflag == 0)
    202 		return "";
    203 	if(QTDIR & db->qid.type)
    204 		return "/";
    205 	if(0111 & db->mode)
    206 		return "*";
    207 	return "";
    208 }
    209 
    210 void
    211 format(Dir *db, char *name)
    212 {
    213 	int i;
    214 
    215 	if(sflag)
    216 		Bprint(&bin, "%*llud ",
    217 			swidth, (db->length+1023)/1024);
    218 	if(mflag){
    219 		Bprint(&bin, "[%s] ", db->muid);
    220 		for(i=2+strlen(db->muid); i<mwidth; i++)
    221 			Bprint(&bin, " ");
    222 	}
    223 	if(qflag)
    224 		Bprint(&bin, "(%.16llux %*lud %.2ux) ",
    225 			db->qid.path,
    226 			qwidth, db->qid.vers,
    227 			db->qid.type);
    228 	if(lflag)
    229 		Bprint(&bin,
    230 			"%M %C %*ud %*s %s %*llud %s ",
    231 			db->mode, db->type,
    232 			vwidth, db->dev,
    233 			-uwidth, db->uid,
    234 			db->gid,
    235 			(int)(glwidth-strlen(db->gid)), db->length,
    236 			asciitime(uflag? db->atime : db->mtime));
    237 	Bprint(&bin,
    238 		Qflag? "%s%s\n" : "%q%s\n",
    239 		name, fileflag(db));
    240 }
    241 
    242 void
    243 growto(long n)
    244 {
    245 	if(n <= ndirbuf)
    246 		return;
    247 	ndirbuf = n;
    248 	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
    249 	if(dirbuf == 0){
    250 		fprint(2, "ls: malloc fail\n");
    251 		exits("malloc fail");
    252 	}		
    253 }
    254 
    255 int
    256 compar(NDir *a, NDir *b)
    257 {
    258 	long i;
    259 	Dir *ad, *bd;
    260 
    261 	ad = a->d;
    262 	bd = b->d;
    263 
    264 	if(tflag){
    265 		if(uflag)
    266 			i = bd->atime-ad->atime;
    267 		else
    268 			i = bd->mtime-ad->mtime;
    269 	}else{
    270 		if(a->prefix && b->prefix){
    271 			i = strcmp(a->prefix, b->prefix);
    272 			if(i == 0)
    273 				i = strcmp(ad->name, bd->name);
    274 		}else if(a->prefix){
    275 			i = strcmp(a->prefix, bd->name);
    276 			if(i == 0)
    277 				i = 1;	/* a is longer than b */
    278 		}else if(b->prefix){
    279 			i = strcmp(ad->name, b->prefix);
    280 			if(i == 0)
    281 				i = -1;	/* b is longer than a */
    282 		}else
    283 			i = strcmp(ad->name, bd->name);
    284 	}
    285 	if(i == 0)
    286 		i = (ad<bd? -1 : 1);
    287 	if(rflag)
    288 		i = -i;
    289 	return i;
    290 }
    291 
    292 char*
    293 asciitime(long l)
    294 {
    295 	static char buf[32];
    296 	char *t;
    297 
    298 	t = ctime(l);
    299 	/* 6 months in the past or a day in the future */
    300 	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
    301 		memmove(buf, t+4, 7);		/* month and day */
    302 		memmove(buf+7, t+23, 5);		/* year */
    303 	}else
    304 		memmove(buf, t+4, 12);		/* skip day of week */
    305 	buf[12] = 0;
    306 	return buf;
    307 }
    308