1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 char copyright[] = 36 "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 37 All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 /*static char sccsid[] = "from: @(#)fmt.c 5.10 (Berkeley) 6/1/90";*/ 42 static char rcsid[] = "$Id: fmt.c,v 1.2 1993/08/01 18:15:53 mycroft Exp $"; 43 #endif /* not lint */ 44 45 #include <stdio.h> 46 #include <ctype.h> 47 48 /* 49 * fmt -- format the concatenation of input files or standard input 50 * onto standard output. Designed for use with Mail ~| 51 * 52 * Syntax : fmt [ goal [ max ] ] [ name ... ] 53 * Authors: Kurt Shoens (UCB) 12/7/78; 54 * Liz Allen (UMCP) 2/24/83 [Addition of goal length concept]. 55 */ 56 57 /* LIZ@UOM 6/18/85 -- Don't need LENGTH any more. 58 * #define LENGTH 72 Max line length in output 59 */ 60 #define NOSTR ((char *) 0) /* Null string pointer for lint */ 61 62 /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */ 63 #define GOAL_LENGTH 65 64 #define MAX_LENGTH 75 65 int goal_length; /* Target or goal line length in output */ 66 int max_length; /* Max line length in output */ 67 int pfx; /* Current leading blank count */ 68 int lineno; /* Current input line */ 69 int mark; /* Last place we saw a head line */ 70 71 char *malloc(); /* for lint . . . */ 72 char *headnames[] = {"To", "Subject", "Cc", 0}; 73 74 /* 75 * Drive the whole formatter by managing input files. Also, 76 * cause initialization of the output stuff and flush it out 77 * at the end. 78 */ 79 80 main(argc, argv) 81 int argc; 82 char **argv; 83 { 84 register FILE *fi; 85 register int errs = 0; 86 int number; /* LIZ@UOM 6/18/85 */ 87 88 goal_length = GOAL_LENGTH; 89 max_length = MAX_LENGTH; 90 setout(); 91 lineno = 1; 92 mark = -10; 93 /* 94 * LIZ@UOM 6/18/85 -- Check for goal and max length arguments 95 */ 96 if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) { 97 argv++; 98 argc--; 99 goal_length = number; 100 if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) { 101 argv++; 102 argc--; 103 max_length = number; 104 } 105 } 106 if (max_length <= goal_length) { 107 fprintf(stderr, "Max length must be greater than %s\n", 108 "goal length"); 109 exit(1); 110 } 111 if (argc < 2) { 112 fmt(stdin); 113 oflush(); 114 exit(0); 115 } 116 while (--argc) { 117 if ((fi = fopen(*++argv, "r")) == NULL) { 118 perror(*argv); 119 errs++; 120 continue; 121 } 122 fmt(fi); 123 fclose(fi); 124 } 125 oflush(); 126 exit(errs); 127 } 128 129 /* 130 * Read up characters from the passed input file, forming lines, 131 * doing ^H processing, expanding tabs, stripping trailing blanks, 132 * and sending each line down for analysis. 133 */ 134 fmt(fi) 135 FILE *fi; 136 { 137 char linebuf[BUFSIZ], canonb[BUFSIZ]; 138 register char *cp, *cp2; 139 register int c, col; 140 141 c = getc(fi); 142 while (c != EOF) { 143 /* 144 * Collect a line, doing ^H processing. 145 * Leave tabs for now. 146 */ 147 cp = linebuf; 148 while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) { 149 if (c == '\b') { 150 if (cp > linebuf) 151 cp--; 152 c = getc(fi); 153 continue; 154 } 155 if ((c < ' ' || c >= 0177) && c != '\t') { 156 c = getc(fi); 157 continue; 158 } 159 *cp++ = c; 160 c = getc(fi); 161 } 162 *cp = '\0'; 163 164 /* 165 * Toss anything remaining on the input line. 166 */ 167 while (c != '\n' && c != EOF) 168 c = getc(fi); 169 170 /* 171 * Expand tabs on the way to canonb. 172 */ 173 col = 0; 174 cp = linebuf; 175 cp2 = canonb; 176 while (c = *cp++) { 177 if (c != '\t') { 178 col++; 179 if (cp2-canonb < BUFSIZ-1) 180 *cp2++ = c; 181 continue; 182 } 183 do { 184 if (cp2-canonb < BUFSIZ-1) 185 *cp2++ = ' '; 186 col++; 187 } while ((col & 07) != 0); 188 } 189 190 /* 191 * Swipe trailing blanks from the line. 192 */ 193 for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--) 194 ; 195 *++cp2 = '\0'; 196 prefix(canonb); 197 if (c != EOF) 198 c = getc(fi); 199 } 200 } 201 202 /* 203 * Take a line devoid of tabs and other garbage and determine its 204 * blank prefix. If the indent changes, call for a linebreak. 205 * If the input line is blank, echo the blank line on the output. 206 * Finally, if the line minus the prefix is a mail header, try to keep 207 * it on a line by itself. 208 */ 209 prefix(line) 210 char line[]; 211 { 212 register char *cp, **hp; 213 register int np, h; 214 215 if (strlen(line) == 0) { 216 oflush(); 217 putchar('\n'); 218 return; 219 } 220 for (cp = line; *cp == ' '; cp++) 221 ; 222 np = cp - line; 223 224 /* 225 * The following horrible expression attempts to avoid linebreaks 226 * when the indent changes due to a paragraph. 227 */ 228 if (np != pfx && (np > pfx || abs(pfx-np) > 8)) 229 oflush(); 230 if (h = ishead(cp)) 231 oflush(), mark = lineno; 232 if (lineno - mark < 3 && lineno - mark > 0) 233 for (hp = &headnames[0]; *hp != (char *) 0; hp++) 234 if (ispref(*hp, cp)) { 235 h = 1; 236 oflush(); 237 break; 238 } 239 if (!h && (h = (*cp == '.'))) 240 oflush(); 241 pfx = np; 242 if (h) 243 pack(cp); 244 else split(cp); 245 if (h) 246 oflush(); 247 lineno++; 248 } 249 250 /* 251 * Split up the passed line into output "words" which are 252 * maximal strings of non-blanks with the blank separation 253 * attached at the end. Pass these words along to the output 254 * line packer. 255 */ 256 split(line) 257 char line[]; 258 { 259 register char *cp, *cp2; 260 char word[BUFSIZ]; 261 int wordl; /* LIZ@UOM 6/18/85 */ 262 263 cp = line; 264 while (*cp) { 265 cp2 = word; 266 wordl = 0; /* LIZ@UOM 6/18/85 */ 267 268 /* 269 * Collect a 'word,' allowing it to contain escaped white 270 * space. 271 */ 272 while (*cp && *cp != ' ') { 273 if (*cp == '\\' && isspace(cp[1])) 274 *cp2++ = *cp++; 275 *cp2++ = *cp++; 276 wordl++;/* LIZ@UOM 6/18/85 */ 277 } 278 279 /* 280 * Guarantee a space at end of line. Two spaces after end of 281 * sentence punctuation. 282 */ 283 if (*cp == '\0') { 284 *cp2++ = ' '; 285 if (index(".:!", cp[-1])) 286 *cp2++ = ' '; 287 } 288 while (*cp == ' ') 289 *cp2++ = *cp++; 290 *cp2 = '\0'; 291 /* 292 * LIZ@UOM 6/18/85 pack(word); 293 */ 294 pack(word, wordl); 295 } 296 } 297 298 /* 299 * Output section. 300 * Build up line images from the words passed in. Prefix 301 * each line with correct number of blanks. The buffer "outbuf" 302 * contains the current partial line image, including prefixed blanks. 303 * "outp" points to the next available space therein. When outp is NOSTR, 304 * there ain't nothing in there yet. At the bottom of this whole mess, 305 * leading tabs are reinserted. 306 */ 307 char outbuf[BUFSIZ]; /* Sandbagged output line image */ 308 char *outp; /* Pointer in above */ 309 310 /* 311 * Initialize the output section. 312 */ 313 setout() 314 { 315 outp = NOSTR; 316 } 317 318 /* 319 * Pack a word onto the output line. If this is the beginning of 320 * the line, push on the appropriately-sized string of blanks first. 321 * If the word won't fit on the current line, flush and begin a new 322 * line. If the word is too long to fit all by itself on a line, 323 * just give it its own and hope for the best. 324 * 325 * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the 326 * goal length, take it. If not, then check to see if the line 327 * will be over the max length; if so put the word on the next 328 * line. If not, check to see if the line will be closer to the 329 * goal length with or without the word and take it or put it on 330 * the next line accordingly. 331 */ 332 333 /* 334 * LIZ@UOM 6/18/85 -- pass in the length of the word as well 335 * pack(word) 336 * char word[]; 337 */ 338 pack(word,wl) 339 char word[]; 340 int wl; 341 { 342 register char *cp; 343 register int s, t; 344 345 if (outp == NOSTR) 346 leadin(); 347 /* 348 * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the 349 * length of the line before the word is added; t is now the length 350 * of the line after the word is added 351 * t = strlen(word); 352 * if (t+s <= LENGTH) 353 */ 354 s = outp - outbuf; 355 t = wl + s; 356 if ((t <= goal_length) || 357 ((t <= max_length) && (t - goal_length <= goal_length - s))) { 358 /* 359 * In like flint! 360 */ 361 for (cp = word; *cp; *outp++ = *cp++); 362 return; 363 } 364 if (s > pfx) { 365 oflush(); 366 leadin(); 367 } 368 for (cp = word; *cp; *outp++ = *cp++); 369 } 370 371 /* 372 * If there is anything on the current output line, send it on 373 * its way. Set outp to NOSTR to indicate the absence of the current 374 * line prefix. 375 */ 376 oflush() 377 { 378 if (outp == NOSTR) 379 return; 380 *outp = '\0'; 381 tabulate(outbuf); 382 outp = NOSTR; 383 } 384 385 /* 386 * Take the passed line buffer, insert leading tabs where possible, and 387 * output on standard output (finally). 388 */ 389 tabulate(line) 390 char line[]; 391 { 392 register char *cp; 393 register int b, t; 394 395 /* 396 * Toss trailing blanks in the output line. 397 */ 398 cp = line + strlen(line) - 1; 399 while (cp >= line && *cp == ' ') 400 cp--; 401 *++cp = '\0'; 402 403 /* 404 * Count the leading blank space and tabulate. 405 */ 406 for (cp = line; *cp == ' '; cp++) 407 ; 408 b = cp-line; 409 t = b >> 3; 410 b &= 07; 411 if (t > 0) 412 do 413 putc('\t', stdout); 414 while (--t); 415 if (b > 0) 416 do 417 putc(' ', stdout); 418 while (--b); 419 while (*cp) 420 putc(*cp++, stdout); 421 putc('\n', stdout); 422 } 423 424 /* 425 * Initialize the output line with the appropriate number of 426 * leading blanks. 427 */ 428 leadin() 429 { 430 register int b; 431 register char *cp; 432 433 for (b = 0, cp = outbuf; b < pfx; b++) 434 *cp++ = ' '; 435 outp = cp; 436 } 437 438 /* 439 * Save a string in dynamic space. 440 * This little goodie is needed for 441 * a headline detector in head.c 442 */ 443 char * 444 savestr(str) 445 char str[]; 446 { 447 register char *top; 448 449 top = malloc(strlen(str) + 1); 450 if (top == NOSTR) { 451 fprintf(stderr, "fmt: Ran out of memory\n"); 452 exit(1); 453 } 454 strcpy(top, str); 455 return (top); 456 } 457 458 /* 459 * Is s1 a prefix of s2?? 460 */ 461 ispref(s1, s2) 462 register char *s1, *s2; 463 { 464 465 while (*s1++ == *s2) 466 ; 467 return (*s1 == '\0'); 468 } 469