Plan 9 from Bell Labs’s /usr/web/sources/contrib/anothy/src/cmd/pfmt.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include	<draw.h>

/*
 * block up paragraphs, possibly with indentation
 */

int extraindent = 0;		/* how many spaces to indent all lines */
int indent = 0;			/* current value of indent, before extra indent */
int length = 70;		/* how many columns per output line */
int join = 1;			/* can lines be joined? */
int maxtab = 8;
char *quote;
Biobuf bin;
Biobuf bout;

int linewidth=500;
int mintab=1;
int tabwidth=0;

void getwidth(void), readbuf(int), error(char *);

typedef struct Word Word;
struct Word{
	int	bol;
	int	indent;
	int	quotes;
	char	text[1];
};

int
indentof(char **linep)
{
	int i, ind;
	char *line;

	ind = 0;
	line = *linep;
	for(i=0; line[i]; i++)
		switch(line[i]){
		default:
			*linep = line;
			return ind;
		case ' ':
			ind++;
			break;
		case '\t':
			ind += maxtab;
			ind -= ind%maxtab;
			break;
		}
			
	/* plain white space doesn't change the indent */
	*linep = "";
	return indent;
}

int
quoteof(char **p)
{
	char *s;
	int i, len;

	if(quote == nil)
		return 0;
	s = *p;
	len = strlen(quote);
	for(i = 0;; i++){
		if(strncmp(s, quote, len) != 0){
			*p = s;
			return i;
		}
		s += len;
	}
}
		
Word**
addword(Word **words, int *nwordp, char *s, int l, int indent, int bol, int quotes)
{
	Word *w;

	w = malloc(sizeof(Word)+l+1);
	memmove(w->text, s, l);
	w->text[l] = '\0';
	w->indent = indent;
	w->bol = bol;
	w->quotes = quotes;
	words = realloc(words, (*nwordp+1)*sizeof(Word*));
	words[(*nwordp)++] = w;
	return words;
}

Word**
parseline(char *line, Word **words, int *nwordp)
{
	int l, bol, quotes;

	quotes = quoteof(&line);
	indent = indentof(&line);
	bol = 1;
	for(;;){
		/* find next word */
		while(*line==' ' || *line=='\t')
			line++;
		if(*line == '\0'){
			if(bol)
				return addword(words, nwordp, "", 0, -1, bol, quotes);
			break;
		}
		/* how long is this word? */
		for(l=0; line[l]; l++)
			if(line[l]==' ' || line[l]=='\t')
				break;
		words = addword(words, nwordp, line, l, indent, bol, quotes);
		bol = 0;
		line += l;
	}
	return words;
}

void
printindent(int w)
{
	while(w >= maxtab){
		Bputc(&bout, '\t');
		w -= (maxtab*stringwidth(font, "\t"));
	}
	while(w > 0){
		Bputc(&bout, ' ');
		w -= (maxtab*stringwidth(font, " "));
	}
}

int
quoteout(Word *line)
{
	int i;

	if(quote == nil)
		return 0;
	for(i = 0; i < line->quotes; i++)
		Bprint(&bout, "%s", quote);
	return line->quotes * strlen(quote);
}

void
printwords(Word **w, int nw)
{
	int i, j, n, pos, fold;
	Word *line;

	/* one output line per loop */
	line = *w;
	fold = 0;
	for(i=0; i<nw; ){
		pos = 0;

		/* if it's a blank line, print it */
		if(w[i]->indent == -1){
			quoteout(line);
			Bputc(&bout, '\n');
			if(++i == nw)	/* out of words */
				break;
		}
		if(!fold && w[i]->bol)
			line = w[i];
		fold = 0;
		/* emit leading indent */
		pos += quoteout(line);
		j = (extraindent+w[i]->indent)*stringwidth(font, "0");
		pos += j;
		printindent(j);
		/* emit words until overflow; always emit at least one word */
		for(n=0;; n++){
			Bprint(&bout, "%s", w[i]->text);
			pos += stringwidth(font, w[i]->text);
			if(++i == nw)
				break;	/* out of words */
			if(w[i]->bol && line != w[i] && w[i]->quotes != line->quotes)
				break;
			if(w[i]->indent != w[i-1]->indent)
				break;	/* indent change */
			if(pos+stringwidth(font, w[i]->text) > (extraindent*stringwidth(font, " "))+linewidth){
				fold = 1;
				break;	/* fold line */
			}
			if(!join && w[i]->bol)
				break;
			Bputc(&bout, ' ');	/* emit space; another word will follow */
			pos += stringwidth(font, " ");
		}
		/* emit newline */
		Bputc(&bout, '\n');
	}
}

