/* scrub.c * A poor attempt at a program to strip comments and whitespace from C programs. * February 2009 - PUBLIC DOMAIN */ /* TODO: remove unneeded spaces between operators */ #include #include #include #include #define MAX_LINE_LENGTH 4194304 #define WHITESPACE "\t\v\r\n " #define SPACE_OR_TAB "\t " /** state varibles */ static int comments, quotes; /** configuration variables */ static int strip_leading_whitespace_fl=1; static int compact_all_whitespace_fl=1; static void uncomment(char *s, const char *filename, unsigned line_no) { char *t; int backslash=0; for(;*s;s++) { if(comments) { /* fprintf(stderr, "%s:%u:Still in comment mode\n", filename, line_no); */ goto check_comment; } else if(!quotes) { if(*s=='/'&&s[1]=='/') { *s=0; return; } else if(*s=='/'&&s[1]=='*') { check_comment: t=strstr(s, "*/"); if(!t) { /* multiline comment */ *s=0; comments=1; return; } else { /* replace comment with a single whitespace */ fprintf(stderr, "%s:%u:partial line comment: '%.*s'\n", filename, line_no, t-s+2, s); t+=2; memmove(s+1, t, strlen(t)+1); comments=0; *s=' '; } } else if(*s=='"') { quotes='"'; } else if(*s=='\'') { quotes='\''; } } else if(quotes && backslash) { backslash=0; } else if(quotes=='"') { /* TODO: handle \ at end of line inside of strings */ if(*s=='\\') { fprintf(stderr, "%s:%u:Backslash in string literal\n", filename, line_no); backslash=1; } else if(!backslash&&*s=='"') { quotes=0; } else { backslash=0; } } else if(quotes=='\'') { /* TODO: handle \ at end of line inside of quotes */ if(*s=='\\') { fprintf(stderr, "%s:%u:Backslash in character literal\n", filename, line_no); backslash=1; } else if(!backslash&&*s=='\'') { quotes=0; } else { backslash=0; } } else { abort(); } } } static void strip_trailing_whitespace(char *s, const char *filename, unsigned line_no) { char last; size_t len; /* ignore if we are quoting */ if(quotes) return; len=strlen(s); if(len<=0) return; /* ignore empty lines */ len--; /* start at last character */ last=s[len--]; /* save last character */ /* find space */ while(len>0 && isspace(s[len])) { len--; } /* truncate the string using last character and null */ s[len+1]=last; s[len+2]=0; } static void quote_process(int *backslash, int *quote_mode, char ch, char quote_ch) { if(*backslash) { /* already in backslash mode, exit it */ *backslash=0; } else if(ch=='\\') { /* enter backslash */ *backslash=1; } else if(ch==quote_ch) { *quote_mode=0; /* leaving quotes mode */ } } static void compact_spaces(char *s, const char *filename, unsigned line_no) { char *t; int backslash=0, whitespace=0, quote_mode=quotes; for(t=s;*t;t++) { if(whitespace && !isspace(*t)) { /* leave whitespace mode */ *s++=whitespace; /* emit the held whitespace */ whitespace=0; } /* do not modify whitespace between quotes */ if(quote_mode=='\"') { quote_process(&backslash, "e_mode, *t, '\"'); } else if(quote_mode=='\'') { quote_process(&backslash, "e_mode, *t, '\''); } else if(isspace(*t)) { /* use the first whitespace and ignore the remaining */ if(whitespace) { fprintf(stderr, "%s:%u:skipping extra spaces.\n", filename, line_no); /* found a newline, hold that instead */ if(*t=='\n') whitespace='\n'; continue; /* ignore further whitespace */ } /* else hold the first whitespace */ whitespace=*t; continue; } else if (*t=='\"'||*t=='\'') { quote_mode=*t; /* enter quote_mode mode */ } /* else: normal stream of characters being copied */ /* copy */ *s++=*t; } if(whitespace) { *s++=whitespace; /* emit the held whitespace */ } *s=0; } void do_file(const char *filename, FILE *in, FILE *out) { char *line; unsigned line_no; /** current line number */ size_t start; /* create a very large buffer */ line=malloc(MAX_LINE_LENGTH); if(!line) { perror(filename); exit(EXIT_FAILURE); } line_no=0; while(fgets(line, MAX_LINE_LENGTH, in)) { line_no++; /* loop through leading whitespace */ if(strip_leading_whitespace_fl) { start=strspn(line, SPACE_OR_TAB); } else { start=0; } uncomment(line+start, filename, line_no); /* go to next line if inside a comment */ if(comments) continue; if(compact_all_whitespace_fl) { compact_spaces(line+start, filename, line_no); } strip_trailing_whitespace(line+start, filename, line_no); /* check that we are manipulating the lines correctly if(line[start]==' ') { fprintf(stderr, "%s:%u:space found at start of line but shouldn't be there.\n", filename, line_no); fprintf(stderr, "\"%s\"\n", line+start); abort(); } */ if(!line[start]||line[start]=='\n') continue; /* swallow empty lines */ fputs(line+start, out); } free(line); } int main(int argc, char **argv) { int i; if(argc<1) { do_file("", stdin, stdout); } else for(i=1;i