diff options
author | Antti-Juhani Kaijanaho <ajk@debian.org> | 2006-10-26 22:33:50 +0100 |
---|---|---|
committer | Antti-Juhani Kaijanaho <ajk@debian.org> | 2006-10-26 22:33:50 +0100 |
commit | f761dc59127e5eebd68d36f9810cad1e89fbd3c8 (patch) | |
tree | 0958ab4c7c1093aee08c7fbf788cbc20735933b7 /lib | |
parent | a599e0d1b8aff9015525059c32d59f4b93b3db4f (diff) | |
download | dctrl-tools-f761dc59127e5eebd68d36f9810cad1e89fbd3c8.tar.gz |
create lib/, man/ and $(program)/
Diffstat (limited to 'lib')
-rw-r--r-- | lib/align.h | 52 | ||||
-rw-r--r-- | lib/fieldtrie.c | 98 | ||||
-rw-r--r-- | lib/fieldtrie.h | 43 | ||||
-rw-r--r-- | lib/fnutil.c | 123 | ||||
-rw-r--r-- | lib/fnutil.h | 25 | ||||
-rw-r--r-- | lib/fsaf.c | 260 | ||||
-rw-r--r-- | lib/fsaf.h | 101 | ||||
-rw-r--r-- | lib/getaline.c | 113 | ||||
-rw-r--r-- | lib/getaline.h | 26 | ||||
-rw-r--r-- | lib/i18n.h | 33 | ||||
-rw-r--r-- | lib/ifile.c | 149 | ||||
-rw-r--r-- | lib/ifile.h | 38 | ||||
-rw-r--r-- | lib/misc.c | 71 | ||||
-rw-r--r-- | lib/misc.h | 28 | ||||
-rw-r--r-- | lib/msg.c | 102 | ||||
-rw-r--r-- | lib/msg.h | 173 | ||||
-rw-r--r-- | lib/para_bundle.c | 67 | ||||
-rw-r--r-- | lib/para_bundle.h | 70 | ||||
-rw-r--r-- | lib/para_pool.c | 29 | ||||
-rw-r--r-- | lib/para_pool.h | 93 | ||||
-rw-r--r-- | lib/paragraph.c | 209 | ||||
-rw-r--r-- | lib/paragraph.h | 90 | ||||
-rw-r--r-- | lib/predicate.c | 241 | ||||
-rw-r--r-- | lib/predicate.h | 102 | ||||
-rw-r--r-- | lib/sorter.c | 85 | ||||
-rw-r--r-- | lib/sorter.h | 77 | ||||
-rw-r--r-- | lib/strutil.c | 157 | ||||
-rw-r--r-- | lib/strutil.h | 54 | ||||
-rw-r--r-- | lib/util.c | 50 | ||||
-rw-r--r-- | lib/util.h | 27 | ||||
-rw-r--r-- | lib/version.c | 158 | ||||
-rw-r--r-- | lib/version.h | 62 |
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 = ¶->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 = ¶->fields[atom->field_inx]; + if (fd->start == fd->end && atom->repl_inx != (size_t)(-1)) { + fd = ¶->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 */ |