1da2e3ebdSchin /*********************************************************************** 2da2e3ebdSchin * * 3da2e3ebdSchin * This software is part of the ast package * 4*7c2fbfb3SApril Chin * Copyright (c) 1992-2008 AT&T Intellectual Property * 5da2e3ebdSchin * and is licensed under the * 6da2e3ebdSchin * Common Public License, Version 1.0 * 7*7c2fbfb3SApril Chin * by AT&T Intellectual Property * 8da2e3ebdSchin * * 9da2e3ebdSchin * A copy of the License is available at * 10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt * 11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12da2e3ebdSchin * * 13da2e3ebdSchin * Information and Software Systems Research * 14da2e3ebdSchin * AT&T Research * 15da2e3ebdSchin * Florham Park NJ * 16da2e3ebdSchin * * 17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> * 18da2e3ebdSchin * David Korn <dgk@research.att.com> * 19da2e3ebdSchin * * 20da2e3ebdSchin ***********************************************************************/ 21da2e3ebdSchin #pragma prototyped 22da2e3ebdSchin 23da2e3ebdSchin static const char usage[] = 24da2e3ebdSchin "[-?\n@(#)$Id: fmt (AT&T Research) 2007-01-02 $\n]" 25da2e3ebdSchin USAGE_LICENSE 26da2e3ebdSchin "[+NAME?fmt - simple text formatter]" 27da2e3ebdSchin "[+DESCRIPTION?\bfmt\b reads the input files and left justifies space " 28da2e3ebdSchin "separated words into lines \awidth\a characters or less in length and " 29da2e3ebdSchin "writes the lines to the standard output. The standard input is read if " 30da2e3ebdSchin "\b-\b or no files are specified. Blank lines and interword spacing are " 31da2e3ebdSchin "preserved in the output. Indentation is preserved, and lines with " 32da2e3ebdSchin "identical indentation are joined and justified.]" 33da2e3ebdSchin "[+?\bfmt\b is meant to format mail messages prior to sending, but may " 34da2e3ebdSchin "also be useful for other simple tasks. For example, in \bvi\b(1) the " 35da2e3ebdSchin "command \b:!}fmt\b will justify the lines in the current paragraph.]" 36da2e3ebdSchin "[c:crown-margin?Preserve the indentation of the first two lines within " 37da2e3ebdSchin "a paragraph, and align the left margin of each subsequent line with " 38da2e3ebdSchin "that of the second line.]" 39da2e3ebdSchin "[o:optget?Format concatenated \boptget\b(3) usage strings.]" 40da2e3ebdSchin "[s:split-only?Split lines only; do not join short lines to form longer " 41da2e3ebdSchin "ones.]" 42da2e3ebdSchin "[u:uniform-spacing?One space between words, two after sentences.]" 43da2e3ebdSchin "[w:width?Set the output line width to \acolumns\a.]#[columns:=72]" 44da2e3ebdSchin "\n\n" 45da2e3ebdSchin "[ file ... ]" 46da2e3ebdSchin "\n\n" 47da2e3ebdSchin "[+SEE ALSO?\bmailx\b(1), \bnroff\b(1), \btroff\b(1), \bvi\b(1), " 48da2e3ebdSchin "\boptget\b(3)]" 49da2e3ebdSchin ; 50da2e3ebdSchin 51da2e3ebdSchin #include <cmd.h> 52da2e3ebdSchin #include <ctype.h> 53da2e3ebdSchin 54da2e3ebdSchin typedef struct Fmt_s 55da2e3ebdSchin { 56da2e3ebdSchin long flags; 57da2e3ebdSchin char* outp; 58da2e3ebdSchin char* outbuf; 59da2e3ebdSchin char* endbuf; 60da2e3ebdSchin Sfio_t* in; 61da2e3ebdSchin Sfio_t* out; 62da2e3ebdSchin int indent; 63da2e3ebdSchin int nextdent; 64da2e3ebdSchin int nwords; 65da2e3ebdSchin int prefix; 66da2e3ebdSchin int quote; 67da2e3ebdSchin int retain; 68da2e3ebdSchin int section; 69da2e3ebdSchin } Fmt_t; 70da2e3ebdSchin 71da2e3ebdSchin #define INDENT 4 72da2e3ebdSchin #define TABSZ 8 73da2e3ebdSchin 74da2e3ebdSchin #define isoption(fp,c) ((fp)->flags&(1L<<((c)-'a'))) 75da2e3ebdSchin #define setoption(fp,c) ((fp)->flags|=(1L<<((c)-'a'))) 76da2e3ebdSchin #define clroption(fp,c) ((fp)->flags&=~(1L<<((c)-'a'))) 77da2e3ebdSchin 78da2e3ebdSchin static void 79da2e3ebdSchin outline(Fmt_t* fp) 80da2e3ebdSchin { 81da2e3ebdSchin register char* cp = fp->outbuf; 82da2e3ebdSchin int n = 0; 83da2e3ebdSchin int c; 84da2e3ebdSchin int d; 85da2e3ebdSchin 86da2e3ebdSchin if (!fp->outp) 87da2e3ebdSchin return; 88da2e3ebdSchin while (fp->outp[-1] == ' ') 89da2e3ebdSchin fp->outp--; 90da2e3ebdSchin *fp->outp = 0; 91da2e3ebdSchin while (*cp++ == ' ') 92da2e3ebdSchin n++; 93da2e3ebdSchin if (n >= TABSZ) 94da2e3ebdSchin { 95da2e3ebdSchin n /= TABSZ; 96da2e3ebdSchin cp = &fp->outbuf[TABSZ*n]; 97da2e3ebdSchin while (n--) 98da2e3ebdSchin *--cp = '\t'; 99da2e3ebdSchin } 100da2e3ebdSchin else 101da2e3ebdSchin cp = fp->outbuf; 102da2e3ebdSchin fp->nwords = 0; 103da2e3ebdSchin if (!isoption(fp, 'o')) 104da2e3ebdSchin sfputr(fp->out, cp, '\n'); 105da2e3ebdSchin else if (*cp) 106da2e3ebdSchin { 107da2e3ebdSchin n = fp->indent; 108da2e3ebdSchin if (*cp != '[') 109da2e3ebdSchin { 110da2e3ebdSchin if (*cp == ' ') 111da2e3ebdSchin cp++; 112da2e3ebdSchin n += INDENT; 113da2e3ebdSchin } 114da2e3ebdSchin while (n--) 115da2e3ebdSchin sfputc(fp->out, ' '); 116da2e3ebdSchin if (fp->quote) 117da2e3ebdSchin { 118da2e3ebdSchin if ((d = (fp->outp - cp)) <= 0) 119da2e3ebdSchin c = 0; 120da2e3ebdSchin else if ((c = fp->outp[-1]) == 'n' && d > 1 && fp->outp[-2] == '\\') 121da2e3ebdSchin c = '}'; 122da2e3ebdSchin sfprintf(fp->out, "\"%s%s\"\n", cp, c == ']' || c == '{' || c == '}' ? "" : " "); 123da2e3ebdSchin } 124da2e3ebdSchin else 125da2e3ebdSchin sfputr(fp->out, cp, '\n'); 126da2e3ebdSchin if (fp->nextdent) 127da2e3ebdSchin { 128da2e3ebdSchin fp->indent += fp->nextdent; 129da2e3ebdSchin fp->endbuf -= fp->nextdent; 130da2e3ebdSchin fp->nextdent = 0; 131da2e3ebdSchin } 132da2e3ebdSchin } 133da2e3ebdSchin fp->outp = 0; 134da2e3ebdSchin } 135da2e3ebdSchin 136da2e3ebdSchin static void 137da2e3ebdSchin split(Fmt_t* fp, char* buf, int splice) 138da2e3ebdSchin { 139da2e3ebdSchin register char* cp; 140da2e3ebdSchin register char* ep; 141da2e3ebdSchin register char* qp; 142da2e3ebdSchin register int c = 1; 143da2e3ebdSchin register int q = 0; 144da2e3ebdSchin register int n; 145da2e3ebdSchin int prefix; 146da2e3ebdSchin 147da2e3ebdSchin for (ep = buf; *ep == ' '; ep++); 148da2e3ebdSchin prefix = ep - buf; 149da2e3ebdSchin 150da2e3ebdSchin /* 151da2e3ebdSchin * preserve blank lines 152da2e3ebdSchin */ 153da2e3ebdSchin 154da2e3ebdSchin if ((*ep == 0 || *buf == '.') && !isoption(fp, 'o')) 155da2e3ebdSchin { 156da2e3ebdSchin if (*ep) 157da2e3ebdSchin prefix = strlen(buf); 158da2e3ebdSchin outline(fp); 159da2e3ebdSchin strcpy(fp->outbuf, buf); 160da2e3ebdSchin fp->outp = fp->outbuf+prefix; 161da2e3ebdSchin outline(fp); 162da2e3ebdSchin return; 163da2e3ebdSchin } 164da2e3ebdSchin if (fp->prefix < prefix && !isoption(fp, 'c')) 165da2e3ebdSchin outline(fp); 166da2e3ebdSchin if (!fp->outp || prefix < fp->prefix) 167da2e3ebdSchin fp->prefix = prefix; 168da2e3ebdSchin while (c) 169da2e3ebdSchin { 170da2e3ebdSchin cp = ep; 171da2e3ebdSchin while (*ep == ' ') 172da2e3ebdSchin ep++; 173da2e3ebdSchin if (cp != ep && isoption(fp, 'u')) 174da2e3ebdSchin cp = ep-1; 175da2e3ebdSchin while (c = *ep) 176da2e3ebdSchin { 177da2e3ebdSchin if (c == ' ') 178da2e3ebdSchin break; 179da2e3ebdSchin ep++; 180da2e3ebdSchin 181da2e3ebdSchin /* 182da2e3ebdSchin * skip over \space 183da2e3ebdSchin */ 184da2e3ebdSchin 185da2e3ebdSchin if (c == '\\' && *ep) 186da2e3ebdSchin ep++; 187da2e3ebdSchin } 188da2e3ebdSchin n = (ep-cp); 189da2e3ebdSchin if (n && isoption(fp, 'o')) 190da2e3ebdSchin { 191da2e3ebdSchin for (qp = cp; qp < ep; qp++) 192da2e3ebdSchin if (*qp == '\\') 193da2e3ebdSchin qp++; 194da2e3ebdSchin else if (*qp == '"') 195da2e3ebdSchin q = !q; 196da2e3ebdSchin if (*(ep-1) == '"') 197da2e3ebdSchin goto skip; 198da2e3ebdSchin } 199da2e3ebdSchin if (fp->nwords > 0 && &fp->outp[n] >= fp->endbuf && !fp->retain && !q) 200da2e3ebdSchin outline(fp); 201da2e3ebdSchin skip: 202da2e3ebdSchin if (fp->nwords == 0) 203da2e3ebdSchin { 204da2e3ebdSchin if (fp->prefix) 205da2e3ebdSchin memset(fp->outbuf, ' ', fp->prefix); 206da2e3ebdSchin fp->outp = &fp->outbuf[fp->prefix]; 207da2e3ebdSchin while (*cp == ' ') 208da2e3ebdSchin cp++; 209da2e3ebdSchin n = (ep-cp); 210da2e3ebdSchin } 211da2e3ebdSchin memcpy(fp->outp, cp, n); 212da2e3ebdSchin fp->outp += n; 213da2e3ebdSchin fp->nwords++; 214da2e3ebdSchin } 215da2e3ebdSchin if (isoption(fp, 's') || *buf == 0) 216da2e3ebdSchin outline(fp); 217da2e3ebdSchin else if (fp->outp) 218da2e3ebdSchin { 219da2e3ebdSchin /* 220da2e3ebdSchin * two spaces at ends of sentences 221da2e3ebdSchin */ 222da2e3ebdSchin 223da2e3ebdSchin if (!isoption(fp, 'o') && strchr(".:!?", fp->outp[-1])) 224da2e3ebdSchin *fp->outp++ = ' '; 225da2e3ebdSchin if (!splice && !fp->retain && (!fp->quote || (fp->outp - fp->outbuf) < 2 || fp->outp[-2] != '\\' || fp->outp[-1] != 'n' && fp->outp[-1] != 't' && fp->outp[-1] != ' ')) 226da2e3ebdSchin *fp->outp++ = ' '; 227da2e3ebdSchin } 228da2e3ebdSchin } 229da2e3ebdSchin 230da2e3ebdSchin static int 231da2e3ebdSchin dofmt(Fmt_t* fp) 232da2e3ebdSchin { 233da2e3ebdSchin register int c; 234da2e3ebdSchin int b; 235da2e3ebdSchin int x; 236da2e3ebdSchin int splice; 237da2e3ebdSchin char* cp; 238da2e3ebdSchin char* dp; 239da2e3ebdSchin char* ep; 240da2e3ebdSchin char* lp; 241da2e3ebdSchin char* tp; 242da2e3ebdSchin char buf[8192]; 243da2e3ebdSchin 244da2e3ebdSchin cp = 0; 245da2e3ebdSchin while (cp || (cp = sfgetr(fp->in, '\n', 0)) && !(splice = 0) && (lp = cp + sfvalue(fp->in) - 1) || (cp = sfgetr(fp->in, '\n', SF_LASTR)) && (splice = 1) && (lp = cp + sfvalue(fp->in))) 246da2e3ebdSchin { 247da2e3ebdSchin if (isoption(fp, 'o')) 248da2e3ebdSchin { 249da2e3ebdSchin if (!isoption(fp, 'i')) 250da2e3ebdSchin { 251da2e3ebdSchin setoption(fp, 'i'); 252da2e3ebdSchin b = 0; 253da2e3ebdSchin while (cp < lp) 254da2e3ebdSchin { 255da2e3ebdSchin if (*cp == ' ') 256da2e3ebdSchin b += 1; 257da2e3ebdSchin else if (*cp == '\t') 258da2e3ebdSchin b += INDENT; 259da2e3ebdSchin else 260da2e3ebdSchin break; 261da2e3ebdSchin cp++; 262da2e3ebdSchin } 263da2e3ebdSchin fp->indent = roundof(b, INDENT); 264da2e3ebdSchin } 265da2e3ebdSchin else 266da2e3ebdSchin while (cp < lp && (*cp == ' ' || *cp == '\t')) 267da2e3ebdSchin cp++; 268da2e3ebdSchin if (!isoption(fp, 'q') && cp < lp) 269da2e3ebdSchin { 270da2e3ebdSchin setoption(fp, 'q'); 271da2e3ebdSchin if (*cp == '"') 272da2e3ebdSchin { 273da2e3ebdSchin ep = lp; 274da2e3ebdSchin while (--ep > cp) 275da2e3ebdSchin if (*ep == '"') 276da2e3ebdSchin { 277da2e3ebdSchin fp->quote = 1; 278da2e3ebdSchin break; 279da2e3ebdSchin } 280da2e3ebdSchin else if (*ep != ' ' && *ep != '\t') 281da2e3ebdSchin break; 282da2e3ebdSchin } 283da2e3ebdSchin } 284da2e3ebdSchin } 285da2e3ebdSchin again: 286da2e3ebdSchin dp = buf; 287da2e3ebdSchin ep = 0; 288da2e3ebdSchin for (b = 1;; b = 0) 289da2e3ebdSchin { 290da2e3ebdSchin if (cp >= lp) 291da2e3ebdSchin { 292da2e3ebdSchin cp = 0; 293da2e3ebdSchin break; 294da2e3ebdSchin } 295da2e3ebdSchin c = *cp++; 296da2e3ebdSchin if (isoption(fp, 'o')) 297da2e3ebdSchin { 298da2e3ebdSchin if (c == '\\') 299da2e3ebdSchin { 300da2e3ebdSchin x = 0; 301da2e3ebdSchin c = ' '; 302da2e3ebdSchin cp--; 303da2e3ebdSchin while (cp < lp) 304da2e3ebdSchin { 305da2e3ebdSchin if (*cp == '\\') 306da2e3ebdSchin { 307da2e3ebdSchin cp++; 308da2e3ebdSchin if ((lp - cp) < 1) 309da2e3ebdSchin { 310da2e3ebdSchin c = '\\'; 311da2e3ebdSchin break; 312da2e3ebdSchin } 313da2e3ebdSchin if (*cp == 'n') 314da2e3ebdSchin { 315da2e3ebdSchin cp++; 316da2e3ebdSchin c = '\n'; 317da2e3ebdSchin if ((lp - cp) > 2) 318da2e3ebdSchin { 319da2e3ebdSchin if (*cp == ']' || *cp == '@' && *(cp + 1) == '(') 320da2e3ebdSchin { 321da2e3ebdSchin *dp++ = '\\'; 322da2e3ebdSchin *dp++ = 'n'; 323da2e3ebdSchin c = *cp++; 324da2e3ebdSchin break; 325da2e3ebdSchin } 326da2e3ebdSchin if (*cp == '\\' && *(cp + 1) == 'n') 327da2e3ebdSchin { 328da2e3ebdSchin cp += 2; 329da2e3ebdSchin *dp++ = '\n'; 330da2e3ebdSchin break; 331da2e3ebdSchin } 332da2e3ebdSchin } 333da2e3ebdSchin } 334da2e3ebdSchin else if (*cp == 't' || *cp == ' ') 335da2e3ebdSchin { 336da2e3ebdSchin cp++; 337da2e3ebdSchin x = 1; 338da2e3ebdSchin c = ' '; 339da2e3ebdSchin } 340da2e3ebdSchin else 341da2e3ebdSchin { 342da2e3ebdSchin if (x && dp != buf && *(dp - 1) != ' ') 343da2e3ebdSchin *dp++ = ' '; 344da2e3ebdSchin *dp++ = '\\'; 345da2e3ebdSchin c = *cp++; 346da2e3ebdSchin break; 347da2e3ebdSchin } 348da2e3ebdSchin } 349da2e3ebdSchin else if (*cp == ' ' || *cp == '\t') 350da2e3ebdSchin { 351da2e3ebdSchin cp++; 352da2e3ebdSchin c = ' '; 353da2e3ebdSchin x = 1; 354da2e3ebdSchin } 355da2e3ebdSchin else 356da2e3ebdSchin { 357da2e3ebdSchin if (x && c != '\n' && dp != buf && *(dp - 1) != ' ') 358da2e3ebdSchin *dp++ = ' '; 359da2e3ebdSchin break; 360da2e3ebdSchin } 361da2e3ebdSchin } 362da2e3ebdSchin if (c == '\n') 363da2e3ebdSchin { 364da2e3ebdSchin c = 0; 365da2e3ebdSchin goto flush; 366da2e3ebdSchin } 367da2e3ebdSchin if (c == ' ' && (dp == buf || *(dp - 1) == ' ')) 368da2e3ebdSchin continue; 369da2e3ebdSchin } 370da2e3ebdSchin else if (c == '"') 371da2e3ebdSchin { 372da2e3ebdSchin if (b || cp >= lp) 373da2e3ebdSchin { 374da2e3ebdSchin if (fp->quote) 375da2e3ebdSchin continue; 376da2e3ebdSchin fp->section = 0; 377da2e3ebdSchin } 378da2e3ebdSchin } 379da2e3ebdSchin else if (c == '\a') 380da2e3ebdSchin { 381da2e3ebdSchin *dp++ = '\\'; 382da2e3ebdSchin c = 'a'; 383da2e3ebdSchin } 384da2e3ebdSchin else if (c == '\b') 385da2e3ebdSchin { 386da2e3ebdSchin *dp++ = '\\'; 387da2e3ebdSchin c = 'b'; 388da2e3ebdSchin } 389da2e3ebdSchin else if (c == '\f') 390da2e3ebdSchin { 391da2e3ebdSchin *dp++ = '\\'; 392da2e3ebdSchin c = 'f'; 393da2e3ebdSchin } 394da2e3ebdSchin else if (c == '\v') 395da2e3ebdSchin { 396da2e3ebdSchin *dp++ = '\\'; 397da2e3ebdSchin c = 'v'; 398da2e3ebdSchin } 399da2e3ebdSchin else if (c == ']' && (cp >= lp || *cp != ':' && *cp != '#' && *cp != '!')) 400da2e3ebdSchin { 401da2e3ebdSchin if (cp < lp && *cp == ']') 402da2e3ebdSchin { 403da2e3ebdSchin cp++; 404da2e3ebdSchin *dp++ = c; 405da2e3ebdSchin } 406da2e3ebdSchin else 407da2e3ebdSchin { 408da2e3ebdSchin fp->section = 1; 409da2e3ebdSchin fp->retain = 0; 410da2e3ebdSchin flush: 411da2e3ebdSchin *dp++ = c; 412da2e3ebdSchin *dp = 0; 413da2e3ebdSchin split(fp, buf, 0); 414da2e3ebdSchin outline(fp); 415da2e3ebdSchin goto again; 416da2e3ebdSchin } 417da2e3ebdSchin } 418da2e3ebdSchin else if (fp->section) 419da2e3ebdSchin { 420da2e3ebdSchin if (c == '[') 421da2e3ebdSchin { 422da2e3ebdSchin if (b) 423da2e3ebdSchin fp->retain = 1; 424da2e3ebdSchin else 425da2e3ebdSchin { 426da2e3ebdSchin cp--; 427da2e3ebdSchin c = 0; 428da2e3ebdSchin goto flush; 429da2e3ebdSchin } 430da2e3ebdSchin fp->section = 0; 431da2e3ebdSchin } 432da2e3ebdSchin else if (c == '{') 433da2e3ebdSchin { 434da2e3ebdSchin x = 1; 435da2e3ebdSchin for (tp = cp; tp < lp; tp++) 436da2e3ebdSchin { 437da2e3ebdSchin if (*tp == '[' || *tp == '\n') 438da2e3ebdSchin break; 439da2e3ebdSchin if (*tp == ' ' || *tp == '\t' || *tp == '"') 440da2e3ebdSchin continue; 441da2e3ebdSchin if (*tp == '\\' && (lp - tp) > 1) 442da2e3ebdSchin { 443da2e3ebdSchin if (*++tp == 'n') 444da2e3ebdSchin break; 445da2e3ebdSchin if (*tp == 't' || *tp == '\n') 446da2e3ebdSchin continue; 447da2e3ebdSchin } 448da2e3ebdSchin x = 0; 449da2e3ebdSchin break; 450da2e3ebdSchin } 451da2e3ebdSchin if (x) 452da2e3ebdSchin { 453da2e3ebdSchin if (fp->endbuf > (fp->outbuf + fp->indent + 2*INDENT)) 454da2e3ebdSchin fp->nextdent = 2*INDENT; 455da2e3ebdSchin goto flush; 456da2e3ebdSchin } 457da2e3ebdSchin else 458da2e3ebdSchin fp->section = 0; 459da2e3ebdSchin } 460da2e3ebdSchin else if (c == '}') 461da2e3ebdSchin { 462da2e3ebdSchin if (fp->indent && (b || *(cp - 2) != 'f')) 463da2e3ebdSchin { 464da2e3ebdSchin if (b) 465da2e3ebdSchin { 466da2e3ebdSchin fp->indent -= 2*INDENT; 467da2e3ebdSchin fp->endbuf += 2*INDENT; 468da2e3ebdSchin } 469da2e3ebdSchin else 470da2e3ebdSchin { 471da2e3ebdSchin cp--; 472da2e3ebdSchin c = 0; 473da2e3ebdSchin } 474da2e3ebdSchin goto flush; 475da2e3ebdSchin } 476da2e3ebdSchin else 477da2e3ebdSchin fp->section = 0; 478da2e3ebdSchin } 479da2e3ebdSchin else if (c == ' ' || c == '\t') 480da2e3ebdSchin continue; 481da2e3ebdSchin else 482da2e3ebdSchin fp->section = 0; 483da2e3ebdSchin } 484da2e3ebdSchin else if (c == '?' && (cp >= lp || *cp != '?')) 485da2e3ebdSchin { 486da2e3ebdSchin if (fp->retain) 487da2e3ebdSchin { 488da2e3ebdSchin cp--; 489da2e3ebdSchin while (cp < lp && *cp != ' ' && *cp != '\t' && *cp != ']' && dp < &buf[sizeof(buf)-3]) 490da2e3ebdSchin *dp++ = *cp++; 491da2e3ebdSchin if (cp < lp && (*cp == ' ' || *cp == '\t')) 492da2e3ebdSchin *dp++ = *cp++; 493da2e3ebdSchin *dp = 0; 494da2e3ebdSchin split(fp, buf, 0); 495da2e3ebdSchin dp = buf; 496da2e3ebdSchin ep = 0; 497da2e3ebdSchin fp->retain = 0; 498da2e3ebdSchin if (fp->outp >= fp->endbuf) 499da2e3ebdSchin outline(fp); 500da2e3ebdSchin continue; 501da2e3ebdSchin } 502da2e3ebdSchin } 503da2e3ebdSchin else if (c == ' ' || c == '\t') 504da2e3ebdSchin for (c = ' '; *cp == ' ' || *cp == '\t'; cp++); 505da2e3ebdSchin } 506da2e3ebdSchin else if (c == '\b') 507da2e3ebdSchin { 508da2e3ebdSchin if (dp > buf) 509da2e3ebdSchin { 510da2e3ebdSchin dp--; 511da2e3ebdSchin if (ep) 512da2e3ebdSchin ep--; 513da2e3ebdSchin } 514da2e3ebdSchin continue; 515da2e3ebdSchin } 516da2e3ebdSchin else if (c == '\t') 517da2e3ebdSchin { 518da2e3ebdSchin /* 519da2e3ebdSchin * expand tabs 520da2e3ebdSchin */ 521da2e3ebdSchin 522da2e3ebdSchin if (!ep) 523da2e3ebdSchin ep = dp; 524da2e3ebdSchin c = isoption(fp, 'o') ? 1 : TABSZ - (dp - buf) % TABSZ; 525da2e3ebdSchin if (dp >= &buf[sizeof(buf) - c - 3]) 526da2e3ebdSchin { 527da2e3ebdSchin cp--; 528da2e3ebdSchin break; 529da2e3ebdSchin } 530da2e3ebdSchin while (c-- > 0) 531da2e3ebdSchin *dp++ = ' '; 532da2e3ebdSchin continue; 533da2e3ebdSchin } 534da2e3ebdSchin else if (!isprint(c)) 535da2e3ebdSchin continue; 536da2e3ebdSchin if (dp >= &buf[sizeof(buf) - 3]) 537da2e3ebdSchin { 538da2e3ebdSchin tp = dp; 539da2e3ebdSchin while (--tp > buf) 540da2e3ebdSchin if (isspace(*tp)) 541da2e3ebdSchin { 542da2e3ebdSchin cp -= dp - tp; 543da2e3ebdSchin dp = tp; 544da2e3ebdSchin break; 545da2e3ebdSchin } 546da2e3ebdSchin ep = 0; 547da2e3ebdSchin break; 548da2e3ebdSchin } 549da2e3ebdSchin if (c != ' ') 550da2e3ebdSchin ep = 0; 551da2e3ebdSchin else if (!ep) 552da2e3ebdSchin ep = dp; 553da2e3ebdSchin *dp++ = c; 554da2e3ebdSchin } 555da2e3ebdSchin if (ep) 556da2e3ebdSchin *ep = 0; 557da2e3ebdSchin else 558da2e3ebdSchin *dp = 0; 559da2e3ebdSchin split(fp, buf, splice); 560da2e3ebdSchin } 561da2e3ebdSchin return 0; 562da2e3ebdSchin } 563da2e3ebdSchin 564da2e3ebdSchin int 565da2e3ebdSchin b_fmt(int argc, char** argv, void *context) 566da2e3ebdSchin { 567da2e3ebdSchin register int n; 568da2e3ebdSchin char* cp; 569da2e3ebdSchin Fmt_t fmt; 570da2e3ebdSchin char outbuf[8 * 1024]; 571da2e3ebdSchin 572da2e3ebdSchin fmt.flags = 0; 573da2e3ebdSchin fmt.out = sfstdout; 574da2e3ebdSchin fmt.outbuf = outbuf; 575da2e3ebdSchin fmt.outp = 0; 576da2e3ebdSchin fmt.endbuf = &outbuf[72]; 577da2e3ebdSchin fmt.indent = 0; 578da2e3ebdSchin fmt.nextdent = 0; 579da2e3ebdSchin fmt.nwords = 0; 580da2e3ebdSchin fmt.prefix = 0; 581da2e3ebdSchin fmt.quote = 0; 582da2e3ebdSchin fmt.retain = 0; 583da2e3ebdSchin fmt.section = 1; 584da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 585da2e3ebdSchin while (n = optget(argv, usage)) 586da2e3ebdSchin switch (n) 587da2e3ebdSchin { 588da2e3ebdSchin case 'c': 589da2e3ebdSchin case 'o': 590da2e3ebdSchin case 's': 591da2e3ebdSchin case 'u': 592da2e3ebdSchin setoption(&fmt, n); 593da2e3ebdSchin break; 594da2e3ebdSchin case 'w': 595da2e3ebdSchin if (opt_info.num < TABSZ || opt_info.num>= sizeof(outbuf)) 596da2e3ebdSchin error(2, "width out of range"); 597da2e3ebdSchin fmt.endbuf = &outbuf[opt_info.num]; 598da2e3ebdSchin break; 599da2e3ebdSchin case ':': 600da2e3ebdSchin error(2, "%s", opt_info.arg); 601da2e3ebdSchin break; 602da2e3ebdSchin case '?': 603da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg); 604da2e3ebdSchin break; 605da2e3ebdSchin } 606da2e3ebdSchin argv += opt_info.index; 607da2e3ebdSchin if (error_info.errors) 608da2e3ebdSchin error(ERROR_usage(2), "%s", optusage(NiL)); 609da2e3ebdSchin if (isoption(&fmt, 'o')) 610da2e3ebdSchin setoption(&fmt, 'c'); 611da2e3ebdSchin if (isoption(&fmt, 's')) 612da2e3ebdSchin clroption(&fmt, 'u'); 613da2e3ebdSchin if (cp = *argv) 614da2e3ebdSchin argv++; 615da2e3ebdSchin do { 616da2e3ebdSchin if (!cp || streq(cp, "-")) 617da2e3ebdSchin fmt.in = sfstdin; 618da2e3ebdSchin else if (!(fmt.in = sfopen(NiL, cp, "r"))) 619da2e3ebdSchin { 620da2e3ebdSchin error(ERROR_system(0), "%s: cannot open", cp); 621da2e3ebdSchin error_info.errors = 1; 622da2e3ebdSchin continue; 623da2e3ebdSchin } 624da2e3ebdSchin dofmt(&fmt); 625da2e3ebdSchin if (fmt.in != sfstdin) 626da2e3ebdSchin sfclose(fmt.in); 627da2e3ebdSchin } while (cp = *argv++); 628da2e3ebdSchin outline(&fmt); 629da2e3ebdSchin if (sfsync(sfstdout)) 630da2e3ebdSchin error(ERROR_system(0), "write error"); 631da2e3ebdSchin return error_info.errors != 0; 632da2e3ebdSchin } 633