+#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
if (munmap(m, map_size) == -1) err(EX_IOERR, "Could not unmap input");
if (close(fd) == -1) err(EX_IOERR, "Could not close input");
}
+
+/* Create a temporary file in $TMPDIR, or /tmp if TMPDIR is not set.
+ * Caller must free temp_filename and fclose temp_file. Succeeds or terminates
+ * the process. */
+static void make_temporary_file(char** temp_filename, FILE** temp_file) {
+ char* TMPDIR = getenv("TMPDIR");
+ if (TMPDIR == NULL) {
+ TMPDIR = "/tmp";
+ }
+ if (asprintf(temp_filename, "%s/reverse.XXXXXX", TMPDIR) == -1) {
+ errx(EX_OSERR, "Could not assemble temporary filename");
+ }
+ int fd = mkstemp(*temp_filename);
+ if (fd == -1) err(EX_IOERR, "Could not make a temporary file");
+ *temp_file = fdopen(fd, "w");
+ if (*temp_file == NULL) err(EX_IOERR, "Could not open temporary file");
+}
+
+/* Copy data from input to output until EOF is reached. */
+static void copy(FILE* input, FILE* output) {
+ for (;;) {
+ int c = fgetc(input);
+ if (c == EOF) {
+ if (ferror(input)) errx(EX_IOERR, "Could not read");
+ if (!feof(input)) errx(EX_IOERR, "Unexpected end of file");
+ break;
+ }
+ if (fputc(c, output) == EOF) errx(EX_IOERR, "Could not write");
+ }
+}
+
+void reverse_stream(FILE* input_stream, FILE* output_stream) {
+ char* temp_filename;
+ FILE* temp_file;
+ make_temporary_file(&temp_filename, &temp_file);
+
+ copy(input_stream, temp_file);
+ if (fclose(temp_file) != 0) err(EX_IOERR, "Could not close temporary file");
+
+ reverse_file(temp_filename, output_stream);
+
+ if (unlink(temp_filename) == -1) err(EX_IOERR, "Could not remove temporary file");
+ free(temp_filename);
+}
static const char test_file[] = "reverse.c";
+/* Reverse input_file. Dump the result in a temporary file. Return the temp
+ * file's name. The caller must free the filename. */
char* reverse_to_temp_file(const char* input_file) {
char* temp_filename = strdup("/tmp/reverse_test.XXXXXX");
int fd = mkstemp(temp_filename);
return temp_filename;
}
-void test_reverse_twice_is_the_same() {
- char* intermediate = reverse_to_temp_file(test_file);
- char* back_to_normal = reverse_to_temp_file(intermediate);
-
- FILE* f1 = fopen(test_file, "r");
- if (f1 == NULL) err(EX_IOERR, "Couldn't open test file");
- FILE* f2 = fopen(back_to_normal, "r");
- if (f2 == NULL) err(EX_IOERR, "Couldn't open twice-reversed temp file");
+/* Compare the contents of two files. Terminate with a diagnostic if they
+ * differ. */
+void compare(const char* filename1, const char* filename2) {
+ FILE* f1 = fopen(filename1, "r");
+ if (f1 == NULL) err(EX_IOERR, "Couldn't open file %s", filename1);
+ FILE* f2 = fopen(filename2, "r");
+ if (f2 == NULL) err(EX_IOERR, "Couldn't open file %s", filename2);
for (int pos = 0; ; pos++) {
int c1 = fgetc(f1);
int c2 = fgetc(f2);
- if (c1 == EOF && ferror(f1)) err(EX_IOERR, "Error reading test file");
- if (c2 == EOF && ferror(f2)) err(EX_IOERR, "Error reading twice-reversed temp file");
+ if (c1 == EOF && ferror(f1)) err(EX_IOERR, "Error reading file %s", filename1);
+ if (c2 == EOF && ferror(f2)) err(EX_IOERR, "Error reading file %s", filename2);
if (c1 != c2) {
errx(EX_SOFTWARE, "Unexpected difference found at offset %d after reversing twice. "
"Expected %d but found %d", pos, c1, c2);
if (c1 == EOF) break;
}
- if (fclose(f1) != 0) err(EX_IOERR, "Couldn't close test file");
- if (fclose(f2) != 0) err(EX_IOERR, "Couldn't close twice-reversed temp file");
+ if (fclose(f1) != 0) err(EX_IOERR, "Couldn't close file %s", filename1);
+ if (fclose(f2) != 0) err(EX_IOERR, "Couldn't close file %s", filename2);
+}
+
+void test_reverse_twice_from_files_is_the_same() {
+ char* intermediate = reverse_to_temp_file(test_file);
+ char* back_to_normal = reverse_to_temp_file(intermediate);
+
+ compare(test_file, back_to_normal);
+
if (unlink(intermediate) == -1) err(EX_IOERR, "Couldn't remove intermediate temp file");
if (unlink(back_to_normal) == -1) err(EX_IOERR, "Couldn't remove twice-reversed temp file");
free(intermediate);
free(back_to_normal);
}
+void test_reverse_from_file_then_from_stream_is_the_same() {
+ int pipefd[2];
+ if (pipe(pipefd) == -1) err(EX_OSERR, "Couldn't create pipe");
+
+ pid_t pid = fork();
+ if (pid == -1) err(EX_OSERR, "Couldn't fork");
+ if (pid == 0) {
+ if (close(pipefd[0]) == -1) err(EX_OSERR, "Couldn't close unneeded pipe descriptor");
+ FILE* to_second = fdopen(pipefd[1], "w");
+ if (to_second == NULL) err(EX_IOERR, "Couldn't open pipe for writing");
+ reverse_file(test_file, to_second);
+ exit(0);
+ }
+ if (close(pipefd[1]) == -1) err(EX_OSERR, "Couldn't close unneeded pipe descriptor");
+ FILE* from_first = fdopen(pipefd[0], "r");
+ if (from_first == NULL) err(EX_IOERR, "Couldn't open pipe for reading");
+ char* out_temp_filename = strdup("/tmp/reverse_test.XXXXXX");
+ int out_fd = mkstemp(out_temp_filename);
+ if (out_fd == -1) err(EX_IOERR, "Couldn't make a temporary file");
+ FILE* out_file = fdopen(out_fd, "w");
+ if (out_file == NULL) err(EX_IOERR, "Couldn't open temporary file");
+ reverse_stream(from_first, out_file);
+ if (fclose(out_file) == EOF) err(EX_IOERR, "Couldn't close temporary file");
+
+ compare(test_file, out_temp_filename);
+
+ if (unlink(out_temp_filename) == -1) err(EX_IOERR, "Couldn't remove temp output file");
+ free(out_temp_filename);
+}
+
int main() {
- test_reverse_twice_is_the_same();
+ test_reverse_twice_from_files_is_the_same();
+ test_reverse_from_file_then_from_stream_is_the_same();
puts("PASS");
return 0;
}