[Forensics-changes] [yara] 222/407: Use argparse library to parse command line arguments and some other improvements

Hilko Bengen bengen at moszumanska.debian.org
Sat Jul 1 10:28:28 UTC 2017


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to annotated tag v3.3.0
in repository yara.

commit f51c60806dca65deaff8812996e2f6510b7ba712
Author: Victor M. Alvarez <plusvic at gmail.com>
Date:   Fri Nov 21 16:36:16 2014 +0100

    Use argparse library to parse command line arguments and some other improvements
---
 Makefile.am              |  10 +-
 argparse/.gitignore      |   5 +
 argparse/.travis.yml     |   5 +
 argparse/LICENSE         |  21 ++
 argparse/README.md       |  89 ++++++
 argparse/argparse.c      | 366 +++++++++++++++++++++++
 argparse/argparse.h      | 158 ++++++++++
 argparse/tap-functions   | 445 ++++++++++++++++++++++++++++
 argparse/test.sh         |  46 +++
 argparse/test_argparse.c |  57 ++++
 yara.c                   | 749 ++++++++++++++++++-----------------------------
 yarac.c                  | 251 ++++++++--------
 12 files changed, 1610 insertions(+), 592 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 04dee0e..6c237a4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,18 +1,18 @@
-AM_CFLAGS=-O3 -Wall -I$(srcdir)/libyara/include
+AM_CFLAGS=-O3 -std=gnu99 -Wall -I$(srcdir)/libyara/include
 
 # Build the library in the hand subdirectory first.
-SUBDIRS = libyara
-DIST_SUBDIRS = libyara
+SUBDIRS = libyara argparse
+DIST_SUBDIRS = libyara argparse
 
 ACLOCAL_AMFLAGS=-I m4
 
 bin_PROGRAMS = yara yarac
 
 yara_SOURCES = threading.c threading.h yara.c
-yara_LDADD = libyara/.libs/libyara.a
+yara_LDADD = libyara/.libs/libyara.a argparse/libargparse.a
 
 yarac_SOURCES = yarac.c
-yarac_LDADD = libyara/.libs/libyara.a
+yarac_LDADD = libyara/.libs/libyara.a argparse/libargparse.a
 
 # man pages
 man1_MANS = yara.man yarac.man