void
fmt(void)
{
	char *s;
	int i, nw;
	Word **w;

	nw = 0;
	w = nil;
	while((s = Brdstr(&bin, '\n', 1)) != nil){
		w = parseline(s, w, &nw);
		free(s);
	}
	printwords(w, nw);
	for(i=0; i<nw; i++)
		free(w[i]);
	free(w);
}

void	fmt(void);

void
usage(void)
{
	fprint(2, "usage: %s [-j] [-i indent] [-l length] [-w width] [file...]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int i, f;
	char *s, *err;

	getwidth();

	ARGBEGIN{
	case 'i':
		extraindent = atoi(EARGF(usage()));
		break;
	case 'j':
		join = 0;
		break;
	case 'w':
		linewidth = atoi(EARGF(usage()));
		break;
	case 'l':
		length = atoi(EARGF(usage()));
		break;
	case 'q':
		quote = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND

	if(length <= indent){
		fprint(2, "%s: line length<=indentation\n", argv0);
		exits("length");
	}

	s=getenv("tabstop");
	if(s!=nil && atoi(s)>0)
		maxtab=atoi(s);
	err = nil;
	Binit(&bout, 1, OWRITE);
	if(argc <= 0){
		Binit(&bin, 0, OREAD);
		fmt();
	}else{
		for(i=0; i<argc; i++){
			f = open(argv[i], OREAD);
			if(f < 0){
				fprint(2, "%s: can't open %s: %r\n", argv0, argv[i]);
				err = "open";
			}else{
				Binit(&bin, f, OREAD);
				fmt();
				Bterm(&bin);
				if(i != argc-1)
					Bputc(&bout, '\n');
			}
		}
	}
	exits(err);
}


void
getwidth(void)
{
	int n, fd;
	char buf[128], *f[10], *p;

	if((p = getenv("winid")) != nil){
		p=smprint("/mnt/wsys/%s/ctl", p);
		if(access(p, OREAD) >= 0){
			if((fd = open(p, OREAD)) < 0)
				return;
			n = read(fd, buf, sizeof buf-1);
			close(fd);
			if(n <= 0)
				return;
			buf[n] = 0;
			n = tokenize(buf, f, nelem(f));
			if(n < 7)
				return;
			if((font = openfont(nil, f[6])) == nil)
				return;
			if(n >= 8)
				tabwidth = atoi(f[7]);
			else
				tabwidth = 4*stringwidth(font, "0");
			mintab = stringwidth(font, "0");
			linewidth = atoi(f[5]);
			return;
		}
	}

	if((p = getenv("font")) == nil)
		return;
	if((font = openfont(nil, p)) == nil)
		return;
	if((fd = open("/dev/window", OREAD)) < 0){
		font = nil;
		return;
	}
	n = read(fd, buf, 5*12);
	close(fd);
	if(n < 5*12){
		font = nil;
		return;
	}
	buf[n] = 0;
	
	/* linewidth is window width in pixels, minus window structure & pad.
	* We assume the structure of windows in rio:
	*	4 bit left edge
	*	1 bit gap
	*	12 bit scrollbar
	*	4 bit gap
	*	text
	*	4 bit right edge
	* Frustrating that nile's different, but whatever.
	*/
	linewidth = atoi(buf+3*12) - atoi(buf+1*12) - (4+1+12+4+4) - 4;
	mintab = stringwidth(font, "0");
	if((p = getenv("tabstop")) != nil)
		tabwidth = atoi(p)*stringwidth(font, "0");
	if(tabwidth == 0)
		tabwidth = 4*stringwidth(font, "0");
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.