1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "platform.h"
6
7#define true       1
8#define false      0
9#define TABSIZE    4
10
11#define DOS_CRLF   0
12#define UNIX_LF    1
13#define MAC_CR     2
14
15typedef struct
16{
17    Bool pushed;
18    int tabs;
19    int curcol;
20    int lastcol;
21    int maxcol;
22    int curline;
23    int pushed_char;
24    uint size;
25    uint length;
26    char *buf;
27    FILE *fp;
28} Stream;
29
30static int tabsize = TABSIZE;
31static int endline = DOS_CRLF;
32static Bool tabs = false;
33
34/*
35 Memory allocation functions vary from one environment to
36 the next, and experience shows that wrapping the local
37 mechanisms up provides for greater flexibility and allows
38 out of memory conditions to be detected in one place.
39*/
40void *MemAlloc(size_t size)
41{
42    void *p;
43
44    p = malloc(size);
45
46    if (!p)
47    {
48        fprintf(stderr, "***** Out of memory! *****\n");
49        exit(1);
50    }
51
52    return p;
53}
54
55void *MemRealloc(void *old, size_t size)
56{
57    void *p;
58
59    p = realloc(old, size);
60
61    if (!p)
62    {
63        fprintf(stderr, "***** Out of memory! *****\n");
64        return NULL;
65    }
66
67    return p;
68}
69
70void MemFree(void *p)
71{
72    free(p);
73    p = NULL;
74}
75
76static Stream *NewStream(FILE *fp)
77{
78    Stream *in;
79
80    in = (Stream *)MemAlloc(sizeof(Stream));
81
82    memset(in, 0, sizeof(Stream));
83    in->fp = fp;
84    return in;
85}
86
87static void FreeStream(Stream *in)
88{
89    if (in->buf)
90        MemFree(in->buf);
91
92    MemFree(in);
93}
94
95static void AddByte(Stream *in, uint c)
96{
97    if (in->size + 1 >= in->length)
98    {
99        while (in->size + 1 >= in->length)
100        {
101            if (in->length == 0)
102                in->length = 8192;
103            else
104                in->length = in->length * 2;
105        }
106
107        in->buf = (char *)MemRealloc(in->buf, in->length*sizeof(char));
108    }
109
110    in->buf[in->size++] = (char)c;
111    in->buf[in->size] = '\0';  /* debug */
112}
113
114
115
116/*
117  Read a character from a stream, keeping track
118  of lines, columns etc. This is used for parsing
119  markup and plain text etc. A single level
120  pushback is allowed with UngetChar(c, in).
121  Returns EndOfStream if there's nothing more to read.
122*/
123static int ReadChar(Stream *in)
124{
125    int c;
126
127    if (in->pushed)
128    {
129        in->pushed = false;
130
131        if (in->pushed_char == '\n')
132            in->curline--;
133
134        return in->pushed_char;
135    }
136
137    in->lastcol = in->curcol;
138
139    /* expanding tab ? */
140    if (in->tabs > 0)
141    {
142        in->curcol++;
143        in->tabs--;
144        return ' ';
145    }
146
147    /* Else go on with normal buffer: */
148    for (;;)
149    {
150        c = getc(in->fp);
151
152        /* end of file? */
153        if (c == EOF)
154            break;
155
156        /* coerce \r\n  and isolated \r as equivalent to \n : */
157        if (c == '\r')
158        {
159            c = getc(in->fp);
160
161            if (c != '\n')
162                ungetc(c, in->fp);
163
164            c = '\n';
165        }
166
167        if (c == '\n')
168        {
169            if (in->maxcol < in->curcol)
170                in->maxcol = in->curcol;
171
172            in->curcol = 1;
173            in->curline++;
174            break;
175        }
176
177        if (c == '\t')
178        {
179            if (tabs)
180              in->curcol += tabsize - ((in->curcol - 1) % tabsize);
181            else /* expand to spaces */
182            {
183                in->tabs = tabsize - ((in->curcol - 1) % tabsize) - 1;
184                in->curcol++;
185                c = ' ';
186            }
187
188            break;
189        }
190
191        if (c == '\033')
192            break;
193
194        /* strip control characters including '\r' */
195
196        if (0 < c && c < 32)
197            continue;
198
199        in->curcol++;
200        break;
201    }
202
203    return c;
204}
205
206static Stream *ReadFile(FILE *fin)
207{
208    int c;
209    Stream *in  = NewStream(fin);
210
211    while ((c = ReadChar(in)) >= 0)
212        AddByte(in, (uint)c);
213
214    return in;
215}
216
217static void WriteFile(Stream *in, FILE *fout)
218{
219    int i, c;
220    char *p;
221
222    i = in->size;
223    p = in->buf;
224
225    while (i--)
226    {
227        c = *p++;
228
229        if (c == '\n')
230        {
231            if (endline == DOS_CRLF)
232            {
233                putc('\r', fout);
234                putc('\n', fout);
235            }
236            else if (endline == UNIX_LF)
237                putc('\n', fout);
238            else if (endline == MAC_CR)
239                putc('\r', fout);
240
241            continue;
242        }
243
244        putc(c, fout);
245    }
246}
247
248static void HelpText(FILE *errout, char *prog)
249{
250    fprintf(errout, "%s: [options] [infile [outfile]] ...\n", prog);
251    fprintf(errout, "Utility to expand tabs and ensure consistent line endings\n");
252    fprintf(errout, "options for tab2space vers: 6th February 2003\n");
253    fprintf(errout, "  -help or -h     display this help message\n");
254    fprintf(errout, "  -dos  or -crlf  set line ends to CRLF (PC-DOS/Windows - default)\n");
255    fprintf(errout, "  -mac  or -cr    set line ends to CR (classic Mac OS)\n");
256    fprintf(errout, "  -unix or -lf    set line ends to LF (Unix)\n");
257    fprintf(errout, "  -tabs           preserve tabs, e.g. for Makefile\n");
258    fprintf(errout, "  -t<n>           set tabs to <n> (default is 4) spaces\n");
259    fprintf(errout, "\nNote this utility doesn't map spaces to tabs!\n");
260}
261
262int main(int argc, char **argv)
263{
264    char const *infile, *outfile;
265    char *prog;
266    FILE *fin, *fout;
267    Stream *in = NULL;
268
269    prog = argv[0];
270
271    while (argc > 0)
272    {
273        if (argc > 1 && argv[1][0] == '-')
274        {
275            if (strcmp(argv[1], "-help") == 0 || argv[1][1] == 'h')
276            {
277                HelpText(stdout, prog);
278                return 1;
279            }
280
281            if (strcmp(argv[1], "-dos") == 0 ||
282                strcmp(argv[1], "-crlf") == 0)
283                 endline = DOS_CRLF;
284
285            else if (strcmp(argv[1], "-mac") == 0 ||
286                strcmp(argv[1], "-cr") == 0)
287                endline = MAC_CR;
288
289            else if (strcmp(argv[1], "-unix") == 0 ||
290                strcmp(argv[1], "-lf") == 0)
291                endline = UNIX_LF;
292
293            else if (strcmp(argv[1], "-tabs") == 0)
294                tabs = true;
295
296            else if (strncmp(argv[1], "-t", 2) == 0)
297                sscanf(argv[1]+2, "%d", &tabsize);
298
299            --argc;
300            ++argv;
301            continue;
302        }
303
304        if (argc > 1)
305        {
306            infile = argv[1];
307            fin = fopen(infile, "rb");
308        }
309        else
310        {
311            infile = "stdin";
312            fin = stdin;
313        }
314
315        if (argc > 2)
316        {
317            outfile = argv[2];
318            fout = NULL;
319            --argc;
320            ++argv;
321        }
322        else
323        {
324            outfile = "stdout";
325            fout = stdout;
326        }
327
328        if (fin)
329        {
330            in = ReadFile(fin);
331
332            if (fin != stdin)
333                fclose(fin);
334
335            if (fout != stdout)
336               fout = fopen(outfile, "wb");
337
338            if (fout)
339            {
340               WriteFile(in, fout);
341
342               if (fout != stdout)
343                  fclose(fout);
344            }
345            else
346                fprintf(stderr, "%s - can't open \"%s\" for writing\n", prog, outfile);
347
348            FreeStream(in);
349        }
350        else
351            fprintf(stderr, "%s - can't open \"%s\" for reading\n", prog, infile);
352
353        --argc;
354        ++argv;
355
356        if (argc <= 1)
357            break;
358    }
359
360    return 0;
361}
362
363