diff --git a/argparse/.gitignore b/argparse/.gitignore
new file mode 100755
index 0000000..fb716d2
--- /dev/null
+++ b/argparse/.gitignore
@@ -0,0 +1,5 @@
+tags
+test_argparse
+*.[ao]
+*.dylib
+*.so
diff --git a/argparse/.travis.yml b/argparse/.travis.yml
new file mode 100755
index 0000000..41bac87
--- /dev/null
+++ b/argparse/.travis.yml
@@ -0,0 +1,5 @@
+language: c
+compiler:
+  - gcc
+  - clang
+script: make test
diff --git a/argparse/LICENSE b/argparse/LICENSE
new file mode 100755
index 0000000..3c77749
--- /dev/null
+++ b/argparse/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2013 Yecheng Fu <cofyc.jackson at gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/argparse/README.md b/argparse/README.md
new file mode 100755
index 0000000..6d2ee87
--- /dev/null
+++ b/argparse/README.md
@@ -0,0 +1,89 @@
+NAME
+====
+
+argparse - A command line arguments parsing library.
+
+[![Build Status](https://travis-ci.org/Cofyc/argparse.png)](https://travis-ci.org/Cofyc/argparse)
+
+DESCRIPTION
+===========
+
+This module is inspired by parse-options.c (git) and python's argparse
+module.
+
+Arguments parsing is common task in cli program, but traditional `getopt`
+libraries are not easy to use. This library provides high-level arguments
+parsing solutions.
+
+The program defines what arguments it requires, and `argparse` will figure
+out how to parse those out of `argc` and `argv`, it also automatically
+generates help and usage messages and issues errors when users give the
+program invalid arguments.
+
+Features
+========
+
+ - handles both optional and positional arguments
+ - produces highly informative usage messages
+ - issures errors when given invalid arguments
+
+There are basically three types of options:
+
+ - boolean options
+ - options with mandatory argument
+ - options with optional argument
+
+There are basically two forms of options:
+
+ - short option consist of one dash (`-`) and one alphanumeric character.
+ - long option begin with two dashes (`--`) and some alphanumeric characters.
+
+Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
+
+Options are case-sensitive.
+
+Options and non-option arguments can clearly be separated using the `--` option.
+
+Examples
+========
+
+```c
+#include "argparse.h"
+
+static const char *const usage[] = {
+   "test_argparse [options] [[--] args]",
+   NULL,
+};
+
+int
+main(int argc, const char **argv)
+{
+   int force = 0;
+   int num = 0;
+   const char *path = NULL;
+   struct argparse_option options[] = {
+       OPT_HELP(),
+       OPT_BOOLEAN('f', "force", &force, "force to do", NULL),
+       OPT_STRING('p', "path", &path, "path to read", NULL),
+       OPT_INTEGER('n', "num", &num, "selected num", NULL),
+       OPT_END(),
+   };
+   struct argparse argparse;
+   argparse_init(&argparse, options, usage, 0);
+   argc = argparse_parse(&argparse, argc, argv);
+   if (force != 0)
+       printf("force: %d\n", force);
+   if (path != NULL)
+       printf("path: %s\n", path);
+   if (num != 0)
+       printf("num: %d\n", num);
+   if (argc != 0) {
+       printf("argc: %d\n", argc);
+       int i;
+       for (i = 0; i < argc; i++) {
+           printf("argv[%d]: %s\n", i, *(argv + i));
+       }
+   }
+   return 0;
+}
+```
diff --git a/argparse/argparse.c b/argparse/argparse.c
new file mode 100755
index 0000000..260f103
--- /dev/null
+++ b/argparse/argparse.c
@@ -0,0 +1,366 @@
+#include "argparse.h"
+
+#define OPT_UNSET 1
+
+static const char *
+prefix_skip(const char *str, const char *prefix)
+{
+    size_t len = strlen(prefix);
+    return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+int
+prefix_cmp(const char *str, const char *prefix)
+{
+    for (;; str++, prefix++)
+        if (!*prefix)
+            return 0;
+        else if (*str != *prefix)
+            return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+static void
+argparse_error(struct argparse *self, struct argparse_option *opt,
+               const char *reason)
+{
+    if (!strncmp(self->argv[0], "--", 2)) {
+        fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason);
+        exit(1);
+    } else {
+        fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason);
+        exit(1);
+    }
+}
+
+static int
+argparse_getvalue(struct argparse *self, struct argparse_option *opt,
+                  int flags)
+{
+    const char *s = NULL;
+
+    if (opt->count == opt->max_count)
+        argparse_error(self, opt, "repeated too many times");
+
+    if (!opt->value)
+        goto skipped;
+
+    switch (opt->type) {
+    case ARGPARSE_OPT_BOOLEAN:
+        if (flags & OPT_UNSET) {
+            *(int *)opt->value = *(int *)opt->value - 1;
+        } else {
+            *(int *)opt->value = *(int *)opt->value + 1;
+        }
+        if (*(int *)opt->value < 0) {
+            *(int *)opt->value = 0;
+        }
+        break;
+    case ARGPARSE_OPT_BIT:
+        if (flags & OPT_UNSET) {
+            *(int *)opt->value &= ~opt->data;
+        } else {
+            *(int *)opt->value |= opt->data;
+        }
+        break;
+    case ARGPARSE_OPT_STRING:
+        if (self->optvalue) {
+            *(const char **)opt->value = self->optvalue;
+            self->optvalue = NULL;
+        } else if (self->argc > 1) {
+            self->argc--;
+            if (opt->max_count > 1) {
+                ((const char**)opt->value)[opt->count] = *++self->argv;
+            }
+            else {
+                *(const char **)opt->value = *++self->argv;
+            }
+        } else {
+            argparse_error(self, opt, "requires a value");
+        }
+        break;
+    case ARGPARSE_OPT_INTEGER:
+        if (self->optvalue) {
+            *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0);
+            self->optvalue = NULL;
+        } else if (self->argc > 1) {
+            self->argc--;
+            if (opt->max_count > 1) {
+                ((int*)opt->value)[opt->count] = strtol(*++self->argv, (char **)&s, 0);
+            }
+            else {
+                *(int *)opt->value = strtol(*++self->argv, (char **)&s, 0);
+            }
+        } else {
+            argparse_error(self, opt, "requires a value");
+        }
+        if (s[0] != '\0')
+            argparse_error(self, opt, "expects a numerical value");
+        break;
+    default:
+        assert(0);
+    }
+
+    opt->count++;
+
+skipped:
+    if (opt->callback) {
+        return opt->callback(self, opt);
+    }
+
+    return 0;
+}
+
+static void
+argparse_options_check(struct argparse_option *options)
+{
+    for (; options->type != ARGPARSE_OPT_END; options++) {
+        switch (options->type) {
+        case ARGPARSE_OPT_END:
+        case ARGPARSE_OPT_BOOLEAN:
+        case ARGPARSE_OPT_BIT:
+        case ARGPARSE_OPT_INTEGER:
+        case ARGPARSE_OPT_STRING:
+        case ARGPARSE_OPT_GROUP:
+            continue;
+        default:
+            fprintf(stderr, "wrong option type: %d", options->type);
+            break;
+        }
+    }
+}
+
+static int
+argparse_short_opt(struct argparse *self, struct argparse_option *options)
+{
+    for (; options->type != ARGPARSE_OPT_END; options++) {
+        if (options->short_name == *self->optvalue) {
+            self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
+            return argparse_getvalue(self, options, 0);
+        }
+    }
+    return -2;
+}
+
+static int
+argparse_long_opt(struct argparse *self, struct argparse_option *options)
+{
+    for (; options->type != ARGPARSE_OPT_END; options++) {
+        const char *rest;
+        int opt_flags = 0;
+        if (!options->long_name)
+            continue;
+
+        rest = prefix_skip(self->argv[0] + 2, options->long_name);
+        if (!rest) {
+            // Negation allowed?
+            if (options->flags & OPT_NONEG) {
+                continue;
+            }
+            // Only boolean/bit allow negation.
+            if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) {
+                continue;
+            }
+
+            if (!prefix_cmp(self->argv[0] + 2, "no-")) {
+                rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
+                if (!rest)
+                    continue;
+                opt_flags |= OPT_UNSET;
+            } else {
+                continue;
+            }
+        }
+        if (*rest) {
+            if (*rest != '=')
+                continue;
+            self->optvalue = rest + 1;
+        }
+        return argparse_getvalue(self, options, opt_flags);
+    }
+    return -2;
+}
+
+int
+argparse_init(struct argparse *self, struct argparse_option *options,
+              const char *const *usage, int flags)
+{
+    memset(self, 0, sizeof(*self));
+    self->options = options;
+    self->usage = usage;
+    self->flags = flags;
+
+    for (; options->type != ARGPARSE_OPT_END; options++)
+        options->count = 0;
+
+    return 0;
+}
+
+int
+argparse_parse(struct argparse *self, int argc, const char **argv)
+{
+    self->argc = argc - 1;
+    self->argv = argv + 1;
+    self->out = argv;
+
+    argparse_options_check(self->options);
+
+    for (; self->argc; self->argc--, self->argv++) {
+        const char *arg = self->argv[0];
+        if (arg[0] != '-' || !arg[1]) {
+            if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
+                goto end;
+            }
+            // if it's not option or is a single char '-', copy verbatimly
+            self->out[self->cpidx++] = self->argv[0];
+            continue;
+        }
+        // short option
+        if (arg[1] != '-') {
+            self->optvalue = arg + 1;
+            switch (argparse_short_opt(self, self->options)) {
+            case -1:
+                break;
+            case -2:
+                goto unknown;
+            }
+            while (self->optvalue) {
+                switch (argparse_short_opt(self, self->options)) {
+                case -1:
+                    break;
+                case -2:
+                    goto unknown;
+                }
+            }
+            continue;
+        }
+        // if '--' presents
+        if (!arg[2]) {
+            self->argc--;
+            self->argv++;
+            break;
+        }
+        // long option
+        switch (argparse_long_opt(self, self->options)) {
+        case -1:
+            break;
+        case -2:
+            goto unknown;
+        }
+        continue;
+
+unknown:
+        fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
+        argparse_usage(self);
+        exit(1);
+    }
+
+end:
+    memmove(self->out + self->cpidx, self->argv,
+            self->argc * sizeof(*self->out));
+    self->out[self->cpidx + self->argc] = NULL;
+
+    return self->cpidx + self->argc;
+}
+
+void
+argparse_usage(struct argparse *self)
+{
+    fprintf(stdout, "Usage: %s\n", *self->usage++);
+    while (*self->usage && **self->usage)
+        fprintf(stdout, "   or: %s\n", *self->usage++);
+    fputc('\n', stdout);
+
+    struct argparse_option *options;
+
+    // figure out best width
+    size_t usage_opts_width = 0;
+    size_t len;
+    options = self->options;
+    for (; options->type != ARGPARSE_OPT_END; options++) {
+        len = 0;
+        if ((options)->short_name) {
+            len += 2;
+        }
+        if ((options)->short_name && (options)->long_name) {
+            len += 2;           // separator ", "
+        }
+        if ((options)->long_name) {
+            len += strlen((options)->long_name) + 2;
+        }
+        if (options->type == ARGPARSE_OPT_INTEGER) {
+            len++; // equal sign "=" or space
+            if (options->type_help != NULL)
+                len += strlen(options->type_help);
+            else
+                len += strlen("<int>");
+        } else if (options->type == ARGPARSE_OPT_STRING) {
+            len++; // equal sign "=" or space
+            if (options->type_help != NULL)
+                len += strlen(options->type_help);
+            else
+                len += strlen("<str>");
+        }
+        len = ceil((float)len / 4) * 4;
+        if (usage_opts_width < len) {
+            usage_opts_width = len;
+        }
+    }
+    usage_opts_width += 4;      // 4 spaces prefix
+
+    options = self->options;
+    for (; options->type != ARGPARSE_OPT_END; options++) {
+        size_t pos = 0;
+        int pad = 0;
+        if (options->type == ARGPARSE_OPT_GROUP) {
+            fputc('\n', stdout);
+            fprintf(stdout, "%s", options->help);
+            fputc('\n', stdout);
+            continue;
+        }
+        pos = fprintf(stdout, "    ");
+        if (options->short_name) {
+            pos += fprintf(stdout, "-%c", options->short_name);
+        }
+        if (options->long_name && options->short_name) {
+            pos += fprintf(stdout, ", ");
+        }
+        if (options->long_name) {
+            pos += fprintf(stdout, "--%s", options->long_name);
+        }
+        if (options->type == ARGPARSE_OPT_INTEGER) {
+            if (options->long_name)
+                pos += fprintf(stdout, "=");
+            else
+                pos += fprintf(stdout, " ");
+            if (options->type_help != NULL)
+                pos += fprintf(stdout, "%s", options->type_help);
+            else
+                pos += fprintf(stdout, "<int>");
+        } else if (options->type == ARGPARSE_OPT_STRING) {
+            if (options->long_name)
+                pos += fprintf(stdout, "=");
+            else
+                pos += fprintf(stdout, " ");
+            if (options->type_help != NULL)
+                pos += fprintf(stdout, "%s", options->type_help);
+            else
+                pos += fprintf(stdout, "<str>");
+        }
+        if (pos <= usage_opts_width) {
+            pad = usage_opts_width - pos;
+        } else {
+            fputc('\n', stdout);
+            pad = usage_opts_width;
+        }
+        fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
+    }
+}
+
+int
+argparse_help_cb(struct argparse *self, const struct argparse_option *option)
+{
+    (void)option;
+    argparse_usage(self);
+    exit(0);
+    return 0;
+}
diff --git a/argparse/argparse.h b/argparse/argparse.h
new file mode 100755
index 0000000..b38a2a7
--- /dev/null
+++ b/argparse/argparse.h
@@ -0,0 +1,158 @@
+#ifndef ARGPARSE_H
+#define ARGPARSE_H
+/**
+ * Command-line arguments parsing library.
+ *
+ * This module is inspired by parse-options.c (git) and python's argparse
+ * module.
+ *
+ * Arguments parsing is common task in cli program, but traditional `getopt`
+ * libraries are not easy to use. This library provides high-level arguments
+ * parsing solutions.
+ *
+ * The program defines what arguments it requires, and `argparse` will figure
+ * out how to parse those out of `argc` and `argv`, it also automatically
+ * generates help and usage messages and issues errors when users give the
+ * program invalid arguments.
+ *
+ * Reserved namespaces:
+ *  argparse
+ *  OPT
+ * Author: Yecheng Fu <cofyc.jackson at gmail.com>
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct argparse;
+struct argparse_option;
+
+typedef int argparse_callback(struct argparse *self,
+                              const struct argparse_option *option);
+
+enum argparse_flag {
+    ARGPARSE_STOP_AT_NON_OPTION = 1,
+};
+
+enum argparse_option_type {
+    /* special */
+    ARGPARSE_OPT_END,
+    ARGPARSE_OPT_GROUP,
+    /* options with no arguments */
+    ARGPARSE_OPT_BOOLEAN,
+    ARGPARSE_OPT_BIT,
+    /* options with arguments (optional or required) */
+    ARGPARSE_OPT_INTEGER,
+    ARGPARSE_OPT_STRING,
+    /* repetable options */
+    ARGPARSE_OPT_INTEGER_MULTI,
+    ARGPARSE_OPT_STRING_MULTI,
+};
+
+enum argparse_option_flags {
+    OPT_NONEG = 1,              /* Negation disabled. */
+};
+
+/*
+ *  Argparse option struct.
+ *
+ *  `type`:
+ *    holds the type of the option, you must have an ARGPARSE_OPT_END last in your
+ *    array.
+ *
+ *  `short_name`:
+ *    the character to use as a short option name, '\0' if none.
+ *
+ *  `long_name`:
+ *    the long option name, without the leading dash, NULL if none.
+ *
+ *  `value`:
+ *    stores pointer to the value to be filled.
+ *
+ *  `max_count`:
+ *
+ *    maximum number of times self option can appear in the command line.
+ *
+ *  `help`:
+ *    the short help message associated to what the option does.
+ *    Must never be NULL (except for ARGPARSE_OPT_END).
+ *
+ *  `callback`:
+ *    function is called when corresponding argument is parsed.
+ *
+ *  `data`:
+ *    associated data. Callbacks can use it like they want.
+ *
+ *  `flags`:
+ *    option flags.
+ *
+ *
+ *
+ */
+struct argparse_option {
+    enum argparse_option_type type;
+    const char short_name;
+    const char *long_name;
+    void *value;
+    int max_count;
+    const char *help;
+    const char *type_help;
+    argparse_callback *callback;
+    intptr_t data;
+    int flags;
+    int count;
+};
+
+/*
+ * argpparse
+ */
+struct argparse {
+    // user supplied
+    struct argparse_option *options;
+    const char *const *usage;
+    int flags;
+    // internal context
+    int argc;
+    const char **argv;
+    const char **out;
+    int cpidx;
+    const char *optvalue;       // current option value
+};
+
+// builtin callbacks
+int argparse_help_cb(struct argparse *self,
+                     const struct argparse_option *option);
+
+// builtin option macros
+#define OPT_BIT(short_name, long_name, value, ...) \
+    { ARGPARSE_OPT_BIT, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_BOOLEAN(short_name, long_name, value, ...) \
+    { ARGPARSE_OPT_BOOLEAN, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_INTEGER(short_name, long_name, value, ...) \
+    { ARGPARSE_OPT_INTEGER, short_name, long_name, value, 1, __VA_ARGS__ }
+
+#define OPT_STRING_MULTI(short_name, long_name, value, max_count, ...) \
+    { ARGPARSE_OPT_STRING, short_name, long_name, value, max_count, __VA_ARGS__ }
+
+#define OPT_STRING(short_name, long_name, value, ...) \
+    OPT_STRING_MULTI(short_name, long_name, value, 1, __VA_ARGS__)
+
+
+#define OPT_GROUP(h)   { ARGPARSE_OPT_GROUP, 0, NULL, NULL, 0, h, NULL, NULL }
+#define OPT_END()      { ARGPARSE_OPT_END }
+
+#define OPT_HELP()     OPT_BOOLEAN('h', "help", NULL, "show self help message and exit", NULL, argparse_help_cb)
+
+
+int argparse_init(struct argparse *self, struct argparse_option *options,
+                  const char *const *usage, int flags);
+int argparse_parse(struct argparse *self, int argc, const char **argv);
+void argparse_usage(struct argparse *self);
+
+#endif
diff --git a/argparse/tap-functions b/argparse/tap-functions
new file mode 100755
index 0000000..84f700e
--- /dev/null
+++ b/argparse/tap-functions
@@ -0,0 +1,445 @@
+#!/bin/bash 
+
+
+_version='1.02'
+	
+_plan_set=0
+_no_plan=0
+_skip_all=0
+_test_died=0
+_expected_tests=0 
+_executed_tests=0 
+_failed_tests=0
+TODO=
+
+
+usage(){
+	cat <<'USAGE'
+tap-functions: A TAP-producing BASH library
+
+PLAN:
+  plan_no_plan
+  plan_skip_all [REASON]
+  plan_tests NB_TESTS
+
+TEST:
+  ok RESULT [NAME]
+  okx COMMAND
+  is RESULT EXPECTED [NAME]
+  isnt RESULT EXPECTED [NAME]
+  like RESULT PATTERN [NAME]
+  unlike RESULT PATTERN [NAME]
+  pass [NAME]
+  fail [NAME]
+
+SKIP:
+  skip [CONDITION] [REASON] [NB_TESTS=1]
+
+  skip $feature_not_present "feature not present" 2 || {
+      is $a "a"
+      is $b "b"
+  }
+
+TODO:
+  Specify TODO mode by setting $TODO:
+    TODO="not implemented yet"
+    ok $result "some not implemented test"
+    unset TODO
+
+OTHER:
+  diag MSG
+
+EXAMPLE:
+  #!/bin/bash
+
+  . tap-functions
+
+  plan_tests 7
+
+  me=$USER
+  is $USER $me "I am myself"
+  like $HOME $me "My home is mine"
+  like "`id`" $me "My id matches myself"
+
+  /bin/ls $HOME 1>&2
+  ok $? "/bin/ls $HOME"
+  # Same thing using okx shortcut
+  okx /bin/ls $HOME
+
+  [[ "`id -u`" != "0" ]]
+  i_am_not_root=$?
+  skip $i_am_not_root "Must be root" || {
+    okx ls /root
+  }
+
+  TODO="figure out how to become root..."
+  okx [ "$HOME" == "/root" ]
+  unset TODO
+USAGE
+	exit
+}
+
+opt=
+set_u=
+while getopts ":sx" opt ; do
+	case $_opt in
+        u) set_u=1 ;;
+        *) usage ;;
+    esac
+done
+shift $(( OPTIND - 1 ))
+# Don't allow uninitialized variables if requested
+[[ -n "$set_u" ]] && set -u
+unset opt set_u
+
+# Used to call _cleanup on shell exit
+trap _exit EXIT
+
+
+
+plan_no_plan(){
+	(( _plan_set != 0 )) && "You tried to plan twice!"
+
+	_plan_set=1
+	_no_plan=1
+
+	return 0
+}
+
+
+plan_skip_all(){
+	local reason=${1:-''}
+
+	(( _plan_set != 0 )) && _die "You tried to plan twice!"
+
+	_print_plan 0 "Skip $reason"
+
+	_skip_all=1
+	_plan_set=1
+	_exit 0
+
+	return 0
+}
+
+
+plan_tests(){
+	local tests=${1:?}
+
+	(( _plan_set != 0 )) && _die "You tried to plan twice!"
+	(( tests == 0 )) && _die "You said to run 0 tests!  You've got to run something."
+
+	_print_plan $tests
+	_expected_tests=$tests
+	_plan_set=1
+
+	return $tests
+}
+
+
+_print_plan(){
+	local tests=${1:?}
+	local directive=${2:-''}
+
+	echo -n "1..$tests"
+	[[ -n "$directive" ]] && echo -n " # $directive"
+	echo
+}
+
+
+pass(){
+	local name=$1
+	ok 0 "$name"
+}
+
+
+fail(){
+	local name=$1
+	ok 1 "$name"
+}
+
+
+# This is the workhorse method that actually
+# prints the tests result.
+ok(){
+	local result=${1:?}
+	local name=${2:-''}
+
+	(( _plan_set == 0 )) && _die "You tried to run a test without a plan!  Gotta have a plan."
+
+	_executed_tests=$(( $_executed_tests + 1 ))
+
+	if [[ -n "$name" ]] ; then
+		if _matches "$name" "^[0-9]+$" ; then
+			diag "    You named your test '$name'.  You shouldn't use numbers for your test names."
+			diag "    Very confusing."
+		fi
+	fi
+
+	if (( result != 0 )) ; then
+		echo -n "not "
+		_failed_tests=$(( _failed_tests + 1 ))
+	fi
+	echo -n "ok $_executed_tests"
+
+	if [[ -n "$name" ]] ; then
+		local ename=${name//\#/\\#}
+		echo -n " - $ename"
+	fi
+
+	if [[ -n "$TODO" ]] ; then
+		echo -n " # TODO $TODO" ;
+		if (( result != 0 )) ; then
+			_failed_tests=$(( _failed_tests - 1 ))
+		fi
+	fi
+
+	echo
+	if (( result != 0 )) ; then
+		local file='tap-functions'
+		local func=
+		local line=
+
+		local i=0
+		local bt=$(caller $i)
+		while _matches "$bt" "tap-functions$" ; do
+			i=$(( $i + 1 ))
+			bt=$(caller $i)
+		done
+		local backtrace=
+		eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\""))
+			
+		local t=
+		[[ -n "$TODO" ]] && t="(TODO) "
+
+		if [[ -n "$name" ]] ; then
+			diag "  Failed ${t}test '$name'"
+			diag "  in $backtrace"
+		else
+			diag "  Failed ${t}test in $backtrace"
+		fi
+	fi
+
+	return $result
+}
+
+
+okx(){
+	local command="$@"
+
+	local line=
+	diag "Output of '$command':"
+	$command | while read line ; do
+		diag "$line"
+	done
+	ok ${PIPESTATUS[0]} "$command"
+}
+
+
+_equals(){
+	local result=${1:?}
+	local expected=${2:?}
+
+	if [[ "$result" == "$expected" ]] ; then
+		return 0
+	else 
+		return 1
+	fi
+}
+
+
+# Thanks to Aaron Kangas for the patch to allow regexp matching
+# under bash < 3.
+ _bash_major_version=${BASH_VERSION%%.*}
+_matches(){
+	local result=${1:?}
+	local pattern=${2:?}
+
+	if [[ -z "$result" || -z "$pattern" ]] ; then
+		return 1
+	else
+		if (( _bash_major_version >= 3 )) ; then
+			eval '[[ "$result" =~ "$pattern" ]]'
+		else
+			echo "$result" | egrep -q "$pattern"
+		fi
+	fi
+}
+
+
+_is_diag(){
+	local result=${1:?}
+	local expected=${2:?}
+
+	diag "         got: '$result'" 
+	diag "    expected: '$expected'"
+}
+
+
+is(){
+	local result=${1:?}
+	local expected=${2:?}
+	local name=${3:-''}
+
+	_equals "$result" "$expected"
+	(( $? == 0 ))
+	ok $? "$name"
+	local r=$?
+	(( r != 0 )) && _is_diag "$result" "$expected"
+	return $r 
+}
+
+
+isnt(){
+	local result=${1:?}
+	local expected=${2:?}
+	local name=${3:-''}
+
+	_equals "$result" "$expected"
+	(( $? != 0 ))
+	ok $? "$name"
+	local r=$?
+	(( r != 0 )) && _is_diag "$result" "$expected"
+	return $r 
+}
+
+
+like(){
+	local result=${1:?}
+	local pattern=${2:?}
+	local name=${3:-''}
+
+	_matches "$result" "$pattern"
+	(( $? == 0 ))
+	ok $? "$name"
+	local r=$?
+	(( r != 0 )) && diag "    '$result' doesn't match '$pattern'"
+	return $r
+}
+
+
+unlike(){
+	local result=${1:?}
+	local pattern=${2:?}
+	local name=${3:-''}
+
+	_matches "$result" "$pattern"
+	(( $? != 0 ))
+	ok $? "$name"
+	local r=$?
+	(( r != 0 )) && diag "    '$result' matches '$pattern'"
+	return $r
+}
+
+
+skip(){
+	local condition=${1:?}
+	local reason=${2:-''}
+	local n=${3:-1}
+
+	if (( condition == 0 )) ; then
+		local i=
+		for (( i=0 ; i<$n ; i++ )) ; do
+			_executed_tests=$(( _executed_tests + 1 ))
+			echo "ok $_executed_tests # skip: $reason" 
+		done
+		return 0
+	else
+		return
+	fi
+}
+
+
+diag(){
+	local msg=${1:?}
+
+	if [[ -n "$msg" ]] ; then
+		echo "# $msg"
+	fi
+	
+	return 1
+}
+
+	
+_die(){
+	local reason=${1:-'<unspecified error>'}
+
+	echo "$reason" >&2
+	_test_died=1
+	_exit 255
+}
+
+
+BAIL_OUT(){
+	local reason=${1:-''}
+
+	echo "Bail out! $reason" >&2
+	_exit 255
+}
+
+
+_cleanup(){
+	local rc=0
+
+	if (( _plan_set == 0 )) ; then
+		diag "Looks like your test died before it could output anything."
+		return $rc
+	fi
+
+	if (( _test_died != 0 )) ; then
+		diag "Looks like your test died just after $_executed_tests."
+		return $rc
+	fi
+
+	if (( _skip_all == 0 && _no_plan != 0 )) ; then
+		_print_plan $_executed_tests
+	fi
+
+	local s=
+	if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then
+		s= ; (( _expected_tests > 1 )) && s=s
+		local extra=$(( _executed_tests - _expected_tests ))
+		diag "Looks like you planned $_expected_tests test$s but ran $extra extra."
+		rc=-1 ;
+	fi
+
+	if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then
+		s= ; (( _expected_tests > 1 )) && s=s
+		diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests."
+	fi
+
+	if (( _failed_tests > 0 )) ; then
+		s= ; (( _failed_tests > 1 )) && s=s
+		diag "Looks like you failed $_failed_tests test$s of $_executed_tests."
+	fi
+
+	return $rc
+}
+
+
+_exit_status(){
+	if (( _no_plan != 0 || _plan_set == 0 )) ; then
+		return $_failed_tests
+	fi
+
+	if (( _expected_tests < _executed_tests )) ; then
+		return $(( _executed_tests - _expected_tests  ))
+	fi
+
+	return $(( _failed_tests + ( _expected_tests - _executed_tests )))
+}
+
+
+_exit(){
+	local rc=${1:-''}
+	if [[ -z "$rc" ]] ; then
+		_exit_status
+		rc=$?
+	fi
+
+	_cleanup
+	local alt_rc=$?
+	(( alt_rc != 0 )) && rc=$alt_rc
+	trap - EXIT
+	exit $rc
+}
+
diff --git a/argparse/test.sh b/argparse/test.sh
new file mode 100755
index 0000000..fdb430a
--- /dev/null
+++ b/argparse/test.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+. tap-functions
+plan_no_plan
+
+is "$(./test_argparse -f --path=/path/to/file a 2>&1)" 'force: 1
+path: /path/to/file
+argc: 1
+argv[0]: a'
+
+is "$(./test_argparse -f -f --force --no-force 2>&1)" 'force: 2'
+
+is "$(./test_argparse -n 2>&1)" 'error: option `n` requires a value'
+
+is "$(./test_argparse -n 2 2>&1)" 'num: 2'
+
+is "$(./test_argparse -n2 2>&1)" 'num: 2'
+
+is "$(./test_argparse -na 2>&1)" 'error: option `n` expects a numerical value'
+
+is "$(./test_argparse -f -- do -f -h 2>&1)" 'force: 1
+argc: 3
+argv[0]: do
+argv[1]: -f
+argv[2]: -h'
+
+is "$(./test_argparse -tf 2>&1)" 'force: 1
+test: 1'
+
+is "$(./test_argparse --read --write 2>&1)" 'perms: 3'
+
+is "$(./test_argparse -h)" 'Usage: test_argparse [options] [[--] args]
+   or: test_argparse [options]
+
+    -h, --help        show this help message and exit
+
+OPTIONS
+    -f, --force       force to do
+    -t, --test        test only
+    -p, --path=<str>  path to read
+    -n, --num=<int>   selected num
+
+BITS
+    --read            read perm
+    --write           write perm
+    --exec            exec perm'
diff --git a/argparse/test_argparse.c b/argparse/test_argparse.c
new file mode 100755
index 0000000..6d95d35
--- /dev/null
+++ b/argparse/test_argparse.c
@@ -0,0 +1,57 @@
+#include "argparse.h"
+
+static const char *const usage[] = {
+    "test_argparse [options] [[--] args]",
+    "test_argparse [options]",
+    NULL,
+};
+
+#define PERM_READ  (1<<0)
+#define PERM_WRITE (1<<1)
+#define PERM_EXEC  (1<<2)
+
+int
+main(int argc, const char **argv)
+{
+    int force = 0;
+    int test = 0;
+    int num = 0;
+    const char *path = NULL;
+    int perms = 0;
+    struct argparse_option options[] = {
+        OPT_HELP(),
+        OPT_GROUP("OPTIONS"),
+        OPT_BOOLEAN('f', "force", &force, "force to do"),
+        OPT_BOOLEAN('t', "test", &test, "test only"),
+        OPT_STRING('p', "path", &path, "path to read"),
+        OPT_INTEGER('n', "num", &num, "selected num"),
+        OPT_GROUP("BITS"),
+        OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG),
+        OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE),
+        OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC),
+        OPT_END(),
+    };
+
+    struct argparse argparse;
+    argparse_init(&argparse, options, usage, 0);
+    argc = argparse_parse(&argparse, argc, argv);
+    if (force != 0)
+        printf("force: %d\n", force);
+    if (test != 0)
+        printf("test: %d\n", test);
+    if (path != NULL)
+        printf("path: %s\n", path);
+    if (num != 0)
+        printf("num: %d\n", num);
+    if (argc != 0) {
+        printf("argc: %d\n", argc);
+        int i;
+        for (i = 0; i < argc; i++) {
+            printf("argv[%d]: %s\n", i, *(argv + i));
+        }
+    }
+    if (perms) {
+        printf("perms: %d\n", perms);
+    }
+    return 0;
+}
diff --git a/yara.c b/yara.c
index 4c3cba2..330cf8c 100644
--- a/yara.c
+++ b/yara.c
@@ -25,7 +25,6 @@ limitations under the License.
 #else
 
 #include <windows.h>
