/* Replace strings in possibly piped input to possibly piped output. * * Written to not use string functions. * * By Kragen Javier Sitaker, 02022. Public domain, CC0. */ #include /* fgets(), fputs(), fprintf() */ #include /* exit() */ char *argv0; void usage() { fprintf(stderr, "%s: Usage: %s pattern replacement outfile\n", argv0, argv0); exit(-1); } void fail(char *why) { fprintf(stderr, "%s: %s\n", argv0, why); exit(1); } /* like strstr, but returns pointer to end of string on failure */ char *find(char *haystack, char *needle) { for (; *haystack; haystack++) { for (size_t i = 0; needle[i]; i++) { if (needle[i] != haystack[i]) goto not_found; } return haystack; /* found: fell off end of needle without mismatch */ not_found: ; } return haystack; } /* basically memcpy, except it aborts if the buffer is too small */ char *cmove(char *dest, char *src, size_t n, char *dest_end) { size_t m = dest_end - dest; if (n > m) fail("string too long"); for (size_t i = 0; i < n; i++) dest[i] = src[i]; return dest + n; } size_t length(char *s) { size_t n = 0; while (s[n]) n++; return n; } enum { bufsiz = 2048 }; char buf1[bufsiz], buf2[bufsiz]; int main(int argc, char **argv) { argv0 = argv[0]; if (argc != 3) usage(); char *pattern = argv[1], *replacement = argv[2], *dest_end = buf2 + bufsiz; size_t n = length(replacement), pattern_len = length(pattern); for (;;) { if (!fgets(buf1, bufsiz, stdin)) return 0; /* ambiguous whether EOF or error */ if (length(buf1) == bufsiz - 1) fail("input line too long"); char *si = buf1, *di = buf2; /* source index, destination index */ for (;;) { char *found = find(si, pattern); di = cmove(di, si, found - si, dest_end); if (!*found) break; di = cmove(di, replacement, n, dest_end); si = found + pattern_len; } di = cmove(di, "", 1, dest_end); /* terminating NUL for fputs */ if (fputs(buf2, stdout) < 0) fail("fputs"); } }