blob: 3f9c21fbf7b2856e7f1571a78cffc56e1abb5b3e [file] [log] [blame]
/*
* mc - columnate
*
* mc[-][-LINEWIDTH][-t][file...]
* - causes break on colon
* -LINEWIDTH sets width of line in which to columnate(default 80)
* -t suppresses expanding multiple blanks into tabs
*
*/
#include <u.h>
#include <sys/ioctl.h>
#include <sys/termios.h>
#include <libc.h>
#include <draw.h>
#include <bio.h>
#include <fcall.h>
#include <9pclient.h>
#include <thread.h>
#define WIDTH 80
#define TAB 4
#define WORD_ALLOC_QUANTA 1024
#define ALLOC_QUANTA 4096
int wordsize(Rune*, int);
int nexttab(int);
int tabwid;
int mintab = 1;
int linewidth=WIDTH;
int colonflag=0;
int tabflag=0; /* -t flag turned off forever, except in acme */
Rune *cbuf, *cbufp;
Rune **word;
int maxwidth=0;
int nalloc=ALLOC_QUANTA;
int nwalloc=WORD_ALLOC_QUANTA;
int nchars=0;
int nwords=0;
Biobuf bin;
Biobuf bout;
void getwidth(void), readbuf(int), error(char *);
void scanwords(void), columnate(void), morechars(void);
void
threadmain(int argc, char *argv[])
{
int i;
int lineset;
int ifd;
lineset = 0;
Binit(&bout, 1, OWRITE);
while(argc > 1 && argv[1][0] == '-'){
--argc; argv++;
switch(argv[0][1]){
case '\0':
colonflag = 1;
break;
case 't':
tabflag = 0;
break;
default:
linewidth = atoi(&argv[0][1]);
if(linewidth <= 1)
linewidth = WIDTH;
lineset = 1;
break;
}
}
if(lineset == 0)
getwidth();
cbuf = cbufp = malloc(ALLOC_QUANTA*(sizeof *cbuf));
word = malloc(WORD_ALLOC_QUANTA*(sizeof *word));
if(word == 0 || cbuf == 0)
error("out of memory");
if(argc == 1)
readbuf(0);
else{
for(i = 1; i < argc; i++){
if((ifd = open(*++argv, OREAD)) == -1)
fprint(2, "mc: can't open %s (%r)\n", *argv);
else{
readbuf(ifd);
Bflush(&bin);
close(ifd);
}
}
}
columnate();
Bflush(&bout);
threadexitsall(0);
}
void
error(char *s)
{
fprint(2, "mc: %s\n", s);
threadexitsall(s);
}
void
readbuf(int fd)
{
int lastwascolon = 0;
long c;
int linesiz = 0;
Binit(&bin, fd, OREAD);
do{
if(nchars++ >= nalloc)
morechars();
*cbufp++ = c = Bgetrune(&bin);
linesiz++;
if(c == '\t') {
cbufp[-1] = L' ';
while(linesiz%TAB != 0) {
if(nchars++ >= nalloc)
morechars();
*cbufp++ = L' ';
linesiz++;
}
}
if(colonflag && c == ':')
lastwascolon++;
else if(lastwascolon){
if(c == '\n'){
--nchars; /* skip newline */
*cbufp = L'\0';
while(nchars > 0 && cbuf[--nchars] != '\n')
;
if(nchars)
nchars++;
columnate();
if (nchars)
Bputc(&bout, '\n');
Bprint(&bout, "%S", cbuf+nchars);
nchars = 0;
cbufp = cbuf;
}
lastwascolon = 0;
}
if(c == '\n')
linesiz = 0;
}while(c >= 0);
}
void
scanwords(void)
{
Rune *p, *q;
int i, w;
nwords=0;
maxwidth=0;
for(p = q = cbuf, i = 0; i < nchars; i++){
if(*p++ == L'\n'){
if(nwords >= nwalloc){
nwalloc += WORD_ALLOC_QUANTA;
if((word = realloc(word, nwalloc*sizeof(*word)))==0)
error("out of memory");
}
word[nwords++] = q;
p[-1] = L'\0';
w = wordsize(q, p-q-1);
if(w > maxwidth)
maxwidth = w;
q = p;
}
}
}
void
columnate(void)
{
int i, j;
int words_per_line;
int nlines;
int col;
int endcol;
scanwords();
if(nwords==0)
return;
maxwidth = nexttab(maxwidth+mintab-1);
words_per_line = linewidth/maxwidth;
if(words_per_line <= 0)
words_per_line = 1;
nlines=(nwords+words_per_line-1)/words_per_line;
for(i = 0; i < nlines; i++){
col = endcol = 0;
for(j = i; j < nwords; j += nlines){
endcol += maxwidth;
Bprint(&bout, "%S", word[j]);
col += wordsize(word[j], runestrlen(word[j]));
if(j+nlines < nwords){
if(tabflag) {
while(col < endcol){
Bputc(&bout, '\t');
col = nexttab(col);
}
}else{
while(col < endcol){
Bputc(&bout, ' ');
col++;
}
}
}
}
Bputc(&bout, '\n');
}
}
int
wordsize(Rune *w, int nw)
{
if(nw < 0)
abort();
if(font)
return runestringnwidth(font, w, nw);
return nw;
}
int
nexttab(int col)
{
if(tabwid){
col += tabwid;
col -= col%tabwid;
return col;
}
return col+1;
}
void
morechars(void)
{
nalloc += ALLOC_QUANTA;
if((cbuf = realloc(cbuf, nalloc*sizeof(*cbuf))) == 0)
error("out of memory");
cbufp = cbuf+nchars-1;
}
/*
* These routines discover the width of the display.
* It takes some work. If we do the easy calls to the
* draw library, the screen flashes due to repainting
* when mc exits.
*/
int
windowrect(struct winsize *ws)
{
int tty;
if((tty = open("/dev/tty", OWRITE)) < 0)
tty = 1;
if(ioctl(tty, TIOCGWINSZ, ws) < 0){
if(tty != 1)
close(tty);
return -1;
}
if(tty != 1)
close(tty);
return 0;
}
void
getwidth(void)
{
CFsys *fs;
char buf[500], *p, *f[10];
int fd, n, nf;
struct winsize ws;
if((p = getenv("winid")) != nil){
fs = nsmount("acme", "");
if(fs == nil)
return;
snprint(buf, sizeof buf, "acme/%d/ctl", atoi(p));
if((fd = fsopenfd(fs, buf, OREAD)) < 0)
return;
if((n=readn(fd, buf, sizeof buf-1)) <= 0)
return;
buf[n] = 0;
if((nf=tokenize(buf, f, nelem(f))) < 7)
return;
tabwid = 0;
if(nf >= 8 && (tabwid = atoi(f[7])) == 0)
return;
if((font = openfont(nil, f[6])) == nil)
return;
mintab = stringwidth(font, "0");
if(tabwid == 0)
tabwid = mintab*4;
linewidth = atoi(f[5]);
tabflag = 1;
return;
}
if((p = getenv("termprog")) != nil && strcmp(p, "9term") == 0)
if((p = getenv("font")) != nil)
font = openfont(nil, p);
if(windowrect(&ws) < 0)
return;
if(ws.ws_xpixel == 0)
font = nil;
if(font){
mintab = stringwidth(font, "0");
if((p = getenv("tabstop")) != nil)
tabwid = atoi(p)*mintab;
else
tabwid = 4*mintab;
tabflag = 1;
linewidth = ws.ws_xpixel;
}else
linewidth = ws.ws_col;
}