Don't include version.h.
[platform/upstream/coreutils.git] / src / expand.c
1 /* expand - convert tabs to spaces
2    Copyright (C) 1989, 1991, 1995 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* By default, convert all tabs to spaces.
19    Preserves backspace characters in the output; they decrement the
20    column count for tab calculations.
21    The default action is equivalent to -8.
22
23    Options:
24    --tabs=tab1[,tab2[,...]]
25    -t tab1[,tab2[,...]]
26    -tab1[,tab2[,...]]   If only one tab stop is given, set the tabs tab1
27                         spaces apart instead of the default 8.  Otherwise,
28                         set the tabs at columns tab1, tab2, etc. (numbered from
29                         0); replace any tabs beyond the tabstops given with
30                         single spaces.
31    --initial
32    -i                   Only convert initial tabs on each line to spaces.
33
34    David MacKenzie <djm@gnu.ai.mit.edu> */
35
36 #include <config.h>
37
38 /* Get isblank from GNU libc.  */
39 #define _GNU_SOURCE
40
41 #include <stdio.h>
42 #include <getopt.h>
43 #include <sys/types.h>
44 #include "system.h"
45 #include "error.h"
46
47 /* The number of bytes added at a time to the amount of memory
48    allocated for the output line. */
49 #define OUTPUT_BLOCK 256
50
51 /* The number of bytes added at a time to the amount of memory
52    allocated for the list of tabstops. */
53 #define TABLIST_BLOCK 256
54
55 char *xmalloc ();
56 char *xrealloc ();
57
58 /* The name this program was run with. */
59 char *program_name;
60
61 /* If nonzero, convert blanks even after nonblank characters have been
62    read on the line. */
63 static int convert_entire_line;
64
65 /* If nonzero, the size of all tab stops.  If zero, use `tab_list' instead. */
66 static int tab_size;
67
68 /* Array of the explicit column numbers of the tab stops;
69    after `tab_list' is exhausted, each additional tab is replaced
70    by a space.  The first column is column 0. */
71 static int *tab_list;
72
73 /* The index of the first invalid element of `tab_list',
74    where the next element can be added. */
75 static int first_free_tab;
76
77 /* Null-terminated array of input filenames. */
78 static char **file_list;
79
80 /* Default for `file_list' if no files are given on the command line. */
81 static char *stdin_argv[] =
82 {
83   "-", NULL
84 };
85
86 /* Nonzero if we have ever read standard input. */
87 static int have_read_stdin;
88
89 /* Status to return to the system. */
90 static int exit_status;
91
92 /* If nonzero, display usage information and exit.  */
93 static int show_help;
94
95 /* If nonzero, print the version on standard output then exit.  */
96 static int show_version;
97
98 static struct option const longopts[] =
99 {
100   {"tabs", required_argument, NULL, 't'},
101   {"initial", no_argument, NULL, 'i'},
102   {"help", no_argument, &show_help, 1},
103   {"version", no_argument, &show_version, 1},
104   {NULL, 0, NULL, 0}
105 };
106
107 static void
108 usage (int status)
109 {
110   if (status != 0)
111     fprintf (stderr, _("Try `%s --help' for more information.\n"),
112              program_name);
113   else
114     {
115       printf (_("\
116 Usage: %s [OPTION]... [FILE]...\n\
117 "),
118               program_name);
119       printf (_("\
120 Convert tabs in each FILE to spaces, writing to standard output.\n\
121 With no FILE, or when FILE is -, read standard input.\n\
122 \n\
123   -i, --initial       do not convert TABs after non whitespace\n\
124   -t, --tabs=NUMBER   have tabs NUMBER characters apart, not 8\n\
125   -t, --tabs=LIST     use comma separated list of explicit tab positions\n\
126       --help          display this help and exit\n\
127       --version       output version information and exit\n\
128 \n\
129 Instead of -t NUMBER or -t LIST, -NUMBER or -LIST may be used.\n\
130 "));
131     }
132   exit (status);
133 }
134
135 /* Add tab stop TABVAL to the end of `tab_list', except
136    if TABVAL is -1, do nothing. */
137
138 static void
139 add_tabstop (int tabval)
140 {
141   if (tabval == -1)
142     return;
143   if (first_free_tab % TABLIST_BLOCK == 0)
144     tab_list = (int *) xrealloc (tab_list, first_free_tab
145                                  + TABLIST_BLOCK * sizeof (tab_list[0]));
146   tab_list[first_free_tab++] = tabval;
147 }
148
149 /* Add the comma or blank separated list of tabstops STOPS
150    to the list of tabstops. */
151
152 static void
153 parse_tabstops (char *stops)
154 {
155   int tabval = -1;
156
157   for (; *stops; stops++)
158     {
159       if (*stops == ',' || ISBLANK (*stops))
160         {
161           add_tabstop (tabval);
162           tabval = -1;
163         }
164       else if (ISDIGIT (*stops))
165         {
166           if (tabval == -1)
167             tabval = 0;
168           tabval = tabval * 10 + *stops - '0';
169         }
170       else
171         error (1, 0, _("tab size contains an invalid character"));
172     }
173
174   add_tabstop (tabval);
175 }
176
177 /* Check that the list of tabstops TABS, with ENTRIES entries,
178    contains only nonzero, ascending values. */
179
180 static void
181 validate_tabstops (int *tabs, int entries)
182 {
183   int prev_tab = 0;
184   int i;
185
186   for (i = 0; i < entries; i++)
187     {
188       if (tabs[i] == 0)
189         error (1, 0, _("tab size cannot be 0"));
190       if (tabs[i] <= prev_tab)
191         error (1, 0, _("tab sizes must be ascending"));
192       prev_tab = tabs[i];
193     }
194 }
195
196 /* Close the old stream pointer FP if it is non-NULL,
197    and return a new one opened to read the next input file.
198    Open a filename of `-' as the standard input.
199    Return NULL if there are no more input files.  */
200
201 static FILE *
202 next_file (FILE *fp)
203 {
204   static char *prev_file;
205   char *file;
206
207   if (fp)
208     {
209       if (ferror (fp))
210         {
211           error (0, errno, "%s", prev_file);
212           exit_status = 1;
213         }
214       if (fp == stdin)
215         clearerr (fp);          /* Also clear EOF. */
216       else if (fclose (fp) == EOF)
217         {
218           error (0, errno, "%s", prev_file);
219           exit_status = 1;
220         }
221     }
222
223   while ((file = *file_list++) != NULL)
224     {
225       if (file[0] == '-' && file[1] == '\0')
226         {
227           have_read_stdin = 1;
228           prev_file = file;
229           return stdin;
230         }
231       fp = fopen (file, "r");
232       if (fp)
233         {
234           prev_file = file;
235           return fp;
236         }
237       error (0, errno, "%s", file);
238       exit_status = 1;
239     }
240   return NULL;
241 }
242
243 /* Change tabs to spaces, writing to stdout.
244    Read each file in `file_list', in order. */
245
246 static void
247 expand (void)
248 {
249   FILE *fp;                     /* Input stream. */
250   int c;                        /* Each input character. */
251   int tab_index = 0;            /* Index in `tab_list' of next tabstop. */
252   int column = 0;               /* Column on screen of the next char. */
253   int next_tab_column;          /* Column the next tab stop is on. */
254   int convert = 1;              /* If nonzero, perform translations. */
255
256   fp = next_file ((FILE *) NULL);
257   if (fp == NULL)
258     return;
259   for (;;)
260     {
261       c = getc (fp);
262       if (c == EOF)
263         {
264           fp = next_file (fp);
265           if (fp == NULL)
266             break;              /* No more files. */
267           else
268             continue;
269         }
270
271       if (c == '\n')
272         {
273           putchar (c);
274           tab_index = 0;
275           column = 0;
276           convert = 1;
277         }
278       else if (c == '\t' && convert)
279         {
280           if (tab_size == 0)
281             {
282               /* Do not let tab_index == first_free_tab;
283                  stop when it is 1 less. */
284               while (tab_index < first_free_tab - 1
285                      && column >= tab_list[tab_index])
286                 tab_index++;
287               next_tab_column = tab_list[tab_index];
288               if (tab_index < first_free_tab - 1)
289                 tab_index++;
290               if (column >= next_tab_column)
291                 next_tab_column = column + 1; /* Ran out of tab stops. */
292             }
293           else
294             {
295               next_tab_column = column + tab_size - column % tab_size;
296             }
297           while (column < next_tab_column)
298             {
299               putchar (' ');
300               ++column;
301             }
302         }
303       else
304         {
305           if (convert)
306             {
307               if (c == '\b')
308                 {
309                   if (column > 0)
310                     --column;
311                 }
312               else
313                 {
314                   ++column;
315                   if (convert_entire_line == 0)
316                     convert = 0;
317                 }
318             }
319           putchar (c);
320         }
321     }
322 }
323
324 void
325 main (int argc, char **argv)
326 {
327   int tabval = -1;              /* Value of tabstop being read, or -1. */
328   int c;                        /* Option character. */
329
330   have_read_stdin = 0;
331   exit_status = 0;
332   convert_entire_line = 1;
333   tab_list = NULL;
334   first_free_tab = 0;
335   program_name = argv[0];
336   setlocale (LC_ALL, "");
337   bindtextdomain (PACKAGE, LOCALEDIR);
338   textdomain (PACKAGE);
339
340   while ((c = getopt_long (argc, argv, "it:,0123456789", longopts, (int *) 0))
341          != EOF)
342     {
343       switch (c)
344         {
345         case 0:
346           break;
347
348         case '?':
349           usage (1);
350         case 'i':
351           convert_entire_line = 0;
352           break;
353         case 't':
354           parse_tabstops (optarg);
355           break;
356         case ',':
357           add_tabstop (tabval);
358           tabval = -1;
359           break;
360         default:
361           if (tabval == -1)
362             tabval = 0;
363           tabval = tabval * 10 + c - '0';
364           break;
365         }
366     }
367
368   if (show_version)
369     {
370       printf ("expand - %s\n", PACKAGE_VERSION);
371       exit (0);
372     }
373
374   if (show_help)
375     usage (0);
376
377   add_tabstop (tabval);
378
379   validate_tabstops (tab_list, first_free_tab);
380
381   if (first_free_tab == 0)
382     tab_size = 8;
383   else if (first_free_tab == 1)
384     tab_size = tab_list[0];
385   else
386     tab_size = 0;
387
388   if (optind == argc)
389     file_list = stdin_argv;
390   else
391     file_list = &argv[optind];
392
393   expand ();
394
395   if (have_read_stdin && fclose (stdin) == EOF)
396     error (1, errno, "-");
397   if (ferror (stdout) || fclose (stdout) == EOF)
398     error (1, errno, _("write error"));
399
400   exit (exit_status);
401 }