Commits

Evan Gates  committed 073db69

fix bugs, clean up, etc.

  • Participants
  • Parent commits e218777

Comments (0)

Files changed (1)

File rand_lines.c

 // rand_lines
 // Usage: rand_lines <delim> <nlines> <file>
 //
-// given file with "delim" delimited lines, print "nlines" random lines in
+// given "file" with "delim" delimited lines, print "nlines" random lines in
 // random order to stdout
 
 #define _POSIX_C_SOURCE 200809L
     size_t len;
 } Line;
 
-char *prog;
+char *prog, *last_line;
 
 // Retry reads and writes on EINTR
 ssize_t rw(ssize_t (*act)(), int fd, void *buf, size_t n)
     size_t i = n;
     while (i-- && *len > 0) {
         char *end = memchr(*file, delim, *len);
-        if (!end) { // reached end of file, no delimiter
+        if (!end) // reached end of file, no delimiter
             end = *file + *len;
-            *end = delim;
-        }
-        ++end;
         lines[i] = (Line){ *file, end - *file };
-        *file = end;
-        *len -= lines[i].len;
+        *file = ++end;
+        *len -= lines[i].len + 1;
     }
     return n - ++i;
 }
 
 // Write n Lines from lines to stdout
-int write_lines(Line *lines, size_t n)
+// Manually write delim to deal with files that don't end with delim
+int write_lines(Line *lines, size_t n, char delim)
 {
     while (n--)
-        if (rw_all(write, 1, lines[n].beg, lines[n].len) < 0)
+        if (rw_all(write, 1, lines[n].beg, lines[n].len) < 0 ||
+            rw_all(write, 1, &delim      ,            1) < 0)
             return -1;
     return 0;
 }
 // Starting at file, read n lines delimited by delim.
 // For each subsequent line read, accept line with
 // probability n / nl and replace random existing line.
+// Shuffle chosen lines then print.
 int rand_lines(char delim, int n, char *file, off_t len)
 {
     Line  *lines;
 
     shuffle_lines(lines, n);
 
-    if (write_lines(lines, n) < 0)
+    if (write_lines(lines, n, delim) < 0)
         BAIL();
 
 cleanup:
 
 int main(int argc, char **argv)
 {
-    char  delim, *file = NULL;
+    char  delim, *file = MAP_FAILED;
     int   n, fd = -1;
-    off_t fsize;
+    off_t fsize = 0; // -Wmaybe-unitialized, but only with -O[s23]
     int   status = 0;
 
     prog = *argv;
 
     if (argc != 4)                  { fprintf(stderr, "Usage: %s <delim> <nlines> <file>\n", prog); return 1; }
-    if (   1 > (n = atoi(argv[2]))) { fprintf(stderr, "%s: nlines must be greater than 1\n", prog); return 1; }
+    if (   1 > (n = atoi(argv[2]))) { fprintf(stderr, "%s: nlines must be greater than 0\n", prog); return 1; }
     delim = *argv[1];
 
-    if (         0 >  srand32())                                                                   BAIL();
-    if (         0 >  (fd    = open(argv[3], O_RDONLY)))                                           BAIL();
-    if (         0 >  (fsize = lseek(fd, 0, SEEK_END)))                                            BAIL();
-    if (MAP_FAILED == (file  = mmap(NULL, fsize + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))) BAIL();
-    if (         0 >  rand_lines(delim, n, file, fsize))                                           BAIL();
+    if (0 > srand32())                         BAIL();
+    if (0 > (fd    = open(argv[3], O_RDONLY))) BAIL();
+    if (0 > (fsize = lseek(fd, 0, SEEK_END)))  BAIL();
+
+    // Special case: empty file is one empty line without final delimiter
+    // mmap() may error with length 0 and read_lines() would be difficult
+    if (fsize == 0) {
+        if (n > 1) {
+            fprintf(stderr, "%s: more lines requested than in file\n", prog);
+            BAIL();
+        }
+        status = rw_all(write, 1, &delim, 1);
+        goto cleanup;
+    }
+
+    if (MAP_FAILED == (file = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0))) BAIL();
+    if (         0 >  rand_lines(delim, n, file, fsize))                        BAIL();
 
 cleanup:
-    if (status && errno)                       perror(prog);
-    if (file && munmap(file, fsize + 1) < 0) { perror(prog); status = 1; }
-    if (fd > 0 && close(fd))                 { perror(prog); status = 1; }
+    if (status             && errno)                 perror(prog);
+    if (file != MAP_FAILED && munmap(file, fsize)) { perror(prog); status = 1; }
+    if (fd    > 0          && close(fd))           { perror(prog); status = 1; }
     return status ? EXIT_FAILURE : EXIT_SUCCESS;
 }