mc.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * mc - columnate
  3. *
  4. * mc[-][-LINEWIDTH][-t][file...]
  5. * - causes break on colon
  6. * -LINEWIDTH sets width of line in which to columnate(default 80)
  7. * -t suppresses expanding multiple blanks into tabs
  8. *
  9. */
  10. #include <u.h>
  11. #include <libc.h>
  12. #include <draw.h>
  13. #include <bio.h>
  14. #define WIDTH 80
  15. #define TAB 4
  16. #define WORD_ALLOC_QUANTA 1024
  17. #define ALLOC_QUANTA 4096
  18. int linewidth=WIDTH;
  19. int mintab=1;
  20. int colonflag=0;
  21. int tabflag=0; /* -t flag turned off forever */
  22. Rune *cbuf, *cbufp;
  23. Rune **word;
  24. int maxwidth=0;
  25. int nalloc=ALLOC_QUANTA;
  26. int nwalloc=WORD_ALLOC_QUANTA;
  27. int nchars=0;
  28. int nwords=0;
  29. int tabwidth=0;
  30. Font *font;
  31. Biobuf bin;
  32. Biobuf bout;
  33. void getwidth(void), readbuf(int), error(char *);
  34. void scanwords(void), columnate(void), morechars(void);
  35. int wordwidth(Rune*, int);
  36. int nexttab(int);
  37. void
  38. main(int argc, char *argv[])
  39. {
  40. int i;
  41. int lineset;
  42. int ifd;
  43. lineset = 0;
  44. Binit(&bout, 1, OWRITE);
  45. while(argc > 1 && argv[1][0] == '-'){
  46. --argc; argv++;
  47. switch(argv[0][1]){
  48. case '\0':
  49. colonflag = 1;
  50. break;
  51. case 't':
  52. tabflag = 0;
  53. break;
  54. default:
  55. linewidth = atoi(&argv[0][1]);
  56. if(linewidth <= 1)
  57. linewidth = WIDTH;
  58. lineset = 1;
  59. break;
  60. }
  61. }
  62. if(lineset == 0){
  63. getwidth();
  64. if(linewidth <= 1){
  65. linewidth = WIDTH;
  66. font = nil;
  67. }
  68. }
  69. cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
  70. word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
  71. if(word == 0 || cbuf == 0)
  72. error("out of memory");
  73. if(argc == 1)
  74. readbuf(0);
  75. else{
  76. for(i = 1; i < argc; i++){
  77. if((ifd = open(*++argv, OREAD)) == -1)
  78. fprint(2, "mc: can't open %s (%r)\n", *argv);
  79. else{
  80. readbuf(ifd);
  81. Bflush(&bin);
  82. close(ifd);
  83. }
  84. }
  85. }
  86. columnate();
  87. exits(0);
  88. }
  89. void
  90. error(char *s)
  91. {
  92. fprint(2, "mc: %s\n", s);
  93. exits(s);
  94. }
  95. void
  96. readbuf(int fd)
  97. {
  98. int lastwascolon = 0;
  99. long c;
  100. int linesiz = 0;
  101. Binit(&bin, fd, OREAD);
  102. do{
  103. if(nchars++ >= nalloc)
  104. morechars();
  105. *cbufp++ = c = Bgetrune(&bin);
  106. linesiz++;
  107. if(c == '\t') {
  108. cbufp[-1] = L' ';
  109. while(linesiz%TAB != 0) {
  110. if(nchars++ >= nalloc)
  111. morechars();
  112. *cbufp++ = L' ';
  113. linesiz++;
  114. }
  115. }
  116. if(colonflag && c == ':')
  117. lastwascolon++;
  118. else if(lastwascolon){
  119. if(c == '\n'){
  120. --nchars; /* skip newline */
  121. *cbufp = L'\0';
  122. while(nchars > 0 && cbuf[--nchars] != '\n')
  123. ;
  124. if(nchars)
  125. nchars++;
  126. columnate();
  127. if (nchars)
  128. Bputc(&bout, '\n');
  129. Bprint(&bout, "%S", cbuf+nchars);
  130. nchars = 0;
  131. cbufp = cbuf;
  132. }
  133. lastwascolon = 0;
  134. }
  135. if(c == '\n')
  136. linesiz = 0;
  137. }while(c >= 0);
  138. }
  139. void
  140. scanwords(void)
  141. {
  142. Rune *p, *q;
  143. int i, w;
  144. nwords=0;
  145. maxwidth=0;
  146. for(p = q = cbuf, i = 0; i < nchars; i++){
  147. if(*p++ == L'\n'){
  148. if(nwords >= nwalloc){
  149. nwalloc += WORD_ALLOC_QUANTA;
  150. if((word = realloc(word, nwalloc*sizeof(*word)))==0)
  151. error("out of memory");
  152. }
  153. word[nwords++] = q;
  154. p[-1] = L'\0';
  155. w = wordwidth(q, p-q-1);
  156. if(w > maxwidth)
  157. maxwidth = w;
  158. q = p;
  159. }
  160. }
  161. }
  162. void
  163. columnate(void)
  164. {
  165. int i, j;
  166. int words_per_line;
  167. int nlines;
  168. int col;
  169. int endcol;
  170. scanwords();
  171. if(nwords==0)
  172. return;
  173. maxwidth = nexttab(maxwidth+mintab-1);
  174. words_per_line = linewidth/maxwidth;
  175. if(words_per_line <= 0)
  176. words_per_line = 1;
  177. nlines=(nwords+words_per_line-1)/words_per_line;
  178. for(i = 0; i < nlines; i++){
  179. col = endcol = 0;
  180. for(j = i; j < nwords; j += nlines){
  181. endcol += maxwidth;
  182. Bprint(&bout, "%S", word[j]);
  183. col += wordwidth(word[j], runestrlen(word[j]));
  184. if(j+nlines < nwords){
  185. if(tabflag) {
  186. while(col < endcol){
  187. Bputc(&bout, '\t');
  188. col = nexttab(col);
  189. }
  190. }else{
  191. while(col < endcol){
  192. Bputc(&bout, ' ');
  193. col++;
  194. }
  195. }
  196. }
  197. }
  198. Bputc(&bout, '\n');
  199. }
  200. }
  201. int
  202. wordwidth(Rune *w, int nw)
  203. {
  204. if(font)
  205. return runestringnwidth(font, w, nw);
  206. return nw;
  207. }
  208. int
  209. nexttab(int col)
  210. {
  211. if(tabwidth){
  212. col += tabwidth;
  213. col -= col%tabwidth;
  214. return col;
  215. }
  216. return col+1;
  217. }
  218. void
  219. morechars(void)
  220. {
  221. nalloc += ALLOC_QUANTA;
  222. if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
  223. error("out of memory");
  224. cbufp = cbuf+nchars-1;
  225. }
  226. /*
  227. * These routines discover the width of the display.
  228. * It takes some work. If we do the easy calls to the
  229. * draw library, the screen flashes due to repainting
  230. * when mc exits.
  231. */
  232. jmp_buf drawjmp;
  233. void
  234. terror(Display*, char*)
  235. {
  236. longjmp(drawjmp, 1);
  237. }
  238. void
  239. getwidth(void)
  240. {
  241. int n, fd;
  242. char buf[128], *f[10], *p;
  243. if(access("/dev/acme", OREAD) >= 0){
  244. if((fd = open("/dev/acme/ctl", OREAD)) < 0)
  245. return;
  246. n = read(fd, buf, sizeof buf-1);
  247. close(fd);
  248. if(n <= 0)
  249. return;
  250. buf[n] = 0;
  251. n = tokenize(buf, f, nelem(f));
  252. if(n < 7)
  253. return;
  254. if((font = openfont(nil, f[6])) == nil)
  255. return;
  256. if(n >= 8)
  257. tabwidth = atoi(f[7]);
  258. else
  259. tabwidth = 4*stringwidth(font, "0");
  260. mintab = stringwidth(font, "0");
  261. linewidth = atoi(f[5]);
  262. tabflag = 1;
  263. return;
  264. }
  265. if((p = getenv("font")) == nil)
  266. return;
  267. if((font = openfont(nil, p)) == nil)
  268. return;
  269. if((fd = open("/dev/window", OREAD)) < 0){
  270. font = nil;
  271. return;
  272. }
  273. n = read(fd, buf, 5*12);
  274. close(fd);
  275. if(n < 5*12){
  276. font = nil;
  277. return;
  278. }
  279. buf[n] = 0;
  280. /* window stucture:
  281. 4 bit left edge
  282. 1 bit gap
  283. 12 bit scrollbar
  284. 4 bit gap
  285. text
  286. 4 bit right edge
  287. */
  288. linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4);
  289. mintab = stringwidth(font, "0");
  290. if((p = getenv("tabstop")) != nil)
  291. tabwidth = atoi(p)*stringwidth(font, "0");
  292. if(tabwidth == 0)
  293. tabwidth = 4*stringwidth(font, "0");
  294. tabflag = 1;
  295. }