-#include "getopt.h"
 
 #define PRIx64 "llx"
 
@@ -35,35 +34,14 @@ limitations under the License.
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+
+#include <argparse/argparse.h>
 #include <yara.h>
 
 #include "threading.h"
 #include "config.h"
 
 
-#define USAGE \
-"usage:  yara [OPTION]... RULES_FILE FILE | PID\n"\
-"options:\n"\
-"  -t <tag>                 only print rules tagged as <tag>.\n"\
-"  -i <identifier>          only print rules named <identifier>.\n"\
-"  -n                       only print not satisfied rules (negate).\n"\
-"  -g                       print tags.\n"\
-"  -m                       print metadata.\n"\
-"  -s                       print matching strings.\n"\
-"  -p <number>              use the specified <number> of threads to scan a directory.\n"\
-"  -l <number>              abort scanning after matching a number rules.\n"\
-"  -a <seconds>             abort scanning after a number of seconds has elapsed.\n"\
-"  -d <identifier>=<value>  define external variable.\n"\
-"  -x <module>=<file>       pass file's content as extra data to module.\n"\
-"  -r                       recursively search directories.\n"\
-"  -f                       fast matching mode.\n"\
-"  -w                       disable warnings.\n"\
-"  -v                       show version information.\n"
-
-#define EXTERNAL_TYPE_INTEGER   1
-#define EXTERNAL_TYPE_BOOLEAN   2
-#define EXTERNAL_TYPE_STRING    3
-
 #define ERROR_COULD_NOT_CREATE_THREAD  100
 
 #ifndef MAX_PATH
