a37dad77691017571e161d42ddd78d38ad9a029c
[indent.git] / indent.c
1 /* depend: */
2 /* cflags: */
3 /* linker: debug.o */
4
5 #include <assert.h>
6 #include <getopt.h>
7 #include <malloc.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "debug.h"
13
14 /* macros */
15
16 #define CEIL(x, y) (((x) + (y) - 1) / (y))
17 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
18 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
19
20 //#define BUFFERSIZE 4096
21 #define BUFFERSIZE 256
22 #define TABSIZE 2
23
24 /* type definition */
25
26 typedef enum {
27 e_unknown = 0,
28 e_ansi,
29 e_kr
30 } cmode_t;
31
32 /* gobal variables */
33
34 char *progname = NULL;
35
36 /* help function */
37
38 void usage (int ret)
39 {
40 FILE *fd = ret ? stderr : stdout;
41 fprintf (fd, "usage: %s [-i file] [-h] [-m k&r|ansi|c99] [-o file] [-v]\n", progname);
42 fprintf (fd, " -i : input file\n");
43 fprintf (fd, " -h : help message\n");
44 fprintf (fd, " -m : indent mode\n");
45 fprintf (fd, " -o : output file\n");
46 fprintf (fd, " -v : verbose level (%d)\n", verbose);
47
48 exit (ret);
49 }
50
51 /* indent function */
52
53 int indent (FILE *fin, FILE *fout, cmode_t cmode) {
54 char bufin[BUFFERSIZE + 1] = {0};
55 char bufout[BUFFERSIZE * TABSIZE + 1] = {0};
56 size_t i, nb;
57 size_t nbindent = 0;
58 int begin = 1;
59 int parent = 0;
60 int comment = 0;
61 int newline = 0;
62 int string = 0;
63 int character = 0;
64 int special = 0;
65
66 while (!feof (fin)) {
67 memset (bufin, 0, sizeof (bufin));
68 memset (bufout, 0, sizeof (bufout));
69
70 /* read file */
71 nb = fread (bufin, 1, BUFFERSIZE, fin);
72 VERBOSE (DEBUG, fprintf (stdout, "buffer in: %d\n", nb));
73 if (errno != 0) {
74 VERBOSE (ERROR, fprintf (stderr, "can't read file (%d)\n", errno));
75 exit (1);
76 }
77
78 /* process line */
79 char *ptin = bufin;
80 char *ptout = bufout;
81 while (*ptin != '\0') {
82 VERBOSE (DEBUG, fprintf (stdout, "caracter: %c\n", *ptin));
83
84 /* manage comment */
85 if (comment > 0) {
86 if (((comment == 1) && (*ptin == '\n')) ||
87 ((comment == 2) && ((*ptin == '*') && (ptin[1] == '/')))) {
88 comment = 0;
89 }
90 special = 0;
91 *ptout++ = *ptin++;
92 continue;
93 }
94
95 /* manage indent */
96 switch (*ptin) {
97 case '/':
98 comment = (ptin[1] == '/') ? 1 : (ptin[1] == '*') ? 2 : 0;
99 if (begin) {
100 for (i = 0; i < nbindent * TABSIZE; i++) {
101 *ptout++ = ' ';
102 }
103 begin = 0;
104 }
105 *ptout++ = *ptin;
106 break;
107 case ' ':
108 case '\t':
109 if (begin == 0) {
110 *ptout++ = *ptin;
111 }
112 break;
113 case '{':
114 *ptout++ = '\n';
115 for (i = 0; i < nbindent * TABSIZE; i++) {
116 *ptout++ = ' ';
117 }
118 *ptout++ = *ptin;
119 *ptout++ = '\n';
120 nbindent++;
121 newline = 1;
122 begin = 1;
123 break;
124 case '}':
125 *ptout++ = '\n';
126 nbindent--;
127 for (i = 0; i < nbindent * TABSIZE; i++) {
128 *ptout++ = ' ';
129 }
130 *ptout++ = *ptin;
131 if (ptin[1] != ';') {
132 *ptout++ = '\n';
133 }
134 newline = 1;
135 begin = 1;
136 break;
137 case ';':
138 *ptout++ = *ptin;
139 if (parent) {
140 break;
141 }
142 *ptout++ = '\n';
143 newline = 1;
144 begin = 1;
145 break;
146 case '\n':
147 if (newline == 1) {
148 newline = 0;
149 } else {
150 *ptout++ = '\n';
151 }
152 begin = 1;
153 break;
154 case '\r':
155 break;
156 default:
157 if ((*ptin == '"') && (!character) && (!special)) {
158 string ^= 1;
159 }
160 if ((*ptin == '\'') && (!string) && (!special)) {
161 character ^= 1;
162 }
163 if (begin) {
164 for (i = 0; i < nbindent * TABSIZE; i++) {
165 *ptout++ = ' ';
166 }
167 begin = 0;
168 }
169 *ptout++ = *ptin;
170 }
171 special = (*ptin == '\\');
172 parent += (*ptin == '(') ? +1 : (*ptin == ')') ? -1 : 0;
173 ptin++;
174 }
175 ptout = '\0';
176
177 /* write file */
178 VERBOSE (DEBUG, fprintf (stdout, "buffer out: %d\n", strlen (bufout)));
179 ptout = bufout;
180 while ((nb = fwrite (ptout, 1, strlen (ptout), fout)) != strlen (ptout)) {
181 VERBOSE (DEBUG, fprintf (stdout, "buffer out: %d/%d\n", nb, strlen (ptout)));
182 if (errno != 0) {
183 VERBOSE (ERROR, fprintf (stderr, "can't write file (%d)\n", errno));
184 exit (1);
185 }
186 ptout += nb;
187 }
188 }
189
190 /* close all */
191 fclose (fin);
192 fclose (fout);
193
194 return 0;
195 }
196
197 /* main function */
198
199 int main (int argc, char *argv[])
200 {
201 cmode_t cmode = e_unknown;
202 char *input = NULL;
203 char *mode = "ansi";
204 char *output = NULL;
205
206 /* get basename */
207 char *pt = progname = argv[0];
208 while (*pt) {
209 if ((*pt == '/') || (*pt == '\\')) {
210 progname = pt + 1;
211 }
212 pt++;
213 }
214
215 int c;
216 while ((c = getopt(argc, argv, "i:hm:o:v:")) != EOF) {
217 switch (c) {
218 case 'i':
219 input = optarg;
220 break;
221 case 'm':
222 mode = optarg;
223 break;
224 case 'o':
225 output = optarg;
226 break;
227 case 'v':
228 verbose = atoi (optarg);
229 break;
230 case 'h':
231 default:
232 usage (c != 'h');
233 }
234 }
235 if (argc - optind != 0) {
236 fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]);
237 usage (1);
238 }
239
240 /* check input */
241 FILE *fin = NULL;
242 if (input) {
243 fin = fopen (input, "rb");
244 if (!fin) {
245 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", input));
246 }
247 } else {
248 fin = stdin;
249 }
250
251 /* check output */
252 FILE *fout = NULL;
253 if (output) {
254 fout = fopen (output, "wb");
255 if (!fout) {
256 VERBOSE (ERROR, fprintf (stderr, "error: can't open file '%s'\n", output));
257 fclose (fin);
258 }
259 } else {
260 fout = stdout;
261 }
262
263 /* check mode */
264 if (strcmp (mode, "ansi") == 0) {
265 cmode = e_ansi;
266 } else if (strcmp (mode, "k&r") == 0) {
267 cmode = e_kr;
268 } else {
269 VERBOSE (ERROR, fprintf (stderr, "error: mode '%s' unknown\n", mode));
270 }
271
272 return indent (fin, fout, cmode);
273 }
274
275 // test: indent.exe -h
276 // test: indent.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
277 // test: indent.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
278 // test: indent.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
279
280 /* vim: set ts=4 sw=4 et: */