fmt.c 4.0 KB

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