gnunet-qr: Reimplement in C - yet only a proof of concept.
authorHartmut Goebel <h.goebel@crazy-compilers.com>
Sat, 2 Mar 2019 10:00:53 +0000 (11:00 +0100)
committerChristian Grothoff <christian@grothoff.org>
Wed, 3 Apr 2019 11:42:16 +0000 (13:42 +0200)
Still to-do:
* running gnunet-uri
* Proper error handling
* integration into build system (automake)

Reimplementing in C was chosen since
- official zbar python-bindings support python 2 only,
- none of the other bindings available at PyPI supports the high-level
  "processor" interface which gnunet-qr uses
- implementing bindings for zbar using ctypes required addin a lot of
  low-level error handling code, thus implementing in C seamed to be
  easier,
- the programm is short, thus re-implementing is not such complicated, and
- this allows to reduce the number of dependencies (here: another
  Python version), which should ease porting to other plattforms (zbar
  is a dependency anyway).

src/util/gnunet-qr.c [new file with mode: 0644]
src/util/gnunet-qr.in [deleted file]
src/util/gnunet-qr.py [deleted file]

diff --git a/src/util/gnunet-qr.c b/src/util/gnunet-qr.c
new file mode 100644 (file)
index 0000000..c02212a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013-2019 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+#include <stdio.h>
+#include <zbar.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+static const char *usage_note =
+  "gnunet-qr\n"
+  "Scan a QR code using a video device and import\n"
+  "\n"
+  "Arguments mandatory for long options are also mandatory for short options.\n"
+  "  -c, --config FILENAME      use configuration file FILENAME\n"
+  "  -d, --device DEVICE        use device DEVICE\n"
+  "  -s, --silent               do not show preview windows\n"
+  "  -h, --help                 print this help\n"
+  "  -v, --verbose              be verbose\n"
+  "Report bugs to gnunet-developers@gnu.org.\n"
+  "\n"
+  "GNUnet home page: https://gnunet.org/\n"
+  "General help using GNU software: https://www.gnu.org/gethelp/\n";
+
+int main (int argc, char **argv)
+{
+  const char* configuration = NULL;
+  const char* device = "/dev/video0";
+  static bool verbose = false;
+  static bool silent = false;
+
+  static struct option long_options[] = {
+      {"verbose", no_argument,       0, 'v'},
+      {"silent",  no_argument,       0, 's'},
+      {"help",    no_argument,       0, 'h'},
+      {"config",  required_argument, 0, 'c'},
+      {"device",  required_argument, 0, 'd'},
+      {0, 0, 0, 0}
+    };
+  while (1) {
+    int opt;
+    opt = getopt_long (argc, argv, "c:hd:sv",
+                    long_options, NULL);
+    if (opt == -1)
+      break;
+
+    switch (opt) {
+    case 'h':
+      printf(usage_note);
+      return 0;
+    case 'c':
+      configuration = optarg;
+      break;
+    case 'd':
+      device = optarg;
+      break;
+    case 's':
+      silent = true;
+      break;
+    case 'v':
+      verbose = true;
+      break;
+    default:
+      printf(usage_note);
+      return 1;
+    }
+  }
+
+  /* create a Processor */
+  if (verbose == true) {
+    printf("Initializing\n");
+  };
+  zbar_processor_t *proc = zbar_processor_create(1);
+
+  // FIXME: Wrap all this into a function which returns an error on
+  // failure. And here ensure the processor is destroyed at the end.
+
+  /* configure the Processor */
+  zbar_processor_parse_config(proc, "enable");
+
+  /* initialize the Processor */
+  if (verbose == true) {
+    printf("Opening video device %s\n", device);
+  };
+  // FIXME: error handling
+  zbar_processor_init(proc, device, 1);
+
+  /* enable the preview window */
+  zbar_processor_set_visible(proc, 1);
+  zbar_processor_set_active(proc, 1);
+
+  /* keep scanning until user provides key/mouse input */
+  //zbar_processor_user_wait(proc, -1);
+
+  // read at least one barcode (or until window closed)
+  if (verbose == true) {
+    printf("Capturing\n");
+  }
+  int n;
+  n = zbar_process_one(proc, -1);
+  if (verbose == true) {
+    printf("Got %i images\n", n);
+  };
+  // FIXME: Error handling (n = -1)
+
+  // hide the preview window
+  zbar_processor_set_active(proc, 0);
+  zbar_processor_set_visible(proc, 0);
+
+  // extract results
+  int rc = 1;
+
+  const zbar_symbol_set_t* symbols = zbar_processor_get_results(proc);
+  const zbar_symbol_t* symbol = zbar_symbol_set_first_symbol(symbols);
+
+  if (symbol != NULL) {
+    const char* data = zbar_symbol_get_data(symbol);
+    if (verbose = true) {
+      zbar_symbol_type_t type =
+      printf("Found %s \"%s\"\n",
+            zbar_get_symbol_name(zbar_symbol_get_type(symbol)), data);
+    }
+    /* TODO
+    args = ["gnunet-uri",
+           // FIXME: "-c", configuration,
+           data];
+    if (verbose = true) {
+      // TODO: print arguments:
+      printf("Running `%s %s %s %s`", *args, "", ""); // FIXME variable num args
+    };
+    rc = popen("gnunet-uri", *args);
+    */
+    if (rc != 0) {
+      printf("Failed to add URI %s\n", data);
+    } else {
+      printf("Added URI %s\n", data);
+    }
+  }
+
+  /* clean up */
+  zbar_processor_destroy(proc);
+
+  return(rc);
+}
diff --git a/src/util/gnunet-qr.in b/src/util/gnunet-qr.in
deleted file mode 100755 (executable)
index ce7a19b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/sh
-#
-# From curl's buildconf, making this script subject to the
-# curl license: https://curl.haxx.se/docs/copyright.html
-# Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
-# Copyright (C) 2019 GNUnet e.V.
-
-# findtool works like which without relying on which (which is a problem
-# for some limited shells.
-findtool(){
-  file="$1"
-
-  if { echo "$file" | grep "/" >/dev/null 2>&1; } then
-    # when file is given with a path check it first
-    if test -f "$file"; then
-      echo "$file"
-      return
-    fi
-  fi
-
-  old_IFS=$IFS; IFS=':'
-  for path in $PATH
-  do
-    IFS=$old_IFS
-    # echo "checks for $file in $path" >&2
-    if test "$path" -a "$path" != '.' -a -f "$path/$file"; then
-      echo "$path/$file"
-      return
-    fi
-  done
-  IFS=$old_IFS
-}
-
-# end curl licensed code
-pythonize=`findtool python2.7 2>/dev/null`
-if test ! -x "$pythonize"; then
-       pythonize=`findtool ${PYTHON2:-python2.7}`
-fi
-
-if test -z "$pythonize"; then
-  echo "ERROR: python2.7 not found."
-  echo "  You need python2.7 installed."
-  exit 1
-fi
-
-${pythonize} @PREFIX@/share/gnunet/gnunet-qr.py $@
diff --git a/src/util/gnunet-qr.py b/src/util/gnunet-qr.py
deleted file mode 100644 (file)
index 0ee0b95..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-import sys
-import getopt
-import subprocess
-from sys import argv
-try:
-    import zbar
-except ImportError as e:
-    print('Cannot run gnunet-qr, please install the zbar module.')
-    print('For Debian, you can obtain it as "python-zbar".')
-    print('Upstream: http://zbar.sourceforge.net/')
-    sys.exit(1)
-
-
-def help():
-    print('gnunet-qr\n\
-Scan a QR code using a video device and import\n\
-Arguments mandatory for long options are also mandatory for short options.\n\
-  -c, --config FILENAME      use configuration file FILENAME\n\
-  -d, --device DEVICE        use device DEVICE\n\
-  -s, --silent               do not show preview windows\n\
-  -h, --help                 print this help\n\
-  -v, --verbose              be verbose\n\
-Report bugs to gnunet-developers@gnu.org.\n\
-GNUnet home page: https://gnunet.org/\n\
-General help using GNU software: https://www.gnu.org/gethelp/')
-
-
-if __name__ == '__main__':
-    configuration = ''
-    device = '/dev/video0'
-    url = ''
-    verbose = False
-    silent = False
-    # Parse arguments
-    try:
-        opts, args = getopt.gnu_getopt(sys.argv[1:], "c:hd:sv", ["config", "help", "device", "silent", "verbose"])
-    except getopt.GetoptError as e:
-        help()
-        print(str(e))
-        exit(1)
-    for o, a in opts:
-        if o in ("-h", "--help"):
-            help()
-            sys.exit(0)
-        elif o in ("-c", "--config"):
-            configuration = a
-        elif o in ("-d", "--device"):
-            device = a
-        elif o in ("-s", "--silent"):
-            silent = True
-        elif o in ("-v", "--verbose"):
-            verbose = True
-    if (True == verbose):
-        print('Initializing')
-    # create a Processor
-    proc = zbar.Processor()
-
-    # configure the Processor
-    proc.parse_config('enable')
-
-    # initialize the Processor
-    try:
-        if (True == verbose):
-            print('Opening video device ' + device)
-        proc.init(device)
-    except Exception as e:
-        print('Failed to open device ' + device)
-        exit(1)
-
-    # enable the preview window
-    # if (True == silent):
-    #       proc.visible = True
-    # else:
-    #               proc.visible = False
-
-    proc.visible = True
-    # read at least one barcode (or until window closed)
-    try:
-        if (True == verbose):
-            print('Capturing')
-        proc.process_one()
-    except Exception as e:
-        # Window was closed without finding code
-        exit(1)
-
-    # hide the preview window
-    proc.visible = False
-
-    # extract results
-    for symbol in proc.results:
-        # do something useful with results
-        if (True == verbose):
-            print('Found ', symbol.type, ' symbol ', '"%s"' % symbol.data)
-        args = list()
-        args.append("gnunet-uri")
-        if (configuration != ''):
-            args.append(str("-c " + str(configuration)))
-        args.append(str(symbol.data))
-        cmd = ''
-        for a in args:
-            cmd += " " + str(a)
-        if (verbose):
-            print('Running `' + cmd +'`')
-        res = subprocess.call(args)
-        if (0 != res):
-            print('Failed to add URI ' + str(symbol.data))
-        else:
-            print('Added URI ' + str(symbol.data))
-        exit(res)
-    exit(1)