/* fmt.c */ /* usage: fmt [-width] [files]... * * Fmt rearrages text in order to make each line have roughly the * same width. Indentation and word spacing is preserved. * * The default width is 72 characters, but you can override that via -width. * If no files are given on the command line, then it reads stdin. */ #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif int width = 72; /* the desired line width */ int isblank; /* is the current output line blank? */ int indent; /* width of the indentation */ char ind[512]; /* indentation text */ char word[1024]; /* word buffer */ /* This function displays a usage message and quits */ void usage() { fprintf(stderr, "usage: fmt [-width] [files]...\n"); exit(2); } /* This function outputs a single word. It takes care of spacing and the * newlines within a paragraph. */ void putword() { int i; /* index into word[], or whatever */ int ww; /* width of the word */ int sw; /* width of spacing after word */ static int psw; /* space width of previous word */ static int tab; /* the width of text already written */ /* separate the word and its spacing */ for (ww = 0; word[ww] && word[ww] != ' '; ww++) { } sw = strlen(word) - ww; word[ww] = '\0'; /* if no spacing (that is, the word was at the end of the line) then * assume 1 space unless the last char of the word was punctuation */ if (sw == 0) { sw = 1; if (word[ww - 1] == '.' || word[ww - 1] == '?' || word[ww - 1] == '!') sw = 2; } /* if this is the first word on the line... */ if (isblank) { /* output the indentation first */ fputs(ind, stdout); tab = indent; } else /* text has already been written to this output line */ { /* will the word fit on this line? */ if (psw + ww + tab <= width) { /* yes - so write the previous word's spacing */ for (i = 0; i < psw; i++) { putchar(' '); } tab += psw; } else { /* no, so write a newline and the indentation */ putchar('\n'); fputs(ind, stdout); tab = indent; } } /* write the word itself */ fputs(word, stdout); tab += ww; /* remember this word's spacing */ psw = sw; /* this output line isn't blank anymore. */ isblank = FALSE; } /* This function reformats text. */ void fmt(in) FILE *in; /* the name of the input stream */ { int ch; /* character from input stream */ int prevch; /* the previous character in the loop */ int i; /* index into ind[] or word[] */ int inword; /* boolean: are we between indent & newline? */ /* for each character in the stream... */ for (indent = -1, isblank = TRUE, inword = FALSE, i = 0, prevch = '\n'; (ch = getc(in)) != EOF; prevch = ch) { /* is this the end of a line? */ if (ch == '\n') { /* if end of last word in the input line */ if (inword) { /* if it really is a word */ if (i > 0) { /* output it */ word[i] = '\0'; putword(); } } else /* blank line in input */ { /* finish the previous paragraph */ if (!isblank) { putchar('\n'); isblank = TRUE; } /* output a blank line */ putchar('\n'); } /* continue with next input line... */ indent = -1; i = 0; inword = FALSE; continue; } /* if we're expecting indentation now... */ if (indent < 0) { /* if this is part of the indentation... */ if (ch == ' ' || ch == '\t') { /* remember it */ ind[i++] = ch; } else /* end of indentation */ { /* mark the end of the indentation string */ ind[i] = '\0'; /* calculate the width of the indentation */ for (i = indent = 0; ind[i]; i++) { if (ind[i] == '\t') indent = (indent | 7) + 1; else indent++; } /* reset the word index */ i = 0; /* reprocess that last character */ ungetc(ch, in); } /* continue in the for-loop */ continue; } /* if we get here, we're either in a word or in the space * after a word. */ inword = TRUE; /* is this the start of a new word? */ if (ch != ' ' && prevch == ' ') { /* yes! output the previous word */ word[i] = '\0'; putword(); /* reset `i' to the start of the word[] buffer */ i = 0; } word[i++] = ch; } /* if necessary, write a final newline */ if (!isblank) { putchar('\n'); isblank = TRUE; } } int main(argc, argv) int argc; char **argv; { FILE *in; /* an input stream */ int error; /* if non-zero, then an error occurred */ int i; /* handle the -width flag, if given */ if (argc > 1 && argv[1][0] == '-') { width = atoi(argv[1] + 1); if (width <= 0) { usage(); } argc--; argv++; } /* if no filenames given, then process stdin */ if (argc == 1) { fmt(stdin); } else /* one or more filenames given */ { for (error = 0, i = 1; i < argc; i++) { in = fopen(argv[i], "r"); if (!in) { perror(argv[i]); error = 3; } else { fmt(in); fclose(in); } } } /* exit, possibly indicating an error */ exit(error); /*NOTREACHED*/ }