9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

fmt.c (3745B)


      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 
     16 Biobuf bin;
     17 Biobuf bout;
     18 
     19 typedef struct Word Word;
     20 struct Word
     21 {
     22 	Word	*next;
     23 
     24 	int	indent;
     25 	int	length;
     26 	char	bol;
     27 	char	text[];
     28 };
     29 
     30 void	fmt(void);
     31 
     32 void
     33 usage(void)
     34 {
     35 	fprint(2, "usage: %s [-j] [-i indent] [-l length] [file...]\n", argv0);
     36 	exits("usage");
     37 }
     38 
     39 void
     40 main(int argc, char **argv)
     41 {
     42 	int i, f;
     43 	char *s, *err;
     44 
     45 	ARGBEGIN{
     46 	case 'i':
     47 		extraindent = atoi(EARGF(usage()));
     48 		break;
     49 	case 'j':
     50 		join = 0;
     51 		break;
     52 	case 'w':
     53 	case 'l':
     54 		length = atoi(EARGF(usage()));
     55 		break;
     56 	default:
     57 		usage();
     58 	}ARGEND
     59 
     60 	if(length <= indent){
     61 		fprint(2, "%s: line length<=indentation\n", argv0);
     62 		exits("length");
     63 	}
     64 
     65 	s=getenv("tabstop");
     66 	if(s!=nil && atoi(s)>0)
     67 		maxtab=atoi(s);
     68 	err = nil;
     69 	Binit(&bout, 1, OWRITE);
     70 	if(argc <= 0){
     71 		Binit(&bin, 0, OREAD);
     72 		fmt();
     73 	}else{
     74 		for(i=0; i<argc; i++){
     75 			f = open(argv[i], OREAD);
     76 			if(f < 0){
     77 				fprint(2, "%s: can't open %s: %r\n", argv0, argv[i]);
     78 				err = "open";
     79 			}else{
     80 				Binit(&bin, f, OREAD);
     81 				fmt();
     82 				Bterm(&bin);
     83 				if(i != argc-1)
     84 					Bputc(&bout, '\n');
     85 			}
     86 		}
     87 	}
     88 	exits(err);
     89 }
     90 
     91 int
     92 indentof(char *s)
     93 {
     94 	int ind;
     95 
     96 	ind = 0;
     97 	for(; *s != '\0'; s++)
     98 		switch(*s){
     99 		default:
    100 			return ind;
    101 		case ' ':
    102 			ind++;
    103 			break;
    104 		case '\t':
    105 			ind += maxtab;
    106 			ind -= ind%maxtab;
    107 			break;
    108 		}
    109 
    110 	/* plain white space doesn't change the indent */
    111 	return indent;
    112 }
    113 
    114 Word*
    115 newword(char *s, int n, int ind, int bol)
    116 {
    117 	Word *w;
    118 
    119 	w = malloc(sizeof(Word) + n+1);
    120 	w->next = nil;
    121 	w->indent = ind;
    122 	w->bol = bol;
    123 	memmove(w->text, s, n);
    124 	w->text[n] = 0;
    125 	w->length = utflen(w->text);
    126 	return w;
    127 }
    128 
    129 Word*
    130 getword(void)
    131 {
    132 	static Word *head, *tail;
    133 	char *line, *s;
    134 	Word *w;
    135 	
    136 	w = head;
    137 	if(w != nil){
    138 		head = w->next;
    139 		return w;
    140 	}
    141 	line = Brdstr(&bin, '\n', 1);
    142 	if(line == nil)
    143 		return nil;
    144 	tail = nil;
    145 	indent = indentof(line);
    146 	for(;;){
    147 		while(*line == ' ' || *line == '\t')
    148 			line++;
    149 		if(*line == '\0'){
    150 			if(head == nil)
    151 				return newword("", 0, -1, 1);
    152 			break;
    153 		}
    154 		/* how long is this word? */
    155 		for(s=line++; *line != '\0'; line++)
    156 			if(*line==' ' || *line=='\t')
    157 				break;
    158 		w = newword(s, line-s, indent, head==nil);
    159 		if(head == nil)
    160 			head = w;
    161 		else
    162 			tail->next = w;
    163 		tail = w;
    164 	}
    165 	w = head;
    166 	head = w->next;
    167 	return w;
    168 }
    169 
    170 void
    171 printindent(int w)
    172 {
    173 	while(w >= maxtab){
    174 		Bputc(&bout, '\t');
    175 		w -= maxtab;
    176 	}
    177 	while(w > 0){
    178 		Bputc(&bout, ' ');
    179 		w--;
    180 	}
    181 }
    182 
    183 /* give extra space if word ends with period, etc. */
    184 int
    185 nspaceafter(char *s)
    186 {
    187 	int n;
    188 
    189 	n = strlen(s);
    190 	if(n < 2)
    191 		return 1;
    192 	if(isupper(s[0]) && n < 4)
    193 		return 1;
    194 	if(strchr(".!?", s[n-1]) != nil)
    195 		return 2;
    196 	return 1;
    197 }
    198 
    199 void
    200 fmt(void)
    201 {
    202 	Word *w, *o;
    203 	int col, nsp;
    204 
    205 	w = getword();
    206 	while(w != nil){
    207 		if(w->indent == -1){
    208 			Bputc(&bout, '\n');
    209 			free(w);
    210 			w = getword();
    211 			if(w == nil)
    212 				break;
    213 		}
    214 		col = w->indent;
    215 		printindent(extraindent+col);
    216 		/* emit words until overflow; always emit at least one word */
    217 		for(;;){
    218 			Bprint(&bout, "%s", w->text);
    219 			col += w->length;
    220 			o = w;
    221 			w = getword();
    222 			if(w == nil)
    223 				break;
    224 			if(w->indent != o->indent)
    225 				break;	/* indent change */
    226 			nsp = nspaceafter(o->text);
    227 			if(col+nsp+w->length > length)
    228 				break;	/* fold line */
    229 			if(!join && w->bol)
    230 				break;
    231 			while(--nsp >= 0){
    232 				Bputc(&bout, ' ');	/* emit space; another word will follow */
    233 				col++;
    234 			}
    235 			free(o);
    236 		}
    237 		free(o);
    238 		Bputc(&bout, '\n');
    239 	}
    240 }