diff options
author | Pedro F. Giffuni <pfg@FreeBSD.org> | 2016-04-13 01:46:48 +0000 |
---|---|---|
committer | Pedro F. Giffuni <pfg@FreeBSD.org> | 2016-04-13 01:46:48 +0000 |
commit | 86aba0f2937bc77b7c6e0a0662d762d41cb31175 (patch) | |
tree | 39e3c6e33f664decc8c169faec51b56c5809ef20 /usr.bin | |
parent | 6dbd80fa74da1603f589522731017631eb3d2844 (diff) | |
download | src-test2-86aba0f2937bc77b7c6e0a0662d762d41cb31175.tar.gz src-test2-86aba0f2937bc77b7c6e0a0662d762d41cb31175.zip |
Notes
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/fmt/fmt.c | 877 |
1 files changed, 497 insertions, 380 deletions
diff --git a/usr.bin/fmt/fmt.c b/usr.bin/fmt/fmt.c index ed85c2e49f2a..cbf54a9d3656 100644 --- a/usr.bin/fmt/fmt.c +++ b/usr.bin/fmt/fmt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fmt.c,v 1.16 2000/06/25 15:35:42 pjanzen Exp $ */ +/* $OpenBSD: fmt.c,v 1.21 2004/04/01 23:14:19 tedu Exp $ */ /* Sensible version of fmt * @@ -170,8 +170,9 @@ #ifndef lint static const char copyright[] = - "Copyright (c) 1997 Gareth McCaughan. All rights reserved.\n"; -#endif /* not lint */ +"Copyright (c) 1997 Gareth McCaughan. All rights reserved.\n"; + +#endif /* not lint */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -198,57 +199,74 @@ __FBSDID("$FreeBSD$"); * (returning 0 instead), but we do complain about bad numbers. */ static size_t -get_positive(const char *s, const char *err_mess, int fussyP) { - char *t; - long result = strtol(s,&t,0); - if (*t) { if (fussyP) goto Lose; else return 0; } - if (result<=0) { Lose: errx(EX_USAGE, "%s", err_mess); } - return (size_t) result; +get_positive(const char *s, const char *err_mess, int fussyP) +{ + char *t; + long result = strtol(s, &t, 0); + + if (*t) { + if (fussyP) + goto Lose; + else + return 0; + } + if (result <= 0) { +Lose: errx(EX_USAGE, "%s", err_mess); + } + return (size_t)result; } static size_t -get_nonnegative(const char *s, const char *err_mess, int fussyP) { - char *t; - long result = strtol(s,&t,0); - if (*t) { if (fussyP) goto Lose; else return 0; } - if (result<0) { Lose: errx(EX_USAGE, "%s", err_mess); } - return (size_t) result; +get_nonnegative(const char *s, const char *err_mess, int fussyP) +{ + char *t; + long result = strtol(s, &t, 0); + + if (*t) { + if (fussyP) + goto Lose; + else + return 0; + } + if (result < 0) { +Lose: errx(EX_USAGE, "%s", err_mess); + } + return (size_t)result; } /* Global variables */ -static int centerP=0; /* Try to center lines? */ -static size_t goal_length=0; /* Target length for output lines */ -static size_t max_length=0; /* Maximum length for output lines */ -static int coalesce_spaces_P=0; /* Coalesce multiple whitespace -> ' ' ? */ -static int allow_indented_paragraphs=0; /* Can first line have diff. ind.? */ -static int tab_width=8; /* Number of spaces per tab stop */ -static size_t output_tab_width=8; /* Ditto, when squashing leading spaces */ -static const wchar_t *sentence_enders=L".?!"; /* Double-space after these */ -static int grok_mail_headers=0; /* treat embedded mail headers magically? */ -static int format_troff=0; /* Format troff? */ - -static int n_errors=0; /* Number of failed files. Return on exit. */ -static wchar_t *output_buffer=0; /* Output line will be built here */ -static size_t x; /* Horizontal position in output line */ -static size_t x0; /* Ditto, ignoring leading whitespace */ +static int centerP = 0; /* Try to center lines? */ +static size_t goal_length = 0; /* Target length for output lines */ +static size_t max_length = 0; /* Maximum length for output lines */ +static int coalesce_spaces_P = 0; /* Coalesce multiple whitespace -> ' ' ? */ +static int allow_indented_paragraphs = 0; /* Can first line have diff. ind.? */ +static int tab_width = 8; /* Number of spaces per tab stop */ +static size_t output_tab_width = 8; /* Ditto, when squashing leading spaces */ +static const wchar_t *sentence_enders = L".?!"; /* Double-space after these */ +static int grok_mail_headers = 0; /* treat embedded mail headers magically? */ +static int format_troff = 0; /* Format troff? */ + +static int n_errors = 0; /* Number of failed files. Return on exit. */ +static wchar_t *output_buffer = 0; /* Output line will be built here */ +static size_t x; /* Horizontal position in output line */ +static size_t x0; /* Ditto, ignoring leading whitespace */ static size_t output_buffer_length = 0; -static size_t pending_spaces; /* Spaces to add before next word */ -static int output_in_paragraph=0; /* Any of current para written out yet? */ +static size_t pending_spaces; /* Spaces to add before next word */ +static int output_in_paragraph = 0; /* Any of current para written out yet? */ /* Prototypes */ -static void process_named_file (const char *); -static void process_stream (FILE *, const char *); -static size_t indent_length (const wchar_t *, size_t); -static int might_be_header (const wchar_t *); -static void new_paragraph (size_t, size_t); -static void output_word (size_t, size_t, const wchar_t *, size_t, - size_t); -static void output_indent (size_t); -static void center_stream (FILE *, const char *); -static wchar_t * get_line (FILE *, size_t *); -static void * xrealloc (void *, size_t); +static void process_named_file(const char *); +static void process_stream(FILE *, const char *); +static size_t indent_length(const wchar_t *, size_t); +static int might_be_header(const wchar_t *); +static void new_paragraph(size_t, size_t); +static void output_word(size_t, size_t, const wchar_t *, size_t, size_t); +static void output_indent(size_t); +static void center_stream(FILE *, const char *); +static wchar_t *get_line(FILE *, size_t *); +static void *xrealloc(void *, size_t); #define XMALLOC(x) xrealloc(0,x) @@ -256,234 +274,277 @@ static void * xrealloc (void *, size_t); * all in top-down order. Hence, |main| comes first. */ int -main(int argc, char *argv[]) { - int ch; /* used for |getopt| processing */ - wchar_t *tmp; - size_t len; - const char *src; - - (void) setlocale(LC_CTYPE, ""); - - /* 1. Grok parameters. */ - - while ((ch = getopt(argc, argv, "0123456789cd:hl:mnpst:w:")) != -1) - switch(ch) { - case 'c': - centerP = 1; - format_troff = 1; - continue; - case 'd': - src = optarg; - len = mbsrtowcs(NULL, &src, 0, NULL); - if (len == (size_t)-1) - err(EX_USAGE, "bad sentence-ending character set"); - tmp = XMALLOC((len + 1) * sizeof(wchar_t)); - mbsrtowcs(tmp, &src, len + 1, NULL); - sentence_enders = tmp; - continue; - case 'l': - output_tab_width - = get_nonnegative(optarg, "output tab width must be non-negative", 1); - continue; - case 'm': - grok_mail_headers = 1; - continue; - case 'n': - format_troff = 1; - continue; - case 'p': - allow_indented_paragraphs = 1; - continue; - case 's': - coalesce_spaces_P = 1; - continue; - case 't': - tab_width = get_positive(optarg, "tab width must be positive", 1); - continue; - case 'w': - goal_length = get_positive(optarg, "width must be positive", 1); - max_length = goal_length; - continue; - case '0': case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - /* XXX this is not a stylistically approved use of getopt() */ - if (goal_length==0) { - char *p; - p = argv[optind - 1]; - if (p[0] == '-' && p[1] == ch && !p[2]) - goal_length = get_positive(++p, "width must be nonzero", 1); - else - goal_length = get_positive(argv[optind]+1, - "width must be nonzero", 1); - max_length = goal_length; - } - continue; - case 'h': default: - fprintf(stderr, -"usage: fmt [-cmps] [-d chars] [-l num] [-t num]\n" -" [-w width | -width | goal [maximum]] [file ...]\n" -"Options: -c center each line instead of formatting\n" -" -d <chars> double-space after <chars> at line end\n" -" -l <n> turn each <n> spaces at start of line into a tab\n" -" -m try to make sure mail header lines stay separate\n" -" -n format lines beginning with a dot\n" -" -p allow indented paragraphs\n" -" -s coalesce whitespace inside lines\n" -" -t <n> have tabs every <n> columns\n" -" -w <n> set maximum width to <n>\n" -" goal set target width to goal\n"); - exit(ch=='h' ? 0 : EX_USAGE); - } - argc -= optind; argv += optind; - - /* [ goal [ maximum ] ] */ - - if (argc>0 && goal_length==0 - && (goal_length=get_positive(*argv,"goal length must be positive", 0)) - != 0) { - --argc; ++argv; - if (argc>0 - && (max_length=get_positive(*argv,"max length must be positive", 0)) - != 0) { - --argc; ++argv; - if (max_length<goal_length) - errx(EX_USAGE, "max length must be >= goal length"); - } - } - if (goal_length==0) goal_length = 65; - if (max_length==0) max_length = goal_length+10; - if (max_length >= SIZE_T_MAX / sizeof (wchar_t)) errx(EX_USAGE, "max length too large"); - /* really needn't be longer */ - output_buffer = XMALLOC((max_length+1) * sizeof(wchar_t)); - - /* 2. Process files. */ - - if (argc>0) { - while (argc-->0) process_named_file(*argv++); - } - else { - process_stream(stdin, "standard input"); - } - - /* We're done. */ - - return n_errors ? EX_NOINPUT : 0; +main(int argc, char *argv[]) +{ + int ch; /* used for |getopt| processing */ + wchar_t *tmp; + size_t len; + const char *src; + + (void)setlocale(LC_CTYPE, ""); + + /* 1. Grok parameters. */ + + while ((ch = getopt(argc, argv, "0123456789cd:hl:mnpst:w:")) != -1) + switch (ch) { + case 'c': + centerP = 1; + format_troff = 1; + continue; + case 'd': + src = optarg; + len = mbsrtowcs(NULL, &src, 0, NULL); + if (len == (size_t)-1) + err(EX_USAGE, "bad sentence-ending character set"); + tmp = XMALLOC((len + 1) * sizeof(wchar_t)); + mbsrtowcs(tmp, &src, len + 1, NULL); + sentence_enders = tmp; + continue; + case 'l': + output_tab_width + = get_nonnegative(optarg, "output tab width must be non-negative", 1); + continue; + case 'm': + grok_mail_headers = 1; + continue; + case 'n': + format_troff = 1; + continue; + case 'p': + allow_indented_paragraphs = 1; + continue; + case 's': + coalesce_spaces_P = 1; + continue; + case 't': + tab_width = get_positive(optarg, "tab width must be positive", 1); + continue; + case 'w': + goal_length = get_positive(optarg, "width must be positive", 1); + max_length = goal_length; + continue; + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + /* + * XXX this is not a stylistically approved use of + * getopt() + */ + if (goal_length == 0) { + char *p; + + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + goal_length = get_positive(++p, "width must be nonzero", 1); + else + goal_length = get_positive(argv[optind] + 1, + "width must be nonzero", 1); + max_length = goal_length; + } + continue; + case 'h': + default: + fprintf(stderr, + "usage: fmt [-cmps] [-d chars] [-l num] [-t num]\n" + " [-w width | -width | goal [maximum]] [file ...]\n" + "Options: -c center each line instead of formatting\n" + " -d <chars> double-space after <chars> at line end\n" + " -l <n> turn each <n> spaces at start of line into a tab\n" + " -m try to make sure mail header lines stay separate\n" + " -n format lines beginning with a dot\n" + " -p allow indented paragraphs\n" + " -s coalesce whitespace inside lines\n" + " -t <n> have tabs every <n> columns\n" + " -w <n> set maximum width to <n>\n" + " goal set target width to goal\n"); + exit(ch == 'h' ? 0 : EX_USAGE); + } + argc -= optind; + argv += optind; + + /* [ goal [ maximum ] ] */ + + if (argc > 0 && goal_length == 0 + && (goal_length = get_positive(*argv, "goal length must be positive", 0)) + != 0) { + --argc; + ++argv; + if (argc > 0 + && (max_length = get_positive(*argv, "max length must be positive", 0)) + != 0) { + --argc; + ++argv; + if (max_length < goal_length) + errx(EX_USAGE, "max length must be >= goal length"); + } + } + if (goal_length == 0) + goal_length = 65; + if (max_length == 0) + max_length = goal_length + 10; + if (max_length >= SIZE_T_MAX / sizeof(wchar_t)) + errx(EX_USAGE, "max length too large"); + /* really needn't be longer */ + output_buffer = XMALLOC((max_length + 1) * sizeof(wchar_t)); + + /* 2. Process files. */ + + if (argc > 0) { + while (argc-- > 0) + process_named_file(*argv++); + } else { + process_stream(stdin, "standard input"); + } + + /* We're done. */ + + return n_errors ? EX_NOINPUT : 0; } /* Process a single file, given its name. */ static void -process_named_file(const char *name) { - FILE *f=fopen(name, "r"); - if (!f) { warn("%s", name); ++n_errors; } - else { - process_stream(f, name); - if (ferror(f)) { warn("%s", name); ++n_errors; } - fclose(f); - } +process_named_file(const char *name) +{ + FILE *f = fopen(name, "r"); + + if (!f) { + warn("%s", name); + ++n_errors; + } else { + process_stream(f, name); + if (ferror(f)) { + warn("%s", name); + ++n_errors; + } + fclose(f); + } } /* Types of mail header continuation lines: */ typedef enum { - hdr_ParagraphStart = -1, - hdr_NonHeader = 0, - hdr_Header = 1, - hdr_Continuation = 2 + hdr_ParagraphStart = -1, + hdr_NonHeader = 0, + hdr_Header = 1, + hdr_Continuation = 2 } HdrType; /* Process a stream. This is where the real work happens, * except that centering is handled separately. */ static void -process_stream(FILE *stream, const char *name) { - size_t last_indent=SILLY; /* how many spaces in last indent? */ - size_t para_line_number=0; /* how many lines already read in this para? */ - size_t first_indent=SILLY; /* indentation of line 0 of paragraph */ - HdrType prev_header_type=hdr_ParagraphStart; +process_stream(FILE *stream, const char *name) +{ + size_t last_indent = SILLY; /* how many spaces in last indent? */ + size_t para_line_number = 0; /* how many lines already read in this para? */ + size_t first_indent = SILLY; /* indentation of line 0 of paragraph */ + HdrType prev_header_type = hdr_ParagraphStart; + /* ^-- header_type of previous line; -1 at para start */ - wchar_t *line; - size_t length; - - if (centerP) { center_stream(stream, name); return; } - while ((line=get_line(stream,&length)) != NULL) { - size_t np=indent_length(line, length); - { HdrType header_type=hdr_NonHeader; - if (grok_mail_headers && prev_header_type!=hdr_NonHeader) { - if (np==0 && might_be_header(line)) - header_type = hdr_Header; - else if (np>0 && prev_header_type>hdr_NonHeader) - header_type = hdr_Continuation; - } - /* We need a new paragraph if and only if: - * this line is blank, - * OR it's a troff request (and we don't format troff), - * OR it's a mail header, - * OR it's not a mail header AND the last line was one, - * OR the indentation has changed - * AND the line isn't a mail header continuation line - * AND this isn't the second line of an indented paragraph. - */ - if ( length==0 - || (line[0]=='.' && !format_troff) - || header_type==hdr_Header - || (header_type==hdr_NonHeader && prev_header_type>hdr_NonHeader) - || (np!=last_indent - && header_type != hdr_Continuation - && (!allow_indented_paragraphs || para_line_number != 1)) ) { - new_paragraph(output_in_paragraph ? last_indent : first_indent, np); - para_line_number = 0; - first_indent = np; - last_indent = np; - if (header_type==hdr_Header) last_indent=2; /* for cont. lines */ - if (length==0 || (line[0]=='.' && !format_troff)) { - if (length==0) - putwchar('\n'); - else - wprintf(L"%.*ls\n", (int)length, line); - prev_header_type=hdr_ParagraphStart; - continue; - } - } - else { - /* If this is an indented paragraph other than a mail header - * continuation, set |last_indent|. - */ - if (np != last_indent && header_type != hdr_Continuation) - last_indent=np; - } - prev_header_type = header_type; - } - - { size_t n=np; - while (n<length) { - /* Find word end and count spaces after it */ - size_t word_length=0, space_length=0; - while (n+word_length < length && line[n+word_length] != ' ') - ++word_length; - space_length = word_length; - while (n+space_length < length && line[n+space_length] == ' ') - ++space_length; - /* Send the word to the output machinery. */ - output_word(first_indent, last_indent, - line+n, word_length, space_length-word_length); - n += space_length; - } - } - ++para_line_number; - } - new_paragraph(output_in_paragraph ? last_indent : first_indent, 0); - if (ferror(stream)) { warn("%s", name); ++n_errors; } + wchar_t *line; + size_t length; + + if (centerP) { + center_stream(stream, name); + return; + } + while ((line = get_line(stream, &length)) != NULL) { + size_t np = indent_length(line, length); + + { + HdrType header_type = hdr_NonHeader; + + if (grok_mail_headers && prev_header_type != hdr_NonHeader) { + if (np == 0 && might_be_header(line)) + header_type = hdr_Header; + else if (np > 0 && prev_header_type > hdr_NonHeader) + header_type = hdr_Continuation; + } + /* + * We need a new paragraph if and only if: this line + * is blank, OR it's a troff request (and we don't + * format troff), OR it's a mail header, OR it's not + * a mail header AND the last line was one, OR the + * indentation has changed AND the line isn't a mail + * header continuation line AND this isn't the + * second line of an indented paragraph. + */ + if (length == 0 + || (line[0] == '.' && !format_troff) + || header_type == hdr_Header + || (header_type == hdr_NonHeader && prev_header_type > hdr_NonHeader) + || (np != last_indent + && header_type != hdr_Continuation + && (!allow_indented_paragraphs || para_line_number != 1))) { + new_paragraph(output_in_paragraph ? last_indent : first_indent, np); + para_line_number = 0; + first_indent = np; + last_indent = np; + if (header_type == hdr_Header) + last_indent = 2; /* for cont. lines */ + if (length == 0 || (line[0] == '.' && !format_troff)) { + if (length == 0) + putwchar('\n'); + else + wprintf(L"%.*ls\n", (int)length, + line); + prev_header_type = hdr_ParagraphStart; + continue; + } + } else { + /* + * If this is an indented paragraph other + * than a mail header continuation, set + * |last_indent|. + */ + if (np != last_indent && + header_type != hdr_Continuation) + last_indent = np; + } + prev_header_type = header_type; + } + + { + size_t n = np; + + while (n < length) { + /* Find word end and count spaces after it */ + size_t word_length = 0, space_length = 0; + + while (n + word_length < length && + line[n + word_length] != ' ') + ++word_length; + space_length = word_length; + while (n + space_length < length && + line[n + space_length] == ' ') + ++space_length; + /* Send the word to the output machinery. */ + output_word(first_indent, last_indent, + line + n, word_length, + space_length - word_length); + n += space_length; + } + } + ++para_line_number; + } + new_paragraph(output_in_paragraph ? last_indent : first_indent, 0); + if (ferror(stream)) { + warn("%s", name); + ++n_errors; + } } /* How long is the indent on this line? */ static size_t -indent_length(const wchar_t *line, size_t length) { - size_t n=0; - while (n<length && *line++ == ' ') ++n; - return n; +indent_length(const wchar_t *line, size_t length) +{ + size_t n = 0; + + while (n < length && *line++ == ' ') + ++n; + return n; } /* Might this line be a mail header? @@ -493,35 +554,45 @@ indent_length(const wchar_t *line, size_t length) { * conservative to avoid mangling ordinary civilised text. */ static int -might_be_header(const wchar_t *line) { - if (!iswupper(*line++)) return 0; - while (*line && (iswalnum(*line) || *line=='-')) ++line; - return (*line==':' && iswspace(line[1])); +might_be_header(const wchar_t *line) +{ + if (!iswupper(*line++)) + return 0; + while (*line && (iswalnum(*line) || *line == '-')) + ++line; + return (*line == ':' && iswspace(line[1])); } /* Begin a new paragraph with an indent of |indent| spaces. */ static void -new_paragraph(size_t old_indent, size_t indent) { - if (output_buffer_length) { - if (old_indent>0) output_indent(old_indent); - wprintf(L"%.*ls\n", (int)output_buffer_length, output_buffer); - } - x=indent; x0=0; output_buffer_length=0; pending_spaces=0; - output_in_paragraph = 0; +new_paragraph(size_t old_indent, size_t indent) +{ + if (output_buffer_length) { + if (old_indent > 0) + output_indent(old_indent); + wprintf(L"%.*ls\n", (int)output_buffer_length, output_buffer); + } + x = indent; + x0 = 0; + output_buffer_length = 0; + pending_spaces = 0; + output_in_paragraph = 0; } /* Output spaces or tabs for leading indentation. */ static void -output_indent(size_t n_spaces) { - if (output_tab_width) { - while (n_spaces >= output_tab_width) { - putwchar('\t'); - n_spaces -= output_tab_width; - } - } - while (n_spaces-- > 0) putwchar(' '); +output_indent(size_t n_spaces) +{ + if (output_tab_width) { + while (n_spaces >= output_tab_width) { + putwchar('\t'); + n_spaces -= output_tab_width; + } + } + while (n_spaces-- > 0) + putwchar(' '); } /* Output a single word, or add it to the buffer. @@ -529,93 +600,120 @@ output_indent(size_t n_spaces) { * lines of a paragraph. They'll often be the same, of course. */ static void -output_word(size_t indent0, size_t indent1, const wchar_t *word, size_t length, size_t spaces) { - size_t new_x; - size_t indent = output_in_paragraph ? indent1 : indent0; - size_t width; - const wchar_t *p; - int cwidth; - - for (p = word, width = 0; p < &word[length]; p++) - width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1; - - new_x = x + pending_spaces + width; - - /* If either |spaces==0| (at end of line) or |coalesce_spaces_P| - * (squashing internal whitespace), then add just one space; - * except that if the last character was a sentence-ender we - * actually add two spaces. - */ - if (coalesce_spaces_P || spaces==0) - spaces = wcschr(sentence_enders, word[length-1]) ? 2 : 1; - - if (new_x<=goal_length) { - /* After adding the word we still aren't at the goal length, - * so clearly we add it to the buffer rather than outputing it. - */ - wmemset(output_buffer+output_buffer_length, L' ', pending_spaces); - x0 += pending_spaces; x += pending_spaces; - output_buffer_length += pending_spaces; - wmemcpy(output_buffer+output_buffer_length, word, length); - x0 += width; x += width; output_buffer_length += length; - pending_spaces = spaces; - } - else { - /* Adding the word takes us past the goal. Print the line-so-far, - * and the word too iff either (1) the lsf is empty or (2) that - * makes us nearer the goal but doesn't take us over the limit, - * or (3) the word on its own takes us over the limit. - * In case (3) we put a newline in between. - */ - if (indent>0) output_indent(indent); - wprintf(L"%.*ls", (int)output_buffer_length, output_buffer); - if (x0==0 || (new_x <= max_length && new_x-goal_length <= goal_length-x)) { - wprintf(L"%*ls", (int)pending_spaces, L""); - goto write_out_word; - } - else { - /* If the word takes us over the limit on its own, just - * spit it out and don't bother buffering it. - */ - if (indent+width > max_length) { - putwchar('\n'); - if (indent>0) output_indent(indent); -write_out_word: - wprintf(L"%.*ls", (int)length, word); - x0 = 0; x = indent1; pending_spaces = 0; - output_buffer_length = 0; - } - else { - wmemcpy(output_buffer, word, length); - x0 = width; x = width+indent1; pending_spaces = spaces; - output_buffer_length = length; - } - } - putwchar('\n'); - output_in_paragraph = 1; - } +output_word(size_t indent0, size_t indent1, const wchar_t *word, size_t length, size_t spaces) +{ + size_t new_x; + size_t indent = output_in_paragraph ? indent1 : indent0; + size_t width; + const wchar_t *p; + int cwidth; + + for (p = word, width = 0; p < &word[length]; p++) + width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1; + + new_x = x + pending_spaces + width; + + /* + * If either |spaces==0| (at end of line) or |coalesce_spaces_P| + * (squashing internal whitespace), then add just one space; except + * that if the last character was a sentence-ender we actually add + * two spaces. + */ + if (coalesce_spaces_P || spaces == 0) + spaces = wcschr(sentence_enders, word[length - 1]) ? 2 : 1; + + if (new_x <= goal_length) { + /* + * After adding the word we still aren't at the goal length, + * so clearly we add it to the buffer rather than outputing + * it. + */ + wmemset(output_buffer + output_buffer_length, L' ', + pending_spaces); + x0 += pending_spaces; + x += pending_spaces; + output_buffer_length += pending_spaces; + wmemcpy(output_buffer + output_buffer_length, word, length); + x0 += width; + x += width; + output_buffer_length += length; + pending_spaces = spaces; + } else { + /* + * Adding the word takes us past the goal. Print the + * line-so-far, and the word too iff either (1) the lsf is + * empty or (2) that makes us nearer the goal but doesn't + * take us over the limit, or (3) the word on its own takes + * us over the limit. In case (3) we put a newline in + * between. + */ + if (indent > 0) + output_indent(indent); + wprintf(L"%.*ls", (int)output_buffer_length, output_buffer); + if (x0 == 0 || (new_x <= max_length && + new_x - goal_length <= goal_length - x)) { + wprintf(L"%*ls", (int)pending_spaces, L""); + goto write_out_word; + } else { + /* + * If the word takes us over the limit on its own, + * just spit it out and don't bother buffering it. + */ + if (indent + width > max_length) { + putwchar('\n'); + if (indent > 0) + output_indent(indent); + write_out_word: + wprintf(L"%.*ls", (int)length, word); + x0 = 0; + x = indent1; + pending_spaces = 0; + output_buffer_length = 0; + } else { + wmemcpy(output_buffer, word, length); + x0 = width; + x = width + indent1; + pending_spaces = spaces; + output_buffer_length = length; + } + } + putwchar('\n'); + output_in_paragraph = 1; + } } /* Process a stream, but just center its lines rather than trying to * format them neatly. */ static void -center_stream(FILE *stream, const char *name) { - wchar_t *line, *p; - size_t length; - size_t width; - int cwidth; - while ((line=get_line(stream, &length)) != 0) { - size_t l=length; - while (l>0 && iswspace(*line)) { ++line; --l; } - length=l; - for (p = line, width = 0; p < &line[length]; p++) - width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1; - l = width; - while (l<goal_length) { putwchar(' '); l+=2; } - wprintf(L"%.*ls\n", (int)length, line); - } - if (ferror(stream)) { warn("%s", name); ++n_errors; } +center_stream(FILE *stream, const char *name) +{ + wchar_t *line, *p; + size_t length; + size_t width; + int cwidth; + + while ((line = get_line(stream, &length)) != 0) { + size_t l = length; + + while (l > 0 && iswspace(*line)) { + ++line; + --l; + } + length = l; + for (p = line, width = 0; p < &line[length]; p++) + width += (cwidth = wcwidth(*p)) > 0 ? cwidth : 1; + l = width; + while (l < goal_length) { + putwchar(' '); + l += 2; + } + wprintf(L"%.*ls\n", (int)length, line); + } + if (ferror(stream)) { + warn("%s", name); + ++n_errors; + } } /* Get a single line from a stream. Expand tabs, strip control @@ -630,41 +728,60 @@ center_stream(FILE *stream, const char *name) { * |pending_spaces|. */ static wchar_t * -get_line(FILE *stream, size_t *lengthp) { - static wchar_t *buf=NULL; - static size_t length=0; - size_t len=0; - wint_t ch; - size_t spaces_pending=0; - int troff=0; - size_t col=0; - int cwidth; - - if (buf==NULL) { length=100; buf=XMALLOC(length * sizeof(wchar_t)); } - while ((ch=getwc(stream)) != '\n' && ch != WEOF) { - if (len+spaces_pending==0 && ch=='.' && !format_troff) troff=1; - if (ch==' ') ++spaces_pending; - else if (troff || iswprint(ch)) { - while (len+spaces_pending >= length) { - length*=2; buf=xrealloc(buf, length * sizeof(wchar_t)); - } - while (spaces_pending > 0) { --spaces_pending; buf[len++]=' '; col++; } - buf[len++] = ch; - col += (cwidth = wcwidth(ch)) > 0 ? cwidth : 1; - } - else if (ch=='\t') - spaces_pending += tab_width - (col+spaces_pending)%tab_width; - else if (ch=='\b') { if (len) --len; if (col) --col; } - } - *lengthp=len; - return (len>0 || ch!=WEOF) ? buf : 0; +get_line(FILE *stream, size_t *lengthp) +{ + static wchar_t *buf = NULL; + static size_t length = 0; + size_t len = 0; + wint_t ch; + size_t spaces_pending = 0; + int troff = 0; + size_t col = 0; + int cwidth; + + if (buf == NULL) { + length = 100; + buf = XMALLOC(length * sizeof(wchar_t)); + } + while ((ch = getwc(stream)) != '\n' && ch != WEOF) { + if (len + spaces_pending == 0 && ch == '.' && !format_troff) + troff = 1; + if (ch == ' ') + ++spaces_pending; + else if (troff || iswprint(ch)) { + while (len + spaces_pending >= length) { + length *= 2; + buf = xrealloc(buf, length * sizeof(wchar_t)); + } + while (spaces_pending > 0) { + --spaces_pending; + buf[len++] = ' '; + col++; + } + buf[len++] = ch; + col += (cwidth = wcwidth(ch)) > 0 ? cwidth : 1; + } else if (ch == '\t') + spaces_pending += tab_width - + (col + spaces_pending) % tab_width; + else if (ch == '\b') { + if (len) + --len; + if (col) + --col; + } + } + *lengthp = len; + return (len > 0 || ch != WEOF) ? buf : 0; } /* (Re)allocate some memory, exiting with an error if we can't. */ static void * -xrealloc(void *ptr, size_t nbytes) { - void *p = realloc(ptr, nbytes); - if (p == NULL) errx(EX_OSERR, "out of memory"); - return p; +xrealloc(void *ptr, size_t nbytes) +{ + void *p = realloc(ptr, nbytes); + + if (p == NULL) + errx(EX_OSERR, "out of memory"); + return p; } |