Tizen 2.0 Release
[external/tizen-coreutils.git] / src / fold.c
1 /* fold -- wrap each input line to fit in specified width.
2    Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
3
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.
8
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.
13
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by David MacKenzie, djm@gnu.ai.mit.edu. */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25
26 #include "system.h"
27 #include "error.h"
28 #include "quote.h"
29 #include "xstrtol.h"
30
31 #define TAB_WIDTH 8
32
33 /* The official name of this program (e.g., no `g' prefix).  */
34 #define PROGRAM_NAME "fold"
35
36 #define AUTHORS "David MacKenzie"
37
38 /* The name this program was run with. */
39 char *program_name;
40
41 /* If nonzero, try to break on whitespace. */
42 static bool break_spaces;
43
44 /* If nonzero, count bytes, not column positions. */
45 static bool count_bytes;
46
47 /* If nonzero, at least one of the files we read was standard input. */
48 static bool have_read_stdin;
49
50 static char const shortopts[] = "bsw:0::1::2::3::4::5::6::7::8::9::";
51
52 static struct option const longopts[] =
53 {
54   {"bytes", no_argument, NULL, 'b'},
55   {"spaces", no_argument, NULL, 's'},
56   {"width", required_argument, NULL, 'w'},
57   {GETOPT_HELP_OPTION_DECL},
58   {GETOPT_VERSION_OPTION_DECL},
59   {NULL, 0, NULL, 0}
60 };
61
62 void
63 usage (int status)
64 {
65   if (status != EXIT_SUCCESS)
66     fprintf (stderr, _("Try `%s --help' for more information.\n"),
67              program_name);
68   else
69     {
70       printf (_("\
71 Usage: %s [OPTION]... [FILE]...\n\
72 "),
73               program_name);
74       fputs (_("\
75 Wrap input lines in each FILE (standard input by default), writing to\n\
76 standard output.\n\
77 \n\
78 "), stdout);
79       fputs (_("\
80 Mandatory arguments to long options are mandatory for short options too.\n\
81 "), stdout);
82       fputs (_("\
83   -b, --bytes         count bytes rather than columns\n\
84   -s, --spaces        break at spaces\n\
85   -w, --width=WIDTH   use WIDTH columns instead of 80\n\
86 "), stdout);
87       fputs (HELP_OPTION_DESCRIPTION, stdout);
88       fputs (VERSION_OPTION_DESCRIPTION, stdout);
89       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
90     }
91   exit (status);
92 }
93
94 /* Assuming the current column is COLUMN, return the column that
95    printing C will move the cursor to.
96    The first column is 0. */
97
98 static size_t
99 adjust_column (size_t column, char c)
100 {
101   if (!count_bytes)
102     {
103       if (c == '\b')
104         {
105           if (column > 0)
106             column--;
107         }
108       else if (c == '\r')
109         column = 0;
110       else if (c == '\t')
111         column += TAB_WIDTH - column % TAB_WIDTH;
112       else /* if (isprint (c)) */
113         column++;
114     }
115   else
116     column++;
117   return column;
118 }
119
120 /* Fold file FILENAME, or standard input if FILENAME is "-",
121    to stdout, with maximum line length WIDTH.
122    Return true if successful.  */
123
124 static bool
125 fold_file (char const *filename, size_t width)
126 {
127   FILE *istream;
128   int c;
129   size_t column = 0;            /* Screen column where next char will go. */
130   size_t offset_out = 0;        /* Index in `line_out' for next char. */
131   static char *line_out = NULL;
132   static size_t allocated_out = 0;
133   int saved_errno;
134
135   if (STREQ (filename, "-"))
136     {
137       istream = stdin;
138       have_read_stdin = true;
139     }
140   else
141     istream = fopen (filename, "r");
142
143   if (istream == NULL)
144     {
145       error (0, errno, "%s", filename);
146       return false;
147     }
148
149   while ((c = getc (istream)) != EOF)
150     {
151       if (offset_out + 1 >= allocated_out)
152         line_out = X2REALLOC (line_out, &allocated_out);
153
154       if (c == '\n')
155         {
156           line_out[offset_out++] = c;
157           fwrite (line_out, sizeof (char), offset_out, stdout);
158           column = offset_out = 0;
159           continue;
160         }
161
162     rescan:
163       column = adjust_column (column, c);
164
165       if (column > width)
166         {
167           /* This character would make the line too long.
168              Print the line plus a newline, and make this character
169              start the next line. */
170           if (break_spaces)
171             {
172               bool found_blank = false;
173               size_t logical_end = offset_out;
174
175               /* Look for the last blank. */
176               while (logical_end)
177                 {
178                   --logical_end;
179                   if (isblank (to_uchar (line_out[logical_end])))
180                     {
181                       found_blank = true;
182                       break;
183                     }
184                 }
185
186               if (found_blank)
187                 {
188                   size_t i;
189
190                   /* Found a blank.  Don't output the part after it. */
191                   logical_end++;
192                   fwrite (line_out, sizeof (char), (size_t) logical_end,
193                           stdout);
194                   putchar ('\n');
195                   /* Move the remainder to the beginning of the next line.
196                      The areas being copied here might overlap. */
197                   memmove (line_out, line_out + logical_end,
198                            offset_out - logical_end);
199                   offset_out -= logical_end;
200                   for (column = i = 0; i < offset_out; i++)
201                     column = adjust_column (column, line_out[i]);
202                   goto rescan;
203                 }
204             }
205
206           if (offset_out == 0)
207             {
208               line_out[offset_out++] = c;
209               continue;
210             }
211
212           line_out[offset_out++] = '\n';
213           fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
214           column = offset_out = 0;
215           goto rescan;
216         }
217
218       line_out[offset_out++] = c;
219     }
220
221   saved_errno = errno;
222
223   if (offset_out)
224     fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
225
226   if (ferror (istream))
227     {
228       error (0, saved_errno, "%s", filename);
229       if (!STREQ (filename, "-"))
230         fclose (istream);
231       return false;
232     }
233   if (!STREQ (filename, "-") && fclose (istream) == EOF)
234     {
235       error (0, errno, "%s", filename);
236       return false;
237     }
238
239   return true;
240 }
241
242 int
243 main (int argc, char **argv)
244 {
245   size_t width = 80;
246   int i;
247   int optc;
248   bool ok;
249
250   initialize_main (&argc, &argv);
251   program_name = argv[0];
252   setlocale (LC_ALL, "");
253   bindtextdomain (PACKAGE, LOCALEDIR);
254   textdomain (PACKAGE);
255
256   atexit (close_stdout);
257
258   break_spaces = count_bytes = have_read_stdin = false;
259
260   while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
261     {
262       char optargbuf[2];
263
264       switch (optc)
265         {
266         case 'b':               /* Count bytes rather than columns. */
267           count_bytes = true;
268           break;
269
270         case 's':               /* Break at word boundaries. */
271           break_spaces = true;
272           break;
273
274         case '0': case '1': case '2': case '3': case '4':
275         case '5': case '6': case '7': case '8': case '9':
276           if (optarg)
277             optarg--;
278           else
279             {
280               optargbuf[0] = optc;
281               optargbuf[1] = '\0';
282               optarg = optargbuf;
283             }
284           /* Fall through.  */
285         case 'w':               /* Line width. */
286           {
287             unsigned long int tmp_ulong;
288             if (! (xstrtoul (optarg, NULL, 10, &tmp_ulong, "") == LONGINT_OK
289                    && 0 < tmp_ulong && tmp_ulong < SIZE_MAX - TAB_WIDTH))
290               error (EXIT_FAILURE, 0,
291                      _("invalid number of columns: %s"), quote (optarg));
292             width = tmp_ulong;
293           }
294           break;
295
296         case_GETOPT_HELP_CHAR;
297
298         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
299
300         default:
301           usage (EXIT_FAILURE);
302         }
303     }
304
305   if (argc == optind)
306     ok = fold_file ("-", width);
307   else
308     {
309       ok = true;
310       for (i = optind; i < argc; i++)
311         ok &= fold_file (argv[i], width);
312     }
313
314   if (have_read_stdin && fclose (stdin) == EOF)
315     error (EXIT_FAILURE, errno, "-");
316
317   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
318 }