@@ -82,36 +60,6 @@ limitations under the License.
 #define MAX_QUEUED_FILES 64
 
 
-typedef struct _TAG
-{
-  char* identifier;
-  struct _TAG* next;
-
-} TAG;
-
-
-typedef struct _IDENTIFIER
-{
-  char* name;
-  struct _IDENTIFIER* next;
-
-} IDENTIFIER;
-
-
-typedef struct _EXTERNAL
-{
-  char type;
-  char*  name;
-  union {
-    char* string;
-    int integer;
-    int boolean;
-  };
-  struct _EXTERNAL* next;
-
-} EXTERNAL;
-
-
 typedef struct _MODULE_DATA
 {
   const char* module_name;
@@ -128,13 +76,23 @@ typedef struct _QUEUED_FILE {
 } QUEUED_FILE;
 
 
+#define MAX_ARGS_TAG            32
+#define MAX_ARGS_IDENTIFIER     32
+#define MAX_ARGS_EXT_VAR        32
+#define MAX_ARGS_MODULE_DATA    32
+
+char* tags[MAX_ARGS_TAG + 1];
+char* identifiers[MAX_ARGS_IDENTIFIER + 1];
+char* ext_vars[MAX_ARGS_EXT_VAR + 1];
+char* modules_data[MAX_ARGS_EXT_VAR + 1];
+
 int recursive_search = FALSE;
 int show_tags = FALSE;
 int show_specified_tags = FALSE;
 int show_specified_rules = FALSE;
 int show_strings = FALSE;
-int show_warnings = TRUE;
 int show_meta = FALSE;
+int ignore_warnings = FALSE;
 int fast_scan = FALSE;
 int negate = FALSE;
 int count = 0;
@@ -143,10 +101,59 @@ int timeout = 0;
 int threads = 8;
 
 
-TAG* specified_tags_list = NULL;
-IDENTIFIER* specified_rules_list = NULL;
-EXTERNAL* externals_list = NULL;
-MODULE_DATA* modules_data_list = NULL;
+static const char *const usage[] = {
+   "yara [OPTION]... RULES_FILE FILE | DIRECTORY | PID",
+   NULL,
+};
+
+
+struct argparse_option options[] =
+{
+  OPT_STRING_MULTI('t', "tag", &tags, MAX_ARGS_TAG,
+      "only print rules tagged as <tag>.", "<tag>"),
+
+  OPT_STRING_MULTI('i', "identifier", &identifiers, MAX_ARGS_IDENTIFIER,
+      "only print rules named <identifier>.", "<identifier>"),
+
+  OPT_BOOLEAN('n', "negate", &negate,
+      "only print not satisfied rules (negate)", NULL),
+
+  OPT_BOOLEAN('g', "print-tags", &show_tags,
+      "print tags"),
+
+  OPT_BOOLEAN('m', "print-meta", &show_meta,
+      "print metadata"),
+
+  OPT_BOOLEAN('s', "print-strings", &show_strings,
+      "print matching strings"),
+
+  OPT_INTEGER('p', "threads", &threads,
+      "use the specified number of threads to scan a directory", "<number>"),
+
+  OPT_INTEGER('l', "max-rules", &limit,
+      "abort scanning after matching a number rules", "<number>"),
+
+  OPT_STRING_MULTI('d', NULL, &ext_vars, MAX_ARGS_EXT_VAR,
+      "define external variable", "<identifier>=<value>"),
+
+  OPT_STRING_MULTI('x', NULL, &modules_data, MAX_ARGS_MODULE_DATA,
+      "pass file's content as extra data to module", "<module>=<file>"),
+
+  OPT_INTEGER('a', "timeout", &timeout,
+      "abort scanning after matching a number of  rules", "<number>"),
+
+  OPT_BOOLEAN('r', "recursive", &recursive_search,
+      "recursively search directories"),
+
+  OPT_BOOLEAN('f', "fast-scan", &fast_scan,
+      "fast matching mode"),
+
+  OPT_BOOLEAN('w', "no-warnings", &ignore_warnings,
+      "disable warnings"),
+
+  OPT_HELP(),
+  OPT_END()
+};
 
 
 // file_queue is size-limited queue stored as a circular array, files are
@@ -166,6 +173,8 @@ SEMAPHORE unused_slots;
 MUTEX queue_mutex;
 MUTEX output_mutex;
 
+MODULE_DATA* modules_data_list = NULL;
+
 
 int file_queue_init()
 {
@@ -263,20 +272,19 @@ void scan_dir(
     YR_RULES* rules,
     YR_CALLBACK_FUNC callback)
 {
-  WIN32_FIND_DATA FindFileData;
-  HANDLE hFind;
-
-  char full_path[MAX_PATH];
   static char path_and_mask[MAX_PATH];
 
   snprintf(path_and_mask, sizeof(path_and_mask), "%s\\*", dir);
 
-  hFind = FindFirstFile(path_and_mask, &FindFileData);
+  WIN32_FIND_DATA FindFileData;
+  HANDLE hFind = FindFirstFile(path_and_mask, &FindFileData);
 
   if (hFind != INVALID_HANDLE_VALUE)
   {
     do
     {
+      char full_path[MAX_PATH];
+
       snprintf(full_path, sizeof(full_path), "%s\\%s",
                dir, FindFileData.cFileName);
 
@@ -314,19 +322,17 @@ void scan_dir(
     YR_RULES* rules,
     YR_CALLBACK_FUNC callback)
 {
-  DIR *dp;
-  struct dirent *de;
-  struct stat st;
-  char full_path[MAX_PATH];
-
-  dp = opendir(dir);
+  DIR* dp = opendir(dir);
 
   if (dp)
   {
-    de = readdir(dp);
+    struct dirent* de = readdir(dp);
 
     while (de)
     {
+      char full_path[MAX_PATH];
+      struct stat st;
+
       snprintf(full_path, sizeof(full_path), "%s/%s", dir, de->d_name);
 
       int err = lstat(full_path, &st);
@@ -359,12 +365,9 @@ void print_string(
     uint8_t* data,
     int length)
 {
-  int i;
-  char* str;
+  char* str = (char*) (data);
 
-  str = (char*) (data);
-
-  for (i = 0; i < length; i++)
+  for (int i = 0; i < length; i++)
   {
     if (str[i] >= 32 && str[i] <= 126)
       printf("%c", str[i]);
@@ -375,13 +378,12 @@ void print_string(
   printf("\n");
 }
 
+
 void print_hex_string(
     uint8_t* data,
     int length)
 {
-  int i;
-
-  for (i = 0; i < min(32, length); i++)
+  for (int i = 0; i < min(32, length); i++)
     printf("%02X ", (uint8_t) data[i]);
 
   if (length > 32)
@@ -434,7 +436,7 @@ void print_compiler_error(
   }
   else
   {
-    if (show_warnings)
+    if (!ignore_warnings)
       fprintf(stderr, "%s(%d): warning: %s\n", file_name, line_number, message);
   }
 }
@@ -442,55 +444,47 @@ void print_compiler_error(
 
 int handle_message(int message, YR_RULE* rule, void* data)
 {
-  TAG* tag;
-  IDENTIFIER* identifier;
-  YR_STRING* string;
-  YR_MATCH* match;
-  YR_META* meta;
-
-  const char* tag_name;
-
-  int is_matching;
+  const char* tag;
   int show = TRUE;
 
-  if (show_specified_tags)
+  if (tags[0] != NULL)
   {
+    // The user specified one or more -t <tag> arguments, let's show this rule
+    // only if it's tagged with some of the specified tags.
+
     show = FALSE;
-    tag = specified_tags_list;
 
-    while (tag != NULL)
+    for (int i = 0; !show && tags[i] != NULL; i++)
     {
-      yr_rule_tags_foreach(rule, tag_name)
+      yr_rule_tags_foreach(rule, tag)
       {
-        if (strcmp(tag_name, tag->identifier) == 0)
+        if (strcmp(tag, tags[i]) == 0)
         {
           show = TRUE;
           break;
         }
       }
-
-      tag = tag->next;
     }
   }
 
-  if (show_specified_rules)
+  if (identifiers[0] != NULL)
   {
+    // The user specified one or more -i <identifier> arguments, let's show
+    // this rule only if it's identifier is among of the provided ones.
+
     show = FALSE;
-    identifier = specified_rules_list;
 
-    while (identifier != NULL)
+    for (int i = 0; !show && identifiers[i] != NULL; i++)
     {
-      if (strcmp(identifier->name, rule->identifier) == 0)
+      if (strcmp(identifiers[i], rule->identifier) == 0)
       {
         show = TRUE;
         break;
       }
-
-      identifier = identifier->next;
     }
   }
 
-  is_matching = (message == CALLBACK_MSG_RULE_MATCHING);
+  int is_matching = (message == CALLBACK_MSG_RULE_MATCHING);
 
   show = show && ((!negate && is_matching) || (negate && !is_matching));
 
@@ -503,13 +497,13 @@ int handle_message(int message, YR_RULE* rule, void* data)
     {
       printf("[");
 
-      yr_rule_tags_foreach(rule, tag_name)
+      yr_rule_tags_foreach(rule, tag)
       {
         // print a comma except for the first tag
-        if (tag_name != rule->tags)
+        if (tag != rule->tags)
           printf(",");
 
-        printf("%s", tag_name);
+        printf("%s", tag);
       }
 
       printf("] ");
@@ -519,6 +513,8 @@ int handle_message(int message, YR_RULE* rule, void* data)
 
     if (show_meta)
     {
+      YR_META* meta;
+
       printf("[");
 
       yr_rule_metas_foreach(rule, meta)
@@ -543,8 +539,12 @@ int handle_message(int message, YR_RULE* rule, void* data)
 
     if (show_strings)
     {
+      YR_STRING* string;
+
       yr_rule_strings_foreach(rule, string)
       {
+        YR_MATCH* match;
+
         yr_string_matches_foreach(string, match)
         {
           printf("0x%" PRIx64 ":%s: ",
@@ -613,14 +613,11 @@ void* scanning_thread(void* param)
 #endif
 {
   YR_RULES* rules = (YR_RULES*) param;
-  char* file_path;
-  int result;
-
-  file_path = file_queue_get();
+  char* file_path = file_queue_get();
 
   while (file_path != NULL)
   {
-    result = yr_rules_scan_file(
+    int result = yr_rules_scan_file(
         rules,
         file_path,
         fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
@@ -631,7 +628,7 @@ void* scanning_thread(void* param)
     if (result != ERROR_SUCCESS)
     {
       mutex_lock(&output_mutex);
-      fprintf(stderr, "Error scanning %s: ", file_path);
+      fprintf(stderr, "error scanning %s: ", file_path);
       print_scanner_error(result);
       mutex_unlock(&output_mutex);
     }
@@ -646,61 +643,6 @@ void* scanning_thread(void* param)
 }
 
 
-void cleanup()
-{
-  IDENTIFIER* identifier;
-  IDENTIFIER* next_identifier;
-  TAG* tag;
-  TAG* next_tag;
-  EXTERNAL* external;
-  EXTERNAL* next_external;
-  MODULE_DATA* module_data;
-  MODULE_DATA* next_module_data;
-
-  tag = specified_tags_list;
-
-  while(tag != NULL)
-  {
-    next_tag = tag->next;
-    free(tag);
-    tag = next_tag;
-  }
-
-  external = externals_list;
-
-  while(external != NULL)
-  {
-    next_external = external->next;
-    free(external);
-    external = next_external;
-  }
-
-  identifier = specified_rules_list;
-
-  while(identifier != NULL)
-  {
-    next_identifier = identifier->next;
-    free(identifier);
-    identifier = next_identifier;
-  }
-
-  module_data = modules_data_list;
-
-  while(module_data != NULL)
-  {
-    next_module_data = module_data->next;
-
-    yr_filemap_unmap(&module_data->mapped_file);
-
-    free((void*) module_data->module_name);
-    free((void*) module_data);
-
-    module_data = next_module_data;
-  }
-
-}
-
-
 int is_numeric(
     const char *str)
 {
@@ -714,399 +656,271 @@ int is_numeric(
 }
 
 
-int process_cmd_line(
-    int argc,
-    char const* argv[])
+int define_external_variables(
+    YR_RULES* rules,
+    YR_COMPILER* compiler)
 {
-  char* equal_sign;
-  char* value;
-  int c;
-
-  TAG* tag;
-  IDENTIFIER* identifier;
-  EXTERNAL* external;
-  MODULE_DATA* module_data;
-
-  opterr = 0;
-
-  while ((c = getopt (argc, (char**) argv, "wrnsvgma:l:t:i:d:x:p:f")) != -1)
+  for (int i = 0; ext_vars[i] != NULL; i++)
   {
-    switch (c)
-    {
-      case 'v':
-        printf("%s\n", PACKAGE_STRING);
-        return 0;
+    char* equal_sign = strchr(ext_vars[i], '=');
 
-      case 'r':
-        recursive_search = TRUE;
-        break;
-
-      case 'g':
-        show_tags = TRUE;
-        break;
-
-      case 'm':
-        show_meta = TRUE;
-        break;
-
-      case 's':
-        show_strings = TRUE;
-        break;
-
-      case 'w':
-        show_warnings = FALSE;
-        break;
-
-      case 'f':
-        fast_scan = TRUE;
-        break;
-
-      case 'n':
-        negate = TRUE;
-        break;
-
-      case 't':
-        show_specified_tags = TRUE;
-        tag = (TAG*) malloc(sizeof(TAG));
-
-        if (tag != NULL)
-        {
-          tag->identifier = optarg;
-          tag->next = specified_tags_list;
-          specified_tags_list = tag;
-        }
-        else
-        {
-          fprintf(stderr, "Not enough memory.\n");
-          return 0;
-        }
-        break;
+    if (!equal_sign)
+    {
+      fprintf(stderr, "error: wrong syntax for `-d` option.\n");
+      return FALSE;
+    }
 
-      case 'i':
-        show_specified_rules = TRUE;
-        identifier = (IDENTIFIER*) malloc(sizeof(IDENTIFIER));
+    // Replace the equal sign with null character to split the external
+    // variable definition (i.e: myvar=somevalue) in two strings: identifier
+    // and value.
 
-        if (identifier != NULL)
-        {
-          identifier->name = optarg;
-          identifier->next = specified_rules_list;
-          specified_rules_list = identifier;
-        }
-        else
-        {
-          fprintf(stderr, "Not enough memory.\n");
-          return 0;
-        }
-        break;
+    *equal_sign = '\0';
 
-      case 'd':
-        equal_sign = strchr(optarg, '=');
-        external = (EXTERNAL*) malloc(sizeof(EXTERNAL));
+    char* identifier = ext_vars[i];
+    char* value = equal_sign + 1;
 
-        if (external != NULL)
-        {
-          external->name = optarg;
-          external->next = externals_list;
-          externals_list = external;
-        }
-        else
-        {
-          fprintf(stderr, "Not enough memory.\n");
-          return 0;
-        }
-
-        if (equal_sign != NULL)
-        {
-          *equal_sign = '\0';
-          value = equal_sign + 1;
-
-          if (is_numeric(value))
-          {
-            external->type = EXTERNAL_TYPE_INTEGER;
-            external->integer = atoi(value);
-          }
-          else if (strcmp(value, "true") == 0  || strcmp(value, "false") == 0)
-          {
-            external->type = EXTERNAL_TYPE_BOOLEAN;
-            external->boolean = strcmp(value, "true") == 0;
-          }
-          else
-          {
-            external->type = EXTERNAL_TYPE_STRING;
-            external->string = value;
-          }
-        }
-        break;
+    if (is_numeric(value))
+    {
+      if (rules != NULL)
+        yr_rules_define_integer_variable(
+            rules,
+            identifier,
+            atoi(value));
+
+      if (compiler != NULL)
+        yr_compiler_define_integer_variable(
+            compiler,
+            identifier,
+            atoi(value));
+    }
+    else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
+    {
+      if (rules != NULL)
+        yr_rules_define_boolean_variable(
+            rules,
+            identifier,
+            strcmp(value, "true") == 0);
+
+      if (compiler != NULL)
+        yr_compiler_define_boolean_variable(
+            compiler,
+            identifier,
+            strcmp(value, "true") == 0);
+    }
+    else
+    {
+      if (rules != NULL)
+        yr_rules_define_string_variable(
+            rules,
+            identifier,
+            value);
+
+      if (compiler != NULL)
+        yr_compiler_define_string_variable(
+            compiler,
+            identifier,
+            value);
+    }
+  }
 
-      case 'x':
+  return TRUE;
+}
 
-        equal_sign = strchr(optarg, '=');
 
-        if (equal_sign == NULL)
-        {
-          fprintf(stderr, "Wrong syntax for -x modifier.\n");
-          return 0;
-        }
+int load_modules_data()
+{
+  for (int i = 0; modules_data[i] != NULL; i++)
+  {
+    char* equal_sign = strchr(modules_data[i], '=');
 
-        module_data = (MODULE_DATA*) malloc(sizeof(MODULE_DATA));
+    if (!equal_sign)
+    {
+      fprintf(stderr, "error: wrong syntax for `-x` option.\n");
+      return FALSE;
+    }
 
-        if (module_data != NULL)
-          module_data->module_name = strdup(optarg);
+    *equal_sign = '\0';
 
-        if (module_data == NULL || module_data->module_name == NULL)
-        {
-          if (module_data != NULL)
-            free(module_data);
+    MODULE_DATA* module_data = (MODULE_DATA*) malloc(sizeof(MODULE_DATA));
 
-          fprintf(stderr, "Not enough memory.\n");
-          return 0;
-        }
+    if (module_data != NULL)
+    {
+      module_data->module_name = modules_data[i];
 
-        *equal_sign = '\0';
-        value = equal_sign + 1;
+      int result = yr_filemap_map(equal_sign + 1, &module_data->mapped_file);
 
-        if (yr_filemap_map(value, &module_data->mapped_file) != ERROR_SUCCESS)
-        {
-          free(module_data);
-          fprintf(stderr, "Could not open file \"%s\".\n", value);
-          return 0;
-        }
+      if (result != ERROR_SUCCESS)
+      {
+        free(module_data);
+        fprintf(stderr, "error: could not open file \"%s\".\n", equal_sign + 1);
+        return FALSE;
+      }
 
-        module_data->next = modules_data_list;
-        modules_data_list = module_data;
+      module_data->next = modules_data_list;
+      modules_data_list = module_data;
+    }
+  }
 
-        break;
+  return TRUE;
+}
 
-      case 'l':
-        limit = atoi(optarg);
-        break;
 
-      case 'a':
-        timeout = atoi(optarg);
-        break;
+void unload_modules_data()
+{
+  MODULE_DATA* module_data = modules_data_list;
 
-      case 'p':
-        threads = atoi(optarg);
-        break;
+  while(module_data != NULL)
+  {
+    MODULE_DATA* next_module_data = module_data->next;
 
-      case '?':
-        if (optopt == 't')
-        {
-          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
-        }
-        else if (isprint(optopt))
-        {
-          fprintf(stderr, "Unknown option `-%c'.\n", optopt);
-        }
-        else
-        {
-          fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
-        }
-        return 0;
+    yr_filemap_unmap(&module_data->mapped_file);
+    free(module_data);
 
-      default:
-        abort();
-    }
+    module_data = next_module_data;
   }
 
-  return 1;
-
+  modules_data_list = NULL;
 }
 
 
-void show_help()
-{
-  printf(USAGE);
-  printf("\nReport bugs to: <%s>\n", PACKAGE_BUGREPORT);
-}
-
+#define exit_with_code(code) { result = code; goto _exit; }
 
 int main(
     int argc,
-    char const* argv[])
+    const char** argv)
 {
-  YR_COMPILER* compiler;
-  YR_RULES* rules;
-  FILE* rule_file;
-  EXTERNAL* external;
+  YR_COMPILER* compiler = NULL;
+  YR_RULES* rules = NULL;
 
-  int pid;
-  int i;
-  int errors;
+  struct argparse argparse;
   int result;
 
-  THREAD thread[MAX_THREADS];
+  argparse_init(&argparse, options, usage, 0);
 
-  if (!process_cmd_line(argc, argv))
-    return EXIT_FAILURE;
+  argc = argparse_parse(&argparse, argc, argv);
 
-  if (argc == 1 || optind == argc)
+  if (argc != 2)
   {
-    show_help();
-    cleanup();
+    // After parsing the command-line options we expect two additional
+    // arguments, the rules file and the target file, directory or pid to
+    // be scanned.
+
+    fprintf(stderr, "error: wrong number of arguments\n");
+    argparse_usage(&argparse);
     return EXIT_FAILURE;
   }
 
+  if (!load_modules_data())
+    exit_with_code(EXIT_FAILURE);
+
   result = yr_initialize();
 
   if (result != ERROR_SUCCESS)
   {
-    fprintf(stderr, "initialization error: %d\n", result);
-    cleanup();
-    return EXIT_FAILURE;
+    fprintf(stderr, "error: initialization error (%d)\n", result);
+    exit_with_code(EXIT_FAILURE);
   }
 
-  result = yr_rules_load(argv[optind], &rules);
+  // Try to load the rules file as a binary file containing
+  // compiled rules first
+
+  result = yr_rules_load(argv[0], &rules);
+
+  // Accepted result are ERROR_SUCCESS or ERROR_INVALID_FILE
+  // if we are passing the rules in source form, if result is
+  // different from those exit with error.
 
   if (result != ERROR_SUCCESS &&
       result != ERROR_INVALID_FILE)
   {
     print_scanner_error(result);
-    yr_finalize();
-    cleanup();
-    return EXIT_FAILURE;
+    exit_with_code(EXIT_FAILURE);
   }
 
   if (result == ERROR_SUCCESS)
   {
-    external = externals_list;
-
-    while (external != NULL)
-    {
-      switch (external->type)
-      {
-        case EXTERNAL_TYPE_INTEGER:
-          yr_rules_define_integer_variable(
-              rules,
-              external->name,
-              external->integer);
-          break;
-
-        case EXTERNAL_TYPE_BOOLEAN:
-          yr_rules_define_boolean_variable(
-              rules,
-              external->name,
-              external->boolean);
-          break;
-
-        case EXTERNAL_TYPE_STRING:
-          yr_rules_define_string_variable(
-              rules,
-              external->name,
-              external->string);
-          break;
-      }
-      external = external->next;
-    }
+    if (!define_external_variables(rules, NULL))
+      exit_with_code(EXIT_FAILURE);
   }
   else
   {
-    if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
-    {
-      yr_finalize();
-      cleanup();
-      return EXIT_FAILURE;
-    }
-
-    external = externals_list;
+    // Rules file didn't contain compiled rules, let's handle it
+    // as a text file containing rules in source form.
 
-    while (external != NULL)
-    {
-      switch (external->type)
-      {
-        case EXTERNAL_TYPE_INTEGER:
-          yr_compiler_define_integer_variable(
-              compiler,
-              external->name,
-              external->integer);
-          break;
-
-        case EXTERNAL_TYPE_BOOLEAN:
-          yr_compiler_define_boolean_variable(
-              compiler,
-              external->name,
-              external->boolean);
-          break;
+    if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+      exit_with_code(EXIT_FAILURE);
 
-        case EXTERNAL_TYPE_STRING:
-          yr_compiler_define_string_variable(
-              compiler,
-              external->name,
-              external->string);
-          break;
-      }
-      external = external->next;
-    }
+    if (!define_external_variables(NULL, compiler))
+      exit_with_code(EXIT_FAILURE);
 
     yr_compiler_set_callback(compiler, print_compiler_error);
 
-    rule_file = fopen(argv[optind], "r");
+    FILE* rule_file = fopen(argv[0], "r");
 
     if (rule_file == NULL)
     {
-      fprintf(stderr, "could not open file: %s\n", argv[optind]);
-      yr_compiler_destroy(compiler);
-      yr_finalize();
-      cleanup();
-      return EXIT_FAILURE;
+      fprintf(stderr, "error: could not open file: %s\n", argv[0]);
+      exit_with_code(EXIT_FAILURE);
     }
 
-    errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[optind]);
+    int errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[0]);
 
     fclose(rule_file);
 
     if (errors > 0)
-    {
-      yr_compiler_destroy(compiler);
-      yr_finalize();
-      cleanup();
-      return EXIT_FAILURE;
-    }
+      exit_with_code(EXIT_FAILURE);
 
     result = yr_compiler_get_rules(compiler, &rules);
 
     yr_compiler_destroy(compiler);
 
+    compiler = NULL;
+
     if (result != ERROR_SUCCESS)
-    {
-      yr_finalize();
-      cleanup();
-      return EXIT_FAILURE;
-    }
+      exit_with_code(EXIT_FAILURE);
   }
 
   mutex_init(&output_mutex);
 
-  if (is_numeric(argv[argc - 1]))
+  if (is_numeric(argv[1]))
   {
-    pid = atoi(argv[argc - 1]);
+    int pid = atoi(argv[1]);
+
     result = yr_rules_scan_proc(
         rules,
         pid,
         fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
         callback,
-        (void*) argv[argc - 1],
+        (void*) argv[1],
         timeout);
 
     if (result != ERROR_SUCCESS)
+    {
       print_scanner_error(result);
+      exit_with_code(EXIT_FAILURE);
+    }
   }
-  else if (is_directory(argv[argc - 1]))
+  else if (is_directory(argv[1]))
   {
     if (file_queue_init() != 0)
+    {
       print_scanner_error(ERROR_INTERNAL_FATAL_ERROR);
+      exit_with_code(EXIT_FAILURE);
+    }
+
+    THREAD thread[MAX_THREADS];
 
-    for (i = 0; i < threads; i++)
+    for (int i = 0; i < threads; i++)
     {
       if (create_thread(&thread[i], scanning_thread, (void*) rules) != 0)
       {
         print_scanner_error(ERROR_COULD_NOT_CREATE_THREAD);
-        return EXIT_FAILURE;
+        exit_with_code(EXIT_FAILURE);
       }
     }
 
     scan_dir(
-        argv[argc - 1],
+        argv[1],
         recursive_search,
         rules,
         callback);
@@ -1114,7 +928,7 @@ int main(
     file_queue_finish();
 
     // Wait for scan threads to finish
-    for (i = 0; i < threads; i++)
+    for (int i = 0; i < threads; i++)
       thread_join(&thread[i]);
 
     file_queue_destroy();
@@ -1123,16 +937,17 @@ int main(
   {
     result = yr_rules_scan_file(
         rules,
-        argv[argc - 1],
+        argv[1],
         fast_scan ? SCAN_FLAGS_FAST_MODE : 0,
         callback,
-        (void*) argv[argc - 1],
+        (void*) argv[1],
         timeout);
 
     if (result != ERROR_SUCCESS)
     {
-      fprintf(stderr, "Error scanning %s: ", argv[argc - 1]);
+      fprintf(stderr, "error scanning %s: ", argv[1]);
       print_scanner_error(result);
+      exit_with_code(EXIT_FAILURE);
     }
   }
 
@@ -1140,11 +955,19 @@ int main(
   yr_rules_print_profiling_info(rules);
   #endif
 
-  yr_rules_destroy(rules);
-  yr_finalize();
+  result = EXIT_SUCCESS;
 
-  mutex_destroy(&output_mutex);
-  cleanup();
+_exit:
 
-  return EXIT_SUCCESS;
+  unload_modules_data();
+
+  if (compiler != NULL)
+    yr_compiler_destroy(compiler);
+
+  if (rules != NULL)
+    yr_rules_destroy(rules);
+
+  yr_finalize();
+
+  return result;
 }
diff --git a/yarac.c b/yarac.c
index f45361d..f4f1e51 100644
--- a/yarac.c
+++ b/yarac.c
@@ -24,7 +24,6 @@ limitations under the License.
 #else
 
 #include <windows.h>
-#include "getopt.h"
 
 #endif
 
@@ -32,6 +31,7 @@ limitations under the License.
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <argparse/argparse.h>
 #include <yara.h>
 
 #include "config.h"
@@ -40,19 +40,30 @@ limitations under the License.
 #define MAX_PATH 255
 #endif
 
+#define MAX_ARGS_EXT_VAR   32
 
-int show_warnings = TRUE;
 
+char* ext_vars[MAX_ARGS_EXT_VAR + 1];
+int ignore_warnings = FALSE;
 
-void show_help()
+
+static const char *const usage[] = {
+   "yarac [option]... [namespace:]rules_file... output_file",
+   NULL,
+};
+
+
+struct argparse_option options[] =
 {
-  printf("usage:  yarac [OPTION]... [RULE_FILE]... OUTPUT_FILE\n");
-  printf("options:\n");
-  printf("  -d <identifier>=<value>   define external variable.\n");
-  printf("  -w                        disable warnings.\n");
-  printf("  -v                        show version information.\n");
-  printf("\nReport bugs to: <%s>\n", PACKAGE_BUGREPORT);
-}
+  OPT_STRING_MULTI('d', NULL, &ext_vars, MAX_ARGS_EXT_VAR,
+      "define external variable"),
+
+  OPT_BOOLEAN('w', "no-warnings", &ignore_warnings,
+      "disable warnings", NULL),
+
+  OPT_HELP(),
+  OPT_END()
+};
 
 
 int is_numeric(
@@ -68,81 +79,6 @@ int is_numeric(
 }
 
 
-int process_cmd_line(
-    YR_COMPILER* compiler,
-    int argc,
-    char const* argv[])
-{
-  char* equal_sign;
-  char* value;
-  char c;
-  opterr = 0;
-
-  while ((c = getopt (argc, (char**) argv, "wvd:")) != -1)
-  {
-    switch (c)
-    {
-      case 'v':
-        printf("%s\n", PACKAGE_STRING);
-        return 0;
-
-      case 'w':
-        show_warnings = FALSE;
-        break;
-
-      case 'd':
-        equal_sign = strchr(optarg, '=');
-
-        if (equal_sign != NULL)
-        {
-          *equal_sign = '\0';
-          value = equal_sign + 1;
-
-          if (is_numeric(value))
-          {
-            yr_compiler_define_integer_variable(
-                compiler,
-                optarg,
-                atol(value));
-          }
-          else if (strcmp(value, "true") == 0  || strcmp(value, "false") == 0)
-          {
-            yr_compiler_define_boolean_variable(
-                compiler,
-                optarg,
-                strcmp(value, "true") == 0);
-          }
-          else
-          {
-            yr_compiler_define_string_variable(
-                compiler,
-                optarg,
-                value);
-          }
-        }
-        break;
-
-      case '?':
-
-        if (isprint(optopt))
-        {
-          fprintf(stderr, "Unknown option `-%c'.\n", optopt);
-        }
-        else
-        {
-          fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
-        }
-        return 0;
-
-      default:
-        abort();
-    }
-  }
-
-  return 1;
-}
-
-
 void report_error(
     int error_level,
     const char* file_name,
@@ -155,70 +91,130 @@ void report_error(
   }
   else
   {
-    if (show_warnings)
+    if (!ignore_warnings)
       fprintf(stderr, "%s(%d): warning: %s\n", file_name, line_number, message);
   }
 }
 
 
+int define_external_variables(
+    YR_COMPILER* compiler)
+{
+  for (int i = 0; ext_vars[i] != NULL; i++)
+  {
+    char* equal_sign = strchr(ext_vars[i], '=');
+
+    if (!equal_sign)
+    {
+      fprintf(stderr, "error: wrong syntax for `-d` option.\n");
+      return FALSE;
+    }
+
+    // Replace the equal sign with null character to split the external
+    // variable definition (i.e: myvar=somevalue) in two strings: identifier
+    // and value.
+
+    *equal_sign = '\0';
+
+    char* identifier = ext_vars[i];
+    char* value = equal_sign + 1;
+
+    if (is_numeric(value))
+    {
+      yr_compiler_define_integer_variable(
+          compiler,
+          identifier,
+          atoi(value));
+    }
+    else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0)
+    {
+      yr_compiler_define_boolean_variable(
+          compiler,
+          identifier,
+          strcmp(value, "true") == 0);
+    }
+    else
+    {
+      yr_compiler_define_string_variable(
+          compiler,
+          identifier,
+          value);
+    }
+  }
+
+  return TRUE;
+}
+
+
+#define exit_with_code(code) { result = code; goto _exit; }
+
+
 int main(
     int argc,
-    char const* argv[])
+    const char** argv)
 {
-  int i, result, errors;
+  YR_COMPILER* compiler = NULL;
+  YR_RULES* rules = NULL;
 
-  YR_COMPILER* compiler;
-  YR_RULES* rules;
-  FILE* rule_file;
+  struct argparse argparse;
+  int result;
 
-  result = yr_initialize();
+  argparse_init(&argparse, options, usage, 0);
 
-  if (result != ERROR_SUCCESS)
-    return EXIT_FAILURE;
+  argc = argparse_parse(&argparse, argc, argv);
 
-  if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+  if (argc < 2)
   {
-    yr_finalize();
-    return EXIT_FAILURE;
+    fprintf(stderr, "error: wrong number of arguments\n");
+    exit_with_code(EXIT_FAILURE);
   }
 
-  if (!process_cmd_line(compiler, argc, argv))
-  {
-    yr_compiler_destroy(compiler);
-    yr_finalize();
-    return EXIT_FAILURE;
-  }
+  result = yr_initialize();
 
-  if (argc == 1 || optind == argc)
-  {
-    show_help();
-    yr_compiler_destroy(compiler);
-    yr_finalize();
-    return EXIT_FAILURE;
-  }
+  if (result != ERROR_SUCCESS)
+    exit_with_code(EXIT_FAILURE);
+
+  if (yr_compiler_create(&compiler) != ERROR_SUCCESS)
+    exit_with_code(EXIT_FAILURE);
+
+  if (!define_external_variables(compiler))
+    exit_with_code(EXIT_FAILURE);
 
   yr_compiler_set_callback(compiler, report_error);
 
-  for (i = optind; i < argc - 1; i++)
+  for (int i = 0; i < argc - 1; i++)
   {
-    rule_file = fopen(argv[i], "r");
+    const char* ns;
+    const char* file_name;
+    char* colon = strchr(argv[i], ':');
+
+    if (colon)
+    {
+      file_name = colon + 1;
+      *colon = '\0';
+      ns = argv[i];
+    }
+    else
+    {
+      file_name = argv[i];
+      ns = NULL;
+    }
+
+    FILE* rule_file = fopen(file_name, "r");
 
     if (rule_file != NULL)
     {
-      errors = yr_compiler_add_file(compiler, rule_file, NULL, argv[i]);
+      int errors = yr_compiler_add_file(
+          compiler, rule_file, ns, file_name);
 
       fclose(rule_file);
 
       if (errors) // errors during compilation
-      {
-        yr_compiler_destroy(compiler);
-        yr_finalize();
-        return EXIT_FAILURE;
-      }
+        exit_with_code(EXIT_FAILURE);
     }
     else
     {
-      fprintf(stderr, "could not open file: %s\n", argv[i]);
+      fprintf(stderr, "error: could not open file: %s\n", file_name);
     }
   }
 
@@ -227,7 +223,7 @@ int main(
   if (result != ERROR_SUCCESS)
   {
     fprintf(stderr, "error: %d\n", result);
-    return EXIT_FAILURE;
+    exit_with_code(EXIT_FAILURE);
   }
 
   result = yr_rules_save(rules, argv[argc - 1]);
@@ -235,14 +231,21 @@ int main(
   if (result != ERROR_SUCCESS)
   {
     fprintf(stderr, "error: %d\n", result);
-    return EXIT_FAILURE;
+    exit_with_code(EXIT_FAILURE);
   }
 
-  yr_rules_destroy(rules);
-  yr_compiler_destroy(compiler);
+  result = EXIT_SUCCESS;
+
+_exit:
+
+  if (compiler != NULL)
+    yr_compiler_destroy(compiler);
+
+  if (rules != NULL)
+    yr_rules_destroy(rules);
 
   yr_finalize();
 
-  return EXIT_SUCCESS;
+  return result;
 }
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/forensics/yara.git



More information about the forensics-changes mailing list