Merged in changes for 1.3.5.
[platform/upstream/coreutils.git] / src / fold.c
1 /* fold -- wrap each input line to fit in specified width.
2    Copyright (C) 1991 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
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* Written by David MacKenzie, djm@gnu.ai.mit.edu. */
19
20 /* Get isblank from GNU libc.  */
21 #define _GNU_SOURCE
22 #include <ctype.h>
23 #ifndef isblank
24 #define isblank(c) ((c) == ' ' || (c) == '\t')
25 #endif
26 #include <stdio.h>
27 #include <getopt.h>
28 #include <sys/types.h>
29 #include "system.h"
30
31 char *xrealloc ();
32 void error ();
33
34 static int adjust_column ();
35 static int fold_file ();
36
37 /* If nonzero, try to break on whitespace. */
38 static int break_spaces;
39
40 /* If nonzero, count bytes, not column positions. */
41 static int count_bytes;
42
43 /* If nonzero, at least one of the files we read was standard input. */
44 static int have_read_stdin;
45
46 /* The name this program was run with. */
47 char *program_name;
48
49 static struct option const longopts[] =
50 {
51   {"bytes", 0, NULL, 'b'},
52   {"spaces", 0, NULL, 's'},
53   {"width", 1, NULL, 'w'},
54   {NULL, 0, NULL, 0}
55 };
56 \f
57 void
58 main (argc, argv)
59      int argc;
60      char **argv;
61 {
62   int width = 80;
63   int i;
64   int optc;
65   int errs = 0;
66
67   program_name = argv[0];
68   break_spaces = count_bytes = have_read_stdin = 0;
69
70   while ((optc = getopt_long (argc, argv, "bsw:", longopts, (int *) 0))
71          != EOF)
72     {
73       switch (optc)
74         {
75         case 'b':               /* Count bytes rather than columns. */
76           count_bytes = 1;
77           break;
78
79         case 's':               /* Break at word boundaries. */
80           break_spaces = 1;
81           break;
82
83         case 'w':               /* Line width. */
84           width = atoi (optarg);
85           if (width < 1)
86             error (1, 0, "%s: invalid line width", optarg);
87           break;
88
89         default:
90           fprintf (stderr, "\
91 Usage: %s [-bs] [-w width] [--bytes] [--spaces] [--width=width] [file...]\n",
92                    argv[0]);
93           exit (1);
94         }
95     }
96
97   if (argc == optind)
98     errs |= fold_file ("-", width);
99   else
100     for (i = optind; i < argc; i++)
101       errs |= fold_file (argv[i], width);
102
103   if (have_read_stdin && fclose (stdin) == EOF)
104     error (1, errno, "-");
105   if (fclose (stdout) == EOF)
106     error (1, errno, "write error");
107
108   exit (errs);
109 }
110
111 /* Fold file FILENAME, or standard input if FILENAME is "-",
112    to stdout, with maximum line length WIDTH.
113    Return 0 if successful, 1 if an error occurs. */
114
115 static int
116 fold_file (filename, width)
117      char *filename;
118      int width;
119 {
120   FILE *istream;
121   register int c;
122   int column = 0;               /* Screen column where next char will go. */
123   int offset_out = 0;           /* Index in `line_out' for next char. */
124   static char *line_out = NULL;
125   static size_t allocated_out = 0;
126
127   if (!strcmp (filename, "-"))
128     {
129       istream = stdin;
130       have_read_stdin = 1;
131     }
132   else
133     istream = fopen (filename, "r");
134
135   if (istream == NULL)
136     {
137       error (0, errno, "%s", filename);
138       return 1;
139     }
140
141   while ((c = getc (istream)) != EOF)
142     {
143       if (offset_out + 1 >= allocated_out)
144         {
145           allocated_out += 1024;
146           line_out = xrealloc (line_out, allocated_out);
147         }
148       
149       if (c == '\n')
150         {
151           line_out[offset_out++] = c;
152           fwrite (line_out, sizeof (char), offset_out, stdout);
153           column = offset_out = 0;
154           continue;
155         }
156
157     rescan:
158       column = adjust_column (column, c);
159
160       if (column > width)
161         {
162           /* This character would make the line too long.
163              Print the line plus a newline, and make this character
164              start the next line. */
165           if (break_spaces)
166             {
167               /* Look for the last blank. */
168               int logical_end;
169
170               for (logical_end = offset_out - 1; logical_end >= 0;
171                    logical_end--)
172                 if (isblank (line_out[logical_end]))
173                   break;
174               if (logical_end >= 0)
175                 {
176                   int i;
177
178                   /* Found a blank.  Don't output the part after it. */
179                   logical_end++;
180                   fwrite (line_out, sizeof (char), logical_end, stdout);
181                   putchar ('\n');
182                   /* Move the remainder to the beginning of the next line.
183                      The areas being copied here might overlap. */
184                   bcopy (line_out + logical_end, line_out,
185                          offset_out - logical_end);
186                   offset_out -= logical_end;
187                   for (column = i = 0; i < offset_out; i++)
188                     column = adjust_column (column, line_out[i]);
189                   goto rescan;
190                 }
191             }
192           line_out[offset_out++] = '\n';
193           fwrite (line_out, sizeof (char), offset_out, stdout);
194           column = offset_out = 0;
195           goto rescan;
196         }
197
198       line_out[offset_out++] = c;
199     }
200
201   if (offset_out)
202     fwrite (line_out, sizeof (char), offset_out, stdout);
203
204   if (ferror (istream))
205     {
206       error (0, errno, "%s", filename);
207       if (strcmp (filename, "-"))
208         fclose (istream);
209       return 1;
210     }
211   if (strcmp (filename, "-") && fclose (istream) == EOF)
212     {
213       error (0, errno, "%s", filename);
214       return 1;
215     }
216
217   if (ferror (stdout))
218     {
219       error (0, errno, "write error");
220       return 1;
221     }
222
223   return 0;
224 }
225
226 /* Assuming the current column is COLUMN, return the column that
227    printing C will move the cursor to.
228    The first column is 0. */
229
230 static int
231 adjust_column (column, c)
232      int column;
233      char c;
234 {
235   if (!count_bytes)
236     {
237       if (c == '\b')
238         {
239           if (column > 0)
240             column--;
241         }
242       else if (c == '\r')
243         column = 0;
244       else if (c == '\t')
245         column = column + 8 - column % 8;
246       else /* if (isprint (c)) */
247         column++;
248     }
249   else
250     column++;
251   return column;
252 }