#include #include #include #include #include #include "liberror.h" void exhaust_buffer(char *b, size_t s){ size_t i; for(i = 0; i < s; ++i) if(b[i] != 0) putc(b[i], stdout); free(b); return; } void print_stdin(void){ char c; while((c = getc(stdin)) != EOF) putc(c, stdout); return; } void substitute(char *phrase, char *substitution, bool exit_on_first, char *name){ char *b = (char *)calloc(strlen(phrase)+1, sizeof(char)); size_t i; if(b == NULL) error(name, ALLOCATION_ERROR); while((b[strlen(phrase)-1] = getc(stdin)) != EOF){ for(i = 0; b[i] == phrase[i] && b[i] != 0 && phrase[i] != 0; ++i); if(i == strlen(phrase)){ fputs(substitution, stdout); for(i = 0; i < strlen(phrase);) b[i++] = 0; if(exit_on_first){ exhaust_buffer(b, strlen(phrase)+1); print_stdin(); } }else putc(*b, stdout); /* There's a more efficient way to maintain a buffer here, * where I keep an unordered string of chars and form an * ordered string when I need to check against the phrase * to match. However it's math-intensive, which sucks not just * for a self-taught lamer but in general in regards to muh * simplicity, and the math undercuts the performance gain * from avoiding touching memory. Also, come on, you're never * gonna have a phrase longer than 100 bytes! Who cares?! */ for(i = 0; i < strlen(phrase); ++i) b[i] = b[i+1]; } exhaust_buffer(b, strlen(phrase)+1); } void usage(char *name){ fprintf(stdout, "Usage: %s (-fhr) [phrase] [substitution]\n", name); exit(1); } int main(int argc, char *argv[]){ extern char *optarg; extern int optind; bool exit_on_first = 0; char c; char *argv0 = argv[0]; bool regex_mode = 0; while((c = getopt(argc, argv, "fhr")) != -1){ switch(c){ /* -f exists because there exist uses of `sed 's/foo/bar/'` * without the trailing `g` for global substitution that * would be borked if made global. Perhaps there are other ways * to do so that mean that this option doesn't need to be * implemented, but it's kind of easy to just add so * whatever. */ case 'f': exit_on_first = 1; case 'h': usage(argv0); /* By default regex is not parsed; this is because regular * expressions are not known to the GENERAL user. The kind of * user that uses this utility /does/ know regex, but the kind * of user this utility targets does not, so don't implement * potentially undesirable functionality by default. * If you know regex you know how to look up a manpage. */ case 'r': regex_mode = 1; break; case '?': default: usage(argv0); } } argc -= optind; argv += optind; if(argc < 2 || argc > 3) usage(argv0); if(regex_mode == 0) substitute(argv[0], argv[1], exit_on_first, argv0); else { printf("Not implemented.\n"); exit(1); } // substitute_regex(argv[0], argv[1], exit_on_first, argv0); return 0; }