Avoid spurious failure on x86 solaris2.9 when using c89.
[coreutils.git] / src / fold.c
blobf6fce6a0155903b290965836aef5c13d33aaf335
1 /* fold -- wrap each input line to fit in specified width.
2 Copyright (C) 91, 1995-2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie, djm@gnu.ai.mit.edu. */
20 #include <config.h>
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
26 #include "system.h"
27 #include "error.h"
28 #include "posixver.h"
29 #include "xstrtol.h"
31 #define TAB_WIDTH 8
33 /* The official name of this program (e.g., no `g' prefix). */
34 #define PROGRAM_NAME "fold"
36 #define AUTHORS "David MacKenzie"
38 /* The name this program was run with. */
39 char *program_name;
41 /* If nonzero, try to break on whitespace. */
42 static bool break_spaces;
44 /* If nonzero, count bytes, not column positions. */
45 static bool count_bytes;
47 /* If nonzero, at least one of the files we read was standard input. */
48 static bool have_read_stdin;
50 static struct option const longopts[] =
52 {"bytes", no_argument, NULL, 'b'},
53 {"spaces", no_argument, NULL, 's'},
54 {"width", required_argument, NULL, 'w'},
55 {GETOPT_HELP_OPTION_DECL},
56 {GETOPT_VERSION_OPTION_DECL},
57 {NULL, 0, NULL, 0}
60 void
61 usage (int status)
63 if (status != EXIT_SUCCESS)
64 fprintf (stderr, _("Try `%s --help' for more information.\n"),
65 program_name);
66 else
68 printf (_("\
69 Usage: %s [OPTION]... [FILE]...\n\
70 "),
71 program_name);
72 fputs (_("\
73 Wrap input lines in each FILE (standard input by default), writing to\n\
74 standard output.\n\
75 \n\
76 "), stdout);
77 fputs (_("\
78 Mandatory arguments to long options are mandatory for short options too.\n\
79 "), stdout);
80 fputs (_("\
81 -b, --bytes count bytes rather than columns\n\
82 -s, --spaces break at spaces\n\
83 -w, --width=WIDTH use WIDTH columns instead of 80\n\
84 "), stdout);
85 fputs (HELP_OPTION_DESCRIPTION, stdout);
86 fputs (VERSION_OPTION_DESCRIPTION, stdout);
87 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
89 exit (status);
92 /* Assuming the current column is COLUMN, return the column that
93 printing C will move the cursor to.
94 The first column is 0. */
96 static size_t
97 adjust_column (size_t column, char c)
99 if (!count_bytes)
101 if (c == '\b')
103 if (column > 0)
104 column--;
106 else if (c == '\r')
107 column = 0;
108 else if (c == '\t')
109 column += TAB_WIDTH - column % TAB_WIDTH;
110 else /* if (isprint (c)) */
111 column++;
113 else
114 column++;
115 return column;
118 /* Fold file FILENAME, or standard input if FILENAME is "-",
119 to stdout, with maximum line length WIDTH.
120 Return true if successful. */
122 static bool
123 fold_file (char *filename, size_t width)
125 FILE *istream;
126 register int c;
127 size_t column = 0; /* Screen column where next char will go. */
128 size_t offset_out = 0; /* Index in `line_out' for next char. */
129 static char *line_out = NULL;
130 static size_t allocated_out = 0;
131 int saved_errno;
133 if (STREQ (filename, "-"))
135 istream = stdin;
136 have_read_stdin = true;
138 else
139 istream = fopen (filename, "r");
141 if (istream == NULL)
143 error (0, errno, "%s", filename);
144 return false;
147 while ((c = getc (istream)) != EOF)
149 if (offset_out + 1 >= allocated_out)
150 line_out = x2nrealloc (line_out, &allocated_out, sizeof *line_out);
152 if (c == '\n')
154 line_out[offset_out++] = c;
155 fwrite (line_out, sizeof (char), offset_out, stdout);
156 column = offset_out = 0;
157 continue;
160 rescan:
161 column = adjust_column (column, c);
163 if (column > width)
165 /* This character would make the line too long.
166 Print the line plus a newline, and make this character
167 start the next line. */
168 if (break_spaces)
170 bool found_blank = false;
171 size_t logical_end = offset_out;
173 /* Look for the last blank. */
174 while (logical_end)
176 --logical_end;
177 if (ISBLANK (line_out[logical_end]))
179 found_blank = true;
180 break;
184 if (found_blank)
186 size_t i;
188 /* Found a blank. Don't output the part after it. */
189 logical_end++;
190 fwrite (line_out, sizeof (char), (size_t) logical_end,
191 stdout);
192 putchar ('\n');
193 /* Move the remainder to the beginning of the next line.
194 The areas being copied here might overlap. */
195 memmove (line_out, line_out + logical_end,
196 offset_out - logical_end);
197 offset_out -= logical_end;
198 for (column = i = 0; i < offset_out; i++)
199 column = adjust_column (column, line_out[i]);
200 goto rescan;
204 if (offset_out == 0)
206 line_out[offset_out++] = c;
207 continue;
210 line_out[offset_out++] = '\n';
211 fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
212 column = offset_out = 0;
213 goto rescan;
216 line_out[offset_out++] = c;
219 saved_errno = errno;
221 if (offset_out)
222 fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
224 if (ferror (istream))
226 error (0, saved_errno, "%s", filename);
227 if (!STREQ (filename, "-"))
228 fclose (istream);
229 return false;
231 if (!STREQ (filename, "-") && fclose (istream) == EOF)
233 error (0, errno, "%s", filename);
234 return false;
237 return true;
241 main (int argc, char **argv)
243 size_t width = 80;
244 int i;
245 int optc;
246 bool ok;
248 initialize_main (&argc, &argv);
249 program_name = argv[0];
250 setlocale (LC_ALL, "");
251 bindtextdomain (PACKAGE, LOCALEDIR);
252 textdomain (PACKAGE);
254 atexit (close_stdout);
256 break_spaces = count_bytes = have_read_stdin = false;
258 /* Turn any numeric options into -w options. */
259 for (i = 1; i < argc; i++)
261 char const *a = argv[i];
262 if (a[0] == '-')
264 if (a[1] == '-' && ! a[2])
265 break;
266 if (ISDIGIT (a[1]))
268 size_t len_a = strlen (a);
269 char *s = xmalloc (len_a + 2);
270 s[0] = '-';
271 s[1] = 'w';
272 memcpy (s + 2, a + 1, len_a);
273 argv[i] = s;
274 if (200112 <= posix2_version ())
276 error (0, 0, _("`%s' option is obsolete; use `%s'"), a, s);
277 usage (EXIT_FAILURE);
283 while ((optc = getopt_long (argc, argv, "bsw:", longopts, NULL)) != -1)
285 switch (optc)
287 case 'b': /* Count bytes rather than columns. */
288 count_bytes = true;
289 break;
291 case 's': /* Break at word boundaries. */
292 break_spaces = true;
293 break;
295 case 'w': /* Line width. */
297 unsigned long int tmp_ulong;
298 if (! (xstrtoul (optarg, NULL, 10, &tmp_ulong, "") == LONGINT_OK
299 && 0 < tmp_ulong && tmp_ulong < SIZE_MAX - TAB_WIDTH))
300 error (EXIT_FAILURE, 0,
301 _("invalid number of columns: `%s'"), optarg);
302 width = tmp_ulong;
304 break;
306 case_GETOPT_HELP_CHAR;
308 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
310 default:
311 usage (EXIT_FAILURE);
315 if (argc == optind)
316 ok = fold_file ("-", width);
317 else
319 ok = true;
320 for (i = optind; i < argc; i++)
321 ok &= fold_file (argv[i], width);
324 if (have_read_stdin && fclose (stdin) == EOF)
325 error (EXIT_FAILURE, errno, "-");
327 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);