From 0bb70d21ffc963b9aaacea2cd464803d6ac5f106 Mon Sep 17 00:00:00 2001
From: Anders Blomdell <anders.blomdell@control.lth.se>
Date: Thu, 1 Jun 2017 18:15:42 +0200
Subject: [PATCH] Make nul terminated lines an option

---
 md5toc.c | 59 ++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 36 insertions(+), 23 deletions(-)

diff --git a/md5toc.c b/md5toc.c
index 3de756b..1eaf676 100644
--- a/md5toc.c
+++ b/md5toc.c
@@ -31,16 +31,17 @@
 #include <inttypes.h>
 #include <time.h>
 #include <sys/types.h>
-#include <attr/xattr.h>
+#include <sys/xattr.h>
 #include <fcntl.h> /* Definition of AT_* constants */
 
 #define XATTR_MD5SUM "trusted.md5sum.control.lth.se"
 
-#define FLAGS_READ_XATTR   0x01
-#define FLAGS_WRITE_XATTR  0x02
-#define FLAGS_CLEAR_XATTR  0x04
-#define FLAGS_MAX_AGE      0x08
-#define FLAGS_VERBOSE      0x10
+#define FLAGS_READ_XATTR      0x01
+#define FLAGS_WRITE_XATTR     0x02
+#define FLAGS_CLEAR_XATTR     0x04
+#define FLAGS_MAX_AGE         0x08
+#define FLAGS_VERBOSE         0x10
+#define FLAGS_NUL_TERMINATED  0x20
 
 struct options {
   int flags;
@@ -60,7 +61,8 @@ void usage()
           "  -w, --write-xattr          write MD5 extended attribute\n"
           "  -c, --clear-xattr          remove MD5 extended attribute\n"
           "  -m, --max-age     SECONDS  max age of MD5 extended attribute\n"
-          "  -v, --verbose              be verbose\n");
+          "  -v, --verbose              be verbose\n"
+          "      --print0               nul terminated lines\n");
   exit(1);
 }
 
@@ -115,17 +117,20 @@ void trim(char *s) {
 
 void printentry(long long size, int mtime, 
 		int uid, int gid, int mode,
-		char *md5, char kind, char *name)
+		char *md5, char kind, char *name, 
+                struct options *options)
 {
   trim(name);
   if (name[0]) {
     /*  Don't print empty filenames */
     if (kind != 'D') {
-      printf("%Ld:%d:%d:%d:%o:%s:%c:%s\n",
-	     size, mtime, uid, gid, mode & ~S_IFMT, md5, kind, name);
+      printf("%Ld:%d:%d:%d:%o:%s:%c:%s%c",
+             size, mtime, uid, gid, mode & ~S_IFMT, md5, kind, name,
+             (options->flags & FLAGS_NUL_TERMINATED) ? '\0' : '\n');
     } else {
-      printf("::%d:%d:%o:%s:%c:%s\n",
-	     uid, gid, mode & ~S_IFMT, md5, kind, name);
+      printf("::%d:%d:%o:%s:%c:%s%c",
+	     uid, gid, mode & ~S_IFMT, md5, kind, name,
+             (options->flags & FLAGS_NUL_TERMINATED) ? '\0' : '\n');
     }
   }
 }
