ls.c 6.1 KB

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