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