@@ -214,7 +219,7 @@ void md5sum_file(char *filename, const struct stat64 buf,
 
   if (options->flags & FLAGS_CLEAR_XATTR) {
     if (removexattr(filename, XATTR_MD5SUM) != 0) {
-      if (errno != ENOATTR) {
+      if (errno != ENODATA) {
 	fprintf(stderr, "Failed to remove xattr '%s'\n", filename);
       }
     } else if (options->flags & FLAGS_VERBOSE) {
@@ -240,7 +245,7 @@ void md5sum_file(char *filename, const struct stat64 buf,
       recalc = 0;
    } else {
       if (removexattr(filename, XATTR_MD5SUM) != 0) {
-        if (errno != ENOATTR) {
+        if (errno != ENODATA) {
           fprintf(stderr, "Failed to remove xattr '%s' (mtime)\n", filename);
         }
       } else if (options->flags & FLAGS_VERBOSE) {
@@ -258,7 +263,7 @@ void md5sum_file(char *filename, const struct stat64 buf,
       if (options->flags & (FLAGS_READ_XATTR|FLAGS_WRITE_XATTR)) {
 	/* Only remove md5sum for unreadble file if we are root */
 	if (removexattr(filename, XATTR_MD5SUM) != 0) {
-          if (errno != ENOATTR) {
+          if (errno != ENODATA) {
             fprintf(stderr, "Failed to remove xattr '%s' (unreadable)\n", 
                     filename);
           }
@@ -294,7 +299,7 @@ void md5sum_file(char *filename, const struct stat64 buf,
   }
   printentry(buf.st_size, buf.st_mtime, 
 	     buf.st_uid, buf.st_gid, buf.st_mode,
-	     md5, 'F', filename);
+	     md5, 'F', filename, options);
 out:
   ;
 }
@@ -316,13 +321,14 @@ void dofile(char *filename, dev_t *dev, struct options *options)
     fprintf(stderr, "Failed to lstat '%s'\n", filename);
   } else if (*dev != buf.st_dev) {
     fprintf(stderr, "skipping mountpoint '%s'\n", filename);
-  } else if (filenameislegal(filename)) {
+  } else if (options->flags & FLAGS_NUL_TERMINATED ||
+             filenameislegal(filename)) {
     if (S_ISREG(buf.st_mode)) {
       md5sum_file(filename, buf, options);
     } else if (S_ISDIR(buf.st_mode)) {
       if (! (options->flags & FLAGS_CLEAR_XATTR)) {
 	printentry(0, 0, buf.st_uid, buf.st_gid, buf.st_mode,
-		   "", 'D', filename);
+		   "", 'D', filename, options);
       }
       if (filename[0] == 0) { filename = "."; }
       dodir(filename, dev, options);
@@ -344,7 +350,7 @@ void dofile(char *filename, dev_t *dev, struct options *options)
 	}
 	printentry(buf.st_size, buf.st_mtime,  
 		   buf.st_uid, buf.st_gid, buf.st_mode,
-		   md5, 'L', filename);
+		   md5, 'L', filename, options);
       }
       free(link);
     } else {
@@ -359,7 +365,7 @@ void dofile(char *filename, dev_t *dev, struct options *options)
       }
       printentry(buf.st_size, buf.st_mtime, 
 		 buf.st_uid, buf.st_gid, buf.st_mode,
-		 "", kind, filename);
+		 "", kind, filename, options);
     }
   }
 out:
@@ -382,6 +388,7 @@ int main(int argc, char *argv[])
     {"clear-xattr", no_argument,       NULL,  'c' },
     {"max-age",     required_argument, NULL,  'm' },
     {"verbose",     no_argument,       NULL,  'v' },
+    {"print0",      no_argument,       NULL,  'z' },
     {0,             0,                 NULL,  0 }
   };
 
@@ -415,6 +422,9 @@ int main(int argc, char *argv[])
       case 'v': {
 	options.flags |= FLAGS_VERBOSE;
       } break;
+      case 'z': {
+	options.flags |= FLAGS_NUL_TERMINATED;
+      } break;
     }
   }
   if (options.flags & (FLAGS_READ_XATTR | FLAGS_WRITE_XATTR | 
@@ -426,11 +436,14 @@ int main(int argc, char *argv[])
     }
   }
 
-  printf("#fields: size:mtime:uid:gid:mode:md5:kind:name\n");
+  printf("#fields: size:mtime:uid:gid:mode:md5:kind:name%c",
+         (options.flags & FLAGS_NUL_TERMINATED) ? '\0' : '\n');
   for (i = optind ; i < argc ; i++) {
-    printf("#path: %s\n", argv[i]);
+    printf("#path: %s%c", argv[i],
+           (options.flags & FLAGS_NUL_TERMINATED) ? '\0' : '\n');
     dofile(argv[i], 0, &options);
   }
-  printf("#endTOC\n");
+  printf("#endTOC%c",
+         (options.flags & FLAGS_NUL_TERMINATED) ? '\0' : '\n');
   return 0;
 }
-- 
GitLab