ls.c 5.6 KB

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