plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

fmt.c (3820B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <ctype.h>
      5 
      6 /*
      7  * block up paragraphs, possibly with indentation
      8  */
      9 
     10 int extraindent = 0;		/* how many spaces to indent all lines */
     11 int indent = 0;			/* current value of indent, before extra indent */
     12 int length = 70;		/* how many columns per output line */
     13 int join = 1;			/* can lines be joined? */
     14 int maxtab = 8;
     15 Biobuf bin;
     16 Biobuf bout;
     17 
     18 typedef struct Word Word;
     19 struct Word{
     20 	int	bol;
     21 	int	indent;
     22 	char	text[1];
     23 };
     24 
     25 void	fmt(void);
     26 
     27 void
     28 usage(void)
     29 {
     30 	fprint(2, "usage: %s [-j] [-i indent] [-l length] [file...]\n", argv0);
     31 	exits("usage");
     32 }
     33 
     34 void
     35 main(int argc, char **argv)
     36 {
     37 	int i, f;
     38 	char *s, *err;
     39 
     40 	ARGBEGIN{
     41 	case 'i':
     42 		extraindent = atoi(EARGF(usage()));
     43 		break;
     44 	case 'j':
     45 		join = 0;
     46 		break;
     47 	case 'w':
     48 	case 'l':
     49 		length = atoi(EARGF(usage()));
     50 		break;
     51 	default:
     52 		usage();
     53 	}ARGEND
     54 
     55 	if(length <= indent){
     56 		fprint(2, "%s: line length<=indentation\n", argv0);
     57 		exits("length");
     58 	}
     59 
     60 	s=getenv("tabstop");
     61 	if(s!=nil && atoi(s)>0)
     62 		maxtab=atoi(s);
     63 	err = nil;
     64 	Binit(&bout, 1, OWRITE);
     65 	if(argc <= 0){
     66 		Binit(&bin, 0, OREAD);
     67 		fmt();
     68 	}else{
     69 		for(i=0; i<argc; i++){
     70 			f = open(argv[i], OREAD);
     71 			if(f < 0){
     72 				fprint(2, "%s: can't open %s: %r\n", argv0, argv[i]);
     73 				err = "open";
     74 			}else{
     75 				Binit(&bin, f, OREAD);
     76 				fmt();
     77 				Bterm(&bin);
     78 				if(i != argc-1)
     79 					Bputc(&bout, '\n');
     80 			}
     81 		}
     82 	}
     83 	exits(err);
     84 }
     85 
     86 int
     87 indentof(char **linep)
     88 {
     89 	int i, ind;
     90 	char *line;
     91 
     92 	ind = 0;
     93 	line = *linep;
     94 	for(i=0; line[i]; i++)
     95 		switch(line[i]){
     96 		default:
     97 			*linep = line;
     98 			return ind;
     99 		case ' ':
    100 			ind++;
    101 			break;
    102 		case '\t':
    103 			ind += maxtab;
    104 			ind -= ind%maxtab;
    105 			break;
    106 		}
    107 
    108 	/* plain white space doesn't change the indent */
    109 	*linep = "";
    110 	return indent;
    111 }
    112 
    113 Word**
    114 addword(Word **words, int *nwordp, char *s, int l, int indent, int bol)
    115 {
    116 	Word *w;
    117 
    118 	w = malloc(sizeof(Word)+l+1);
    119 	memmove(w->text, s, l);
    120 	w->text[l] = '\0';
    121 	w->indent = indent;
    122 	w->bol = bol;
    123 	words = realloc(words, (*nwordp+1)*sizeof(Word*));
    124 	words[(*nwordp)++] = w;
    125 	return words;
    126 }
    127 
    128 Word**
    129 parseline(char *line, Word **words, int *nwordp)
    130 {
    131 	int ind, l, bol;
    132 
    133 	ind = indentof(&line);
    134 	indent = ind;
    135 	bol = 1;
    136 	for(;;){
    137 		/* find next word */
    138 		while(*line==' ' || *line=='\t')
    139 			line++;
    140 		if(*line == '\0'){
    141 			if(bol)
    142 				return addword(words, nwordp, "", 0, -1, bol);
    143 			break;
    144 		}
    145 		/* how long is this word? */
    146 		for(l=0; line[l]; l++)
    147 			if(line[l]==' ' || line[l]=='\t')
    148 				break;
    149 		words = addword(words, nwordp, line, l, indent, bol);
    150 		bol = 0;
    151 		line += l;
    152 	}
    153 	return words;
    154 }
    155 
    156 void
    157 printindent(int w)
    158 {
    159 	while(w >= maxtab){
    160 		Bputc(&bout, '\t');
    161 		w -= maxtab;
    162 	}
    163 	while(w > 0){
    164 		Bputc(&bout, ' ');
    165 		w--;
    166 	}
    167 }
    168 
    169 void
    170 printwords(Word **w, int nw)
    171 {
    172 	int i, j, n, col, nsp;
    173 
    174 	/* one output line per loop */
    175 	for(i=0; i<nw; ){
    176 		/* if it's a blank line, print it */
    177 		if(w[i]->indent == -1){
    178 			Bputc(&bout, '\n');
    179 			if(++i == nw)	/* out of words */
    180 				break;
    181 		}
    182 		/* emit leading indent */
    183 		col = extraindent+w[i]->indent;
    184 		printindent(col);
    185 		/* emit words until overflow; always emit at least one word */
    186 		for(n=0;; n++){
    187 			Bprint(&bout, "%s", w[i]->text);
    188 			col += utflen(w[i]->text);
    189 			if(++i == nw)
    190 				break;	/* out of words */
    191 			if(w[i]->indent != w[i-1]->indent)
    192 				break;	/* indent change */
    193 			nsp = 1;
    194 			if(col+nsp+utflen(w[i]->text) > extraindent+length)
    195 				break;	/* fold line */
    196 			if(!join && w[i]->bol)
    197 				break;
    198 			for(j=0; j<nsp; j++)
    199 				Bputc(&bout, ' ');	/* emit space; another word will follow */
    200 			col += nsp;
    201 		}
    202 		/* emit newline */
    203 		Bputc(&bout, '\n');
    204 	}
    205 }
    206 
    207 void
    208 fmt(void)
    209 {
    210 	char *s;
    211 	int i, nw;
    212 	Word **w;
    213 
    214 	nw = 0;
    215 	w = nil;
    216 	while((s = Brdstr(&bin, '\n', 1)) != nil){
    217 		w = parseline(s, w, &nw);
    218 		free(s);
    219 	}
    220 	printwords(w, nw);
    221 	for(i=0; i<nw; i++)
    222 		free(w[i]);
    223 	free(w);
    224 }