13e12c5d1SDavid du Colombier /*
23e12c5d1SDavid du Colombier * mc - columnate
33e12c5d1SDavid du Colombier *
43e12c5d1SDavid du Colombier * mc[-][-LINEWIDTH][-t][file...]
53e12c5d1SDavid du Colombier * - causes break on colon
63e12c5d1SDavid du Colombier * -LINEWIDTH sets width of line in which to columnate(default 80)
73e12c5d1SDavid du Colombier * -t suppresses expanding multiple blanks into tabs
83e12c5d1SDavid du Colombier *
93e12c5d1SDavid du Colombier */
103e12c5d1SDavid du Colombier #include <u.h>
113e12c5d1SDavid du Colombier #include <libc.h>
127dd7cddfSDavid du Colombier #include <draw.h>
133e12c5d1SDavid du Colombier #include <bio.h>
143e12c5d1SDavid du Colombier
153e12c5d1SDavid du Colombier #define WIDTH 80
167dd7cddfSDavid du Colombier #define TAB 4
173e12c5d1SDavid du Colombier #define WORD_ALLOC_QUANTA 1024
183e12c5d1SDavid du Colombier #define ALLOC_QUANTA 4096
193e12c5d1SDavid du Colombier
203e12c5d1SDavid du Colombier int linewidth=WIDTH;
21*b8661318SDavid du Colombier int mintab=1;
223e12c5d1SDavid du Colombier int colonflag=0;
23219b2ee8SDavid du Colombier int tabflag=0; /* -t flag turned off forever */
243e12c5d1SDavid du Colombier Rune *cbuf, *cbufp;
253e12c5d1SDavid du Colombier Rune **word;
263e12c5d1SDavid du Colombier int maxwidth=0;
273e12c5d1SDavid du Colombier int nalloc=ALLOC_QUANTA;
283e12c5d1SDavid du Colombier int nwalloc=WORD_ALLOC_QUANTA;
293e12c5d1SDavid du Colombier int nchars=0;
303e12c5d1SDavid du Colombier int nwords=0;
31*b8661318SDavid du Colombier int tabwidth=0;
32*b8661318SDavid du Colombier Font *font;
333e12c5d1SDavid du Colombier Biobuf bin;
343e12c5d1SDavid du Colombier Biobuf bout;
353e12c5d1SDavid du Colombier
36bd389b36SDavid du Colombier void getwidth(void), readbuf(int), error(char *);
373e12c5d1SDavid du Colombier void scanwords(void), columnate(void), morechars(void);
38*b8661318SDavid du Colombier int wordwidth(Rune*, int);
39*b8661318SDavid du Colombier int nexttab(int);
403e12c5d1SDavid du Colombier
413e12c5d1SDavid du Colombier void
main(int argc,char * argv[])423e12c5d1SDavid du Colombier main(int argc, char *argv[])
433e12c5d1SDavid du Colombier {
443e12c5d1SDavid du Colombier int i;
453e12c5d1SDavid du Colombier int lineset;
46bd389b36SDavid du Colombier int ifd;
47bd389b36SDavid du Colombier
483e12c5d1SDavid du Colombier lineset = 0;
493e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE);
503e12c5d1SDavid du Colombier while(argc > 1 && argv[1][0] == '-'){
513e12c5d1SDavid du Colombier --argc; argv++;
523e12c5d1SDavid du Colombier switch(argv[0][1]){
533e12c5d1SDavid du Colombier case '\0':
543e12c5d1SDavid du Colombier colonflag = 1;
553e12c5d1SDavid du Colombier break;
563e12c5d1SDavid du Colombier case 't':
573e12c5d1SDavid du Colombier tabflag = 0;
583e12c5d1SDavid du Colombier break;
593e12c5d1SDavid du Colombier default:
603e12c5d1SDavid du Colombier linewidth = atoi(&argv[0][1]);
613e12c5d1SDavid du Colombier if(linewidth <= 1)
623e12c5d1SDavid du Colombier linewidth = WIDTH;
633e12c5d1SDavid du Colombier lineset = 1;
643e12c5d1SDavid du Colombier break;
653e12c5d1SDavid du Colombier }
663e12c5d1SDavid du Colombier }
67*b8661318SDavid du Colombier if(lineset == 0){
683e12c5d1SDavid du Colombier getwidth();
69*b8661318SDavid du Colombier if(linewidth <= 1){
70*b8661318SDavid du Colombier linewidth = WIDTH;
71*b8661318SDavid du Colombier font = nil;
72*b8661318SDavid du Colombier }
73*b8661318SDavid du Colombier }
74*b8661318SDavid du Colombier
753e12c5d1SDavid du Colombier cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
763e12c5d1SDavid du Colombier word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
773e12c5d1SDavid du Colombier if(word == 0 || cbuf == 0)
783e12c5d1SDavid du Colombier error("out of memory");
793e12c5d1SDavid du Colombier if(argc == 1)
80bd389b36SDavid du Colombier readbuf(0);
813e12c5d1SDavid du Colombier else{
823e12c5d1SDavid du Colombier for(i = 1; i < argc; i++){
83bd389b36SDavid du Colombier if((ifd = open(*++argv, OREAD)) == -1)
84bd389b36SDavid du Colombier fprint(2, "mc: can't open %s (%r)\n", *argv);
853e12c5d1SDavid du Colombier else{
86bd389b36SDavid du Colombier readbuf(ifd);
87bd389b36SDavid du Colombier Bflush(&bin);
88bd389b36SDavid du Colombier close(ifd);
893e12c5d1SDavid du Colombier }
903e12c5d1SDavid du Colombier }
913e12c5d1SDavid du Colombier }
923e12c5d1SDavid du Colombier columnate();
933e12c5d1SDavid du Colombier exits(0);
943e12c5d1SDavid du Colombier }
953e12c5d1SDavid du Colombier void
error(char * s)963e12c5d1SDavid du Colombier error(char *s)
973e12c5d1SDavid du Colombier {
983e12c5d1SDavid du Colombier fprint(2, "mc: %s\n", s);
993e12c5d1SDavid du Colombier exits(s);
1003e12c5d1SDavid du Colombier }
1013e12c5d1SDavid du Colombier void
readbuf(int fd)102bd389b36SDavid du Colombier readbuf(int fd)
1033e12c5d1SDavid du Colombier {
1043e12c5d1SDavid du Colombier int lastwascolon = 0;
1053e12c5d1SDavid du Colombier long c;
1063e12c5d1SDavid du Colombier int linesiz = 0;
1073e12c5d1SDavid du Colombier
108bd389b36SDavid du Colombier Binit(&bin, fd, OREAD);
1093e12c5d1SDavid du Colombier do{
1103e12c5d1SDavid du Colombier if(nchars++ >= nalloc)
1113e12c5d1SDavid du Colombier morechars();
1123e12c5d1SDavid du Colombier *cbufp++ = c = Bgetrune(&bin);
1133e12c5d1SDavid du Colombier linesiz++;
1143e12c5d1SDavid du Colombier if(c == '\t') {
1153e12c5d1SDavid du Colombier cbufp[-1] = L' ';
1167dd7cddfSDavid du Colombier while(linesiz%TAB != 0) {
1173e12c5d1SDavid du Colombier if(nchars++ >= nalloc)
1183e12c5d1SDavid du Colombier morechars();
1193e12c5d1SDavid du Colombier *cbufp++ = L' ';
1203e12c5d1SDavid du Colombier linesiz++;
1213e12c5d1SDavid du Colombier }
1223e12c5d1SDavid du Colombier }
1233e12c5d1SDavid du Colombier if(colonflag && c == ':')
1243e12c5d1SDavid du Colombier lastwascolon++;
1253e12c5d1SDavid du Colombier else if(lastwascolon){
1263e12c5d1SDavid du Colombier if(c == '\n'){
1273e12c5d1SDavid du Colombier --nchars; /* skip newline */
1283e12c5d1SDavid du Colombier *cbufp = L'\0';
1293e12c5d1SDavid du Colombier while(nchars > 0 && cbuf[--nchars] != '\n')
1303e12c5d1SDavid du Colombier ;
1313e12c5d1SDavid du Colombier if(nchars)
1323e12c5d1SDavid du Colombier nchars++;
1333e12c5d1SDavid du Colombier columnate();
1343e12c5d1SDavid du Colombier if (nchars)
1357dd7cddfSDavid du Colombier Bputc(&bout, '\n');
1363e12c5d1SDavid du Colombier Bprint(&bout, "%S", cbuf+nchars);
1373e12c5d1SDavid du Colombier nchars = 0;
1383e12c5d1SDavid du Colombier cbufp = cbuf;
1393e12c5d1SDavid du Colombier }
1403e12c5d1SDavid du Colombier lastwascolon = 0;
1413e12c5d1SDavid du Colombier }
1423e12c5d1SDavid du Colombier if(c == '\n')
1433e12c5d1SDavid du Colombier linesiz = 0;
1443e12c5d1SDavid du Colombier }while(c >= 0);
1453e12c5d1SDavid du Colombier }
1463e12c5d1SDavid du Colombier void
scanwords(void)1473e12c5d1SDavid du Colombier scanwords(void)
1483e12c5d1SDavid du Colombier {
1493e12c5d1SDavid du Colombier Rune *p, *q;
150*b8661318SDavid du Colombier int i, w;
1513e12c5d1SDavid du Colombier
1523e12c5d1SDavid du Colombier nwords=0;
1533e12c5d1SDavid du Colombier maxwidth=0;
1543e12c5d1SDavid du Colombier for(p = q = cbuf, i = 0; i < nchars; i++){
1553e12c5d1SDavid du Colombier if(*p++ == L'\n'){
1563e12c5d1SDavid du Colombier if(nwords >= nwalloc){
1573e12c5d1SDavid du Colombier nwalloc += WORD_ALLOC_QUANTA;
1583e12c5d1SDavid du Colombier if((word = realloc(word, nwalloc*sizeof(*word)))==0)
1593e12c5d1SDavid du Colombier error("out of memory");
1603e12c5d1SDavid du Colombier }
1613e12c5d1SDavid du Colombier word[nwords++] = q;
1623e12c5d1SDavid du Colombier p[-1] = L'\0';
163*b8661318SDavid du Colombier w = wordwidth(q, p-q-1);
164*b8661318SDavid du Colombier if(w > maxwidth)
165*b8661318SDavid du Colombier maxwidth = w;
1663e12c5d1SDavid du Colombier q = p;
1673e12c5d1SDavid du Colombier }
1683e12c5d1SDavid du Colombier }
1693e12c5d1SDavid du Colombier }
1703e12c5d1SDavid du Colombier
1713e12c5d1SDavid du Colombier void
columnate(void)1723e12c5d1SDavid du Colombier columnate(void)
1733e12c5d1SDavid du Colombier {
1743e12c5d1SDavid du Colombier int i, j;
1753e12c5d1SDavid du Colombier int words_per_line;
1763e12c5d1SDavid du Colombier int nlines;
1773e12c5d1SDavid du Colombier int col;
1783e12c5d1SDavid du Colombier int endcol;
1793e12c5d1SDavid du Colombier
1803e12c5d1SDavid du Colombier
1813e12c5d1SDavid du Colombier scanwords();
1823e12c5d1SDavid du Colombier if(nwords==0)
1833e12c5d1SDavid du Colombier return;
184*b8661318SDavid du Colombier maxwidth = nexttab(maxwidth+mintab-1);
1853e12c5d1SDavid du Colombier words_per_line = linewidth/maxwidth;
1863e12c5d1SDavid du Colombier if(words_per_line <= 0)
1873e12c5d1SDavid du Colombier words_per_line = 1;
1883e12c5d1SDavid du Colombier nlines=(nwords+words_per_line-1)/words_per_line;
1893e12c5d1SDavid du Colombier for(i = 0; i < nlines; i++){
1903e12c5d1SDavid du Colombier col = endcol = 0;
1913e12c5d1SDavid du Colombier for(j = i; j < nwords; j += nlines){
1923e12c5d1SDavid du Colombier endcol += maxwidth;
1933e12c5d1SDavid du Colombier Bprint(&bout, "%S", word[j]);
194*b8661318SDavid du Colombier col += wordwidth(word[j], runestrlen(word[j]));
1953e12c5d1SDavid du Colombier if(j+nlines < nwords){
1963e12c5d1SDavid du Colombier if(tabflag) {
197*b8661318SDavid du Colombier while(col < endcol){
1987dd7cddfSDavid du Colombier Bputc(&bout, '\t');
199*b8661318SDavid du Colombier col = nexttab(col);
2003e12c5d1SDavid du Colombier }
201*b8661318SDavid du Colombier }else{
2023e12c5d1SDavid du Colombier while(col < endcol){
2037dd7cddfSDavid du Colombier Bputc(&bout, ' ');
2043e12c5d1SDavid du Colombier col++;
2053e12c5d1SDavid du Colombier }
2063e12c5d1SDavid du Colombier }
2073e12c5d1SDavid du Colombier }
208*b8661318SDavid du Colombier }
2097dd7cddfSDavid du Colombier Bputc(&bout, '\n');
2103e12c5d1SDavid du Colombier }
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier
213*b8661318SDavid du Colombier int
wordwidth(Rune * w,int nw)214*b8661318SDavid du Colombier wordwidth(Rune *w, int nw)
215*b8661318SDavid du Colombier {
216*b8661318SDavid du Colombier if(font)
217*b8661318SDavid du Colombier return runestringnwidth(font, w, nw);
218*b8661318SDavid du Colombier return nw;
219*b8661318SDavid du Colombier }
220*b8661318SDavid du Colombier
221*b8661318SDavid du Colombier int
nexttab(int col)222*b8661318SDavid du Colombier nexttab(int col)
223*b8661318SDavid du Colombier {
224*b8661318SDavid du Colombier if(tabwidth){
225*b8661318SDavid du Colombier col += tabwidth;
226*b8661318SDavid du Colombier col -= col%tabwidth;
227*b8661318SDavid du Colombier return col;
228*b8661318SDavid du Colombier }
229*b8661318SDavid du Colombier return col+1;
230*b8661318SDavid du Colombier }
231*b8661318SDavid du Colombier
2323e12c5d1SDavid du Colombier void
morechars(void)2333e12c5d1SDavid du Colombier morechars(void)
2343e12c5d1SDavid du Colombier {
2353e12c5d1SDavid du Colombier nalloc += ALLOC_QUANTA;
2363e12c5d1SDavid du Colombier if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
2373e12c5d1SDavid du Colombier error("out of memory");
2383e12c5d1SDavid du Colombier cbufp = cbuf+nchars-1;
2393e12c5d1SDavid du Colombier }
2403e12c5d1SDavid du Colombier
2417dd7cddfSDavid du Colombier /*
2427dd7cddfSDavid du Colombier * These routines discover the width of the display.
2437dd7cddfSDavid du Colombier * It takes some work. If we do the easy calls to the
2447dd7cddfSDavid du Colombier * draw library, the screen flashes due to repainting
2457dd7cddfSDavid du Colombier * when mc exits.
2467dd7cddfSDavid du Colombier */
2477dd7cddfSDavid du Colombier
2487dd7cddfSDavid du Colombier jmp_buf drawjmp;
2497dd7cddfSDavid du Colombier
2507dd7cddfSDavid du Colombier void
terror(Display *,char *)2517dd7cddfSDavid du Colombier terror(Display*, char*)
2527dd7cddfSDavid du Colombier {
2537dd7cddfSDavid du Colombier longjmp(drawjmp, 1);
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier
2563e12c5d1SDavid du Colombier void
getwidth(void)2573e12c5d1SDavid du Colombier getwidth(void)
2583e12c5d1SDavid du Colombier {
259*b8661318SDavid du Colombier int n, fd;
260*b8661318SDavid du Colombier char buf[128], *f[10], *p;
2613e12c5d1SDavid du Colombier
262*b8661318SDavid du Colombier if(access("/dev/acme", OREAD) >= 0){
263*b8661318SDavid du Colombier if((fd = open("/dev/acme/ctl", OREAD)) < 0)
2647dd7cddfSDavid du Colombier return;
265*b8661318SDavid du Colombier n = read(fd, buf, sizeof buf-1);
2667dd7cddfSDavid du Colombier close(fd);
267*b8661318SDavid du Colombier if(n <= 0)
2687dd7cddfSDavid du Colombier return;
269*b8661318SDavid du Colombier buf[n] = 0;
270*b8661318SDavid du Colombier n = tokenize(buf, f, nelem(f));
271*b8661318SDavid du Colombier if(n < 7)
2727dd7cddfSDavid du Colombier return;
273*b8661318SDavid du Colombier if((font = openfont(nil, f[6])) == nil)
2747dd7cddfSDavid du Colombier return;
275*b8661318SDavid du Colombier if(n >= 8)
276*b8661318SDavid du Colombier tabwidth = atoi(f[7]);
277*b8661318SDavid du Colombier else
278*b8661318SDavid du Colombier tabwidth = 4*stringwidth(font, "0");
279*b8661318SDavid du Colombier mintab = stringwidth(font, "0");
280*b8661318SDavid du Colombier linewidth = atoi(f[5]);
281*b8661318SDavid du Colombier tabflag = 1;
282*b8661318SDavid du Colombier return;
283*b8661318SDavid du Colombier }
284*b8661318SDavid du Colombier
285*b8661318SDavid du Colombier if((p = getenv("font")) == nil)
286*b8661318SDavid du Colombier return;
287*b8661318SDavid du Colombier if((font = openfont(nil, p)) == nil)
288*b8661318SDavid du Colombier return;
289*b8661318SDavid du Colombier if((fd = open("/dev/window", OREAD)) < 0){
290*b8661318SDavid du Colombier font = nil;
291*b8661318SDavid du Colombier return;
292*b8661318SDavid du Colombier }
293*b8661318SDavid du Colombier n = read(fd, buf, 5*12);
294*b8661318SDavid du Colombier close(fd);
295*b8661318SDavid du Colombier if(n < 5*12){
296*b8661318SDavid du Colombier font = nil;
297*b8661318SDavid du Colombier return;
298*b8661318SDavid du Colombier }
299*b8661318SDavid du Colombier buf[n] = 0;
300*b8661318SDavid du Colombier
3013e12c5d1SDavid du Colombier /* window stucture:
3023e12c5d1SDavid du Colombier 4 bit left edge
3033e12c5d1SDavid du Colombier 1 bit gap
3043e12c5d1SDavid du Colombier 12 bit scrollbar
3053e12c5d1SDavid du Colombier 4 bit gap
3063e12c5d1SDavid du Colombier text
3073e12c5d1SDavid du Colombier 4 bit right edge
3083e12c5d1SDavid du Colombier */
309*b8661318SDavid du Colombier linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4);
310*b8661318SDavid du Colombier mintab = stringwidth(font, "0");
311*b8661318SDavid du Colombier if((p = getenv("tabstop")) != nil)
312*b8661318SDavid du Colombier tabwidth = atoi(p)*stringwidth(font, "0");
313*b8661318SDavid du Colombier if(tabwidth == 0)
314*b8661318SDavid du Colombier tabwidth = 4*stringwidth(font, "0");
315*b8661318SDavid du Colombier tabflag = 1;
3163e12c5d1SDavid du Colombier }
317