/* ** ppmr5.c - redact portions of a PPM picture ** Written by Harry Sintonen ** ** Compile: ** % gcc -Wall -O2 ppmr5.c -o ppmr5 ** ** Usage: ** Redact two rectangles: ** % echo -e '5 8 90 24\n5 32 90 48' | ./ppmr5 secret.ppm > out.ppm ** Redact the whole picture: ** % echo '0 0 99999 99999' | ./ppmr5 secret.ppm > out.ppm */ #include #include #include #define iswhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') struct rect { int x1, y1, x2, y2; }; static void fail(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(EXIT_FAILURE); } int main(int argc, char **argv) { int state = 0, c, var[3] = {0}, vindex = 0, numrects, x = 0, y = 0; struct rect *rects, rect, *r; long dataoffs = 0, offs = 0; FILE *fh; if (argc != 2) fail("usage: %s in.ppm < redactions.txt > out.ppm\n", argv[0]); if (!(fh = fopen(argv[1], "r"))) fail("could not open file \"%s\"\n", argv[1]); for (rects = 0, numrects = 0; scanf("%d %d %d %d", &rect.x1, &rect.y1, &rect.x2, &rect.y2) == 4 && (rects = realloc(rects, ++numrects * sizeof(struct rect)));) rects[numrects - 1] = rect; while ((c = fgetc(fh)) != EOF) { switch (state) { case 0: case 1: /* check file header ID */ if (c != "P6"[state++]) fail("unsupported file format\n"); break; case 2: case 5: case 8: /* skip whitespaces/check for comment */ if (iswhite(c)) break; if (c == '#') { state += 2; break; } state++; /* fall thru */ case 3: case 6: case 9: /* parse decimal number */ if (c < '0' || c > '9') { if (var[vindex] == 0) fail("unsupported file format\n"); state += 2; vindex++; ungetc(c, fh); break; } var[vindex] = var[vindex] * 10 + c - '0'; break; case 4: case 7: case 10: /* skip comment until newline */ if (c == '\n') state -= 2; break; case 11: /* check final delimiter, store data start */ if (!iswhite(c)) fail("unsupported file format\n"); if ((dataoffs = ftell(fh)) < 0) fail("ftell error\n"); state++; break; case 12: case 15: /* seek to file beginning to clone header */ if (fseek(fh, 0, SEEK_SET) < 0) fail("fseek error\n"); state++; break; case 14: /* process data, redacting pixels */ for (r = rects; r < &rects[numrects]; r++) if (x >= r->x1 * 3 && x < r->x2 * 3 && y >= r->y1 && y < r->y2) { c = 0; break; } if (++x == var[0] * 3) { x = 0; y++; } /* fall thru */ default: /* write char */ putchar(c); if (++offs >= dataoffs) { dataoffs += var[0] * 3 * var[1]; state++; } } } return EXIT_SUCCESS; }