Add optional json output from --version.
authorWayne Davison <wayne@opencoder.net>
Fri, 9 Sep 2022 19:16:37 +0000 (12:16 -0700)
committerWayne Davison <wayne@opencoder.net>
Fri, 9 Sep 2022 19:16:37 +0000 (12:16 -0700)
json-version.diff [new file with mode: 0644]

diff --git a/json-version.diff b/json-version.diff
new file mode 100644 (file)
index 0000000..86ba1d2
--- /dev/null
@@ -0,0 +1,182 @@
+This patch changes a repeated `--version` (`-V`) to output the
+version, capabilities, etc. in json format.
+
+To use this patch, run these commands for a successful build:
+
+    patch -p1 <patches/json-version.diff
+    ./configure                               (optional if already run)
+    make
+
+diff --git a/options.c b/options.c
+index 3f8d5d08..6c8534c3 100644
+--- a/options.c
++++ b/options.c
+@@ -1926,7 +1926,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+               saw_stderr_opt = 1;
+       if (version_opt_cnt) {
+-              print_rsync_version(FINFO);
++              print_rsync_version(FINFO, version_opt_cnt > 1);
+               exit_cleanup(0);
+       }
+diff --git a/usage.c b/usage.c
+index 048fd4cb..615049eb 100644
+--- a/usage.c
++++ b/usage.c
+@@ -22,6 +22,7 @@
+ #include "latest-year.h"
+ #include "git-version.h"
+ #include "default-cvsignore.h"
++#include "itypes.h"
+ extern struct name_num_obj valid_checksums;
+ extern struct name_num_obj valid_compressions;
+@@ -34,10 +35,10 @@ static char *istring(const char *fmt, int val)
+       return str;
+ }
+-static void print_info_flags(enum logcode f)
++static void print_info_flags(enum logcode f, BOOL as_json)
+ {
+       STRUCT_STAT *dumstat;
+-      char line_buf[75];
++      char line_buf[75], *quot = as_json ? "\"" : "";
+       int line_len, j;
+       char *info_flags[] = {
+@@ -161,10 +162,12 @@ static void print_info_flags(enum logcode f)
+               NULL
+       };
++      if (as_json)
++              as_json = 1; /* We use 1 == first attribute, 2 == need closing array */
+       for (line_len = 0, j = 0; ; j++) {
+               char *str = info_flags[j], *next_nfo = str ? info_flags[j+1] : NULL;
+-              int str_len = str && *str != '*' ? strlen(str) : 1000;
++              int str_len = str && *str != '*' ? strlen(str) + (as_json ? 2 : 0) : 1000;
+               int need_comma = next_nfo && *next_nfo != '*' ? 1 : 0;
+               if (line_len && line_len + 1 + str_len + need_comma >= (int)sizeof line_buf) {
+                       rprintf(f, "   %s\n", line_buf);
+@@ -173,37 +176,88 @@ static void print_info_flags(enum logcode f)
+               if (!str)
+                       break;
+               if (*str == '*') {
+-                      rprintf(f, "%s:\n", str+1);
++                      if (as_json) {
++                              if (as_json == 2)
++                                      printf("  ],\n");
++                              else
++                                      as_json = 2;
++                              printf("  \"%c%s\": [\n", toLower(str+1), str+2);
++                      } else
++                              rprintf(f, "%s:\n", str+1);
+                       continue;
+               }
+-              line_len += snprintf(line_buf+line_len, sizeof line_buf - line_len, " %s%s", str, need_comma ? "," : "");
++              line_len += snprintf(line_buf+line_len, sizeof line_buf - line_len,
++                                   " %s%s%s%s", quot, str, quot, need_comma ? "," : "");
+       }
++      if (as_json == 2)
++              printf("  ],\n");
+ }
+-void print_rsync_version(enum logcode f)
++void output_json_list(const char *name, char *tmpbuf)
++{
++      char *comma = ",";
++      char *tok = strtok(tmpbuf, " ");
++      char *next_tok = tok ? strtok(NULL, " ") : NULL;
++
++      printf("  \"%s\": [\n   ", name);
++
++      while (tok) {
++              printf(" \"%s\"%s", tok, comma + (next_tok ? 0 : 1));
++              if ((tok = next_tok) != NULL)
++                      next_tok = strtok(NULL, " ");
++      }
++      printf("\n  ],\n");
++}
++
++void print_rsync_version(enum logcode f, BOOL as_json)
+ {
+       char tmpbuf[256], *subprotocol = "";
+ #if SUBPROTOCOL_VERSION != 0
+       subprotocol = istring(".PR%d", SUBPROTOCOL_VERSION);
+ #endif
+-      rprintf(f, "%s  version %s  protocol version %d%s\n",
+-              RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
+-      rprintf(f, "Copyright (C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.\n");
+-      rprintf(f, "Web site: https://rsync.samba.org/\n");
++      if (as_json) {
++              printf("{\n  \"program\": \"%s\",\n  \"version\": \"%s\",\n  \"protocol\": \"%d%s\",\n",
++                      RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
++      } else {
++              rprintf(f, "%s  version %s  protocol version %d%s\n",
++                      RSYNC_NAME, rsync_version(), PROTOCOL_VERSION, subprotocol);
++      }
+-      print_info_flags(f);
++#define vinfo(name, pre, msg, suf) \
++      if (as_json) \
++              printf("  \"%s\": \"%s\",\n", name, msg); \
++      else \
++              rprintf(f, "%s%s%s", pre ? pre : name, msg, suf);
++
++      vinfo("copyright", "Copyright ", "(C) 1996-" LATEST_YEAR " by Andrew Tridgell, Wayne Davison, and others.", "\n");
++      vinfo("url", "Web site: ", "https://rsync.samba.org/", "\n");
++
++      print_info_flags(f, as_json);
+       init_checksum_choices();
+-      rprintf(f, "Checksum list:\n");
+-      get_default_nno_list(&valid_checksums, tmpbuf, sizeof tmpbuf, '(');
+-      rprintf(f, "    %s\n", tmpbuf);
++      get_default_nno_list(&valid_checksums, tmpbuf, sizeof tmpbuf, as_json ? '\0' : '(');
++      if (as_json)
++              output_json_list("checksum_list", tmpbuf);
++      else {
++              rprintf(f, "Checksum list:\n");
++              rprintf(f, "    %s\n", tmpbuf);
++      }
+-      rprintf(f, "Compress list:\n");
+-      get_default_nno_list(&valid_compressions, tmpbuf, sizeof tmpbuf, '(');
+-      rprintf(f, "    %s\n", tmpbuf);
++      get_default_nno_list(&valid_compressions, tmpbuf, sizeof tmpbuf, as_json ? '\0' : '(');
++      if (as_json)
++              output_json_list("compress_list", tmpbuf);
++      else {
++              rprintf(f, "Compress list:\n");
++              rprintf(f, "    %s\n", tmpbuf);
++      }
++
++      if (as_json) {
++              printf("  \"caveat\": \"rsync comes with ABSOLUTELY NO WARRANTY\"\n}\n");
++              return;
++      }
+ #ifdef MAINTAINER_MODE
+       rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
+@@ -226,7 +280,7 @@ void print_rsync_version(enum logcode f)
+ void usage(enum logcode F)
+ {
+-  print_rsync_version(F);
++  print_rsync_version(F, 0);
+   rprintf(F,"\n");
+   rprintf(F,"rsync is a file transfer program capable of efficient remote update\n");
+@@ -253,7 +307,7 @@ void usage(enum logcode F)
+ void daemon_usage(enum logcode F)
+ {
+-  print_rsync_version(F);
++  print_rsync_version(F, 0);
+   rprintf(F,"\n");
+   rprintf(F,"Usage: rsync --daemon [OPTION]...\n");