summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAntti-Juhani Kaijanaho <ajk@debian.org>2006-10-26 22:33:50 +0100
committerAntti-Juhani Kaijanaho <ajk@debian.org>2006-10-26 22:33:50 +0100
commitf761dc59127e5eebd68d36f9810cad1e89fbd3c8 (patch)
tree0958ab4c7c1093aee08c7fbf788cbc20735933b7 /lib
parenta599e0d1b8aff9015525059c32d59f4b93b3db4f (diff)
downloaddctrl-tools-f761dc59127e5eebd68d36f9810cad1e89fbd3c8.tar.gz
create lib/, man/ and $(program)/
Diffstat (limited to 'lib')
-rw-r--r--lib/align.h52
-rw-r--r--lib/fieldtrie.c98
-rw-r--r--lib/fieldtrie.h43
-rw-r--r--lib/fnutil.c123
-rw-r--r--lib/fnutil.h25
-rw-r--r--lib/fsaf.c260
-rw-r--r--lib/fsaf.h101
-rw-r--r--lib/getaline.c113
-rw-r--r--lib/getaline.h26
-rw-r--r--lib/i18n.h33
-rw-r--r--lib/ifile.c149
-rw-r--r--lib/ifile.h38
-rw-r--r--lib/misc.c71
-rw-r--r--lib/misc.h28
-rw-r--r--lib/msg.c102
-rw-r--r--lib/msg.h173
-rw-r--r--lib/para_bundle.c67
-rw-r--r--lib/para_bundle.h70
-rw-r--r--lib/para_pool.c29
-rw-r--r--lib/para_pool.h93
-rw-r--r--lib/paragraph.c209
-rw-r--r--lib/paragraph.h90
-rw-r--r--lib/predicate.c241
-rw-r--r--lib/predicate.h102
-rw-r--r--lib/sorter.c85
-rw-r--r--lib/sorter.h77
-rw-r--r--lib/strutil.c157
-rw-r--r--lib/strutil.h54
-rw-r--r--lib/util.c50
-rw-r--r--lib/util.h27
-rw-r--r--lib/version.c158
-rw-r--r--lib/version.h62
32 files changed, 3006 insertions, 0 deletions
diff --git a/lib/align.h b/lib/align.h
new file mode 100644
index 0000000..4acaa1b
--- /dev/null
+++ b/lib/align.h
@@ -0,0 +1,52 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ALIGN_H
+#define ALIGN_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#define MAX_ALIGN 8
+
+static inline
+size_t get_pagesize(void)
+{
+ static size_t pagesize = 0;
+ if (pagesize == 0) {
+ pagesize = (size_t) sysconf(_SC_PAGESIZE);
+ }
+ assert(pagesize != 0);
+ return pagesize;
+}
+
+static inline
+size_t align(size_t offset, size_t alignment, bool ceil)
+{
+ return (offset / alignment + (ceil && offset % alignment != 0))
+ * alignment;
+}
+
+static inline
+size_t pg_align(size_t offset, bool ceil)
+{
+ return align(offset, get_pagesize(), ceil);
+}
+
+#endif /* ALIGN_H */
diff --git a/lib/fieldtrie.c b/lib/fieldtrie.c
new file mode 100644
index 0000000..250fc89
--- /dev/null
+++ b/lib/fieldtrie.c
@@ -0,0 +1,98 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include "fieldtrie.h"
+#include "msg.h"
+
+struct field_bucket {
+ char const * name;
+ size_t namelen;
+ struct field_attr attr;
+ struct field_bucket * next;
+};
+
+struct fieldtrie_private {
+ size_t nextfree;
+ /* A one-level trie listing all field names occurring in the
+ * atomic predicates. */
+ struct field_bucket * fields[UCHAR_MAX];
+};
+
+static struct fieldtrie_private trie;
+
+void fieldtrie_init(void)
+{
+ for (size_t i = 0; i < UCHAR_MAX; i++) {
+ trie.fields[i] = 0;
+ }
+ trie.nextfree = 0;
+}
+
+size_t fieldtrie_insert(char const * s)
+{
+ size_t slen = strlen(s);
+ struct field_attr l_attr = fieldtrie_lookup(s, slen);
+ if (l_attr.valid) return l_attr.inx;
+ struct field_bucket * b = malloc(sizeof *b);
+ if (b == 0) fatal_enomem(0);
+ b->name = malloc(slen+1);
+ if (b->name == 0) fatal_enomem(0);
+ strcpy((char*)b->name, s);
+ b->namelen = slen;
+ b->attr.inx = trie.nextfree++;
+ b->attr.valid = true;
+ unsigned char c = tolower((unsigned char)(b->name[0]));
+ b->next = trie.fields[c];
+ trie.fields[c] = b;
+ return b->attr.inx;
+}
+
+struct field_attr fieldtrie_lookup(char const * s, size_t n)
+{
+ for (struct field_bucket * b = trie.fields[tolower((unsigned char)s[0])];
+ b != 0;
+ b = b->next) {
+ if (n == b->namelen &&
+ strncasecmp(s, b->name, n) == 0) return b->attr;
+ }
+ return (struct field_attr){ .valid = false };
+}
+
+size_t fieldtrie_count(void)
+{
+ return trie.nextfree;
+}
+
+#if 0
+void fieldtrie_clear(void)
+{
+ for (size_t i = 0; i < UCHAR_MAX; i++) {
+ struct field_bucket * b = trie.fields[i];
+ while (b != 0) {
+ struct field_bucket * bn = b->next;
+ free(b);
+ b = bn;
+ }
+ trie->fields[i] = 0;
+ }
+ trie->nextfree = 0;
+}
+#endif
diff --git a/lib/fieldtrie.h b/lib/fieldtrie.h
new file mode 100644
index 0000000..0e1c634
--- /dev/null
+++ b/lib/fieldtrie.h
@@ -0,0 +1,43 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FIELDTRIE_H
+#define FIELDTRIE_H
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct field_attr {
+ bool valid;
+ size_t inx;
+};
+
+void fieldtrie_init(void);
+
+// case-insensitive
+size_t fieldtrie_insert(char const *);
+
+// case-insensitive
+struct field_attr fieldtrie_lookup(char const *, size_t n);
+
+//void fieldtrie_clear(void);
+
+size_t fieldtrie_count(void);
+
+#endif /* FIELDTRIE_H */
diff --git a/lib/fnutil.c b/lib/fnutil.c
new file mode 100644
index 0000000..1ec5ca4
--- /dev/null
+++ b/lib/fnutil.c
@@ -0,0 +1,123 @@
+/* fnutil.c - file name utilities
+ (Originally composed from several files in Lars Wirzenius' publib.)
+
+ Copyright (c) 1994 Lars Wirzenius. All rights reserved.
+ Copyright (C) 2004 Antti-Juhani Kaijanaho. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef USERNAME_MAX
+#define USERNAME_MAX 9
+#endif
+
+char *fnbase(const char *fname)
+{
+ char *base;
+
+ assert(fname != NULL);
+ base = strrchr(fname, '/');
+ if (base == NULL)
+ return (char *) fname;
+ return base+1;
+}
+
+char * fnqualify(char const * path)
+{
+ size_t len, size;
+ struct passwd *pwd;
+ const char *p;
+
+ assert(path != NULL);
+
+ /* Is it qualified already? */
+ if (path[0] == '/') {
+ return strdup(path);
+ }
+
+ /* Do we just need to prepend the current directory? */
+ if (path[0] != '~') {
+ char * cwd = get_current_dir_name();
+ if (cwd == 0) return 0;
+ len = strlen(cwd);
+ size = len + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */
+ char * res = malloc(size);
+ if (res == 0) {
+ free(cwd);
+ return 0;
+ }
+ sprintf(res, "%s/%s", cwd, path);
+ return res;
+ }
+
+ /* We need to do tilde substitution, get the password entry (which
+ includes the name of the home directory) */
+ if (path[1] == '\0' || path[1] == '/') {
+ pwd = getpwuid(getuid());
+ if (path[1] == '\0')
+ p = path + 1;
+ else
+ p = path + 2;
+ } else {
+
+ p = strchr(path, '/');
+ if (p == NULL)
+ p = strchr(path, '\0');
+ size = (size_t) (p-path);
+ char * username = malloc(size);
+ if (username == 0) {
+ errno = ENOMEM;
+ return 0;
+ }
+ memcpy(username, path+1, size);
+ username[size-1] = '\0';
+
+ pwd = getpwnam(username);
+ if (*p == '/')
+ ++p;
+ free(username);
+ }
+ if (pwd == NULL) {
+ errno = ENOENT;
+ return 0;
+ }
+
+
+ /* Now we have all the necessary information, build the result */
+ size = strlen(pwd->pw_dir) + 1 + strlen(p) + 1;
+ char * result = malloc(size);
+ if (result == 0) return 0;
+ sprintf(result, "%s/%s", pwd->pw_dir, p);
+ return result;
+}
diff --git a/lib/fnutil.h b/lib/fnutil.h
new file mode 100644
index 0000000..c5e38f8
--- /dev/null
+++ b/lib/fnutil.h
@@ -0,0 +1,25 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FNUTIL_H
+#define FNUTIL_H
+
+char *fnbase(const char *fname);
+char * fnqualify(char const * path);
+
+#endif /* FNUTIL_H */
diff --git a/lib/fsaf.c b/lib/fsaf.c
new file mode 100644
index 0000000..07a4979
--- /dev/null
+++ b/lib/fsaf.c
@@ -0,0 +1,260 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "align.h"
+#include "fsaf.h"
+#include "msg.h"
+
+#if !defined(_POSIX_MAPPED_FILES) || _POSIX_MAPPED_FILES == -1
+# warning "No mmap support detected."
+#endif
+
+#define READAHEAD 1
+
+bool fsaf_mmap;
+
+FSAF * fsaf_fdopen(int fd, char const *fname)
+{
+ FSAF * rv = malloc(sizeof *rv);
+ if (rv == 0) { errno = ENOMEM; return 0; }
+
+ rv->fname = strdup(fname);
+ if (rv->fname == NULL) { errno = ENOMEM; free(rv); return 0; }
+ rv->fd = fd;
+ rv->eof_mark = (size_t)(-1);
+ rv->buf = 0;
+ rv->buf_capacity = 0;
+ rv->buf_offset = 0;
+ rv->buf_size = 0;
+ rv->invalid_mark = 0;
+#ifdef _POSIX_MAPPED_FILES
+ rv->mapped = false;
+ rv->topread = 0;
+#endif
+
+ struct stat stat;
+ int res = fstat(fd, &stat);
+ if (res == -1) goto fail;
+
+ if (S_ISREG(stat.st_mode)) {
+ rv->eof_mark = stat.st_size;
+#ifdef _POSIX_MAPPED_FILES
+ if (fsaf_mmap) {
+ /* try mmapping, it is a regular file */
+ size_t eof_pagebound = pg_align(rv->eof_mark, true);
+ char * buf = mmap(0, eof_pagebound, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (buf != MAP_FAILED) {
+ debug_message("mmapping", 0);
+ rv->mapped = 1;
+ rv->buf = buf;
+ rv->buf_capacity = eof_pagebound;
+ rv->buf_offset = 0;
+ rv->buf_size = rv->eof_mark;
+ //madvise(rv->buf, rv->buf_capacity,
+ // MADV_SEQUENTIAL);
+ }
+ }
+#endif
+ }
+
+#ifdef _POSIX_MAPPED_FILES
+ if (!rv->mapped)
+#endif
+ {
+ rv->buf_capacity = 65536;
+ rv->buf = malloc(rv->buf_capacity);
+ if (rv->buf == 0) rv->buf_capacity = 0;
+ }
+
+ return rv;
+fail:
+ free(rv);
+ return 0;
+}
+
+void fsaf_close(FSAF * fp)
+{
+ assert(fp != 0);
+
+#if _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+ munmap(fp->buf, fp->buf_capacity);
+ free(fp);
+ return;
+ }
+#endif
+ free(fp->fname);
+ free(fp->buf);
+ free(fp);
+}
+
+
+static void slide(FSAF *fp)
+{
+ assert(fp->invalid_mark >= fp->buf_offset);
+ size_t delta = fp->invalid_mark - fp->buf_offset;
+ assert(fp->buf_capacity > delta);
+ if (delta == 0) return;
+ memmove(fp->buf, fp->buf + delta, fp->buf_size - delta);
+ fp->buf_offset += delta;
+ fp->buf_size -= delta;
+}
+
+static void slurp(FSAF * fp, size_t len)
+{
+ assert(fp != 0);
+ assert(len > 0);
+ if (fp->invalid_mark - fp->buf_offset > fp->buf_size / 2) {
+ slide(fp);
+ }
+ if (fp->buf_size + len > fp->buf_capacity) {
+ size_t nc = fp->buf_capacity;
+ if (nc == 0) nc = 256;
+ while (nc < fp->buf_size + len) nc *= 2;
+ char * nb = realloc(fp->buf, nc);
+ if (nb != 0) {
+ fp->buf = nb;
+ fp->buf_capacity = nc;
+ } else {
+ slide(fp);
+ if (fp->buf_size > fp->buf_capacity) {
+ fatal_enomem(fp->fname);
+ }
+ if (fp->buf_size + len > fp->buf_capacity) {
+ len = fp->buf_capacity - fp->buf_size;
+ }
+ }
+ }
+ assert(len > 0);
+ assert(fp->buf_size + len <= fp->buf_capacity);
+
+ ssize_t res;
+ do {
+// res = read(fp->fd, fp->buf + fp->buf_size, len);
+ res = read(fp->fd, fp->buf + fp->buf_size, fp->buf_capacity - fp->buf_size);
+ } while (res == -1 && errno == EINTR);
+ if (res == 0) {
+ fp->eof_mark = fp->buf_offset + fp->buf_size;
+ }
+ fp->buf_size += res;
+}
+
+//static inline
+struct fsaf_read_rv fsaf_read(FSAF * fp, size_t offset, size_t len)
+{
+ struct fsaf_read_rv rv;
+
+ /* Reading nothing - since offset can be bogus in this
+ * situation, this could foul up our assumptions later, so
+ * return already here. */
+ if (len == 0) {
+ rv.b = "";
+ rv.len = 0;
+ return rv;
+ }
+
+ /* Make sure we don't read past the EOF mark. */
+ if (offset + len > fp->eof_mark) len = fp->eof_mark - offset;
+
+#if _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+#if 0 /* madvise seems not to give a noteworthy efficiency boost */
+ /* Notify the kernel that we will soon want more of
+ * the data, but do this only once for each page! */
+ if (offset + len >= fp->topread) {
+ size_t base = align(offset - fp->buf_offset, 1);
+ size_t rl = READAHEAD * pagesize;
+ madvise(fp->buf + base, rl, MADV_WILLNEED);
+ fp->topread += rl;
+ }
+#endif
+ } else
+#endif
+ {
+ /* Ensure that we have enough data in the buffer.
+ * This is only executed if we are not dealing with a
+ * mapped file. */
+ assert(offset >= fp->buf_offset);
+ if (offset - fp->buf_offset + len > fp->buf_size) {
+ slurp(fp, offset - fp->buf_offset + len - fp->buf_size);
+ if (offset - fp->buf_offset + len > fp->buf_size) {
+ len = fp->buf_size - (offset - fp->buf_offset);
+ }
+ }
+
+ }
+
+ assert(offset - fp->buf_offset + len <= fp->buf_size);
+ assert(offset + len <= fp->eof_mark);
+ rv.b = fp->buf + (offset - fp->buf_offset);
+ rv.len = len;
+ return rv;
+}
+
+void fsaf_invalidate(FSAF * fp, size_t offset)
+{
+ assert(fp->eof_mark >= offset);
+ if (fp->invalid_mark >= offset) return;
+
+#ifdef _POSIX_MAPPED_FILES
+ if (fp->mapped) {
+ size_t old = pg_align(fp->eof_mark - fp->buf_offset, 0);
+ size_t new = pg_align(offset - fp->buf_offset, 0);
+ madvise(fp->buf + old, new - old, MADV_DONTNEED);
+ }
+#endif
+
+ fp->invalid_mark = offset;
+}
+
+#ifdef TESTMAIN
+#include <stdio.h>
+volatile char c;
+int main(int argc, char * argv[])
+{
+ if (argc == 1) {
+ set_loglevel(L_DEBUG);
+ FSAF * fp = fsaf_fdopen(STDIN_FILENO);
+ assert(fp != 0);
+ size_t i = 0;
+ while (i < fsaf_eof(fp)) {
+ struct fsaf_read_rv rr = fsaf_read(fp, i, 1);
+ //fwrite(rr.b, 1, rr.len, stdout);
+ c = rr.b[0];
+ i += rr.len;
+ }
+ fsaf_close(fp);
+ } else {
+ int ch;
+ while ((ch = getchar()) != EOF) {
+ c = ch;
+ }
+ }
+ return 0;
+}
+#endif
diff --git a/lib/fsaf.h b/lib/fsaf.h
new file mode 100644
index 0000000..f95cf54
--- /dev/null
+++ b/lib/fsaf.h
@@ -0,0 +1,101 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FSAF_H
+#define FSAF_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* FAST (MOSTLY) SEQUENTIAL-ACCESS FILE LAYER */
+
+struct fsaf_private {
+ char *fname;
+ int fd;
+ char * buf;
+ size_t buf_capacity;
+ size_t buf_offset;
+ size_t buf_size;
+ size_t invalid_mark;
+ size_t eof_mark; /* can be (size_t)(-1) if not reached yet */
+#ifdef _POSIX_MAPPED_FILES
+ bool mapped;
+ size_t topread; /* marks the top of what has been read ahead */
+#endif
+};
+
+typedef struct fsaf_private FSAF;
+
+/* True if FSAF should not use mmap even if it is possible. */
+extern bool fsaf_mmap;
+
+/* Open a FSAF for the given fd. Only read access is supported for
+ * now. The whole file is initially valid. */
+FSAF * fsaf_fdopen(int fd, char const *fname);
+
+/* Close the given FSAF. This DOES NOT close the underlying fd. */
+void fsaf_close(FSAF *);
+
+/* Return a pointer to a buffer containing len bytes from the FSAF
+ * starting at offset. Note that if offset is below the invalid mark,
+ * we return b = NULL. This is a CHEAP operation, if the full range
+ * has already been accessed. The buffer may be invalidated by any
+ * subsequent call to this function. */
+struct fsaf_read_rv {
+ char const * b;
+ size_t len;
+} fsaf_read(FSAF *, size_t offset, size_t len);
+
+/* Behaves like fsaf_read except that the result is put in a malloc'd
+ * zero-terminated buffer. NULL return value indicates either memory
+ * allocation failure or that the read was below the invalid mark. */
+static inline
+char * fsaf_getas(FSAF * fp, size_t offset, size_t len)
+{
+ struct fsaf_read_rv r = fsaf_read(fp, offset, len);
+ if (r.b == 0) return 0;
+ char * rv = malloc(r.len+1);
+ if (rv == 0) return 0;
+ memcpy(rv, r.b, r.len);
+ rv[r.len] = 0;
+ return rv;
+}
+
+static inline
+int fsaf_getc(FSAF * fp, size_t offset)
+{
+ if (offset >= fp->eof_mark) return -1;
+ struct fsaf_read_rv r = fsaf_read(fp, offset, 1);
+ if (offset >= fp->eof_mark) return -1;
+ assert(r.len == 1);
+ return (unsigned char)r.b[0];
+}
+
+
+/* Invalidate all bytes in FSAF up to and excluding offset. */
+void fsaf_invalidate(FSAF *, size_t offset);
+
+/* Return an upper bound for the end of file, either the smallest
+ * offset beyond eof or (size_t)(-1). */
+static inline
+size_t fsaf_eof(FSAF * fp) { return fp->eof_mark; }
+
+
+#endif /* FSAF_H */
diff --git a/lib/getaline.c b/lib/getaline.c
new file mode 100644
index 0000000..df02498
--- /dev/null
+++ b/lib/getaline.c
@@ -0,0 +1,113 @@
+/* getaline.c -- read arbitrarily long line from file
+ (Originally from Lars Wirzenius' publib.)
+
+ Copyright (c) 1994 Lars Wirzenius. All rights reserved.
+ Copyright (C) 2004 Antti-Juhani Kaijanaho. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getaline.h"
+
+// NULL indicates error, EOF is indicated by a string of length zero
+char * getaline (FILE * f)
+{
+ char *buf; /* buffer for line */
+ size_t size; /* size of buffer */
+ size_t inc; /* how much to enlarge buffer */
+ size_t len; /* # of chars stored into buf
+ before '\0' */
+ char *p;
+ const size_t thres = 128; /* initial buffer size (most
+ lines should fit into this
+ size, so think of this as
+ the "long line
+ threshold"). */
+ const size_t mucho = 128; /* if there is at least this
+ much wasted space when the
+ whole buffer has been read,
+ try to reclaim it. Don't
+ make this too small, else
+ there is too much time
+ wasted trying to reclaim a
+ couple of bytes. */
+ const size_t mininc = 64; /* minimum number of bytes by
+ which to increase the
+ allocated memory */
+
+ len = 0;
+ size = thres;
+ buf = malloc (size);
+ if (buf == NULL) return NULL;
+
+ buf[0] = '\0';
+
+ while (fgets (buf + len, size - len, f) != NULL) {
+ len += strlen(buf + len);
+ if (len > 0 && buf[len - 1] == '\n')
+ break; /* the whole line has
+ been read */
+
+ for (inc = size, p = NULL; inc > mininc; inc /= 2)
+ if ((p = realloc (buf, size + inc)) != NULL)
+ break;
+
+ if (p == NULL) {
+ free (buf);
+ return NULL; /* couldn't get more memory */
+ }
+
+ size += inc;
+ buf = p;
+ }
+
+ if (len == 0 && ferror(f)) {
+ free (buf);
+ return NULL; /* nothing read (eof or error) */
+ }
+
+#if 0
+ if (buf[len - 1] == '\n') /* remove newline, if there */
+ buf[--len] = '\0';
+#else
+ buf[len] = '\0';
+#endif
+
+ if (size - len > mucho) { /* a plenitude of unused memory? */
+ p = realloc (buf, len + 1);
+
+ if (p != NULL) {
+ buf = p;
+ size = len + 1;
+ }
+ }
+
+ return buf;
+}
+
diff --git a/lib/getaline.h b/lib/getaline.h
new file mode 100644
index 0000000..e7f60d1
--- /dev/null
+++ b/lib/getaline.h
@@ -0,0 +1,26 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GETALINE_H
+#define GETALINE_H
+
+#include <stdio.h>
+
+char * getaline (FILE * f);
+
+#endif /* GETALINE_H */
diff --git a/lib/i18n.h b/lib/i18n.h
new file mode 100644
index 0000000..67a3b5f
--- /dev/null
+++ b/lib/i18n.h
@@ -0,0 +1,33 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 1999, 2003 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef I18N_H__
+#define I18N_H__
+
+#ifdef HAVE_GETTEXT
+#include <libintl.h>
+#define _(String) gettext(String)
+#else
+#define _(String) (String)
+#define textdomain(Domain)
+#define bindtextdomain(Package, Directory)
+#endif
+#define N_(String) (String)
+
+#endif /* I18N_H__ */
diff --git a/lib/ifile.c b/lib/ifile.c
new file mode 100644
index 0000000..b44b708
--- /dev/null
+++ b/lib/ifile.c
@@ -0,0 +1,149 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "ifile.h"
+#include "msg.h"
+
+static int open_pipe(char const * s)
+{
+ int ps[2];
+ int r = pipe(ps);
+ if (r == -1) {
+ errno_msg(L_IMPORTANT, 0);
+ return -1;
+ }
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ errno_msg(L_IMPORTANT, 0);
+ close(ps[0]);
+ close(ps[1]);
+ return -1;
+ case 0:
+ // child
+ close(STDIN_FILENO);
+ r = dup2(ps[1], STDOUT_FILENO);
+ if (r == -1) {
+ fprintf(stderr, "%s (child): %s\n",
+ get_progname(), strerror(errno));
+ _exit(1);
+ }
+ close(ps[0]);
+ close(ps[1]);
+ execl("/bin/sh", "/bin/sh", "-c", s, 0);
+ fprintf(stderr, _("%s (child): failed to exec /bin/sh: %s\n"),
+ get_progname(), strerror(errno));
+ _exit(1);
+ }
+ // parent
+ close(ps[1]);
+ return ps[0];
+}
+
+int open_ifile(struct ifile f)
+{
+ switch (f.mode) {
+ case m_read:
+ if (strcmp(f.s, "-") == 0) {
+ return STDIN_FILENO;
+ }
+ {
+ int fd = open(f.s, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "%s: %s: %s\n",
+ get_progname(), f.s, strerror(errno));
+ record_error();
+ return -1;
+ }
+ return fd;
+ }
+ case m_exec:
+ return open_pipe(f.s);
+ case m_error:
+ abort();
+ }
+ abort();
+}
+
+void close_ifile(struct ifile f, int fd)
+{
+ close(fd);
+ if (f.mode == m_exec) {
+ int status;
+ int r = wait(&status);
+ if (r == -1) {
+ errno_msg(L_IMPORTANT, f.s);
+ return;
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == 0) return;
+ fprintf(stderr, _("%s: command (%s) failed "
+ "(exit status %d)\n"),
+ get_progname(),
+ f.s,
+ WEXITSTATUS(status));
+ record_error();
+ return;
+ }
+ if (WIFSIGNALED(status)) {
+ fprintf(stderr, _("%s: command (%s) was killed "
+ "by signal %d\n"),
+ get_progname(),
+ f.s,
+ WTERMSIG(status));
+ record_error();
+ return;
+ }
+ }
+}
+
+bool chk_ifile(struct ifile fname, int fd)
+{
+ struct stat stat;
+ int r = fstat(fd, &stat);
+ mode_t m = stat.st_mode;
+ if (r == -1) {
+ if (do_msg(L_IMPORTANT)) {
+ fprintf(stderr, _("%s: %s: cannot stat: %s\n"),
+ get_progname(), fname.s, strerror(errno));
+ }
+ record_error();
+ close_ifile(fname, fd);
+ return 0;
+ }
+ if (!(S_ISREG(m) || S_ISCHR(m) || S_ISFIFO(m))) {
+ if (do_msg(L_IMPORTANT)) {
+ fprintf(stderr, "%s: %s: %s\n",
+ get_progname(), fname.s,
+ S_ISDIR(m) ? _("is a directory, skipping") :
+ S_ISBLK(m) ? _("is a block device, skipping") :
+ S_ISLNK(m) ? _("internal error") :
+ S_ISSOCK(m) ? _("is a socket, skipping") :
+ _("unknown file type, skipping"));
+ }
+ record_error();
+ close_ifile(fname, fd);
+ return 0;
+ }
+ return 1;
+}
diff --git a/lib/ifile.h b/lib/ifile.h
new file mode 100644
index 0000000..1405516
--- /dev/null
+++ b/lib/ifile.h
@@ -0,0 +1,38 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IFILE_H
+#define IFILE_H
+
+#include <stdbool.h>
+
+struct ifile {
+ enum ifile_mode { m_error, m_read, m_exec } mode;
+ char const * s;
+};
+
+// returns fd
+int open_ifile(struct ifile f);
+
+// must be used on ifile-opened fd's
+void close_ifile(struct ifile f, int fd);
+
+// check if a safe file to read from
+bool chk_ifile(struct ifile fname, int fd);
+
+#endif /* IFILE_H */
diff --git a/lib/misc.c b/lib/misc.c
new file mode 100644
index 0000000..1b299b6
--- /dev/null
+++ b/lib/misc.c
@@ -0,0 +1,71 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "misc.h"
+#include "msg.h"
+
+int to_stdout (const char * fname)
+{
+ FILE * f;
+ FILE * t = stdout;
+ bool pipe = false;
+ int c;
+ int rv = 1;
+
+ if (isatty(STDOUT_FILENO)) {
+ char * cmd = getenv("PAGER");
+ if (cmd == 0) cmd = "/usr/bin/pager";
+ if (do_msg(L_INFORMATIONAL)) {
+ fprintf(stderr, _("%s: using `%s' as pager\n"),
+ get_progname(), cmd);
+ }
+ // popen does not set errno if memory allocation fails
+ errno = ENOMEM;
+ FILE * p = popen(cmd, "w");
+ if (p != 0) {
+ t = p;
+ pipe = true;
+ } else if (do_msg(L_IMPORTANT)) {
+ fprintf(stderr, _("%s: popen failed for %s: %s\n"),
+ get_progname(), cmd, strerror(errno));
+ }
+ }
+
+ f = fopen (fname, "r");
+ if (f == 0) {
+ message (L_FATAL, strerror (errno), COPYING);
+ return 0;
+ }
+
+ while ( ( c = getc (f)) != EOF) putc(c, t);
+
+ if (ferror (f)) {
+ message (L_FATAL, strerror (errno), COPYING);
+ rv = 0;
+ }
+
+ fclose (f);
+
+ if (pipe) pclose(t);
+
+ return rv;
+}
+
diff --git a/lib/misc.h b/lib/misc.h
new file mode 100644
index 0000000..674f2b9
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,28 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MISC_H
+#define MISC_H
+
+#define COPYING "/usr/share/common-licenses/GPL"
+
+/* Copy the file called fname to standard outuput stream. Return zero
+ iff problems were encountered. */
+int to_stdout (const char * fname);
+
+#endif /* MISC_H */
diff --git a/lib/msg.c b/lib/msg.c
new file mode 100644
index 0000000..85e8382
--- /dev/null
+++ b/lib/msg.c
@@ -0,0 +1,102 @@
+/* grep-dctrl - grep Debian control files
+ Copyright (C) 1999, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ The author can be reached via mail at (ISO 8859-1 charset for the city)
+ Antti-Juhani Kaijanaho
+ Helokantie 1 A 16
+ FIN-40640 JYVÄSKYLÄ
+ FINLAND
+ EUROPE
+ and via electronic mail from
+ gaia@iki.fi
+ If you have a choice, use the email address; it is more likely to
+ stay current.
+
+*/
+
+
+#define MSG_C__
+
+#include <stdbool.h>
+
+#include <assert.h>
+#include <string.h>
+#include "msg.h"
+
+#define PROGNAME_MAXLEN 64
+
+bool errors = false;
+
+int loglevel = L_IMPORTANT;
+char progname [PROGNAME_MAXLEN];
+
+struct str2int_avec_t {
+ const char * str;
+ int integer;
+};
+
+int
+within_interval (int n, int a, int b)
+{
+ if (a > b)
+ {
+ int tmp;
+ tmp = b;
+ b = a;
+ a = tmp;
+ }
+
+ assert (a <= b);
+
+ return (a <= n && n <= b);
+}
+
+void
+msg_set_progname (const char * pn)
+{
+ strncpy (progname, pn, PROGNAME_MAXLEN);
+ progname [PROGNAME_MAXLEN - 1] = 0;
+}
+
+void
+set_loglevel (int ll)
+{
+ assert (within_interval (loglevel, L_FATAL, L_DEBUG));
+
+ loglevel = ll;
+}
+
+int
+str2loglevel (const char * s)
+{
+ static struct str2int_avec_t avec [] = {
+ { "fatal", L_FATAL },
+ { "important", L_IMPORTANT },
+ { "informational", L_INFORMATIONAL },
+#if !defined(NDEBUG) && defined(ENABLE_L_DEBUG)
+ { "debug", L_DEBUG },
+#endif
+ { 0, 0 } };
+
+ int i;
+
+ for (i = 0; avec [i].str != 0; i++)
+ if (strcasecmp (avec [i].str, s) == 0)
+ return avec[i].integer;
+ return -1;
+}
diff --git a/lib/msg.h b/lib/msg.h
new file mode 100644
index 0000000..bda8531
--- /dev/null
+++ b/lib/msg.h
@@ -0,0 +1,173 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 1999 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MSG_H__
+#define MSG_H__
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "i18n.h"
+
+static inline
+void fail(void) { exit(2); }
+
+/* log levels */
+#define L_FATAL 3
+#define L_IMPORTANT 2
+#define L_INFORMATIONAL 1
+#define L_DEBUG 0
+
+#if !defined(NDEBUG) && !defined(TEST_NODEBUG) && defined(ENABLE_L_DEBUG)
+# define INCLUDE_DEBUG_MSGS
+#endif
+
+inline static int
+do_msg(int severity)
+{
+ extern int loglevel;
+
+#if defined(TEST_NODEBUG)
+ if (severity == L_DEBUG) {
+ message (L_FATAL, _("I'm broken - please report this bug."), 0, 0);
+ abort ();
+ }
+#endif
+
+ return severity >= loglevel;
+}
+
+inline static char const *
+get_progname(void)
+{
+ extern char progname [];
+ return progname;
+}
+
+static inline
+void record_error(void)
+{
+ extern bool errors;
+ errors = 1;
+}
+
+inline static void
+line_message (int severity, const char * s, const char * fname, int line)
+{
+ if (do_msg(severity)) {
+
+ if (fname == 0) {
+ fprintf (stderr, "%s: %s.\n", get_progname(), s);
+ } else {
+ if (line > 0) {
+ fprintf (stderr, "%s: %s:%i: %s.\n",
+ get_progname(), fname, line, s);
+ } else {
+ fprintf (stderr, "%s: %s: %s.\n",
+ get_progname(), fname, s);
+ }
+ }
+ if (severity >= L_IMPORTANT) record_error();
+ }
+}
+
+inline static void
+message (int severity, const char * s, const char * fname)
+{
+ line_message (severity, s, fname, 0);
+}
+
+#ifndef MSG_C__
+#undef PROGNAME_MAXLEN
+#endif
+
+inline static void
+debug_message (const char * s, const char * fname)
+{
+#ifdef INCLUDE_DEBUG_MSGS
+ message (L_DEBUG, s, fname);
+#endif
+}
+
+static void
+debug(const char * s, ...) __attribute__((format(printf, 1, 2)));
+
+inline static void
+debug(const char * s, ...)
+{
+#ifdef INCLUDE_DEBUG_MSGS
+ if (do_msg(L_DEBUG)) {
+ va_list va;
+ va_start(va, s);
+ fprintf(stderr, "%s: ", get_progname());
+ vfprintf(stderr, s, va);
+ fprintf(stderr, "\n");
+ va_end(va);
+ }
+#endif
+}
+
+inline static void
+errno_msg(int severity, char const * fname)
+{
+ message(severity, strerror(errno), fname);
+}
+
+#define enomem_msg _("cannot find enough memory")
+
+inline static void
+enomem (const char * fname)
+{
+ message (L_IMPORTANT, enomem_msg, fname);
+}
+
+inline static void
+fatal_enomem (const char * fname)
+{
+ message(L_FATAL, enomem_msg, fname);
+ fail();
+}
+
+#undef enomem_msg
+
+/* Given a string that represents a log level, return the number that
+ repsesents that loglevel. A negative return value means that the
+ string is not a valid log level. */
+int
+str2loglevel (const char * s);
+
+/* Set current log level to ll. */
+void
+set_loglevel (int ll);
+
+/* Set program name to pn. */
+void
+msg_set_progname (const char * pn);
+
+/* Return true iff loglevels IMPORTANT or FATAL have been used. */
+static inline
+bool errors_reported(void)
+{
+ extern bool errors;
+ return errors;
+}
+
+#endif /* MSG_H__ */
diff --git a/lib/para_bundle.c b/lib/para_bundle.c
new file mode 100644
index 0000000..d52edbf
--- /dev/null
+++ b/lib/para_bundle.c
@@ -0,0 +1,67 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "msg.h"
+#include "para_bundle.h"
+
+void bundle_slurp(struct para_bundle * pb, struct ifile ifi)
+{
+ int fd = open_ifile(ifi);
+ if (fd == -1) {
+ record_error();
+ return;
+ }
+
+ FSAF * f = fsaf_fdopen(fd, ifi.s);
+ if (f == 0) fatal_enomem(0);
+
+ struct srcfile * sf = malloc(sizeof *sf);
+ if (sf == 0) fatal_enomem(0);
+
+ sf->ifi = ifi;
+ sf->fd = fd;
+ sf->fs = f;
+ sf->next = pb->files;
+ pb->files = sf;
+
+ para_parser_init(&sf->pp, f, false, false);
+ while (true) {
+ if (pb->num_paras == pb->max_num) {
+ size_t max_num = pb->max_num == 0 ? 256 :
+ 2 * pb->max_num;
+ para_t ** paras = realloc(pb->paras,
+ max_num
+ * sizeof *paras);
+ if (paras == 0) fatal_enomem(0);
+ pb->max_num = max_num;
+ pb->paras = paras;
+ }
+ assert(pb->num_paras < pb->max_num);
+ assert(pb->paras != 0);
+ para_t * p = new_para(&pb->pool, &sf->pp);
+ if (p == 0) fatal_enomem(0);
+ para_parse_next(p);
+ if (para_eof(&sf->pp)) break;
+ debug("pb->num_paras = %zi", pb->num_paras);
+ pb->paras[pb->num_paras++] = p;
+ }
+
+// DO NOT CLOSE fsaf -- this makes the whole para_bundle useless!
+// fsaf_close(f);
+ close_ifile(ifi, fd);
+}
diff --git a/lib/para_bundle.h b/lib/para_bundle.h
new file mode 100644
index 0000000..2cb8a63
--- /dev/null
+++ b/lib/para_bundle.h
@@ -0,0 +1,70 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PARA_BUNDLE_H
+#define PARA_BUNDLE_H
+
+#include "fieldtrie.h"
+#include "fsaf.h"
+#include "ifile.h"
+#include "paragraph.h"
+#include "para_pool.h"
+
+struct srcfile {
+ struct ifile ifi;
+ int fd;
+ FSAF * fs;
+ para_parser_t pp;
+ struct srcfile * next;
+};
+
+struct para_bundle {
+ para_pool_t pool;
+ size_t num_paras;
+ size_t max_num;
+ para_t ** paras;
+ struct srcfile * files;
+};
+
+static inline
+void bundle_init(struct para_bundle * pb)
+{
+ para_pool_init(&pb->pool);
+ pb->num_paras = 0;
+ pb->max_num = 0;
+ pb->paras = 0;
+ pb->files = 0;
+}
+
+void bundle_slurp(struct para_bundle *, struct ifile);
+
+static inline
+size_t bundle_size(struct para_bundle * pb)
+{
+ assert(pb != 0);
+ return pb->num_paras;
+}
+
+static inline
+para_t ** bundle_vec(struct para_bundle * pb)
+{
+ assert(pb != 0);
+ return pb->paras;
+}
+
+#endif /* PARA_BUNDLE_H */
diff --git a/lib/para_pool.c b/lib/para_pool.c
new file mode 100644
index 0000000..eb907ac
--- /dev/null
+++ b/lib/para_pool.c
@@ -0,0 +1,29 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "para_pool.h"
+
+void para_pool_fini(para_pool_t * pp)
+{
+ while (pp->curr_pages != 0) {
+ struct para_pages * pgs = pp->curr_pages->next;
+ free(pp->curr_pages);
+ pp->curr_pages = pgs;
+ }
+}
+
diff --git a/lib/para_pool.h b/lib/para_pool.h
new file mode 100644
index 0000000..9ec37f5
--- /dev/null
+++ b/lib/para_pool.h
@@ -0,0 +1,93 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PARA_POOL_H
+#define PARA_POOL_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "align.h"
+#include "msg.h"
+#include "paragraph.h"
+
+#define NUM_PAGES 1
+
+struct para_pages {
+ size_t max;
+ size_t nap; // next allocation "pointer" (offset from base of this)
+ struct para_pages * next;
+};
+
+struct para_pool_private {
+ struct para_pages * curr_pages;
+};
+
+typedef struct para_pool_private para_pool_t;
+
+static inline
+void para_pool_init(para_pool_t * pp)
+{
+ pp->curr_pages = 0;
+}
+
+void para_pool_fini(para_pool_t *); // deallocates everything allocated as well
+
+/*
+static inline
+bool para_pool_more(para_pool_t * pp)
+{
+ size_t sz = NUM_PAGES * get_pagesize();
+ struct para_pages * np = malloc(sz);
+ assert(sizeof *np < sz);
+ if (np == 0) return false;
+ np->next = pp->curr_pages;
+ pp->curr_pages = np;
+ np->nap = align(sizeof *np, MAX_ALIGN, true);
+ np->max = sz;
+ return true;
+}
+*/
+
+// calls para_init
+static inline
+para_t * new_para(para_pool_t * ppo, para_parser_t * ppa)
+{
+ para_t * rv = 0;
+/*
+ struct para_pages * cp = ppo->curr_pages;
+ if (cp == 0 || cp->nap + sizeof *rv >= cp->max) {
+ bool success = para_pool_more(ppo);
+ if (!success) return 0;
+ cp = ppo->curr_pages;
+ }
+ assert(cp != 0);
+ assert(cp->nap + sizeof *rv < cp->max);
+ debug("cp->nap = %zi", cp->nap);
+ rv = (para_t *) cp + cp->nap;
+ assert((intptr_t)rv % __alignof__(*rv) == 0);
+ cp->nap = align(cp->nap + sizeof *rv, MAX_ALIGN, true);
+*/
+ rv = malloc(sizeof *rv);
+ if (rv == 0) return 0;
+ para_init(ppa, rv);
+ return rv;
+}
+
+
+
+#endif /* PARA_POOL_H */
diff --git a/lib/paragraph.c b/lib/paragraph.c
new file mode 100644
index 0000000..5dc06a4
--- /dev/null
+++ b/lib/paragraph.c
@@ -0,0 +1,209 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include "msg.h"
+#include "paragraph.h"
+#include "strutil.h"
+
+void para_parser_init(para_parser_t * pp, FSAF * fp,
+ bool invalidate_p, bool ignore_failing_paras)
+{
+ pp->fp = fp;
+ pp->eof = false;
+ pp->loc = 0;
+ pp->line = 1;
+ pp->invalidate_p = invalidate_p;
+ pp->ignore_broken_paras = ignore_failing_paras;
+}
+
+void para_init(para_parser_t * pp, para_t * para)
+{
+ para->common = pp;
+ para->line = pp->line;
+ para->start = 0;
+ para->end = 0;
+ para->fields = 0;
+ para->nfields = 0;
+ para->nfields = fieldtrie_count();
+ para->fields = malloc(para->nfields * sizeof *para->fields);
+ if (para->fields == 0) fatal_enomem(0);
+}
+
+void para_finalize(para_t * para)
+{
+ para->common = 0;
+ para->start = 0;
+ para->end = 0;
+ if (para->fields != 0) {
+ free(para->fields);
+ }
+ para->nfields = 0;
+ para->fields = 0;
+}
+
+void para_parse_next(para_t * para)
+{
+redo:
+ debug_message("para_parse_next", 0);
+ assert(para != 0);
+ para_parser_t * pp = para->common;
+ assert(para->nfields == fieldtrie_count());
+ para->start = pp->loc;
+ para->line = pp->line;
+ for (size_t i = 0; i < para->nfields; i++) {
+ para->fields[i].start = 0;
+ para->fields[i].end = 0;
+ }
+ if (pp->invalidate_p) {
+ fsaf_invalidate(pp->fp, para->start);
+ }
+ register enum { START, FIELD_NAME, BODY, BODY_NEWLINE,
+ BODY_SKIPBLANKS, END, FAIL } state = START;
+ register size_t pos = para->start;
+ register size_t line = para->line;
+ register FSAF * fp = pp->fp;
+ size_t field_start = 0;
+ struct field_data * field_data = 0;
+ while (state != END && state != FAIL) {
+# ifndef TEST_NODEBUG
+ static char * const stnm[] = { "START", "FIELD_NAME",
+ "BODY", "BODY_NEWLINE",
+ "BODY_SKIPBLANKS", "END",
+ "FAIL" };
+ if (do_msg(L_DEBUG)) {
+ fprintf(stderr, "%s:%zu: state: %s\n",
+ fp->fname, line, stnm[state]);
+ }
+# endif
+ int c = fsaf_getc(fp, pos++);
+ if (c == '\n') line++;
+ switch (state) {
+ case START:
+ switch (c) {
+ case -1:
+ pp->eof = true;
+ state = END;
+ break;
+ case '\n':
+ para->start++;
+ break;
+ default:
+ field_start = --pos;
+ state = FIELD_NAME;
+ }
+ break;
+ case FIELD_NAME:
+ switch (c) {
+ case '\n': case -1:
+ if (pp->ignore_broken_paras) {
+ line_message(L_IMPORTANT,
+ _("warning: "
+ "expected a colon"),
+ fp->fname,
+ c == '\n' ?line-1:line);
+ state = FAIL;
+ } else {
+ line_message(L_FATAL,
+ _("expected a colon"),
+ fp->fname,
+ c == '\n' ?line-1:line);
+ fail();
+ }
+ break;
+ case ':': {
+ size_t len = (pos-1) - field_start;
+ struct fsaf_read_rv r = fsaf_read(fp,
+ field_start,
+ len);
+ assert(r.len == len);
+ struct field_attr attr =
+ fieldtrie_lookup(r.b, len);
+ if (!attr.valid) {
+ field_data = 0;
+ } else {
+ assert(attr.inx < MAX_FIELDS);
+ field_data = &para->fields[attr.inx];
+ field_data->start = pos;
+ field_data->line = line;
+ }
+ state = BODY;
+ }
+ break;
+ }
+ break;
+ case BODY:
+ if (c == -1 || c == '\n') {
+ if (field_data != 0) {
+ field_data->end = pos-1;
+ while (field_data->start < field_data->end
+ && fsaf_getc(fp, field_data->start)
+ == ' ') {
+ ++field_data->start;
+ }
+ }
+ state = BODY_NEWLINE;
+ }
+ if (c != -1) break;
+ /* conditional passthrough */
+ case BODY_NEWLINE:
+ switch (c) {
+ case -1:
+ //para->eof = true;
+ /* pass through */
+ case '\n':
+ state = END;
+ break;
+ case ' ': case '\t':
+ state = BODY_SKIPBLANKS;
+ break;
+ default:
+ field_start = --pos;
+ state = FIELD_NAME;
+ }
+ break;
+ case BODY_SKIPBLANKS:
+ switch (c) {
+ case -1:
+ /* pass through */
+ case '\n':
+ state = END;
+ break;
+ case ' ': case '\t':
+ break;
+ default:
+ state = BODY;
+ }
+ break;
+ default: assert(0);
+ }
+ }
+ para->end = pos-1;
+ pp->loc = para->end;
+ pp->line = fsaf_getc(fp, pp->loc) == '\n' ? line-1 : line;
+
+ if (state == FAIL) {
+ /* skip the rest of the broken line */
+ int c;
+ do {
+ c = fsaf_getc(fp, pp->loc++);
+ if (c == '\n') pp->line++;
+ } while (c != -1 && c != '\n');
+ goto redo;
+ }
+}
diff --git a/lib/paragraph.h b/lib/paragraph.h
new file mode 100644
index 0000000..b3daafc
--- /dev/null
+++ b/lib/paragraph.h
@@ -0,0 +1,90 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PARAGRAPH_H
+#define PARAGRAPH_H
+
+#define MAX_FIELDS 100
+
+#include <stddef.h>
+#include "fsaf.h"
+#include "fieldtrie.h"
+
+struct field_data {
+ size_t line;
+ size_t start, end; /* offsets to the file; [start,end) is the body */
+};
+
+struct paragraph_parser {
+ bool eof;
+ bool invalidate_p;
+ FSAF * fp;
+ size_t loc;
+ size_t line;
+
+ bool ignore_broken_paras;
+};
+
+struct paragraph {
+ size_t line;
+ struct paragraph_parser * common;
+ size_t start, end; /* offsets to the file; [start,end) is the paragraph */
+ size_t nfields;
+ struct field_data * fields;
+ //struct field_data fields[MAX_FIELDS];
+};
+
+typedef struct paragraph_parser para_parser_t;
+typedef struct paragraph para_t;
+
+/* Initialize the given para_parser_t, associating with it the given FSAF. */
+void para_parser_init(para_parser_t *, FSAF *,
+ bool invalidate_p, bool ignore_broken_paras);
+
+/* Initialize the given para_t for the given parser. */
+void para_init(para_parser_t *, para_t *);
+
+void para_parse_next(para_t *);
+
+static inline
+struct fsaf_read_rv get_whole_para(para_t * p)
+{
+ return fsaf_read(p->common->fp, p->start, p->end - p->start);
+}
+
+static inline
+struct fsaf_read_rv get_field(para_t * p, size_t fld_inx, size_t repl_inx)
+{
+ struct field_data * fd = &p->fields[fld_inx];
+ if (fd->start == fd->end && repl_inx != (size_t)(-1)) {
+ fd = &p->fields[repl_inx];
+ }
+ return fsaf_read(p->common->fp, fd->start, fd->end - fd->start);
+}
+
+static inline
+char * get_field_as(para_t * p, size_t fld_inx)
+{
+ struct field_data * fd = &p->fields[fld_inx];
+ return fsaf_getas(p->common->fp, fd->start, fd->end - fd->start);
+}
+
+static inline
+bool para_eof(para_parser_t * para) { return para->eof; }
+
+#endif /* PARAGRAPH_H */
diff --git a/lib/predicate.c b/lib/predicate.c
new file mode 100644
index 0000000..67614cc
--- /dev/null
+++ b/lib/predicate.c
@@ -0,0 +1,241 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <string.h>
+#include "fsaf.h"
+#include "msg.h"
+#include "util.h"
+#include "predicate.h"
+#include "strutil.h"
+#include "version.h"
+
+void init_predicate(struct predicate * p)
+{
+ p->num_atoms = 0;
+ p->proglen = 0;
+}
+
+void addinsn(struct predicate * p, int insn)
+{
+ if (insn == I_NOP) return;
+ if (p->proglen >= MAX_OPS) {
+ message(L_FATAL, _("predicate is too complex"), 0);
+ fail();
+ }
+ p->program[p->proglen++] = insn;
+}
+
+void predicate_finish_atom(struct predicate * p)
+{
+ struct atom * atom = get_current_atom(p);
+ debug_message("predicate_finish_atom", 0);
+ if (atom->field_name != 0) {
+ char * repl = strchr(atom->field_name, ':');
+ if (repl != NULL) {
+ *repl++ = '\0';
+ atom->repl_inx = fieldtrie_insert(repl);
+ } else {
+ atom->repl_inx = -1;
+ }
+ atom->field_inx = fieldtrie_insert(atom->field_name);
+ }
+
+ if (atom->mode == M_REGEX || atom->mode == M_EREGEX) {
+ debug_message("compiling:", 0);
+ debug_message(atom->pat, 0);
+ int rerr = regcomp(&atom->regex, atom->pat,
+ (atom->mode == M_EREGEX ? REG_EXTENDED : 0)
+ | REG_NOSUB
+ | (atom->ignore_case ? REG_ICASE : 0));
+ if (rerr != 0) {
+ char * s;
+ s = get_regerror(rerr, &atom->regex);
+ if (s == 0) fatal_enomem(0);
+ message(L_FATAL, s, 0);
+ free(s);
+ fail();
+ }
+ }
+
+}
+
+static bool verify_atom(struct atom * atom, para_t * para)
+{
+ size_t start, end;
+ if (atom->field_inx == -1) {
+ /* Take the full paragraph */
+ start = para->start;
+ end = para->end;
+ } else {
+ /* Take the field */
+ struct field_data * fd = &para->fields[atom->field_inx];
+ if (fd->start == fd->end && atom->repl_inx != (size_t)(-1)) {
+ fd = &para->fields[atom->repl_inx];
+ }
+ start = fd->start;
+ end = fd->end;
+ }
+ size_t len = end - start;
+ struct fsaf_read_rv r = fsaf_read(para->common->fp, start, len);
+ assert(r.len == len);
+ switch (atom->mode) {
+ case M_EXACT:
+ if (len != atom->patlen) return false;
+ if (atom->ignore_case) {
+ return strncasecmp(atom->pat, r.b, len) == 0;
+ } else {
+ return strncmp(atom->pat, r.b, len) == 0;
+ }
+ case M_SUBSTR: {
+#if 0
+ if (atom->ignore_case) {
+ return strncasestr(r.b, atom->pat, len);
+ } else {
+ return strnstr(r.b, atom->pat, len);
+ }
+#else
+ bool rv;
+ char * s = strndup(r.b, len);
+ if (s == 0) fatal_enomem(0);
+ if (atom->ignore_case) {
+ rv = strcasestr(s, atom->pat) != 0;
+ } else {
+ rv = strstr(s, atom->pat) != 0;
+ }
+ free(s);
+ return rv;
+#endif
+ }
+ case M_REGEX: case M_EREGEX: {
+ char * s = strndup(r.b, len);
+ if (s == 0) fatal_enomem(0);
+ int regex_errcode = regexec(&atom->regex, s, 0, 0, 0);
+ free(s);
+ if (regex_errcode == 0 || regex_errcode == REG_NOMATCH) {
+ return (regex_errcode == 0);
+ }
+ /* Error handling be here. */
+ assert(regex_errcode != 0 && regex_errcode != REG_NOMATCH);
+ s = get_regerror (regex_errcode, &atom->regex);
+ if (s == 0) { enomem (0); return false; }
+ message(L_IMPORTANT, s, 0);
+ free(s);
+ return false;
+ }
+ case M_VER_EQ:case M_VER_LT:case M_VER_LE:case M_VER_GT:case M_VER_GE:
+ ;
+ char *pats = strndup(atom->pat, atom->patlen);
+ char *cands = strndup(r.b, len);
+ struct versionrevision pat, cand;
+ if (!parse_version(&pat, pats, atom->patlen)) {
+ free(pats);
+ free(cands);
+ return false;
+ }
+ if (!parse_version(&cand, cands, len)) {
+ free(pats);
+ free(cands);
+ return false;
+ }
+ int res = versioncompare(&cand, &pat);
+ free(pats);
+ free(cands);
+ switch (atom->mode) {
+ case M_VER_EQ:
+ return res == 0;
+ case M_VER_LT:
+ return res < 0;
+ case M_VER_LE:
+ return res <= 0;
+ case M_VER_GT:
+ return res > 0;
+ case M_VER_GE:
+ return res >= 0;
+ default:
+ assert(0);
+ }
+ }
+ assert(0);
+}
+
+bool check_predicate(struct predicate * p)
+{
+ size_t sp = 0;
+ /* Simulate the program. */
+ for (size_t i = 0; i < p->proglen; i++) {
+ switch (p->program[i]) {
+ case I_NOP: break;
+ case I_NEG:
+ if (sp == 0) return false;
+ break;
+ case I_AND: case I_OR:
+ if (sp < 2) return false;
+ --sp;
+ break;
+ default:
+ ++sp;
+ }
+ }
+ if (sp != 1) return false;
+ return true;
+}
+
+bool does_para_satisfy(struct predicate * p, para_t * para)
+{
+ bool sat_atom[MAX_ATOMS];
+ bool stack[MAX_OPS];
+ size_t sp = 0;
+
+ /* Verify atoms. */
+ for (size_t i = 0; i < p->num_atoms; i++) {
+ sat_atom[i] = verify_atom(&p->atoms[i], para);
+ }
+
+ /* Run the program. */
+ for (size_t i = 0; i < p->proglen; i++) {
+ switch (p->program[i]) {
+ case I_NOP: break;
+ case I_NEG:
+ assert(sp >= 1);
+ stack[sp-1] = !stack[sp-1];
+ break;
+ case I_AND:
+ assert(sp >= 2);
+ stack[sp-2] = stack[sp-2] && stack[sp-1];
+ --sp;
+ break;
+ case I_OR:
+ assert(sp >= 2);
+ stack[sp-2] = stack[sp-2] || stack[sp-1];
+ --sp;
+ break;
+ default:
+ {
+ int atom = p->program[i] - I_PUSH(0);
+ assert(atom <= p->num_atoms);
+ stack[sp] = sat_atom[atom];
+ ++sp;
+ }
+ }
+ }
+ assert(sp == 1);
+ return stack[0];
+}
diff --git a/lib/predicate.h b/lib/predicate.h
new file mode 100644
index 0000000..5860b6f
--- /dev/null
+++ b/lib/predicate.h
@@ -0,0 +1,102 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PREDICATE_H
+#define PREDICATE_H
+
+#include <assert.h>
+#include <regex.h>
+#include <stdint.h>
+#include "fieldtrie.h"
+#include "paragraph.h"
+
+#define MAX_OPS 4096
+#define MAX_ATOMS 4096
+
+#define I_NOP 0
+#define I_NEG 1 /* --not; 1-1 */
+#define I_AND 2 /* --and; 2-1 */
+#define I_OR 3 /* --or; 2-1 */
+#define I_PUSH(n) (4+(n)) /* push result of nth atomic proposition */
+
+/* An atomic predicate. */
+struct atom {
+ /* The name of field to which matching is limited. Empty
+ * field_name specifies the whole paragraph (in which case
+ * field_inx is -1. */
+ char const * field_name; size_t field_inx;
+ /* The index to the field whose value is to be used when this
+ * field is empty. */
+ size_t repl_inx;
+ /* Matching mode */
+ enum matching_mode {
+ M_SUBSTR, /* substring matching */
+ M_REGEX, /* POSIX regular expression match */
+ M_EREGEX, /* POSIX extended regular expression matching */
+ M_EXACT, /* exact string match */
+#define M_FIRST_VERSION M_VER_EQ
+ M_VER_EQ, /* numeric equality comparison */
+ M_VER_LT, /* numeric < */
+ M_VER_LE, /* numeric <= */
+ M_VER_GT, /* numeric > */
+ M_VER_GE, /* numeric >= */
+#define M_LAST_VERSION M_VER_GE
+ } mode;
+ /* Flag: should matching ignore case */
+ unsigned ignore_case;
+ /* The pattern as given on the command line; interpretation
+ * depends on matching mode. Must be null-terminated and
+ * patlen must equal strlen(pat). */
+ char const * pat; size_t patlen;
+ /* A compiled version of pat; valid only when mode is M_REGEX
+ * or M_EREGEX. */
+ regex_t regex;
+};
+
+/* A predicate is represented as a set of atomic predicates and a
+ * program - a sequence of stack-based "bytecode" instructions - that
+ * specifies the structure of the combined predicate. */
+struct predicate {
+ /* Number of atomic predicates. */
+ size_t num_atoms;
+ /* Length of the program */
+ size_t proglen;
+ /* The program */
+ int program[MAX_OPS];
+ /* The atomic predicates */
+ struct atom atoms[MAX_ATOMS];
+};
+
+void init_predicate(struct predicate * p);
+
+static inline
+struct atom * get_current_atom(struct predicate * p)
+{
+ assert(p->num_atoms > 0);
+ return &p->atoms[p->num_atoms-1];
+}
+
+void predicate_finish_atom(struct predicate *);
+
+void addinsn(struct predicate * p, int insn);
+
+bool does_para_satisfy(struct predicate * p, para_t *);
+
+bool check_predicate(struct predicate * p);
+
+#endif /* PREDICATE_H */
diff --git a/lib/sorter.c b/lib/sorter.c
new file mode 100644
index 0000000..c8ae301
--- /dev/null
+++ b/lib/sorter.c
@@ -0,0 +1,85 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+
+#include "msg.h"
+#include "sorter.h"
+#include "version.h"
+
+//void keys_fini(keys_t *);
+
+
+
+static keys_t *keys;
+
+static
+int para_compare(const void * av, const void * bv)
+{
+ debug("cmp: av = %p, bv = %p", av, bv);
+ para_t ** a = (para_t **)av;
+ para_t ** b = (para_t **)bv;
+
+ int r = 0;
+ for (size_t i = 0; i < keys->nks; i++) {
+ char * af = get_field_as(*a, keys->keys[i].field_inx);
+ char * bf = get_field_as(*b, keys->keys[i].field_inx);
+ if (af == 0 || bf == 0) fatal_enomem(0);
+ switch (keys->keys[i].type) {
+ case FT_STRING:
+ r = strcmp(af, bf);
+ break;
+ case FT_VERSION:
+ ;
+ struct versionrevision ar, br;
+ bool aok = parse_version(&ar, af, strlen(af));
+ bool bok = parse_version(&br, bf, strlen(bf));
+ if (!aok || !bok) {
+ message(L_IMPORTANT,
+ _("Parse error in field."), 0);
+ free(af);
+ free(bf);
+ return aok ? (bok ? 0 : 1) : (bok ? -1 : 0);
+ }
+ r = versioncompare(&ar, &br);
+ break;
+ }
+ debug("cmp: a = %s, b = %s, verdict is %d", af, bf, r);
+ free(af);
+ free(bf);
+ if (keys->keys[i].reverse) r = -r;
+ if (r != 0) break;
+ }
+
+ return r;
+}
+
+void sort_bundle(keys_t * ks, struct para_bundle * pb)
+{
+ size_t num_paras = bundle_size(pb);
+ para_t ** paras = bundle_vec(pb);
+
+ keys = ks;
+
+ debug("sort_bundle: num_paras = %zd", num_paras);
+ debug("sort_bundle: sizeof *paras = %zd", sizeof *paras);
+ debug("sort_bundle: paras = %p, paras+num_paras*sizeof *paras = %p",
+ paras, paras+num_paras*sizeof *paras);
+
+ qsort(paras, num_paras, sizeof *paras, para_compare);
+}
diff --git a/lib/sorter.h b/lib/sorter.h
new file mode 100644
index 0000000..7b3ada2
--- /dev/null
+++ b/lib/sorter.h
@@ -0,0 +1,77 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2004, 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SORTER_H
+#define SORTER_H
+
+#include <stdbool.h>
+#include "para_bundle.h"
+
+struct key {
+ int field_inx;
+ enum { FT_STRING, FT_VERSION } type;
+ bool reverse : 1;
+};
+
+struct keys {
+ size_t max;
+ size_t nks;
+ struct key * keys;
+};
+
+typedef struct keys keys_t;
+
+static inline
+void keys_init(keys_t * ks)
+{
+ ks->max = 0;
+ ks->nks = 0;
+ ks->keys = 0;
+}
+
+static inline
+void keys_append(keys_t * ks, struct key key)
+{
+ assert(ks != 0);
+ if (ks->nks == ks->max) {
+ size_t max = ks->max == 0 ? 4 : 2 * ks->max;
+ struct key * keys = realloc(ks->keys, max * sizeof *keys);
+ if (keys == 0) fatal_enomem(0);
+ ks->max = max;
+ ks->keys = keys;
+ }
+ assert(ks->nks < ks->max);
+ ks->keys[ks->nks++] = key;
+}
+
+static inline
+void keys_fini(keys_t * ks)
+{
+ assert(ks != 0);
+ ks->max = 0;
+ ks->nks = 0;
+ free(ks->keys);
+ ks->keys = 0;
+}
+
+/*
+ Note that this function is NOT reentrant!
+*/
+void sort_bundle(keys_t *, struct para_bundle *);
+
+#endif /* SORTER_H */
diff --git a/lib/strutil.c b/lib/strutil.c
new file mode 100644
index 0000000..585abc1
--- /dev/null
+++ b/lib/strutil.c
@@ -0,0 +1,157 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 1999, 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include "strutil.h"
+
+bool str2intmax(intmax_t * rvp, char const * s, size_t len)
+{
+ bool negative = false;
+ size_t i = 0;
+ while (i < len
+ && s[i] == ' '
+ && s[i] == '\t'
+ && s[i] == '\n'
+ && s[i] == '\r')
+ ++i;
+ if (i < len && s[i] == '-') {
+ ++i;
+ negative = true;
+ }
+ uintmax_t abs = 0;
+ for (; i < len; ++i) {
+ char ch = s[i];
+ if (! ('0' <= ch && ch <= '9')) {
+ break;
+ }
+ int r = ch - '0';
+ abs = abs * 10 + r;
+ if (abs > (uintmax_t)INTMAX_MAX) {
+ return false;
+ }
+ }
+ while (i < len
+ && s[i] == ' '
+ && s[i] == '\t'
+ && s[i] == '\n'
+ && s[i] == '\r')
+ ++i;
+ if (i < len) return false; // broken input
+ *rvp = negative ? -abs : abs;
+ return true;
+}
+
+const char * left_trimmed(const char * s)
+{
+ const char * p;
+
+ for (p = s; *p != 0 && isspace (*p); p++);
+
+ return p;
+}
+
+void chomp(char * s)
+{
+ size_t sl = strlen(s);
+ if (sl == 0) return;
+ if (s[sl-1] != '\n') return;
+ s[sl-1] = '\0';
+}
+
+void trim_right (char * s)
+{
+ char * p;
+ char * herep;
+ enum { BLANKS, NONBLANKS } state = NONBLANKS;
+
+ for (herep = 0, p = s; *p != 0; p++) {
+ switch (state) {
+ case BLANKS:
+ if (!isspace (*p)) {
+ state = NONBLANKS;
+ }
+ break;
+ case NONBLANKS:
+ if (isspace (*p)) {
+ herep = p;
+ state = BLANKS;
+ }
+ }
+ }
+ if (state == BLANKS) *herep = 0;
+}
+
+#ifdef TESTMAIN
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+printout (const char * s)
+{
+ printf("\"%s\"\n", s);
+}
+
+int
+main (void)
+{
+ static const char * teststrings [] = {
+ "autoksiko",
+ " autoksiko",
+ "autoksiko ",
+ " autoksiko ",
+ "autoksiko teen",
+ " autoksiko teen",
+ "autoksiko teen ",
+ " autoksiko teen ",
+ " miljoona miljoona ruusua ",
+ 0 };
+ int i;
+
+ for (i = 0; teststrings [i] != 0; i++)
+ {
+ char * ts;
+ char * orig_ts;
+
+ orig_ts = ts = strdup (teststrings [i]);
+ if (ts == 0) {
+ fail();
+ }
+
+ printf ("Plain: ");
+ printout (ts);
+
+ printf ("Left-trimmed: ");
+ printout (left_trimmed (ts));
+
+ printf ("Right-trimmed: ");
+ trim_right (ts);
+ printout (ts);
+
+ printf ("Completely trimmed: ");
+ printout (left_trimmed (ts));
+
+ puts ("");
+ free (orig_ts);
+ }
+ return 0;
+}
+#endif /* TESTMAIN */
diff --git a/lib/strutil.h b/lib/strutil.h
new file mode 100644
index 0000000..431e9e1
--- /dev/null
+++ b/lib/strutil.h
@@ -0,0 +1,54 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 1999, 2003 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef STRUTIL_H__
+#define STRUTIL_H__
+
+#include <stddef.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Parse an integer from the (possibly not null-terminated) string s
+ * of length len, and place the result in *rvp. If return value is
+ * false, *rvp is untouched. */
+bool str2intmax(intmax_t * rvp, char const * s, size_t len);
+
+/* Return a pointer to first nonblank character in s. */
+const char * left_trimmed(const char * s);
+
+/* Mutate s and trim whitespace off its right end. */
+void trim_right(char * s);
+
+/* Remove a trailing newline, if any. */
+void chomp(char *);
+
+/* Chop off everything after and including the first comchar in line.
+ This mutates line! */
+static inline void chop_comment(char * line, char comchar)
+{
+ char * comstart;
+
+ comstart = strchr (line, comchar);
+
+ if (comstart != 0)
+ *comstart = 0;
+}
+
+#endif /* STRUTIL_H__ */
diff --git a/lib/util.c b/lib/util.c
new file mode 100644
index 0000000..bb69764
--- /dev/null
+++ b/lib/util.c
@@ -0,0 +1,50 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//#include <publib.h>
+#include <stdlib.h>
+#include <regex.h>
+#include "util.h"
+
+char * get_regerror (int errcode, regex_t *compiled)
+{
+ size_t length = regerror (errcode, compiled, NULL, 0);
+ char * buffer = malloc (length);
+
+ if (buffer == 0)
+ return 0;
+
+ (void) regerror (errcode, compiled, buffer, length);
+ return buffer;
+}
+
+/*
+char * fnqualify_xalloc(const char * fname)
+{
+ char * rv = 0;
+ size_t rv_len = 0;
+ int actual_len = 0;
+
+ while (actual_len >= rv_len) {
+ rv = xrealloc(rv, rv_len += 64);
+ actual_len = fnqualify(rv, fname, rv_len);
+ }
+
+ return rv;
+}
+*/
diff --git a/lib/util.h b/lib/util.h
new file mode 100644
index 0000000..36293ae
--- /dev/null
+++ b/lib/util.h
@@ -0,0 +1,27 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright (C) 2003, 2004 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <regex.h>
+
+char * get_regerror (int errcode, regex_t *compiled);
+
+
+#endif /* UTIL_H */
diff --git a/lib/version.c b/lib/version.c
new file mode 100644
index 0000000..624e089
--- /dev/null
+++ b/lib/version.c
@@ -0,0 +1,158 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright © 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Marked portions of this file are covered by the following
+// copyright:
+/*
+ * libdpkg - Debian packaging suite library routines
+ * vercmp.c - comparison of version numbers
+ * utils.c - Helper functions for dpkg
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ * Copyright (C) 2001 Wichert Akkerman <wakkerma@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include "version.h"
+
+/* <<<<<<< originally from dpkg >>>>>>> */
+
+/* Reimplementation of the standard ctype.h is* functions. Since gettext
+ * has overloaded the meaning of LC_CTYPE we can't use that to force C
+ * locale, so use these cis* functions instead.
+ */
+static int cisdigit(int c) {
+ return (c>='0') && (c<='9');
+}
+
+static int cisalpha(int c) {
+ return ((c>='a') && (c<='z')) || ((c>='A') && (c<='Z'));
+}
+
+/* assume ascii; warning: evaluates x multiple times! */
+#define order(x) ((x) == '~' ? -1 \
+ : cisdigit((x)) ? 0 \
+ : !(x) ? 0 \
+ : cisalpha((x)) ? (x) \
+ : (x) + 256)
+
+static int verrevcmp(const char *val, const char *ref) {
+ if (!val) val= "";
+ if (!ref) ref= "";
+
+ while (*val || *ref) {
+ int first_diff= 0;
+
+ while ( (*val && !cisdigit(*val)) || (*ref && !cisdigit(*ref)) ) {
+ int vc= order(*val), rc= order(*ref);
+ if (vc != rc) return vc - rc;
+ val++; ref++;
+ }
+
+ while ( *val == '0' ) val++;
+ while ( *ref == '0' ) ref++;
+ while (cisdigit(*val) && cisdigit(*ref)) {
+ if (!first_diff) first_diff= *val - *ref;
+ val++; ref++;
+ }
+ if (cisdigit(*val)) return 1;
+ if (cisdigit(*ref)) return -1;
+ if (first_diff) return first_diff;
+ }
+ return 0;
+}
+
+int versioncompare(const struct versionrevision *version,
+ const struct versionrevision *refversion) {
+ int r;
+
+ if (version->epoch > refversion->epoch) return 1;
+ if (version->epoch < refversion->epoch) return -1;
+ r= verrevcmp(version->version,refversion->version); if (r) return r;
+ return verrevcmp(version->revision,refversion->revision);
+}
+
+/* <<<<<<< END OF originally from dpkg >>>>>>> */
+
+/* warning: modifies ver by adding nul chars; return pointers point
+ * inside ver. */
+bool parse_version(struct versionrevision *rv, char *ver, size_t len)
+{
+ rv->version = strchr(ver, ':');
+ if (rv->version == NULL) {
+ rv->version = ver;
+ } else {
+ *(rv->version++) = '\0';
+ }
+ rv->revision = strrchr(ver, '-');
+
+ if (rv->revision == NULL) {
+ rv->revision = ver + len;
+ } else {
+ *(rv->revision++) = '\0';
+ }
+
+ if (rv->version != ver) {
+ rv->epoch = 0;
+ for (char *p = ver; *p != '\0'; p++) {
+ if (!('0' <= *p && *p <= '9')) return false;
+ rv->epoch = rv->epoch * 10 + (*p - '0');
+ }
+ } else {
+ rv->epoch = 0;
+ }
+
+ for (char *p = rv->version; *p != '\0'; p++) {
+ if (('a' <= *p && *p <= 'z') ||
+ ('A' <= *p && *p <= 'Z') ||
+ ('0' <= *p && *p <= '9') ||
+ *p == '.' ||
+ *p == '-' ||
+ *p == '+' ||
+ *p == ':' ||
+ *p == '~') continue;
+ return false;
+ }
+
+ for (char *p = rv->revision; *p != '\0'; p++) {
+ if (('a' <= *p && *p <= 'z') ||
+ ('A' <= *p && *p <= 'Z') ||
+ ('0' <= *p && *p <= '9') ||
+ *p == '.' ||
+ *p == '+' ||
+ *p == '~') continue;
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/lib/version.h b/lib/version.h
new file mode 100644
index 0000000..90aaf9d
--- /dev/null
+++ b/lib/version.h
@@ -0,0 +1,62 @@
+/* dctrl-tools - Debian control file inspection tools
+ Copyright © 2005 Antti-Juhani Kaijanaho
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Marked portions of this file are covered by the following
+// copyright:
+/*
+ * libdpkg - Debian packaging suite library routines
+ * vercmp.c - comparison of version numbers
+ *
+ * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct versionrevision { // from dpkg
+ unsigned long epoch;
+ char *version;
+ char *revision;
+};
+
+// from dpkg
+int versioncompare(const struct versionrevision *version,
+ const struct versionrevision *refversion);
+
+/* warning: modifies ver by adding nul chars; return pointers point
+ * inside ver. */
+bool parse_version(struct versionrevision *rv, char *ver, size_t len);
+
+#endif /* VERSION_H */