From f761dc59127e5eebd68d36f9810cad1e89fbd3c8 Mon Sep 17 00:00:00 2001 From: Antti-Juhani Kaijanaho Date: Thu, 26 Oct 2006 22:33:50 +0100 Subject: create lib/, man/ and $(program)/ --- Makefile | 36 +- align.h | 52 --- debian/rules | 18 +- fieldtrie.c | 98 ----- fieldtrie.h | 43 -- fnutil.c | 123 ------ fnutil.h | 25 -- fsaf.c | 260 ------------ fsaf.h | 101 ----- getaline.c | 113 ----- getaline.h | 26 -- grep-dctrl.1.cp | 506 ---------------------- grep-dctrl.c | 875 --------------------------------------- grep-dctrl.rc | 26 -- grep-dctrl/grep-dctrl.c | 875 +++++++++++++++++++++++++++++++++++++++ grep-dctrl/grep-dctrl.rc | 26 ++ grep-dctrl/rc.c | 206 +++++++++ grep-dctrl/rc.h | 36 ++ i18n.h | 33 -- ifile.c | 149 ------- ifile.h | 38 -- lib/align.h | 52 +++ lib/fieldtrie.c | 98 +++++ lib/fieldtrie.h | 43 ++ lib/fnutil.c | 123 ++++++ lib/fnutil.h | 25 ++ lib/fsaf.c | 260 ++++++++++++ lib/fsaf.h | 101 +++++ lib/getaline.c | 113 +++++ lib/getaline.h | 26 ++ lib/i18n.h | 33 ++ lib/ifile.c | 149 +++++++ lib/ifile.h | 38 ++ lib/misc.c | 71 ++++ lib/misc.h | 28 ++ lib/msg.c | 102 +++++ lib/msg.h | 173 ++++++++ lib/para_bundle.c | 67 +++ lib/para_bundle.h | 70 ++++ lib/para_pool.c | 29 ++ lib/para_pool.h | 93 +++++ lib/paragraph.c | 209 ++++++++++ lib/paragraph.h | 90 ++++ lib/predicate.c | 241 +++++++++++ lib/predicate.h | 102 +++++ lib/sorter.c | 85 ++++ lib/sorter.h | 77 ++++ lib/strutil.c | 157 +++++++ lib/strutil.h | 54 +++ lib/util.c | 50 +++ lib/util.h | 27 ++ lib/version.c | 158 +++++++ lib/version.h | 62 +++ man/grep-dctrl.1.cp | 506 ++++++++++++++++++++++ man/sort-dctrl.1.cp | 129 ++++++ man/sync-available.8 | 60 +++ man/tbl-dctrl.1 | 203 +++++++++ misc.c | 71 ---- misc.h | 28 -- msg.c | 102 ----- msg.h | 173 -------- para_bundle.c | 67 --- para_bundle.h | 70 ---- para_pool.c | 29 -- para_pool.h | 93 ----- paragraph.c | 209 ---------- paragraph.h | 90 ---- predicate.c | 241 ----------- predicate.h | 102 ----- rc.c | 206 --------- rc.h | 36 -- sort-dctrl.1.cp | 129 ------ sort-dctrl.c | 177 -------- sort-dctrl/sort-dctrl.c | 177 ++++++++ sorter.c | 85 ---- sorter.h | 77 ---- strutil.c | 157 ------- strutil.h | 54 --- sync-available.8 | 60 --- sync-available.cp | 54 --- sync-available/sync-available.cp | 54 +++ tbl-dctrl.1 | 203 --------- tbl-dctrl.c | 426 ------------------- tbl-dctrl/tbl-dctrl.c | 426 +++++++++++++++++++ util.c | 50 --- util.h | 27 -- version.c | 158 ------- version.h | 62 --- 88 files changed, 5735 insertions(+), 5727 deletions(-) delete mode 100644 align.h delete mode 100644 fieldtrie.c delete mode 100644 fieldtrie.h delete mode 100644 fnutil.c delete mode 100644 fnutil.h delete mode 100644 fsaf.c delete mode 100644 fsaf.h delete mode 100644 getaline.c delete mode 100644 getaline.h delete mode 100644 grep-dctrl.1.cp delete mode 100644 grep-dctrl.c delete mode 100644 grep-dctrl.rc create mode 100644 grep-dctrl/grep-dctrl.c create mode 100644 grep-dctrl/grep-dctrl.rc create mode 100644 grep-dctrl/rc.c create mode 100644 grep-dctrl/rc.h delete mode 100644 i18n.h delete mode 100644 ifile.c delete mode 100644 ifile.h create mode 100644 lib/align.h create mode 100644 lib/fieldtrie.c create mode 100644 lib/fieldtrie.h create mode 100644 lib/fnutil.c create mode 100644 lib/fnutil.h create mode 100644 lib/fsaf.c create mode 100644 lib/fsaf.h create mode 100644 lib/getaline.c create mode 100644 lib/getaline.h create mode 100644 lib/i18n.h create mode 100644 lib/ifile.c create mode 100644 lib/ifile.h create mode 100644 lib/misc.c create mode 100644 lib/misc.h create mode 100644 lib/msg.c create mode 100644 lib/msg.h create mode 100644 lib/para_bundle.c create mode 100644 lib/para_bundle.h create mode 100644 lib/para_pool.c create mode 100644 lib/para_pool.h create mode 100644 lib/paragraph.c create mode 100644 lib/paragraph.h create mode 100644 lib/predicate.c create mode 100644 lib/predicate.h create mode 100644 lib/sorter.c create mode 100644 lib/sorter.h create mode 100644 lib/strutil.c create mode 100644 lib/strutil.h create mode 100644 lib/util.c create mode 100644 lib/util.h create mode 100644 lib/version.c create mode 100644 lib/version.h create mode 100644 man/grep-dctrl.1.cp create mode 100644 man/sort-dctrl.1.cp create mode 100644 man/sync-available.8 create mode 100644 man/tbl-dctrl.1 delete mode 100644 misc.c delete mode 100644 misc.h delete mode 100644 msg.c delete mode 100644 msg.h delete mode 100644 para_bundle.c delete mode 100644 para_bundle.h delete mode 100644 para_pool.c delete mode 100644 para_pool.h delete mode 100644 paragraph.c delete mode 100644 paragraph.h delete mode 100644 predicate.c delete mode 100644 predicate.h delete mode 100644 rc.c delete mode 100644 rc.h delete mode 100644 sort-dctrl.1.cp delete mode 100644 sort-dctrl.c create mode 100644 sort-dctrl/sort-dctrl.c delete mode 100644 sorter.c delete mode 100644 sorter.h delete mode 100644 strutil.c delete mode 100644 strutil.h delete mode 100644 sync-available.8 delete mode 100644 sync-available.cp create mode 100644 sync-available/sync-available.cp delete mode 100644 tbl-dctrl.1 delete mode 100644 tbl-dctrl.c create mode 100644 tbl-dctrl/tbl-dctrl.c delete mode 100644 util.c delete mode 100644 util.h delete mode 100644 version.c delete mode 100644 version.h diff --git a/Makefile b/Makefile index 2cf6371..f0c255e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ localedir = /usr/share/locale version := $(shell dpkg-parsechangelog | grep '^Version' | cut -b10-) CC = gcc -std=gnu99 -CFLAGS = -g -Wall -DENABLE_L_DEBUG -D_GNU_SOURCE -DSYSCONF=\"$(sysconfdir)\" \ +CFLAGS = -g -Wall -Ilib \ + -DENABLE_L_DEBUG -D_GNU_SOURCE -DSYSCONF=\"$(sysconfdir)\" \ -DHAVE_GETTEXT -DPACKAGE=\"dctrl-tools\" -DLOCALEDIR=\"$(localedir)\" CFLAGS += -DVERSION=\"$(version)\" @@ -15,12 +16,17 @@ CFLAGS += -DMAINTAINER='"$(shell grep ^Maintainer: debian/control | cut -b13-)"' #CFLAGS += -pg #LDFLAGS += -pg -libobj = misc.o msg.o predicate.o util.o fsaf.o paragraph.o \ - fieldtrie.o rc.o strutil.o getaline.o fnutil.o para_pool.o \ - ifile.o para_bundle.o sorter.o version.o +libsrc = $(wildcard lib/*.c) +libobj = $(libsrc:.c=.o) -obj = $(libobj) grep-dctrl.o sort-dctrl.o tbl-dctrl.o -src = $(obj:.o=.c) +src = $(libsrc) \ + $(wildcard grep-dctrl/*.c) \ + $(wildcard sort-dctrl/*.c) \ + $(wildcard tbl-dctrl/*.c) + +exe = grep-dctrl/grep-dctrl \ + sort-dctrl/sort-dctrl \ + tbl-dctrl/tbl-dctrl LDLIBS = -L. -ldctrl @@ -29,8 +35,10 @@ include langs.mk all : all-no-mo mo -all-no-mo : grep-dctrl sort-dctrl tbl-dctrl sync-available \ - grep-dctrl.1 sort-dctrl.1 +all-no-mo : sync-available/sync-available \ + man/grep-dctrl.1 \ + man/sort-dctrl.1 \ + $(exe) pot : po/dctrl-tools.pot @@ -38,14 +46,14 @@ po : $(foreach f,$(langs),po/$(f).po) mo : $(foreach f,$(langs),po/$(f).mo) -grep-dctrl : grep-dctrl.o libdctrl.a +grep-dctrl/grep-dctrl : grep-dctrl/grep-dctrl.o grep-dctrl/rc.o libdctrl.a -sort-dctrl : sort-dctrl.o libdctrl.a +sort-dctrl/sort-dctrl : sort-dctrl/sort-dctrl.o libdctrl.a -tbl-dctrl : tbl-dctrl.o libdctrl.a +tbl-dctrl/tbl-dctrl : tbl-dctrl/tbl-dctrl.o libdctrl.a % : %.o - $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) %.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ @@ -71,7 +79,7 @@ so/%.o : %.c %.1 : %.1.cp sed 's*SYSCONF*$(sysconf)*' $< > $@ -sync-available : sync-available.cp +sync-available/sync-available : sync-available/sync-available.cp sed 's*VERSION*$(version)*' $< > $@ chmod 755 $@ @@ -91,7 +99,7 @@ po/dctrl-tools.pot : $(src) fsaf.test : fsaf.test.o msg.o clean : - $(RM) core grep-dctrl grep-dctrl.1 *.o so/*.o libdctrl.a libdctrl.so + $(RM) core $(exe) grep-dctrl.1 $(obj) so/*.o libdctrl.a libdctrl.so $(RM) po/*.mo TAGS *.d $(RM) sync-available diff --git a/align.h b/align.h deleted file mode 100644 index 4acaa1b..0000000 --- a/align.h +++ /dev/null @@ -1,52 +0,0 @@ -/* 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 -#include -#include - -#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/debian/rules b/debian/rules index 7f114d2..8f77b2a 100644 --- a/debian/rules +++ b/debian/rules @@ -46,27 +46,27 @@ debian/stamp/binary/dctrl-tools: debian/stamp/build # mandir=$(mandir) install # Install configuration file $(install_dir) $(etcdir) - $(install_nonex) grep-dctrl.rc $(etcdir) + $(install_nonex) grep-dctrl/grep-dctrl.rc $(etcdir) # Install the binaries. $(install_dir) $(sbindir) - $(install_script) sync-available $(sbindir) + $(install_script) sync-available/sync-available $(sbindir) $(install_dir) $(bindir) - $(install_exec) tbl-dctrl $(bindir) - $(install_exec) sort-dctrl $(bindir) - $(install_exec) grep-dctrl $(bindir) + $(install_exec) tbl-dctrl/tbl-dctrl $(bindir) + $(install_exec) sort-dctrl/sort-dctrl $(bindir) + $(install_exec) grep-dctrl/grep-dctrl $(bindir) set -e ; for dest in $(aliases) ; do \ $(install_symlink) grep-dctrl $(bindir)/$$dest ; \ done # Install and zip the manual pages. $(install_dir) $(man8dir) - $(install_nonex) sync-available.8 $(man8dir)/ + $(install_nonex) man/sync-available.8 $(man8dir)/ $(gzip) $(man8dir)/sync-available.8 $(install_dir) $(man1dir) - $(install_nonex) sort-dctrl.1 $(man1dir)/ + $(install_nonex) man/sort-dctrl.1 $(man1dir)/ $(gzip) $(man1dir)/sort-dctrl.1 - $(install_nonex) tbl-dctrl.1 $(man1dir)/ + $(install_nonex) man/tbl-dctrl.1 $(man1dir)/ $(gzip) $(man1dir)/tbl-dctrl.1 - $(install_nonex) grep-dctrl.1 $(man1dir)/ + $(install_nonex) man/grep-dctrl.1 $(man1dir)/ $(gzip) $(man1dir)/grep-dctrl.1 set -e ; for dest in $(aliases) ; do \ $(install_symlink) grep-dctrl.1.gz $(man1dir)/$$dest.1.gz ; \ diff --git a/fieldtrie.c b/fieldtrie.c deleted file mode 100644 index 250fc89..0000000 --- a/fieldtrie.c +++ /dev/null @@ -1,98 +0,0 @@ -/* 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 -#include -#include -#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/fieldtrie.h b/fieldtrie.h deleted file mode 100644 index 0e1c634..0000000 --- a/fieldtrie.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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 -#include -#include - -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/fnutil.c b/fnutil.c deleted file mode 100644 index 1ec5ca4..0000000 --- a/fnutil.c +++ /dev/null @@ -1,123 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include - -#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/fnutil.h b/fnutil.h deleted file mode 100644 index c5e38f8..0000000 --- a/fnutil.h +++ /dev/null @@ -1,25 +0,0 @@ -/* 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/fsaf.c b/fsaf.c deleted file mode 100644 index 07a4979..0000000 --- a/fsaf.c +++ /dev/null @@ -1,260 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#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 -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/fsaf.h b/fsaf.h deleted file mode 100644 index f95cf54..0000000 --- a/fsaf.h +++ /dev/null @@ -1,101 +0,0 @@ -/* 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 -#include -#include -#include - -/* 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/getaline.c b/getaline.c deleted file mode 100644 index df02498..0000000 --- a/getaline.c +++ /dev/null @@ -1,113 +0,0 @@ -/* 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 -#include -#include -#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/getaline.h b/getaline.h deleted file mode 100644 index e7f60d1..0000000 --- a/getaline.h +++ /dev/null @@ -1,26 +0,0 @@ -/* 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 - -char * getaline (FILE * f); - -#endif /* GETALINE_H */ diff --git a/grep-dctrl.1.cp b/grep-dctrl.1.cp deleted file mode 100644 index bfa50c2..0000000 --- a/grep-dctrl.1.cp +++ /dev/null @@ -1,506 +0,0 @@ -.TH GREP-DCTRL 1 2006-04-02 "Debian Project" "Debian user's manual" -\" Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 -\" 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. -.SH NAME -grep\-dctrl, grep\-status, grep\-available, grep\-aptavail \- grep Debian control files -.SH SYNOPSIS -.I command ---copying|-C | --help|-h | --version|-V -.sp -.I command -[options] predicate [ -.IR file "..." -] -.sp -where -.I command -is one of -.BR grep\-dctrl , -.BR grep\-status , -.B grep\-available -and -.BR grep\-aptavail . -.SH DESCRIPTION -The grep\-dctrl program can answer such questions as -.IR "What is the Debian package foo?" , -.IR "Which version of the Debian package bar is now current?" , -.IR "Which Debian packages does John Doe maintain?" , -.I "Which Debian packages are somehow related to the Scheme" -.IR " programming language?" , -and with some help, -.IR "Who maintain the essential packages of a Debian system?" , -given a useful input file. -.PP -The programs -.BR grep\-available, -.B grep\-status -and -.B grep\-aptavail -are aliases of (actually, symbolic links to) -.BR grep\-dctrl . -In the shipped configuration, these aliases use as their default input -the -.BR dpkg (8) -available and status files and the apt\-cache dumpavail output, -respectively. -.PP -.B grep\-dctrl -is a specialised grep program that is meant for processing any file -which has the general format of a Debian package control file, as -described in the Debian Packaging Manual. These include the dpkg -available file, the dpkg status file, and the Packages files on a -distribution medium (such as a Debian CD-ROM or an FTP site carrying -Debian). -.PP -You must give a filter expression on the command line. The filter -defines which kind of paragraphs (aka package records) are output. A -simple filter is a search pattern along with any options that modify -it. Possible modifiers are \-\-eregex, \-\-field, \-\-ignore\-case, -\-\-regex and \-\-exact\-match, along with their single-letter -equivalents. By -default, the search is a case-sensitive fixed substring match on each -paragraph (in other words, package record) in the input. With -suitable modifiers, this can be changed: the search can be -case-insensitive and the pattern can be seen as an extended POSIX -regular expression. -.PP -Filters can be combined to form more complex filters using the -connectives \-\-and, \-\-or and \-\-not. Parentheses (which usually -need to be escaped for the shell) can be used for grouping. -.PP -By default, the full matching paragraphs are printed on the standard -output; specific fields can be selected for output with the \-s option. -.PP -After the filter expression comes zero or more file names. If no file names -are specified, the file name is searched in configuration files. The -input file from the first program name \- input file association with -the correct program name is used. The program names are matched with -the base form of the name of the current program (the 0'th command -line argument, if you will). The file name "\-" is taken to mean the -standard input stream. The files are searched in order but -separately; they are -.B not -concatenated together. In other words, the end of a file always -implies the end of the current paragraph. -.PP -There is one exception to the above: if the program name is -.BR grep\-dctrl , -the default input source is always standard input; this cannot be -overridden by the configuration file. -.SH OPTIONS -.SS Specifying the search pattern -.IP "\-\-pattern=PATTERN" -Specify a pattern to be searched. This switch is not generally needed, as -the pattern can be given by itself. However, patterns that start -with a dash must be given using this switch, so that they wouldn't -be mistaken for switches. -.SS Modifiers of simple filters -.IP "\-F FIELD,FIELD,...; \-\-field=FIELD,FIELD,..." -Restrict pattern matching to the FIELDs given. Multiple field names -in one \-F option and multiple \-F options in one simple filter are -allowed. The search named by the filter will be performed -among all the fields named, and as soon as any one of them matches, the -whole simple filter is considered matching. -.PP -A FIELD specification can contain a colon. In such a case, the part -up to the colon is taken as the name of the field to be searched in, -and the part after the colon is taken as the name of the field whose -content is to be used if the field to search in is empty. -.IP \-P -Shorthand for "\-FPackage". -.IP \-S -Shorthand for "\-FSource:Package". -.IP "\-e, \-\-eregex" -Regard the pattern of the current simple filter as an extended -POSIX regular expression -.IP "\-r, \-\-regex" -The pattern of the current simple filter is a standard POSIX regular expression. -.IP "\-i, \-\-ignore\-case" -Ignore case when looking for a match in the current simple filter. -.IP "\-X, \-\-exact\-match" -Do an exact match (as opposed to a substring match) in the current -simple filter. -.IP "\-\-eq" -Do an equality comparison under the Debian version number system. If -the pattern or the field to be searched in is not a valid Debian -version number, the paragraph is regarded as not matching. As a -special case, this is capable of comparing simple nonnegative integers -for equality. -.IP "\-\-lt" -Do an strictly-less-than comparison under the Debian version number -system. If the pattern or the field to be searched in is not a valid -Debian version number, the paragraph is regarded as not matching. As -a special case, this is capable of comparing simple nonnegative -integers. -.IP "\-\-le" -Do an less-than-or-equal comparison under the Debian version number -system. If the pattern or the field to be searched in is not a valid -Debian version number, the paragraph is regarded as not matching. As -a special case, this is capable of comparing simple nonnegative -integers. -.IP "\-\-gt" -Do an strictly-greater-than comparison under the Debian version number -system. If the pattern or the field to be searched in is not a valid -Debian version number, the paragraph is regarded as not matching. As -a special case, this is capable of comparing simple nonnegative -integers. -.IP "\-\-ge" -Do an greater-than-or-equal comparison under the Debian version number -system. If the pattern or the field to be searched in is not a valid -Debian version number, the paragraph is regarded as not matching. As -a special case, this is capable of comparing simple nonnegative -integers. -.SS Combining filters -.IP "\-!, \-\-not, !" -Match if the following filter does -.I not -match. -.IP "\-o, \-\-or" -Match if either one or both of the preceding and following filters -matches. -.IP "\-a, \-\-and" -Match if both the preceding and the following filter match. -.IP "( ... )" -Parentheses can be used for grouping. Note that they need to be -escaped for most shells. -.SS Output format modifiers -.IP "\-s FIELD,FIELD,...; \-\-show\-field=FIELD,FIELD,..." -Show only the body of these fields from the matching paragraphs. The -field names must not include any colons or commas. Commas are used to -delimit field names in the argument to this option. The fields are -shown in the order given here. -.PP -A FIELD specification can contain a colon. In such a case, the part -up to the colon is taken as the name of the field to be shown, and the -part after the colon is taken as the name of the field whose content -is to be used if the field to be shown is empty. -.IP "\-d" -Show only the first line of the Description field from the matching -paragraphs. If no "\-s" option is specified, this option also effects -"\-s Description"; if there is a "\-s" option but it does not include -the Description field name, one is appended to the option. Thus the -Description field's location in the output is determined by the "\-s" -option, if any, the last field being the default. -.IP "\-n, \-\-no\-field\-names" -Suppress field names when showing specified fields, only their bodies -are shown. Each field is printed in its original form without the -field name, the colon after it and any whitespace preceding the start -of the body. -.IP "\-v, \-\-invert\-match" -Instead of showing all the paragraphs that match, show those paragraphs -that do -.I not -match. -.IP "\-c, \-\-count" -Instead of showing the paragraphs that match (or, with \-v, that don't -match), show the count of those paragraphs. -.IP "\-q, \-\-quiet, \-\-silent" -Output nothing to the standard output stream. Instead, exit -immediately after finding the first match. -.SS Miscellaneous -.IP "\-\-ignore\-parse\-errors" -Ignore errors in parsing input. A paragraph which cannot be parsed -is ignored in its entirety, and the next paragraph is assumed to start -after the first newline since the location of the error. -.IP "\-\-config\-file=FNAME" -Use FNAME as the config file instead of the defaults. -.IP "\-\-debug\-optparse" -Show how the current command line has been parsed. -.IP "\-l LEVEL, \-\-errorlevel=LEVEL" -Set debugging level to LEVEL. LEVEL is one of "fatal", "important", -"informational" and "debug", but the last may not be available, -depending on the compile-time options. These categories are given -here in order; every message that is emitted when "fatal" is in -effect, will be emitted in the "important" error level, and so on. -The default is "important". -.IP "\-V, \-\-version" -Print out version information. -.IP "\-C, \-\-copying" -Print out the copyright license. This produces much output; be sure -to redirect or pipe it somewhere (such as your favourite pager). -.IP "\-h, \-\-help" -Print out a help summary. -.SH EXAMPLES -The following example queries assume that the default configuration is -in effect. -.PP -The almost simplest use of this program is to print out the status or -available record of a package. In this respect, -.B grep\-dctrl -is like -.B "dpkg \-s" -or -.BR "dpkg \-\-print\-avail". -To print out the status record of the package "mixal", do -.nf -% grep\-status \-PX mixal -.fi -and to get its available record, use -.nf -% grep\-available \-PX mixal -.fi -In fact, you can ask for the record of the "mixal" package -from any Debian control file. Say, you have the Debian 2.2 -CD-ROM's Packages file in the current directory; now you -can do a -.nf -% grep\-dctrl \-PX mixal Packages -.fi -.PP -But -.B grep\-dctrl -can do more than just emulate -.BR dpkg . -It can more-or-less emulate -.BR apt\-cache ! -That program has a search feature that searches package descriptions. -But we can do that too: -.nf -% grep\-available \-F Description foo -.fi -searches for the string "foo" case-sensitively in the descriptions of -all available packages. If you want case-insensitivity, use -.nf -% grep\-available \-F Description \-i foo -.fi -Truth to be told, -.B apt\-cache -searches package names, too. We can separately search in the names; -to do so, do -.nf -% grep\-available \-F Package foo -.fi -or -.nf -% grep\-available \-P foo -.fi -which is pretty much the same thing. We can also search in both -descriptions and names; if match is found in either, the package -record is printed: -.nf -% grep\-available \-P \-F Description foo -.fi -or -.nf -% grep\-available \-F Package \-F Description foo -.fi -This kind of search is the exactly same that -.B apt\-cache -does. -.PP -Here's one thing neither -.B dpkg -nor -.B apt\-cache -do. Search for a string in the whole status or available file (or -any Debian control file, for that matter) and print out all package -records where we have a match. Try -.nf -% grep\-available dpkg -.fi -sometime and watch how thoroughly -.B dpkg -has infiltrated Debian. -.PP -All the above queries were based on simple substring searches. -But -.B grep\-dctrl -can handle regular expressions in the search pattern. For example, -to see the status records of all packages with either "apt" or -"dpkg" in their names, use -.nf -% grep\-status \-P \-e 'apt|dpkg' -.fi -.PP -Now that we have seen all these fine and dandy queries, you might -begin to wonder whether it is necessary to always see the whole -paragraph. You may be, for example, interest only in the dependency -information of the packages involved. Fine. To show the depends -lines of all packages maintained by me, do a -.nf -% grep\-available \-F Maintainer \-s Depends 'ajk@debian.org' -.fi -If you want to see the packages' names, too, use -.nf -% grep\-available \-F Maintainer \-s Package,Depends \\ - 'ajk@debian.org' -.fi -Note that there must be no spaces in the argument to the \-s switch. -.PP -More complex queries are also possible. For example, to see the list of packages -maintained by me and depending on libc6, do -.nf -% grep\-available \-F Maintainer 'ajk@debian.org' \\ - \-a \-F Depends libc6 \-s Package,Depends -.fi -Remember that you can use other Unix filters to help you, too. Ever -wondered, who's the most active Debian developer based on the number -of source packages being maintained? Easy. You just need to have a -copy of the most recent Sources file from any Debian mirror. -.nf -% grep\-dctrl \-n \-s Maintainer '' Sources | sort | - uniq \-c | sort \-nr -.fi -This example shows a neat trick: if you want to selectively -show only some field of -.I all -packages, just supply an empty pattern. -.PP -The term "bogopackage" means the count of the packages that a Debian -developer maintains. To get the bogopackage count for the maintainer -of -.BR grep\-dctrl , -say -.nf -% grep\-available \-c \-FMaintainer \\ - "`grep\-available \-sMaintainer \-n \-PX grep\-dctrl`" -.fi -.PP -Sometimes it is useful to output the data of several fields on the -same line. For example, the following command outputs the list of -installed packages, sorted by their Installed\-Size. -.nf -% grep\-status \-FStatus \-sInstalled\-Size,Package \-n \\ - "install ok installed" | paste \-sd " \\n" | sort \-n -.fi -Note that there should be exactly 2 spaces in the " \\n" string. -.PP -These examples cover a lot of typical uses of this utility, but not -all possible uses. Use your imagination! The building blocks are -there, and if something's missing, let me know. -.SH DIAGNOSTICS -In the absence of errors, the exit code 0 is used if at least one -match was found, and the exit code 1 is used if no matches were found. -If there were errors, the exit code is 2, with one exception. If the -\-q, \-\-quiet or \-\-silent options are used, the exit code 0 is used when -a match is found regardless of whether there have been non-fatal -errors. -.PP -These messages are emitted in log levels "fatal" and "important". -Additional messages may be provided by the system libraries. -.B This list is out of date. -.IP "a pattern is mandatory" -You must specify a pattern to be searched for. -.IP "a predicate is required" -No predicate was specified, but one is required. -.IP "cannot find enough memory" -More memory was needed than was available. This error may be -transient, that is, if you try again, all may go well. -.IP "cannot suppress field names when showing whole paragraphs" -When you do not use the \-s switch, -.B grep\-dctrl -just passes the matching paragraphs through, not touching them any -way. This means, for example, that you can only use \-n when you use -\-s. -.IP "inconsistent atom modifiers" -Conflicting atom modifiers were used; for example, perhaps both \-X and -\-e were specified for the same atom. -.IP "missing ')' in command line" -There were more opening than closing parentheses in the given -predicate. -.IP "no such log level" -The argument to \-l was invalid. -.IP "predicate is too complex" -The predicate's complexity (the number of atoms and connectives) -exceed compile-time limits. -.IP "read failure or out of memory" -There was a problem reading the configuration file. Maybe there was a -transput error; maybe memory was exhausted. This error may be -transient, that is, if you try again, all may go well. -.IP "syntax error in command line" -There is a problem in the command line. Look, and you shall find it. -.IP "syntax error: need a executable name" -There is a problem in the configuration file. Look, and you shall find it. -.IP "syntax error: need an input file name" -There is a problem in the configuration file. Look, and you shall find it. -.IP "too many file names" -The number of file names specified in the command line exceeded a -compile-time limit. -.IP "too many output fields" -The argument to \-s had too many field names in it. This number is -limited to 256. -.IP "unexpected ')' in command line" -There was no opening parenthesis that would match some closing -parenthesis in the command line. -.IP "unexpected end of file" -The input file is broken: it ends before it should. -.IP "unexpected end of line" -The input file is broken: a line ends before it should. -.IP "Unexpected atom in command line. Did you forget to use a connective?" -There was an atom on the command line where there should not be any. -The most likely reason is that an atom modifier option (such as \-F) -follows directly after a closing parenthesis. Adding a connective -(\-\-and, \-\-or) between the parenthesis and the option is often the -correct solution. -.SH COMPATIBILITY -If you use grep\-dctrl in a Debian package, you should depend on the -grep\-dctrl package and heed the following compatibility notes: -.PP -.IP "Always call only the \fBgrep\-dctrl\fR executable." -Although the \fBgrep\-status\fR and \fBgrep\-available\fR symlinks are installed -by default, this may change in the future. Those symlinks are meant -for manual and not scripted use. -.IP "Always specify an explicit file name" -Don't rely on the implicit file name feature. The system -administrator may have changed the default file name. You should -always specify the "\-" file, too. -.IP "Not all features have been with us in every version" -Check if any of the features you use is mentioned in the changelog. -Use a versioned dependency on grep\-dctrl, if it is necessary. -.SH FILES -.IP SYSCONF/grep\-dctrl.rc -See the next file. -.IP ~/.grep\-dctrlrc -These files are the default configuration files for -.BR grep\-dctrl . -The format is line-based, with `#' introducing a comment that lasts to -the end of the line. Each line defines one association between a -program name and a default input file. These two things are listed in -the line in order, separated by whitespace. Empty lines are ignored. -.sp -If the default input file name starts with two at (@) signs, one of -them is ignored. This allows specifying a file name that starts with -an at sign. If it starts with the string "@exec", the rest of the -name is interpreted as a command name that is fed to -.BR /bin/sh " " \-c , -and the standard output stream is used as the default input. -.IP /var/lib/dpkg/available -The default input file of -.B grep\-available -when the shipped configuration is in effect. -.IP /var/lib/dpkg/status -The default input file of -.B grep\-status -when the shipped configuration is in effect. -.SH AUTHOR -The program and this manual page were written by Antti-Juhani -Kaijanaho . Bill Allombert -provided one of the examples in the manual page. -.SH "SEE ALSO" -Ian Jackson et al.: Debian Packaging Manual. Published as the Debian -package packaging\-manual. Also available in the Debian website. The -Debian project, 2003. -.PP -.BR ara (1), -.BR apt\-cache (1), -.BR dpkg (8), -.BR dpkg\-awk (1), -.BR sgrep (1) -\" Local variables: -\" mode: nroff -\" End: - diff --git a/grep-dctrl.c b/grep-dctrl.c deleted file mode 100644 index 687f8cf..0000000 --- a/grep-dctrl.c +++ /dev/null @@ -1,875 +0,0 @@ -/* dctrl-tools - Debian control file inspection tools - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fnutil.h" -#include "fsaf.h" -#include "i18n.h" -#include "misc.h" -#include "msg.h" -#include "paragraph.h" -#include "predicate.h" -#include "rc.h" -#include "util.h" - -const char * argp_program_version = "grep-dctrl (dctrl-tools) " VERSION; -const char * argp_program_bug_address = MAINTAINER; - -const char description [] = "Description"; -size_t description_inx; - -static char progdoc [] = N_("grep-dctrl -- grep Debian control files"); - -static char argsdoc [] = "PREDICATE [FILENAME...]"; - -enum { - OPT_CONFIG=256, - OPT_OPTPARSE, - OPT_SILENT, - OPT_EQ, - OPT_LT, - OPT_LE, - OPT_GT, - OPT_GE, - OPT_MMAP, - OPT_IGN_ERRS, - OPT_PATTERN -}; - -#undef BANNER - -#ifdef BANNER -void banner(bool automatic) -{ - char * fname = fnqualify_xalloc("~/.grep-dctrl-banner-shown"); - struct stat st; - if (automatic) { - int r = stat(fname, &st); - if (r == 0) goto end; - } - FILE * fp = fopen("/dev/tty", "w"); - if (fp == 0) { - perror("/dev/tty"); - goto end; - } - fprintf(fp, - "==========================================================================\n" - " NOTE \n" - " grep-dctrl has been rewritten from scratch. Although this does add new \n" - " features, regressions are certainly possible. Please watch for them and \n" - " report them to the BTS. \n" - "==========================================================================\n" - "(The above annoying banner will not be shown to you again, unless you\n" - "request it with the -B switch. It will also be removed entirely soon.)\n"); - - int r = creat(fname, 0644); - if (r == -1) perror(fname); - - if (!automatic) exit(0); - - for (int i = 15; i > 0; i--) { - fprintf(fp, "%2d seconds until program is resumed...\r", i); - fflush(fp); - sleep(1); - } - fprintf(fp, " \r"); - fflush(fp); -end: - free(fname); -} -#endif - -static struct argp_option options[] = { -#ifdef BANNER - { "banner", 'B', 0, 0, N_("Show the testing banner.") }, -#endif - { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, - { "field", 'F', N_("FIELD,FIELD,..."), 0, N_("Restrict pattern matching to the FIELDs given.") }, - { 0, 'P', 0, 0, N_("This is a shorthand for -FPackage.") }, - { 0, 'S', 0, 0, N_("This is a shorthand for -FSource:Package.") }, - { "show-field", 's', N_("FIELD,FIELD,..."), 0, N_("Show only the body of these fields from the matching paragraphs.") }, - { 0, 'd', 0, 0, N_("Show only the first line of the \"Description\" field from the matching paragraphs.") }, - { "no-field-names", 'n', 0, 0, N_("Suppress field names when showing specified fields.") }, - { "eregex", 'e', 0, 0, N_("Regard the pattern as an extended POSIX regular expression.") }, - { "regex", 'r', 0, 0, N_("The pattern is a standard POSIX regular expression.") }, - { "ignore-case", 'i', 0, 0, N_("Ignore case when looking for a match.") }, - { "invert-match", 'v', 0, 0, N_("Show only paragraphs that do not match.") }, - { "count", 'c', 0, 0, N_("Show only the count of matching paragraphs.") }, - { "config-file", OPT_CONFIG, N_("FNAME"),0, N_("Use FNAME as the config file.") }, - { "exact-match", 'X', 0, 0, N_("Do an exact match.") }, - { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, - { "and", 'a', 0, 0, N_("Conjunct predicates.") }, - { "or", 'o', 0, 0, N_("Disjunct predicates.") }, - { "not", '!', 0, 0, N_("Negate the following predicate.") }, - { "eq", OPT_EQ, 0, 0, N_("Test for version number equality.") }, - { "lt", OPT_LT, 0, 0, N_("Version number comparison: <.") }, - { "le", OPT_LE, 0, 0, N_("Version number comparison: <=.") }, - { "gt", OPT_GT, 0, 0, N_("Version number comparison: >.") }, - { "ge", OPT_GE, 0, 0, N_("Version number comparison: >=.") }, - { "debug-optparse", OPT_OPTPARSE, 0, 0, N_("Debug option parsing.") }, - { "quiet", 'q', 0, 0, N_("Do no output to stdout.") }, - { "silent", OPT_SILENT, 0, 0, N_("Do no output to stdout.") }, - { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, - { "ignore-parse-errors", OPT_IGN_ERRS, 0, 0, N_("Ignore parse errors") }, - { "pattern", OPT_PATTERN, N_("PATTERN"), 0, N_("Specify the pattern to search for") }, - { 0 } -}; - - -// Tokens -#define TOK_EOD 0 -#define TOK_NOT 1 -#define TOK_AND 2 -#define TOK_OR 3 -#define TOK_LP 4 /* left paren */ -#define TOK_RP 5 /* right paren */ -#define TOK_ATOM_BASE 6 /* All tokens >= TOK_ATOM_BASE are atoms; the - difference is the atom index. */ - -#define MAX_FNAMES 4096 -#define MAX_TOKS 16384 - -static int debug_optparse = 0; - -struct arguments { - /* Parser state flag: last token seen was ')' */ - bool just_seen_cparen; - /* Top of the parser stack. */ - size_t top; - /* Number of file names seen. */ - size_t num_fnames; - /**/ - size_t num_show_fields; - /**/ - size_t num_search_fields; - /* A machine-readable representation of the predicate. */ - struct predicate p; - /* Configuration file name */ - char const * rcname; - /* Ignore parse errors? */ - bool ignore_errors; - /* Quiet operation? */ - bool quiet; - /* Do show field names? */ - bool show_field_name; - /* Do show (only) first line of Description? */ - bool short_descr; - /* Does show_fields contain Description? */ - bool description_selected; - /* Count matching paragraphs? */ - bool count; - /* Invert match? */ - bool invert_match; - /* First unused position in toks. */ - size_t toks_np; - /* Token read position. */ - size_t toks_pos; - /* Finished with the predicate scanning? */ - bool finished; - /* Are we inside an atom? */ - bool in_atom; - /* Pattern error? */ - bool pattern_error; - /* Token stream for the predicate parser. */ - int toks[MAX_TOKS]; - /* For each atom, give code with which it can be accessed. */ - struct atom_code { - size_t n; - int * routine; - } *atom_code[MAX_ATOMS]; - /* File names seen on the command line. */ - struct ifile fname[MAX_FNAMES]; - /**/ - struct show_fields { - char const * name; - size_t inx; - size_t repl; // the field to use if this is empty - } show_fields[MAX_FIELDS]; - /* Search field names seen during current atom. */ - char * search_fields[MAX_FIELDS]; -}; - -struct atom * clone_atom(struct arguments * args) -{ - if (args->p.num_atoms >= MAX_ATOMS) { - message(L_FATAL, _("predicate is too complex"), 0); - fail(); - } - int oa = args->p.num_atoms-1; - struct atom * atom = get_current_atom(&args->p); - int na = args->p.num_atoms; - struct atom * rv = &args->p.atoms[args->p.num_atoms++]; - rv->field_name = atom->field_name; - rv->field_inx = atom->field_inx; - rv->mode = atom->mode; - rv->ignore_case = atom->ignore_case; - rv->pat = atom->pat; - rv->patlen = atom->patlen; - struct atom_code * ac = args->atom_code[oa]; - args->atom_code[na] = ac; - assert(ac->n > 0); - ac->n += 2; - ac->routine = realloc(ac->routine, ac->n * sizeof *ac->routine); - if (ac->routine == 0) fatal_enomem(0); - ac->routine[ac->n-2] = I_PUSH(na); - ac->routine[ac->n-1] = I_OR; - return rv; -} - -static void finish_atom(struct arguments * args) -{ - assert(args->in_atom); - args->in_atom = false; - struct atom * atom = get_current_atom(&args->p); - if (atom->pat == 0) { - args->pattern_error = true; - return; - } - for (size_t i = 0; i < args->num_search_fields; i++) { - if (i > 0) atom = clone_atom(args); - atom->field_name = args->search_fields[i]; - predicate_finish_atom(&args->p); - } - // If there are no fields, we have not yet run this... - // ... but it must be done (especially with -r/-e atoms) - if (args->num_search_fields == 0) predicate_finish_atom(&args->p); - args->num_search_fields = 0; -} - -#if 0 -/* Pop off one stack state, inserting the associated instructions to - * the predicate program. If paren is true, current state must be - * STATE_PAREN, and if paren is false, it must not be STATE_PAREN. */ -static void leave(struct arguments * args, int paren) -{ - debug_message("leaving...", 0); - assert(paren == (args->state == STATE_PAREN)); - if (args->state == STATE_ATOM) finish_atom(args); - assert(args->top > 0); - --args->top; - for (struct insn_node * it = args->stack[args->top].insns_first; - it != 0;) { - addinsn(&args->p, it->insn); - struct insn_node * next = it->next; - free(it); - it = next; - } - args->stack[args->top].insns_first = 0; - args->stack[args->top].insns_last = 0; - args->state = args->stack[args->top].state; -} -#endif - -#define APPTOK(tok) do { apptok(args, (tok)); } while (0) - -static void apptok(struct arguments * args, const int tok) -{ - debug_message("apptok", 0); - if (args->in_atom && tok < TOK_ATOM_BASE) { - finish_atom(args); - } - if (args->toks_np >= MAX_TOKS) { - message(L_FATAL, _("predicate is too long"), 0); - fail(); - } - args->toks[args->toks_np++] = tok; -} - -#define FINISH do { finish(args); } while (0) - -/* Flush the state stack. */ -static void finish(struct arguments * args) -{ - assert(!args->finished); - if (args->in_atom) finish_atom(args); - args->finished = true; -} - -#define ENTER_ATOM (enter_atom((args))) - -/* FIXME: UPDATE COMMENT -If necessary, enter STATE_ATOM and allocate a new atom, pushing - * along with the old state a PUSH instruction for the new atom to the - * parser stack. If we are already in STATE_ATOM, reuse the current - * atom. */ -static struct atom * enter_atom(struct arguments * args) -{ - struct atom * rv; - if (args->in_atom) { - assert(args->p.num_atoms > 0); - return &args->p.atoms[args->p.num_atoms-1]; - } - args->in_atom = true; - if (args->p.num_atoms >= MAX_ATOMS) { - message(L_FATAL, _("predicate is too complex"), 0); - fail(); - } - APPTOK(args->p.num_atoms + TOK_ATOM_BASE); - args->atom_code[args->p.num_atoms] = - malloc(sizeof *args->atom_code[args->p.num_atoms]); - if (args->atom_code[args->p.num_atoms] == 0) fatal_enomem(0); - args->atom_code[args->p.num_atoms]->n = 1; - args->atom_code[args->p.num_atoms]->routine = malloc(1 * sizeof(int)); - if (args->atom_code[args->p.num_atoms]->routine == 0) { - fatal_enomem(0); - } - args->atom_code[args->p.num_atoms]->routine[0] - = I_PUSH(args->p.num_atoms); - rv = &args->p.atoms[args->p.num_atoms++]; - rv->field_name = 0; - rv->field_inx = -1; - rv->mode = M_SUBSTR; - rv->ignore_case = 0; - rv->pat = 0; - rv->patlen = 0; - return rv; -} - -#define set_mode(nmode) do { \ - atom = ENTER_ATOM; \ - if (atom->mode != M_SUBSTR) { \ - message(L_FATAL, _("inconsistent atom modifiers"), 0); \ - fail(); \ - } \ - atom->mode = (nmode); \ -} while (0) - -static error_t parse_opt (int key, char * arg, struct argp_state * state) -{ - struct arguments * args = state->input; - bool just_seen_cparen = args->just_seen_cparen; - args->just_seen_cparen = false; - struct atom * atom; - debug_message("parse_opt", 0); -#ifdef INCLUDE_DEBUG_MSGS - if (do_msg(L_DEBUG)) { - fprintf(stderr, "%s: in_atom = %s\n", - get_progname(), - args->in_atom ? "true" : "false"); - } -#endif - switch (key) { - case 'C': - if (!to_stdout (COPYING)) fail(); - exit(0); -#ifdef BANNER - case 'B': - banner(false); -#endif - case 'v': - args->invert_match = true; - break; - case 'c': - args->count = true; - break; - case 'q': case OPT_SILENT: - debug_message("parse_opt: q", 0); - args->quiet = true; - break; - case 'n': - debug_message("parse_opt: n", 0); - args->show_field_name = false; - break; - case 'd': - args->short_descr = true; - break; - case 's': { - char * carg = strdup(arg); - if (carg == 0) fatal_enomem(0); - for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){ - struct show_fields * sf = - &args->show_fields[args->num_show_fields]; - sf->name = strdup(s); - if (sf->name == 0) fatal_enomem(0); - char * repl = strchr(sf->name, ':'); - if (repl != NULL) { - *repl = '\0'; - ++repl; - } - sf->inx = fieldtrie_insert(sf->name); - if (sf->inx == description_inx) { - args->description_selected = true; - } - sf->repl = repl == NULL - ? (size_t)(-1) - : fieldtrie_insert(repl); - ++args->num_show_fields; - } - free(carg); - } - break; - case 'l': { - int ll = str2loglevel(arg); - if (ll < 0) - { - message(L_FATAL, _("no such log level"), arg); - fail(); - } - set_loglevel(ll); - debug_message("parse_opt: l", 0); - } - break; - case '!': - debug_message("parse_opt: !", 0); - APPTOK(TOK_NOT); - break; - case 'a': - debug_message("parse_opt: a", 0); - APPTOK(TOK_AND); - break; - case 'o': - debug_message("parse_opt: o", 0); - APPTOK(I_OR); - break; - case 'S': - debug_message("parse_opt: S", 0); - arg = "Source:Package"; - goto case_F; - case 'P': - debug_message("parse_opt: P", 0); - arg = "Package"; - goto case_F; - case_F: - case 'F': { - debug_message("parse_opt: Fv", 0); - atom = ENTER_ATOM; - char * carg = strdup(arg); - if (carg == 0) fatal_enomem(0); - for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){ - char * tmp = strdup(s); - if (tmp == 0) fatal_enomem(0); - args->search_fields[args->num_search_fields++] = tmp; - } - free(carg); - } - break; - case 'X': - debug_message("parse_opt: X", 0); - set_mode(M_EXACT); - break; - case 'r': - debug_message("parse_opt: r", 0); - set_mode(M_REGEX); - break; - case 'e': - debug_message("parse_opt: e", 0); - set_mode(M_EREGEX); - break; - case OPT_EQ: - debug_message("parse_opt: eq", 0); - set_mode(M_VER_EQ); - break; - case OPT_LT: - debug_message("parse_opt: lt", 0); - set_mode(M_VER_LT); - break; - case OPT_LE: - debug_message("parse_opt: le", 0); - set_mode(M_VER_LE); - break; - case OPT_GT: - debug_message("parse_opt: gt", 0); - set_mode(M_VER_GT); - break; - case OPT_GE: - debug_message("parse_opt: ge", 0); - set_mode(M_VER_GE); - break; - case OPT_MMAP: - debug_message("parse_opt: mmap", 0); - fsaf_mmap = 1; - break; - case 'i': - debug_message("parse_opt: i", 0); - atom = ENTER_ATOM; - atom->ignore_case = 1; - break; - case OPT_OPTPARSE: - debug_message("parse_opt: optparse", 0); - debug_optparse = 1; - break; - case OPT_IGN_ERRS: - debug_message("parse_opt: ignore-parse-errors", 0); - args->ignore_errors = 1; - break; - case OPT_PATTERN: - debug_message("parse_opt: pattern", 0); - atom = ENTER_ATOM; - if (atom->pat != 0) { - message(L_FATAL, _("Multiple patterns for the same " - "atom are not allowed"), 0); - fail(); - } - atom->patlen = strlen(arg); - atom->pat = malloc(atom->patlen+1); - if (atom->pat == 0) fatal_enomem(0); - strcpy((char*)atom->pat, arg); - break; - case ARGP_KEY_ARG: - debug_message("parse_opt: argument", 0); - redo: - debug_message("!!!", 0); - if (strcmp(arg, "!") == 0) { - debug_message("parse_opt: !", 0); - APPTOK(TOK_NOT); - break; - } - if (strcmp(arg, "(") == 0) { - debug_message("parse_opt: (", 0); - APPTOK(TOK_LP); - break; - } - if (strcmp(arg, ")") == 0) { - debug_message("parse_opt: )", 0); - args->just_seen_cparen = true; - APPTOK(TOK_RP); - break; - } - if (args->finished) { - char const * s; - if (args->num_fnames >= MAX_FNAMES) { - message(L_FATAL, _("too many file names"), 0); - fail(); - } - s = strdup(arg); - if (s == 0) fatal_enomem(0); - args->fname[args->num_fnames++] = - (struct ifile){ .mode = m_read, .s = s }; - break; - } - if (just_seen_cparen) { FINISH; goto redo; } - if (strcmp(arg, "--") == 0) { FINISH; break; } - atom = ENTER_ATOM; - if (atom->pat != 0) { FINISH; goto redo; } - atom->patlen = strlen(arg); - atom->pat = malloc(atom->patlen+1); - if (atom->pat == 0) fatal_enomem(0); - strcpy((char*)atom->pat, arg); - break; - case ARGP_KEY_END: - debug_message("parse_opt: end", 0); - if (!args->finished) FINISH; - break; - case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: - case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: - debug_message("parse_opt: ignored", 0); - break; - case OPT_CONFIG: - debug_message("parse_opt: --config-file", 0); - args->rcname = strdup(arg); - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static void dump_args(struct arguments * args) -{ - size_t i; - assert(args->finished); - assert(args->top == 0); - printf("num_atoms = %zi\n", args->p.num_atoms); - for (i = 0; i < args->p.num_atoms; i++) { - printf("atoms[%zi].field_name = %s\n", i, args->p.atoms[i].field_name); - printf("atoms[%zi].mode = %i\n", i, args->p.atoms[i].mode); - printf("atoms[%zi].ignore_case = %i\n", i, args->p.atoms[i].ignore_case); - printf("atoms[%zi].pat = %s\n", i, args->p.atoms[i].pat); - } - printf("proglen = %zi\n", args->p.proglen); - for (i = 0; i < args->p.proglen; i++) { - int op = args->p.program[i]; - printf("program[%zi] = ", i); - switch (op) { - case I_NOP: puts("NOP"); break; - case I_NEG: puts("NEG"); break; - case I_AND: puts("AND"); break; - case I_OR: puts("OR"); break; - default: - printf("PUSH(%i)\n", op - I_PUSH(0)); - } - } - printf("num_fnames = %zi\n", args->num_fnames); - for (i = 0; i < args->num_fnames; i++) { - printf("fname[%zi].mode = %s, fname[%zi].s = %s\n", - i, ifile_modes[args->fname[i].mode], - i, args->fname[i].s); - } -} - -static -int peek_token(struct arguments const * args) -{ - assert(args->toks_pos <= args->toks_np); - if (args->toks_pos == args->toks_np) return TOK_EOD; - return args->toks[args->toks_pos]; -} - -static -int get_token(struct arguments * args) -{ - assert(args->toks_pos <= args->toks_np); - if (args->toks_pos == args->toks_np) return TOK_EOD; - return args->toks[args->toks_pos++]; -} - -static void unexpected(int tok) -{ - switch (tok) { - case TOK_EOD: - message(L_FATAL, _("unexpected end of predicate"), 0); - fail(); - case TOK_NOT: - message(L_FATAL, _("unexpected '!' in command line"), 0); - fail(); - case TOK_AND: - message(L_FATAL, _("unexpected '-a' in command line"), 0); - fail(); - case TOK_OR : - message(L_FATAL, _("unexpected '-o' in command line"), 0); - fail(); - case TOK_LP : - message(L_FATAL, _("unexpected '(' in command line"), 0); - fail(); - case TOK_RP : - message(L_FATAL, _("unexpected ')' in command line"), 0); - fail(); - default: - assert(tok >=TOK_ATOM_BASE); - message(L_FATAL, _("unexpected atom in command line"), 0); - fail(); - } -} - -static void parse_conj(struct arguments * args); - -static void parse_prim(struct arguments * args) -{ - if (peek_token(args) == TOK_LP) { - get_token(args); - parse_conj(args); - if (get_token(args) != TOK_RP) { - message(L_FATAL, _("missing ')' in command line"), 0); - fail(); - } - return; - } - if (peek_token(args) < TOK_ATOM_BASE) unexpected(peek_token(args)); - int atom = get_token(args) - TOK_ATOM_BASE; - assert(atom >= 0); - assert(atom < MAX_ATOMS); - struct atom_code *ac = args->atom_code[atom]; - for (size_t i = 0; i < ac->n; i++) { - addinsn(&args->p, ac->routine[i]); - } -/* - addinsn(&args->p, I_PUSH(atom)); -*/ -} - -static void parse_neg(struct arguments * args) -{ - bool neg = false; - if (peek_token(args) == TOK_NOT) { - neg = true; - get_token(args); - } - parse_prim(args); - if (neg) addinsn(&args->p, I_NEG); -} - -static void parse_disj(struct arguments * args) -{ - parse_neg(args); - while (peek_token(args) == TOK_OR) { - get_token(args); - parse_neg(args); - addinsn(&args->p, I_OR); - } -} - -static void parse_conj(struct arguments * args) -{ - parse_disj(args); - while (peek_token(args) == TOK_AND) { - get_token(args); - parse_disj(args); - addinsn(&args->p, I_AND); - } -} - -static void parse_predicate(struct arguments * args) -{ - args->toks_pos = 0; - parse_conj(args); - int tok = peek_token(args); - if (tok != TOK_EOD) unexpected(tok); -} - -static struct argp argp = { .options = options, - .parser = parse_opt, - .args_doc = argsdoc, - .doc = progdoc }; - -int main (int argc, char * argv[]) -{ - setlocale(LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - fieldtrie_init(); - - static struct arguments args; - args.show_field_name = true; - msg_set_progname(argv[0]); - init_predicate(&args.p); - description_inx = fieldtrie_insert(description); - argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); -#ifdef BANNER - banner(true); -#endif - parse_predicate(&args); - if (args.pattern_error) { - message(L_FATAL, _("A pattern is mandatory"), 0); - fail(); - } - - if (debug_optparse) { dump_args(&args); return 0; } - - if (args.p.num_atoms == 0) { - message(L_FATAL, _("a predicate is required"), 0); - fail(); - } - - if (!check_predicate(&args.p)) { - message(L_FATAL, _("malformed predicate"), 0); - fail(); - } - - if (args.short_descr && !args.description_selected) { - if (args.num_show_fields >= MAX_FIELDS) { - message(L_FATAL, _("too many output fields"), 0); - fail(); - } - message(L_INFORMATIONAL, - _("Adding \"Description\" to selected output fields because of -d"), - 0); - args.show_fields[args.num_show_fields].name = description; - args.show_fields[args.num_show_fields].inx = description_inx; - ++args.num_show_fields; - } - - if (!args.show_field_name && args.num_show_fields == 0) { - message(L_FATAL, - _("cannot suppress field names when showing whole paragraphs"), - 0); - fail(); - } - - size_t count = 0; - bool found = false; - for (size_t i = 0; i < args.num_fnames || (i == 0 && args.num_fnames == 0); ++i) { - int fd; - struct ifile fname; - if (args.num_fnames == 0) { - // Hardcode grep-dctrl <-> "-" mapping so that - // Debian packages can genuinely depend on it. - char * argv0 = fnbase(argv[0]); - if (strcmp(argv0, "grep-dctrl") == 0) { - fname = (struct ifile){ .mode = m_read, - .s = "-" }; - } else { - fname = find_ifile_by_exename(argv0, args.rcname); - } - } else { - fname = args.fname[i]; - } - - if (fname.mode == m_error) break; - - fd = open_ifile(fname); - if (fd == -1) break; - - if (!chk_ifile(fname, fd)) break; - - FSAF * fp = fsaf_fdopen(fd, fname.s); - para_parser_t pp; - para_parser_init(&pp, fp, true, args.ignore_errors); - para_t para; - para_init(&pp, ¶); - while (1) { - para_parse_next(¶); - if (para_eof(&pp)) break; - if ((args.invert_match - || !does_para_satisfy(&args.p, ¶)) - && (!args.invert_match - || does_para_satisfy(&args.p, ¶))) { - continue; - } - if (args.quiet) { - exit(0); - } - found = true; - if (args.count) { - ++count; - continue; - } - if (args.num_show_fields == 0) { - struct fsaf_read_rv r = get_whole_para(¶); - fwrite(r.b, 1, r.len, stdout); - putchar('\n'); - continue; - } - for (size_t j = 0; j < args.num_show_fields; j++) { - if (args.show_field_name) { - printf("%s: ", args.show_fields[j].name); - } - struct fsaf_read_rv r - = get_field(¶, - args.show_fields[j].inx, - args.show_fields[j].repl); - if (args.short_descr && - args.show_fields[j].inx == description_inx) { - char * nl = memchr(r.b, '\n', r.len); - if (nl != 0) r.len = nl - r.b; - } - fwrite(r.b, 1, r.len, stdout); - puts(""); - continue; - } - if (args.num_show_fields > 1) puts(""); - } - - fsaf_close(fp); - close_ifile(fname, fd); - } - if (count) printf("%zi\n", count); - return errors_reported() ? 2 : found ? 0 : 1; -} - diff --git a/grep-dctrl.rc b/grep-dctrl.rc deleted file mode 100644 index 31e03b5..0000000 --- a/grep-dctrl.rc +++ /dev/null @@ -1,26 +0,0 @@ -# Configuration for grep-dctrl. The config line format is -# line-oriented. Each line defines one executable name - default -# input file pair; the elements of the pair are in order and separated -# by whitespace. The hash '#' begins a comment that lasts to the end -# of line. Empty lines are ignored. - -# There is some magic in the default input file name field. -# If it starts with -# @exec, the rest of the name is given to /bin/sh -c and its stdout is -# used as input, if no explicit file name is given on the command -# line -# A double @ at the start of the field denotes a single @ in the actual -# name. - -# Template: -# - -# The following line used to be required, but it is now hardcoded. -# grep-dctrl - - -# The following two pairs define common aliases for grep-dctrl. -grep-status /var/lib/dpkg/status -grep-available /var/lib/dpkg/available - -# A new feature: grepping apt's available information -grep-aptavail @exec apt-cache dumpavail diff --git a/grep-dctrl/grep-dctrl.c b/grep-dctrl/grep-dctrl.c new file mode 100644 index 0000000..687f8cf --- /dev/null +++ b/grep-dctrl/grep-dctrl.c @@ -0,0 +1,875 @@ +/* dctrl-tools - Debian control file inspection tools + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fnutil.h" +#include "fsaf.h" +#include "i18n.h" +#include "misc.h" +#include "msg.h" +#include "paragraph.h" +#include "predicate.h" +#include "rc.h" +#include "util.h" + +const char * argp_program_version = "grep-dctrl (dctrl-tools) " VERSION; +const char * argp_program_bug_address = MAINTAINER; + +const char description [] = "Description"; +size_t description_inx; + +static char progdoc [] = N_("grep-dctrl -- grep Debian control files"); + +static char argsdoc [] = "PREDICATE [FILENAME...]"; + +enum { + OPT_CONFIG=256, + OPT_OPTPARSE, + OPT_SILENT, + OPT_EQ, + OPT_LT, + OPT_LE, + OPT_GT, + OPT_GE, + OPT_MMAP, + OPT_IGN_ERRS, + OPT_PATTERN +}; + +#undef BANNER + +#ifdef BANNER +void banner(bool automatic) +{ + char * fname = fnqualify_xalloc("~/.grep-dctrl-banner-shown"); + struct stat st; + if (automatic) { + int r = stat(fname, &st); + if (r == 0) goto end; + } + FILE * fp = fopen("/dev/tty", "w"); + if (fp == 0) { + perror("/dev/tty"); + goto end; + } + fprintf(fp, + "==========================================================================\n" + " NOTE \n" + " grep-dctrl has been rewritten from scratch. Although this does add new \n" + " features, regressions are certainly possible. Please watch for them and \n" + " report them to the BTS. \n" + "==========================================================================\n" + "(The above annoying banner will not be shown to you again, unless you\n" + "request it with the -B switch. It will also be removed entirely soon.)\n"); + + int r = creat(fname, 0644); + if (r == -1) perror(fname); + + if (!automatic) exit(0); + + for (int i = 15; i > 0; i--) { + fprintf(fp, "%2d seconds until program is resumed...\r", i); + fflush(fp); + sleep(1); + } + fprintf(fp, " \r"); + fflush(fp); +end: + free(fname); +} +#endif + +static struct argp_option options[] = { +#ifdef BANNER + { "banner", 'B', 0, 0, N_("Show the testing banner.") }, +#endif + { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, + { "field", 'F', N_("FIELD,FIELD,..."), 0, N_("Restrict pattern matching to the FIELDs given.") }, + { 0, 'P', 0, 0, N_("This is a shorthand for -FPackage.") }, + { 0, 'S', 0, 0, N_("This is a shorthand for -FSource:Package.") }, + { "show-field", 's', N_("FIELD,FIELD,..."), 0, N_("Show only the body of these fields from the matching paragraphs.") }, + { 0, 'd', 0, 0, N_("Show only the first line of the \"Description\" field from the matching paragraphs.") }, + { "no-field-names", 'n', 0, 0, N_("Suppress field names when showing specified fields.") }, + { "eregex", 'e', 0, 0, N_("Regard the pattern as an extended POSIX regular expression.") }, + { "regex", 'r', 0, 0, N_("The pattern is a standard POSIX regular expression.") }, + { "ignore-case", 'i', 0, 0, N_("Ignore case when looking for a match.") }, + { "invert-match", 'v', 0, 0, N_("Show only paragraphs that do not match.") }, + { "count", 'c', 0, 0, N_("Show only the count of matching paragraphs.") }, + { "config-file", OPT_CONFIG, N_("FNAME"),0, N_("Use FNAME as the config file.") }, + { "exact-match", 'X', 0, 0, N_("Do an exact match.") }, + { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, + { "and", 'a', 0, 0, N_("Conjunct predicates.") }, + { "or", 'o', 0, 0, N_("Disjunct predicates.") }, + { "not", '!', 0, 0, N_("Negate the following predicate.") }, + { "eq", OPT_EQ, 0, 0, N_("Test for version number equality.") }, + { "lt", OPT_LT, 0, 0, N_("Version number comparison: <.") }, + { "le", OPT_LE, 0, 0, N_("Version number comparison: <=.") }, + { "gt", OPT_GT, 0, 0, N_("Version number comparison: >.") }, + { "ge", OPT_GE, 0, 0, N_("Version number comparison: >=.") }, + { "debug-optparse", OPT_OPTPARSE, 0, 0, N_("Debug option parsing.") }, + { "quiet", 'q', 0, 0, N_("Do no output to stdout.") }, + { "silent", OPT_SILENT, 0, 0, N_("Do no output to stdout.") }, + { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, + { "ignore-parse-errors", OPT_IGN_ERRS, 0, 0, N_("Ignore parse errors") }, + { "pattern", OPT_PATTERN, N_("PATTERN"), 0, N_("Specify the pattern to search for") }, + { 0 } +}; + + +// Tokens +#define TOK_EOD 0 +#define TOK_NOT 1 +#define TOK_AND 2 +#define TOK_OR 3 +#define TOK_LP 4 /* left paren */ +#define TOK_RP 5 /* right paren */ +#define TOK_ATOM_BASE 6 /* All tokens >= TOK_ATOM_BASE are atoms; the + difference is the atom index. */ + +#define MAX_FNAMES 4096 +#define MAX_TOKS 16384 + +static int debug_optparse = 0; + +struct arguments { + /* Parser state flag: last token seen was ')' */ + bool just_seen_cparen; + /* Top of the parser stack. */ + size_t top; + /* Number of file names seen. */ + size_t num_fnames; + /**/ + size_t num_show_fields; + /**/ + size_t num_search_fields; + /* A machine-readable representation of the predicate. */ + struct predicate p; + /* Configuration file name */ + char const * rcname; + /* Ignore parse errors? */ + bool ignore_errors; + /* Quiet operation? */ + bool quiet; + /* Do show field names? */ + bool show_field_name; + /* Do show (only) first line of Description? */ + bool short_descr; + /* Does show_fields contain Description? */ + bool description_selected; + /* Count matching paragraphs? */ + bool count; + /* Invert match? */ + bool invert_match; + /* First unused position in toks. */ + size_t toks_np; + /* Token read position. */ + size_t toks_pos; + /* Finished with the predicate scanning? */ + bool finished; + /* Are we inside an atom? */ + bool in_atom; + /* Pattern error? */ + bool pattern_error; + /* Token stream for the predicate parser. */ + int toks[MAX_TOKS]; + /* For each atom, give code with which it can be accessed. */ + struct atom_code { + size_t n; + int * routine; + } *atom_code[MAX_ATOMS]; + /* File names seen on the command line. */ + struct ifile fname[MAX_FNAMES]; + /**/ + struct show_fields { + char const * name; + size_t inx; + size_t repl; // the field to use if this is empty + } show_fields[MAX_FIELDS]; + /* Search field names seen during current atom. */ + char * search_fields[MAX_FIELDS]; +}; + +struct atom * clone_atom(struct arguments * args) +{ + if (args->p.num_atoms >= MAX_ATOMS) { + message(L_FATAL, _("predicate is too complex"), 0); + fail(); + } + int oa = args->p.num_atoms-1; + struct atom * atom = get_current_atom(&args->p); + int na = args->p.num_atoms; + struct atom * rv = &args->p.atoms[args->p.num_atoms++]; + rv->field_name = atom->field_name; + rv->field_inx = atom->field_inx; + rv->mode = atom->mode; + rv->ignore_case = atom->ignore_case; + rv->pat = atom->pat; + rv->patlen = atom->patlen; + struct atom_code * ac = args->atom_code[oa]; + args->atom_code[na] = ac; + assert(ac->n > 0); + ac->n += 2; + ac->routine = realloc(ac->routine, ac->n * sizeof *ac->routine); + if (ac->routine == 0) fatal_enomem(0); + ac->routine[ac->n-2] = I_PUSH(na); + ac->routine[ac->n-1] = I_OR; + return rv; +} + +static void finish_atom(struct arguments * args) +{ + assert(args->in_atom); + args->in_atom = false; + struct atom * atom = get_current_atom(&args->p); + if (atom->pat == 0) { + args->pattern_error = true; + return; + } + for (size_t i = 0; i < args->num_search_fields; i++) { + if (i > 0) atom = clone_atom(args); + atom->field_name = args->search_fields[i]; + predicate_finish_atom(&args->p); + } + // If there are no fields, we have not yet run this... + // ... but it must be done (especially with -r/-e atoms) + if (args->num_search_fields == 0) predicate_finish_atom(&args->p); + args->num_search_fields = 0; +} + +#if 0 +/* Pop off one stack state, inserting the associated instructions to + * the predicate program. If paren is true, current state must be + * STATE_PAREN, and if paren is false, it must not be STATE_PAREN. */ +static void leave(struct arguments * args, int paren) +{ + debug_message("leaving...", 0); + assert(paren == (args->state == STATE_PAREN)); + if (args->state == STATE_ATOM) finish_atom(args); + assert(args->top > 0); + --args->top; + for (struct insn_node * it = args->stack[args->top].insns_first; + it != 0;) { + addinsn(&args->p, it->insn); + struct insn_node * next = it->next; + free(it); + it = next; + } + args->stack[args->top].insns_first = 0; + args->stack[args->top].insns_last = 0; + args->state = args->stack[args->top].state; +} +#endif + +#define APPTOK(tok) do { apptok(args, (tok)); } while (0) + +static void apptok(struct arguments * args, const int tok) +{ + debug_message("apptok", 0); + if (args->in_atom && tok < TOK_ATOM_BASE) { + finish_atom(args); + } + if (args->toks_np >= MAX_TOKS) { + message(L_FATAL, _("predicate is too long"), 0); + fail(); + } + args->toks[args->toks_np++] = tok; +} + +#define FINISH do { finish(args); } while (0) + +/* Flush the state stack. */ +static void finish(struct arguments * args) +{ + assert(!args->finished); + if (args->in_atom) finish_atom(args); + args->finished = true; +} + +#define ENTER_ATOM (enter_atom((args))) + +/* FIXME: UPDATE COMMENT +If necessary, enter STATE_ATOM and allocate a new atom, pushing + * along with the old state a PUSH instruction for the new atom to the + * parser stack. If we are already in STATE_ATOM, reuse the current + * atom. */ +static struct atom * enter_atom(struct arguments * args) +{ + struct atom * rv; + if (args->in_atom) { + assert(args->p.num_atoms > 0); + return &args->p.atoms[args->p.num_atoms-1]; + } + args->in_atom = true; + if (args->p.num_atoms >= MAX_ATOMS) { + message(L_FATAL, _("predicate is too complex"), 0); + fail(); + } + APPTOK(args->p.num_atoms + TOK_ATOM_BASE); + args->atom_code[args->p.num_atoms] = + malloc(sizeof *args->atom_code[args->p.num_atoms]); + if (args->atom_code[args->p.num_atoms] == 0) fatal_enomem(0); + args->atom_code[args->p.num_atoms]->n = 1; + args->atom_code[args->p.num_atoms]->routine = malloc(1 * sizeof(int)); + if (args->atom_code[args->p.num_atoms]->routine == 0) { + fatal_enomem(0); + } + args->atom_code[args->p.num_atoms]->routine[0] + = I_PUSH(args->p.num_atoms); + rv = &args->p.atoms[args->p.num_atoms++]; + rv->field_name = 0; + rv->field_inx = -1; + rv->mode = M_SUBSTR; + rv->ignore_case = 0; + rv->pat = 0; + rv->patlen = 0; + return rv; +} + +#define set_mode(nmode) do { \ + atom = ENTER_ATOM; \ + if (atom->mode != M_SUBSTR) { \ + message(L_FATAL, _("inconsistent atom modifiers"), 0); \ + fail(); \ + } \ + atom->mode = (nmode); \ +} while (0) + +static error_t parse_opt (int key, char * arg, struct argp_state * state) +{ + struct arguments * args = state->input; + bool just_seen_cparen = args->just_seen_cparen; + args->just_seen_cparen = false; + struct atom * atom; + debug_message("parse_opt", 0); +#ifdef INCLUDE_DEBUG_MSGS + if (do_msg(L_DEBUG)) { + fprintf(stderr, "%s: in_atom = %s\n", + get_progname(), + args->in_atom ? "true" : "false"); + } +#endif + switch (key) { + case 'C': + if (!to_stdout (COPYING)) fail(); + exit(0); +#ifdef BANNER + case 'B': + banner(false); +#endif + case 'v': + args->invert_match = true; + break; + case 'c': + args->count = true; + break; + case 'q': case OPT_SILENT: + debug_message("parse_opt: q", 0); + args->quiet = true; + break; + case 'n': + debug_message("parse_opt: n", 0); + args->show_field_name = false; + break; + case 'd': + args->short_descr = true; + break; + case 's': { + char * carg = strdup(arg); + if (carg == 0) fatal_enomem(0); + for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){ + struct show_fields * sf = + &args->show_fields[args->num_show_fields]; + sf->name = strdup(s); + if (sf->name == 0) fatal_enomem(0); + char * repl = strchr(sf->name, ':'); + if (repl != NULL) { + *repl = '\0'; + ++repl; + } + sf->inx = fieldtrie_insert(sf->name); + if (sf->inx == description_inx) { + args->description_selected = true; + } + sf->repl = repl == NULL + ? (size_t)(-1) + : fieldtrie_insert(repl); + ++args->num_show_fields; + } + free(carg); + } + break; + case 'l': { + int ll = str2loglevel(arg); + if (ll < 0) + { + message(L_FATAL, _("no such log level"), arg); + fail(); + } + set_loglevel(ll); + debug_message("parse_opt: l", 0); + } + break; + case '!': + debug_message("parse_opt: !", 0); + APPTOK(TOK_NOT); + break; + case 'a': + debug_message("parse_opt: a", 0); + APPTOK(TOK_AND); + break; + case 'o': + debug_message("parse_opt: o", 0); + APPTOK(I_OR); + break; + case 'S': + debug_message("parse_opt: S", 0); + arg = "Source:Package"; + goto case_F; + case 'P': + debug_message("parse_opt: P", 0); + arg = "Package"; + goto case_F; + case_F: + case 'F': { + debug_message("parse_opt: Fv", 0); + atom = ENTER_ATOM; + char * carg = strdup(arg); + if (carg == 0) fatal_enomem(0); + for (char * s = strtok(carg, ","); s != 0; s = strtok(0, ",")){ + char * tmp = strdup(s); + if (tmp == 0) fatal_enomem(0); + args->search_fields[args->num_search_fields++] = tmp; + } + free(carg); + } + break; + case 'X': + debug_message("parse_opt: X", 0); + set_mode(M_EXACT); + break; + case 'r': + debug_message("parse_opt: r", 0); + set_mode(M_REGEX); + break; + case 'e': + debug_message("parse_opt: e", 0); + set_mode(M_EREGEX); + break; + case OPT_EQ: + debug_message("parse_opt: eq", 0); + set_mode(M_VER_EQ); + break; + case OPT_LT: + debug_message("parse_opt: lt", 0); + set_mode(M_VER_LT); + break; + case OPT_LE: + debug_message("parse_opt: le", 0); + set_mode(M_VER_LE); + break; + case OPT_GT: + debug_message("parse_opt: gt", 0); + set_mode(M_VER_GT); + break; + case OPT_GE: + debug_message("parse_opt: ge", 0); + set_mode(M_VER_GE); + break; + case OPT_MMAP: + debug_message("parse_opt: mmap", 0); + fsaf_mmap = 1; + break; + case 'i': + debug_message("parse_opt: i", 0); + atom = ENTER_ATOM; + atom->ignore_case = 1; + break; + case OPT_OPTPARSE: + debug_message("parse_opt: optparse", 0); + debug_optparse = 1; + break; + case OPT_IGN_ERRS: + debug_message("parse_opt: ignore-parse-errors", 0); + args->ignore_errors = 1; + break; + case OPT_PATTERN: + debug_message("parse_opt: pattern", 0); + atom = ENTER_ATOM; + if (atom->pat != 0) { + message(L_FATAL, _("Multiple patterns for the same " + "atom are not allowed"), 0); + fail(); + } + atom->patlen = strlen(arg); + atom->pat = malloc(atom->patlen+1); + if (atom->pat == 0) fatal_enomem(0); + strcpy((char*)atom->pat, arg); + break; + case ARGP_KEY_ARG: + debug_message("parse_opt: argument", 0); + redo: + debug_message("!!!", 0); + if (strcmp(arg, "!") == 0) { + debug_message("parse_opt: !", 0); + APPTOK(TOK_NOT); + break; + } + if (strcmp(arg, "(") == 0) { + debug_message("parse_opt: (", 0); + APPTOK(TOK_LP); + break; + } + if (strcmp(arg, ")") == 0) { + debug_message("parse_opt: )", 0); + args->just_seen_cparen = true; + APPTOK(TOK_RP); + break; + } + if (args->finished) { + char const * s; + if (args->num_fnames >= MAX_FNAMES) { + message(L_FATAL, _("too many file names"), 0); + fail(); + } + s = strdup(arg); + if (s == 0) fatal_enomem(0); + args->fname[args->num_fnames++] = + (struct ifile){ .mode = m_read, .s = s }; + break; + } + if (just_seen_cparen) { FINISH; goto redo; } + if (strcmp(arg, "--") == 0) { FINISH; break; } + atom = ENTER_ATOM; + if (atom->pat != 0) { FINISH; goto redo; } + atom->patlen = strlen(arg); + atom->pat = malloc(atom->patlen+1); + if (atom->pat == 0) fatal_enomem(0); + strcpy((char*)atom->pat, arg); + break; + case ARGP_KEY_END: + debug_message("parse_opt: end", 0); + if (!args->finished) FINISH; + break; + case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: + debug_message("parse_opt: ignored", 0); + break; + case OPT_CONFIG: + debug_message("parse_opt: --config-file", 0); + args->rcname = strdup(arg); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static void dump_args(struct arguments * args) +{ + size_t i; + assert(args->finished); + assert(args->top == 0); + printf("num_atoms = %zi\n", args->p.num_atoms); + for (i = 0; i < args->p.num_atoms; i++) { + printf("atoms[%zi].field_name = %s\n", i, args->p.atoms[i].field_name); + printf("atoms[%zi].mode = %i\n", i, args->p.atoms[i].mode); + printf("atoms[%zi].ignore_case = %i\n", i, args->p.atoms[i].ignore_case); + printf("atoms[%zi].pat = %s\n", i, args->p.atoms[i].pat); + } + printf("proglen = %zi\n", args->p.proglen); + for (i = 0; i < args->p.proglen; i++) { + int op = args->p.program[i]; + printf("program[%zi] = ", i); + switch (op) { + case I_NOP: puts("NOP"); break; + case I_NEG: puts("NEG"); break; + case I_AND: puts("AND"); break; + case I_OR: puts("OR"); break; + default: + printf("PUSH(%i)\n", op - I_PUSH(0)); + } + } + printf("num_fnames = %zi\n", args->num_fnames); + for (i = 0; i < args->num_fnames; i++) { + printf("fname[%zi].mode = %s, fname[%zi].s = %s\n", + i, ifile_modes[args->fname[i].mode], + i, args->fname[i].s); + } +} + +static +int peek_token(struct arguments const * args) +{ + assert(args->toks_pos <= args->toks_np); + if (args->toks_pos == args->toks_np) return TOK_EOD; + return args->toks[args->toks_pos]; +} + +static +int get_token(struct arguments * args) +{ + assert(args->toks_pos <= args->toks_np); + if (args->toks_pos == args->toks_np) return TOK_EOD; + return args->toks[args->toks_pos++]; +} + +static void unexpected(int tok) +{ + switch (tok) { + case TOK_EOD: + message(L_FATAL, _("unexpected end of predicate"), 0); + fail(); + case TOK_NOT: + message(L_FATAL, _("unexpected '!' in command line"), 0); + fail(); + case TOK_AND: + message(L_FATAL, _("unexpected '-a' in command line"), 0); + fail(); + case TOK_OR : + message(L_FATAL, _("unexpected '-o' in command line"), 0); + fail(); + case TOK_LP : + message(L_FATAL, _("unexpected '(' in command line"), 0); + fail(); + case TOK_RP : + message(L_FATAL, _("unexpected ')' in command line"), 0); + fail(); + default: + assert(tok >=TOK_ATOM_BASE); + message(L_FATAL, _("unexpected atom in command line"), 0); + fail(); + } +} + +static void parse_conj(struct arguments * args); + +static void parse_prim(struct arguments * args) +{ + if (peek_token(args) == TOK_LP) { + get_token(args); + parse_conj(args); + if (get_token(args) != TOK_RP) { + message(L_FATAL, _("missing ')' in command line"), 0); + fail(); + } + return; + } + if (peek_token(args) < TOK_ATOM_BASE) unexpected(peek_token(args)); + int atom = get_token(args) - TOK_ATOM_BASE; + assert(atom >= 0); + assert(atom < MAX_ATOMS); + struct atom_code *ac = args->atom_code[atom]; + for (size_t i = 0; i < ac->n; i++) { + addinsn(&args->p, ac->routine[i]); + } +/* + addinsn(&args->p, I_PUSH(atom)); +*/ +} + +static void parse_neg(struct arguments * args) +{ + bool neg = false; + if (peek_token(args) == TOK_NOT) { + neg = true; + get_token(args); + } + parse_prim(args); + if (neg) addinsn(&args->p, I_NEG); +} + +static void parse_disj(struct arguments * args) +{ + parse_neg(args); + while (peek_token(args) == TOK_OR) { + get_token(args); + parse_neg(args); + addinsn(&args->p, I_OR); + } +} + +static void parse_conj(struct arguments * args) +{ + parse_disj(args); + while (peek_token(args) == TOK_AND) { + get_token(args); + parse_disj(args); + addinsn(&args->p, I_AND); + } +} + +static void parse_predicate(struct arguments * args) +{ + args->toks_pos = 0; + parse_conj(args); + int tok = peek_token(args); + if (tok != TOK_EOD) unexpected(tok); +} + +static struct argp argp = { .options = options, + .parser = parse_opt, + .args_doc = argsdoc, + .doc = progdoc }; + +int main (int argc, char * argv[]) +{ + setlocale(LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + fieldtrie_init(); + + static struct arguments args; + args.show_field_name = true; + msg_set_progname(argv[0]); + init_predicate(&args.p); + description_inx = fieldtrie_insert(description); + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); +#ifdef BANNER + banner(true); +#endif + parse_predicate(&args); + if (args.pattern_error) { + message(L_FATAL, _("A pattern is mandatory"), 0); + fail(); + } + + if (debug_optparse) { dump_args(&args); return 0; } + + if (args.p.num_atoms == 0) { + message(L_FATAL, _("a predicate is required"), 0); + fail(); + } + + if (!check_predicate(&args.p)) { + message(L_FATAL, _("malformed predicate"), 0); + fail(); + } + + if (args.short_descr && !args.description_selected) { + if (args.num_show_fields >= MAX_FIELDS) { + message(L_FATAL, _("too many output fields"), 0); + fail(); + } + message(L_INFORMATIONAL, + _("Adding \"Description\" to selected output fields because of -d"), + 0); + args.show_fields[args.num_show_fields].name = description; + args.show_fields[args.num_show_fields].inx = description_inx; + ++args.num_show_fields; + } + + if (!args.show_field_name && args.num_show_fields == 0) { + message(L_FATAL, + _("cannot suppress field names when showing whole paragraphs"), + 0); + fail(); + } + + size_t count = 0; + bool found = false; + for (size_t i = 0; i < args.num_fnames || (i == 0 && args.num_fnames == 0); ++i) { + int fd; + struct ifile fname; + if (args.num_fnames == 0) { + // Hardcode grep-dctrl <-> "-" mapping so that + // Debian packages can genuinely depend on it. + char * argv0 = fnbase(argv[0]); + if (strcmp(argv0, "grep-dctrl") == 0) { + fname = (struct ifile){ .mode = m_read, + .s = "-" }; + } else { + fname = find_ifile_by_exename(argv0, args.rcname); + } + } else { + fname = args.fname[i]; + } + + if (fname.mode == m_error) break; + + fd = open_ifile(fname); + if (fd == -1) break; + + if (!chk_ifile(fname, fd)) break; + + FSAF * fp = fsaf_fdopen(fd, fname.s); + para_parser_t pp; + para_parser_init(&pp, fp, true, args.ignore_errors); + para_t para; + para_init(&pp, ¶); + while (1) { + para_parse_next(¶); + if (para_eof(&pp)) break; + if ((args.invert_match + || !does_para_satisfy(&args.p, ¶)) + && (!args.invert_match + || does_para_satisfy(&args.p, ¶))) { + continue; + } + if (args.quiet) { + exit(0); + } + found = true; + if (args.count) { + ++count; + continue; + } + if (args.num_show_fields == 0) { + struct fsaf_read_rv r = get_whole_para(¶); + fwrite(r.b, 1, r.len, stdout); + putchar('\n'); + continue; + } + for (size_t j = 0; j < args.num_show_fields; j++) { + if (args.show_field_name) { + printf("%s: ", args.show_fields[j].name); + } + struct fsaf_read_rv r + = get_field(¶, + args.show_fields[j].inx, + args.show_fields[j].repl); + if (args.short_descr && + args.show_fields[j].inx == description_inx) { + char * nl = memchr(r.b, '\n', r.len); + if (nl != 0) r.len = nl - r.b; + } + fwrite(r.b, 1, r.len, stdout); + puts(""); + continue; + } + if (args.num_show_fields > 1) puts(""); + } + + fsaf_close(fp); + close_ifile(fname, fd); + } + if (count) printf("%zi\n", count); + return errors_reported() ? 2 : found ? 0 : 1; +} + diff --git a/grep-dctrl/grep-dctrl.rc b/grep-dctrl/grep-dctrl.rc new file mode 100644 index 0000000..31e03b5 --- /dev/null +++ b/grep-dctrl/grep-dctrl.rc @@ -0,0 +1,26 @@ +# Configuration for grep-dctrl. The config line format is +# line-oriented. Each line defines one executable name - default +# input file pair; the elements of the pair are in order and separated +# by whitespace. The hash '#' begins a comment that lasts to the end +# of line. Empty lines are ignored. + +# There is some magic in the default input file name field. +# If it starts with +# @exec, the rest of the name is given to /bin/sh -c and its stdout is +# used as input, if no explicit file name is given on the command +# line +# A double @ at the start of the field denotes a single @ in the actual +# name. + +# Template: +# + +# The following line used to be required, but it is now hardcoded. +# grep-dctrl - + +# The following two pairs define common aliases for grep-dctrl. +grep-status /var/lib/dpkg/status +grep-available /var/lib/dpkg/available + +# A new feature: grepping apt's available information +grep-aptavail @exec apt-cache dumpavail diff --git a/grep-dctrl/rc.c b/grep-dctrl/rc.c new file mode 100644 index 0000000..b94b669 --- /dev/null +++ b/grep-dctrl/rc.c @@ -0,0 +1,206 @@ +/* grep-dctrl - grep Debian control files + 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; 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. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msg.h" +#include "fnutil.h" +#include "getaline.h" +#include "rc.h" +#include "strutil.h" +#include "util.h" + +char const * const ifile_modes[] = { [m_error] = "m_error", + [m_read] = "m_read", + [m_exec] = "m_exec" }; + +static struct ifile parse(char * s) +{ + assert(s != 0); + s = (char*)left_trimmed(s); + trim_right(s); + if (*s == 0) return (struct ifile) { .mode = m_read, .s = "-" }; + static const char at_exec[] = "@exec "; + if (s[0] != '@') return (struct ifile){ .mode = m_read, .s = s }; + assert(s[0] == '@'); + if (s[1] == '@') return (struct ifile){ .mode = m_read, .s = s+1 }; + if (strncmp(s, at_exec, sizeof at_exec - 1) == 0) { + return (struct ifile){ .mode = m_exec, + .s = s + sizeof at_exec - 1 }; + } + debug_message(s, 0); + message(L_IMPORTANT, _("Malformed default input file name"), 0); + return (struct ifile){ .mode = m_error, .s = 0 }; +} + +static bool perms_ok(char const * fname, int fd) +{ + struct stat stat; + int r = fstat(fd, &stat); + if (r == -1) { + errno_msg(L_IMPORTANT, fname); + return false; + } + if (stat.st_uid != 0 && stat.st_uid != getuid()) { + message(L_IMPORTANT, _("not owned by you or root, ignoring"), + fname); + return false; + } + if ((stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + message(L_IMPORTANT, _("write permissions for " + "group or others, ignoring"), + fname); + return false; + } + return true; +} + +struct ifile find_ifile_by_exename(const char * exename, const char * rcfname) +{ + static const char * default_rcfiles [] = { + /* Order is significant: earlier files override later ones. */ + "~/.grep-dctrlrc", + SYSCONF "/grep-dctrl.rc", + 0 }; + int i; + char * fname; + int lineno; + FILE * f; + + assert(exename != 0); + + if (rcfname == 0) { + struct ifile rv = { .mode = m_error, .s = 0 }; + for (i = 0; + rv.mode == m_error && default_rcfiles [i] != 0; + i++) { + rv = find_ifile_by_exename(exename, default_rcfiles [i]); + } + return rv; + } + + assert(rcfname != 0); + + fname = fnqualify(rcfname); + if (fname == 0) { + errno_msg(L_IMPORTANT, rcfname); + return (struct ifile){ .mode = m_error, .s = 0 }; + } + + message(L_INFORMATIONAL, _("reading config file"), fname); + + f = fopen(fname, "r"); + if (f == 0) { + message(L_INFORMATIONAL, strerror(errno), fname); + return (struct ifile){ .mode = m_error, .s = 0 }; + } + + // check permissions + if (!perms_ok(fname, fileno(f))) { + return (struct ifile){ .mode = m_error, .s = 0 }; + } + + lineno = 0; + char * rv = 0; + while (1) { + static char * line = 0; + char * line_exe; + char * line_ifile; + + /* If this is not the first call, line may be non-null + and must be freed. It must be freed on all + non-first iterations, too. */ + free(line); + + line = getaline (f); + if (line == 0) { + message(L_FATAL, _("read failure or out of memory"), + fname); + fail(); + } + + ++lineno; + + if (*line == 0) { + rv = 0; + break; + } + + chop_comment(line, '#'); + chomp(line); + + if (left_trimmed(line) [0] == 0) { + continue; + } + + line_exe = strtok(line, " \t"); + if (line_exe == 0) { + line_message(L_IMPORTANT, + _("syntax error: need a executable name"), + fname, lineno); + continue; + } + + line_ifile = strtok(0, "\n\t"); + if (line_ifile == 0) { + line_message(L_IMPORTANT, + _("syntax error: need an input file name"), + fname, lineno); + continue; + } + + message(L_INFORMATIONAL, _("considering executable name"), line_exe); + if (strcmp (exename, line_exe) == 0) { + message(L_INFORMATIONAL, _("yes, will use executable name"), line_exe); + rv = line_ifile; + message(L_INFORMATIONAL, _("default input file"), rv); + break; + } + } + + fclose(f); + free(fname); + + if (rv != 0) { + return parse(rv); + } else { + message(L_IMPORTANT, _("executable name not found; " + "reading from standard input"), 0); + return (struct ifile) { .mode = m_read, .s = "-" }; + } +} + diff --git a/grep-dctrl/rc.h b/grep-dctrl/rc.h new file mode 100644 index 0000000..e112424 --- /dev/null +++ b/grep-dctrl/rc.h @@ -0,0 +1,36 @@ +/* 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 RC_H__ +#define RC_H__ + +#include "ifile.h" + +extern char const * const ifile_modes[]; + +/* Search for exename (only basename, though) in rcfname (or if it is + null, in default rc files) and return the found corresponding + default input file name. The mode will be m_error, if not found or + other error occurred, m_read, if it is intended to be interpreted + as a file name, and m_exec, if the string is to be interpreted as a + shell command whose stdout should be used. The returned pointer + will be invalidated by the next call to this function. */ +struct ifile +find_ifile_by_exename (const char * exename, const char * rcfname); + +#endif diff --git a/i18n.h b/i18n.h deleted file mode 100644 index 67a3b5f..0000000 --- a/i18n.h +++ /dev/null @@ -1,33 +0,0 @@ -/* 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 -#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/ifile.c b/ifile.c deleted file mode 100644 index b44b708..0000000 --- a/ifile.c +++ /dev/null @@ -1,149 +0,0 @@ -/* 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 -#include -#include -#include -#include -#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/ifile.h b/ifile.h deleted file mode 100644 index 1405516..0000000 --- a/ifile.h +++ /dev/null @@ -1,38 +0,0 @@ -/* 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 - -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/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 +#include +#include + +#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 +#include +#include +#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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +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 +#include +#include +#include + +/* 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 +#include +#include +#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 + +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 +#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 +#include +#include +#include +#include +#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 + +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 +#include +#include +#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 + +#include +#include +#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 +#include +#include +#include +#include +#include +#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 +#include +#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 +#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 +#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 +#include +#include +#include +#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 +#include +#include +#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 + +#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 +#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 +#include +#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 +#include +#include + +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 +#include +#include +#include + +/* 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 +#include +#include +#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 + +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 + * Copyright (C) 2001 Wichert Akkerman + * + * 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 +#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 + * + * 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 +#include + +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 */ diff --git a/man/grep-dctrl.1.cp b/man/grep-dctrl.1.cp new file mode 100644 index 0000000..bfa50c2 --- /dev/null +++ b/man/grep-dctrl.1.cp @@ -0,0 +1,506 @@ +.TH GREP-DCTRL 1 2006-04-02 "Debian Project" "Debian user's manual" +\" Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 +\" 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. +.SH NAME +grep\-dctrl, grep\-status, grep\-available, grep\-aptavail \- grep Debian control files +.SH SYNOPSIS +.I command +--copying|-C | --help|-h | --version|-V +.sp +.I command +[options] predicate [ +.IR file "..." +] +.sp +where +.I command +is one of +.BR grep\-dctrl , +.BR grep\-status , +.B grep\-available +and +.BR grep\-aptavail . +.SH DESCRIPTION +The grep\-dctrl program can answer such questions as +.IR "What is the Debian package foo?" , +.IR "Which version of the Debian package bar is now current?" , +.IR "Which Debian packages does John Doe maintain?" , +.I "Which Debian packages are somehow related to the Scheme" +.IR " programming language?" , +and with some help, +.IR "Who maintain the essential packages of a Debian system?" , +given a useful input file. +.PP +The programs +.BR grep\-available, +.B grep\-status +and +.B grep\-aptavail +are aliases of (actually, symbolic links to) +.BR grep\-dctrl . +In the shipped configuration, these aliases use as their default input +the +.BR dpkg (8) +available and status files and the apt\-cache dumpavail output, +respectively. +.PP +.B grep\-dctrl +is a specialised grep program that is meant for processing any file +which has the general format of a Debian package control file, as +described in the Debian Packaging Manual. These include the dpkg +available file, the dpkg status file, and the Packages files on a +distribution medium (such as a Debian CD-ROM or an FTP site carrying +Debian). +.PP +You must give a filter expression on the command line. The filter +defines which kind of paragraphs (aka package records) are output. A +simple filter is a search pattern along with any options that modify +it. Possible modifiers are \-\-eregex, \-\-field, \-\-ignore\-case, +\-\-regex and \-\-exact\-match, along with their single-letter +equivalents. By +default, the search is a case-sensitive fixed substring match on each +paragraph (in other words, package record) in the input. With +suitable modifiers, this can be changed: the search can be +case-insensitive and the pattern can be seen as an extended POSIX +regular expression. +.PP +Filters can be combined to form more complex filters using the +connectives \-\-and, \-\-or and \-\-not. Parentheses (which usually +need to be escaped for the shell) can be used for grouping. +.PP +By default, the full matching paragraphs are printed on the standard +output; specific fields can be selected for output with the \-s option. +.PP +After the filter expression comes zero or more file names. If no file names +are specified, the file name is searched in configuration files. The +input file from the first program name \- input file association with +the correct program name is used. The program names are matched with +the base form of the name of the current program (the 0'th command +line argument, if you will). The file name "\-" is taken to mean the +standard input stream. The files are searched in order but +separately; they are +.B not +concatenated together. In other words, the end of a file always +implies the end of the current paragraph. +.PP +There is one exception to the above: if the program name is +.BR grep\-dctrl , +the default input source is always standard input; this cannot be +overridden by the configuration file. +.SH OPTIONS +.SS Specifying the search pattern +.IP "\-\-pattern=PATTERN" +Specify a pattern to be searched. This switch is not generally needed, as +the pattern can be given by itself. However, patterns that start +with a dash must be given using this switch, so that they wouldn't +be mistaken for switches. +.SS Modifiers of simple filters +.IP "\-F FIELD,FIELD,...; \-\-field=FIELD,FIELD,..." +Restrict pattern matching to the FIELDs given. Multiple field names +in one \-F option and multiple \-F options in one simple filter are +allowed. The search named by the filter will be performed +among all the fields named, and as soon as any one of them matches, the +whole simple filter is considered matching. +.PP +A FIELD specification can contain a colon. In such a case, the part +up to the colon is taken as the name of the field to be searched in, +and the part after the colon is taken as the name of the field whose +content is to be used if the field to search in is empty. +.IP \-P +Shorthand for "\-FPackage". +.IP \-S +Shorthand for "\-FSource:Package". +.IP "\-e, \-\-eregex" +Regard the pattern of the current simple filter as an extended +POSIX regular expression +.IP "\-r, \-\-regex" +The pattern of the current simple filter is a standard POSIX regular expression. +.IP "\-i, \-\-ignore\-case" +Ignore case when looking for a match in the current simple filter. +.IP "\-X, \-\-exact\-match" +Do an exact match (as opposed to a substring match) in the current +simple filter. +.IP "\-\-eq" +Do an equality comparison under the Debian version number system. If +the pattern or the field to be searched in is not a valid Debian +version number, the paragraph is regarded as not matching. As a +special case, this is capable of comparing simple nonnegative integers +for equality. +.IP "\-\-lt" +Do an strictly-less-than comparison under the Debian version number +system. If the pattern or the field to be searched in is not a valid +Debian version number, the paragraph is regarded as not matching. As +a special case, this is capable of comparing simple nonnegative +integers. +.IP "\-\-le" +Do an less-than-or-equal comparison under the Debian version number +system. If the pattern or the field to be searched in is not a valid +Debian version number, the paragraph is regarded as not matching. As +a special case, this is capable of comparing simple nonnegative +integers. +.IP "\-\-gt" +Do an strictly-greater-than comparison under the Debian version number +system. If the pattern or the field to be searched in is not a valid +Debian version number, the paragraph is regarded as not matching. As +a special case, this is capable of comparing simple nonnegative +integers. +.IP "\-\-ge" +Do an greater-than-or-equal comparison under the Debian version number +system. If the pattern or the field to be searched in is not a valid +Debian version number, the paragraph is regarded as not matching. As +a special case, this is capable of comparing simple nonnegative +integers. +.SS Combining filters +.IP "\-!, \-\-not, !" +Match if the following filter does +.I not +match. +.IP "\-o, \-\-or" +Match if either one or both of the preceding and following filters +matches. +.IP "\-a, \-\-and" +Match if both the preceding and the following filter match. +.IP "( ... )" +Parentheses can be used for grouping. Note that they need to be +escaped for most shells. +.SS Output format modifiers +.IP "\-s FIELD,FIELD,...; \-\-show\-field=FIELD,FIELD,..." +Show only the body of these fields from the matching paragraphs. The +field names must not include any colons or commas. Commas are used to +delimit field names in the argument to this option. The fields are +shown in the order given here. +.PP +A FIELD specification can contain a colon. In such a case, the part +up to the colon is taken as the name of the field to be shown, and the +part after the colon is taken as the name of the field whose content +is to be used if the field to be shown is empty. +.IP "\-d" +Show only the first line of the Description field from the matching +paragraphs. If no "\-s" option is specified, this option also effects +"\-s Description"; if there is a "\-s" option but it does not include +the Description field name, one is appended to the option. Thus the +Description field's location in the output is determined by the "\-s" +option, if any, the last field being the default. +.IP "\-n, \-\-no\-field\-names" +Suppress field names when showing specified fields, only their bodies +are shown. Each field is printed in its original form without the +field name, the colon after it and any whitespace preceding the start +of the body. +.IP "\-v, \-\-invert\-match" +Instead of showing all the paragraphs that match, show those paragraphs +that do +.I not +match. +.IP "\-c, \-\-count" +Instead of showing the paragraphs that match (or, with \-v, that don't +match), show the count of those paragraphs. +.IP "\-q, \-\-quiet, \-\-silent" +Output nothing to the standard output stream. Instead, exit +immediately after finding the first match. +.SS Miscellaneous +.IP "\-\-ignore\-parse\-errors" +Ignore errors in parsing input. A paragraph which cannot be parsed +is ignored in its entirety, and the next paragraph is assumed to start +after the first newline since the location of the error. +.IP "\-\-config\-file=FNAME" +Use FNAME as the config file instead of the defaults. +.IP "\-\-debug\-optparse" +Show how the current command line has been parsed. +.IP "\-l LEVEL, \-\-errorlevel=LEVEL" +Set debugging level to LEVEL. LEVEL is one of "fatal", "important", +"informational" and "debug", but the last may not be available, +depending on the compile-time options. These categories are given +here in order; every message that is emitted when "fatal" is in +effect, will be emitted in the "important" error level, and so on. +The default is "important". +.IP "\-V, \-\-version" +Print out version information. +.IP "\-C, \-\-copying" +Print out the copyright license. This produces much output; be sure +to redirect or pipe it somewhere (such as your favourite pager). +.IP "\-h, \-\-help" +Print out a help summary. +.SH EXAMPLES +The following example queries assume that the default configuration is +in effect. +.PP +The almost simplest use of this program is to print out the status or +available record of a package. In this respect, +.B grep\-dctrl +is like +.B "dpkg \-s" +or +.BR "dpkg \-\-print\-avail". +To print out the status record of the package "mixal", do +.nf +% grep\-status \-PX mixal +.fi +and to get its available record, use +.nf +% grep\-available \-PX mixal +.fi +In fact, you can ask for the record of the "mixal" package +from any Debian control file. Say, you have the Debian 2.2 +CD-ROM's Packages file in the current directory; now you +can do a +.nf +% grep\-dctrl \-PX mixal Packages +.fi +.PP +But +.B grep\-dctrl +can do more than just emulate +.BR dpkg . +It can more-or-less emulate +.BR apt\-cache ! +That program has a search feature that searches package descriptions. +But we can do that too: +.nf +% grep\-available \-F Description foo +.fi +searches for the string "foo" case-sensitively in the descriptions of +all available packages. If you want case-insensitivity, use +.nf +% grep\-available \-F Description \-i foo +.fi +Truth to be told, +.B apt\-cache +searches package names, too. We can separately search in the names; +to do so, do +.nf +% grep\-available \-F Package foo +.fi +or +.nf +% grep\-available \-P foo +.fi +which is pretty much the same thing. We can also search in both +descriptions and names; if match is found in either, the package +record is printed: +.nf +% grep\-available \-P \-F Description foo +.fi +or +.nf +% grep\-available \-F Package \-F Description foo +.fi +This kind of search is the exactly same that +.B apt\-cache +does. +.PP +Here's one thing neither +.B dpkg +nor +.B apt\-cache +do. Search for a string in the whole status or available file (or +any Debian control file, for that matter) and print out all package +records where we have a match. Try +.nf +% grep\-available dpkg +.fi +sometime and watch how thoroughly +.B dpkg +has infiltrated Debian. +.PP +All the above queries were based on simple substring searches. +But +.B grep\-dctrl +can handle regular expressions in the search pattern. For example, +to see the status records of all packages with either "apt" or +"dpkg" in their names, use +.nf +% grep\-status \-P \-e 'apt|dpkg' +.fi +.PP +Now that we have seen all these fine and dandy queries, you might +begin to wonder whether it is necessary to always see the whole +paragraph. You may be, for example, interest only in the dependency +information of the packages involved. Fine. To show the depends +lines of all packages maintained by me, do a +.nf +% grep\-available \-F Maintainer \-s Depends 'ajk@debian.org' +.fi +If you want to see the packages' names, too, use +.nf +% grep\-available \-F Maintainer \-s Package,Depends \\ + 'ajk@debian.org' +.fi +Note that there must be no spaces in the argument to the \-s switch. +.PP +More complex queries are also possible. For example, to see the list of packages +maintained by me and depending on libc6, do +.nf +% grep\-available \-F Maintainer 'ajk@debian.org' \\ + \-a \-F Depends libc6 \-s Package,Depends +.fi +Remember that you can use other Unix filters to help you, too. Ever +wondered, who's the most active Debian developer based on the number +of source packages being maintained? Easy. You just need to have a +copy of the most recent Sources file from any Debian mirror. +.nf +% grep\-dctrl \-n \-s Maintainer '' Sources | sort | + uniq \-c | sort \-nr +.fi +This example shows a neat trick: if you want to selectively +show only some field of +.I all +packages, just supply an empty pattern. +.PP +The term "bogopackage" means the count of the packages that a Debian +developer maintains. To get the bogopackage count for the maintainer +of +.BR grep\-dctrl , +say +.nf +% grep\-available \-c \-FMaintainer \\ + "`grep\-available \-sMaintainer \-n \-PX grep\-dctrl`" +.fi +.PP +Sometimes it is useful to output the data of several fields on the +same line. For example, the following command outputs the list of +installed packages, sorted by their Installed\-Size. +.nf +% grep\-status \-FStatus \-sInstalled\-Size,Package \-n \\ + "install ok installed" | paste \-sd " \\n" | sort \-n +.fi +Note that there should be exactly 2 spaces in the " \\n" string. +.PP +These examples cover a lot of typical uses of this utility, but not +all possible uses. Use your imagination! The building blocks are +there, and if something's missing, let me know. +.SH DIAGNOSTICS +In the absence of errors, the exit code 0 is used if at least one +match was found, and the exit code 1 is used if no matches were found. +If there were errors, the exit code is 2, with one exception. If the +\-q, \-\-quiet or \-\-silent options are used, the exit code 0 is used when +a match is found regardless of whether there have been non-fatal +errors. +.PP +These messages are emitted in log levels "fatal" and "important". +Additional messages may be provided by the system libraries. +.B This list is out of date. +.IP "a pattern is mandatory" +You must specify a pattern to be searched for. +.IP "a predicate is required" +No predicate was specified, but one is required. +.IP "cannot find enough memory" +More memory was needed than was available. This error may be +transient, that is, if you try again, all may go well. +.IP "cannot suppress field names when showing whole paragraphs" +When you do not use the \-s switch, +.B grep\-dctrl +just passes the matching paragraphs through, not touching them any +way. This means, for example, that you can only use \-n when you use +\-s. +.IP "inconsistent atom modifiers" +Conflicting atom modifiers were used; for example, perhaps both \-X and +\-e were specified for the same atom. +.IP "missing ')' in command line" +There were more opening than closing parentheses in the given +predicate. +.IP "no such log level" +The argument to \-l was invalid. +.IP "predicate is too complex" +The predicate's complexity (the number of atoms and connectives) +exceed compile-time limits. +.IP "read failure or out of memory" +There was a problem reading the configuration file. Maybe there was a +transput error; maybe memory was exhausted. This error may be +transient, that is, if you try again, all may go well. +.IP "syntax error in command line" +There is a problem in the command line. Look, and you shall find it. +.IP "syntax error: need a executable name" +There is a problem in the configuration file. Look, and you shall find it. +.IP "syntax error: need an input file name" +There is a problem in the configuration file. Look, and you shall find it. +.IP "too many file names" +The number of file names specified in the command line exceeded a +compile-time limit. +.IP "too many output fields" +The argument to \-s had too many field names in it. This number is +limited to 256. +.IP "unexpected ')' in command line" +There was no opening parenthesis that would match some closing +parenthesis in the command line. +.IP "unexpected end of file" +The input file is broken: it ends before it should. +.IP "unexpected end of line" +The input file is broken: a line ends before it should. +.IP "Unexpected atom in command line. Did you forget to use a connective?" +There was an atom on the command line where there should not be any. +The most likely reason is that an atom modifier option (such as \-F) +follows directly after a closing parenthesis. Adding a connective +(\-\-and, \-\-or) between the parenthesis and the option is often the +correct solution. +.SH COMPATIBILITY +If you use grep\-dctrl in a Debian package, you should depend on the +grep\-dctrl package and heed the following compatibility notes: +.PP +.IP "Always call only the \fBgrep\-dctrl\fR executable." +Although the \fBgrep\-status\fR and \fBgrep\-available\fR symlinks are installed +by default, this may change in the future. Those symlinks are meant +for manual and not scripted use. +.IP "Always specify an explicit file name" +Don't rely on the implicit file name feature. The system +administrator may have changed the default file name. You should +always specify the "\-" file, too. +.IP "Not all features have been with us in every version" +Check if any of the features you use is mentioned in the changelog. +Use a versioned dependency on grep\-dctrl, if it is necessary. +.SH FILES +.IP SYSCONF/grep\-dctrl.rc +See the next file. +.IP ~/.grep\-dctrlrc +These files are the default configuration files for +.BR grep\-dctrl . +The format is line-based, with `#' introducing a comment that lasts to +the end of the line. Each line defines one association between a +program name and a default input file. These two things are listed in +the line in order, separated by whitespace. Empty lines are ignored. +.sp +If the default input file name starts with two at (@) signs, one of +them is ignored. This allows specifying a file name that starts with +an at sign. If it starts with the string "@exec", the rest of the +name is interpreted as a command name that is fed to +.BR /bin/sh " " \-c , +and the standard output stream is used as the default input. +.IP /var/lib/dpkg/available +The default input file of +.B grep\-available +when the shipped configuration is in effect. +.IP /var/lib/dpkg/status +The default input file of +.B grep\-status +when the shipped configuration is in effect. +.SH AUTHOR +The program and this manual page were written by Antti-Juhani +Kaijanaho . Bill Allombert +provided one of the examples in the manual page. +.SH "SEE ALSO" +Ian Jackson et al.: Debian Packaging Manual. Published as the Debian +package packaging\-manual. Also available in the Debian website. The +Debian project, 2003. +.PP +.BR ara (1), +.BR apt\-cache (1), +.BR dpkg (8), +.BR dpkg\-awk (1), +.BR sgrep (1) +\" Local variables: +\" mode: nroff +\" End: + diff --git a/man/sort-dctrl.1.cp b/man/sort-dctrl.1.cp new file mode 100644 index 0000000..225cbd3 --- /dev/null +++ b/man/sort-dctrl.1.cp @@ -0,0 +1,129 @@ +.TH SORT-DCTRL 1 2005-06-08 "Debian Project" "Debian user's manual" +\" Copyright (C) 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; see the file COPYING. If not, write to +\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +\" Boston, MA 02111-1307, USA. +.SH NAME +sort\-dctrl \- sort Debian control files +.SH SYNOPSIS +.B sort\-dctrl +[options] +[ +.IR file " ..." +] +.sp +.B sort\-dctrl +\-\-copying | \-\-help | \-\-version | \-ChV +.SH DESCRIPTION +The +.B sort\-dctrl +program sorts Debian control files according to specified criteria. +.PP +A Debian control (dctrl) file is a semistructured single-table +database stored in a machine-parseable text file. Such a database +consists of a set of records; each record is a mapping from field +names to field content. Textually, records are separated by empty +lines, while each field is encoded as one or more nonempty lines +inside a record. A field starts with its name, followed by a colon, +followed by the field content. The colon must reside on the first +line of the field, and the first line must start with no whitespace. +Subsequent lines, in contrast, always start with linear whitespace +(one or more space or tab characters). +.PP +The +.B sort\-dctrl +program recognizes two field types: string fields and version fields. +Version fields act also as numeric fields. String fields are compared +according to strict lexicographical octet-by-octet comparison, after +ignoring any initial whitespace after the colon. Version fields are +parsed and compared as Debian version numbers. When comparing version +numbers, if a field content does not in fact conform to the version +number syntax, it compares less than any field content that does +conform, and equal to any other nonconforming field content. The +.B sort\-dctrl +program assumes all fields are string fields unless told otherwise. +.PP +You can specify arbitrary number of keys for sorting, using the \-k +option. The keys are interpreted in a descending order of priority: +the first key specified is primary, the second key specified is +secondary, and so on. If two records compare equal under the primary +key, then they are compared under the secondary key, and so on. If no +keys are specified, a default key is assumed (the "Package" field with +no modifiers). +.SH OPTIONS +.IP "-k KEYSPEC, --key-spec=KEYSPEC" +Specify one or more keys for sorting. You may specify this option any +number of times. The KEYSPEC argument consists of a comma-separated +list of key specifications. Each key specification consists of the +name of the field that serves as the key, optionally followed by a +colon and key modifiers. The following key modifiers are supported: +.RS +.TP +.B r +Invert the comparison for this key, reversing the sorting order. +.TP +.B v +Treat this field as a version number field. +.TP +.B n +Treat this field as numeric, which currently is synonymous with +.BR v . +.RE +.IP "\-q, \-\-quiet, \-\-silent" +Output nothing to the standard output stream. Instead, exit +immediately after finding the first match. +.IP "\-l LEVEL, \-\-errorlevel=LEVEL" +Set debugging level to LEVEL. LEVEL is one of "fatal", "important", +"informational" and "debug", but the last may not be available, +depending on the compile-time options. These categories are given +here in order; every message that is emitted when "fatal" is in +effect, will be emitted in the "important" error level, and so on. +The default is "important". +.IP "\-V, \-\-version" +Print out version information. +.IP "\-C, \-\-copying" +Print out the copyright license. This produces much output; be sure +to redirect or pipe it somewhere (such as your favourite pager). +.IP "\-h, \-\-help" +Print out a help summary. +.SH EXAMPLES +Here are some sample invocations of the program: +.IP "sort\-dctrl /var/lib/dpkg/available" +Output the dpkg available file sorted by the package name. +.IP "sort\-dctrl -k Version:v /var/lib/dpkg/available" +Output the dpkg available file sorted in ascending order of +version numbers. +.IP "sort\-dctrl -k Version:vr /var/lib/dpkg/available" +Output the dpkg available file sorted in descending order of +version numbers. +.IP "sort\-dctrl -k Package,Version:v /var/lib/dpkg/available" +Output the dpkg available file sorted primarily in ascending order of +package names and secondarily in descending order of version numbers. +.IP "sort\-dctrl -k Installed-Size:n,Size:nr /var/lib/dpkg/available" +Output the dpkg available file sorted primarily in ascending order of +installation sizes and secondarily in descending order of package +sizes. +.SH AUTHOR +The program and this manual page were written by Antti-Juhani +Kaijanaho . +.SH "SEE ALSO" +Ian Jackson et al.: Debian Policy Manual. Published as the Debian +package debian\-policy. Also available in the Debian website. The +Debian project, 2004. +.PP +.BR grep-dctrl (1) +\" Local variables: +\" mode: nroff +\" End: + diff --git a/man/sync-available.8 b/man/sync-available.8 new file mode 100644 index 0000000..a8fbbf9 --- /dev/null +++ b/man/sync-available.8 @@ -0,0 +1,60 @@ +.TH SYNC\-AVAILABLE 8 2005-07-11 "Debian Project" "Debian administrator's manual" +\" 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; see the file COPYING. If not, write to +\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +\" Boston, MA 02111-1307, USA. +.SH NAME +sync\-available \- sync dpkg's available database with apt's database +.SH SYNOPSIS +.B sync\-available +.sp +.B sync\-available --version +.sp +.B sync\-available --help +.SH DESCRIPTION +This program updates the +.BR dpkg (8) +available database with the data in the +.BR apt (8) +package database. This is required for +.BR grep\-available (1) +and other similar programs to give up-to-date results. +.PP +The same functionality is available through +.BR dselect (8), +when it is configured to use +.BR apt (8) +as its acquisition method. However, this program does not require that +.BR dselect (8) +is installed. +.SH "EXIT STATUS" +The program exits with status code 0 if all went well, and a non-zero +status code otherwise. +.SH OPTIONS +.IP --version +Output the name and version of the program onto standard output stream. +.IP --help +Give a brief usage statement on the standard output stream. +.SH ENVIRONMENT +.IP TMPDIR +The name of the directory where temporary files are created. Default is +.IR /tmp . +.SH AUTHOR +This program and this manual page were written by Antti-Juhani +Kaijanaho . +.SH SEE ALSO +.BR apt (8), +.BR dpkg (8), +.BR dselect (8), +.BR grep\-available (1) diff --git a/man/tbl-dctrl.1 b/man/tbl-dctrl.1 new file mode 100644 index 0000000..cca74ee --- /dev/null +++ b/man/tbl-dctrl.1 @@ -0,0 +1,203 @@ +.TH tbl\-dctrl 1 2006-04-02 "Debian Project" "Debian administrator's manual" +\" Copyright (C) 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; see the file COPYING. If not, write to +\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +\" Boston, MA 02111-1307, USA. +.SH NAME +tbl\-dctrl \- generate tabular representations of data in dctrl format +.SH SYNOPSIS +.B tbl\-dctrl +[ +.I options +] +.B \-c +.IR column\-specification " ..." +[ +.I filename +] ... +.sp +.B tbl\-dctrl +.B \-\-version +.sp +.B tbl\-dctrl +.B \-\-help +.SH DESCRIPTION +.B tbl\-dctrl +creates tabular representations of data given to it in Debian control +file format. +.PP +By default, +.B tbl\-dctrl +reads the whole database, looking for the longest entry in each +requested column; it then outputs a table, with borders and column +titles, where each column is just wide enough to fit the longest +entry. +. +Most of this behaviour can be customized as described below. +.PP +A column is requested by specifying the +.BR \-c " (" \-\-column ) +switch with a column specification. +. +The simplest kind of a column specification consists solely of the name +of a field. +. +In such a case, +.B tbl\-dctrl +will include in the output a column whose title is the literal column +specification and whose data is drawn from fields with that name. +. +At least one column must be requested. +.PP +There are two optional additions one can make to a column +specification. +. +Prefixing the field name with some text followed by an equality sign +(for example, +.BR "\-c 'Package name=Package'" ) +modifies the column in such a way that the text before the equality +sign is used as the column title, while the text after the equality +sign is used as the name of the field from which data is drawn. +. +One can also append a colon followed by a positive whole number to the +field name. In such a case, the number after the colon specifies the +width of the column. +. +These two additions can be used separately or together. +. +If there are more than one colon, the last one is significant. +. +If there are more than one equals sign, the first one is significant. +. +Other colons and equals signs are used simply as data. +. +Note that the whole column specification must be given to +.B tbl\-dctrl +as one argument, so if it contains spaces, it must be quoted for the +shell. +.PP +If all requested columns have a specified width, +.B tbl\-dctrl +will produce output immediately, not waiting for the whole input to be +read in. +.SH OPTIONS +.TP +.BI \-d " DELIMITER, " \-\-delimiter= DELIMITER +Instead of drawing nice borders to the table, use the specified +delimiter string to delimit columns in a row. +.TP +.BI \-l " LEVEL, " \-\-errorlevel= LEVEL +Set debugging level to LEVEL. LEVEL is one of "fatal", "important", +"informational" and "debug", but the last may not be available, +depending on the compile-time options. These categories are given +here in order; every message that is emitted when "fatal" is in +effect, will be emitted in the "important" error level, and so on. +The default is "important". +.TP +.BI \-V ", " \-\-version +Print out version information. +.TP +.BI \-C ", " \-\-copying +Print out the copyright license. This produces much output; be sure +to redirect or pipe it somewhere (such as your favourite pager). +.TP +.Bi \-h ", " \-\-help +Print out a help summary. +.SH OPERANDS +.B tbl\-dctrl +will read its input from the files named on the command line, +in the specified order. +. +A file called +.B \- +represents the program's standard input stream. +. +If no files are named, the program behaves as if +.B \- +alone had been named, that is, input is read from the standard input +stream. +.SH STDIN +The standard input stream may be used as input as specified above in +the OPERANDS section. +.SH "INPUT FILES" +All input to +.B tbl\-dctrl +is in the format of a Debian control file. +.PP +A Debian control (dctrl) file is a semistructured single-table +database stored in a machine-parseable text file. +. +Such a database consists of a set of records; each record is a mapping +from field names to field content. +. +Textually, records are separated by empty lines, while each field is +encoded as one or more nonempty lines inside a record. +. +A field starts with its name, followed by a colon, followed by the +field content. +. +The colon must reside on the first line of the field, and the first +line must start with no whitespace. +. +Subsequent lines, in contrast, always start with linear whitespace +(one or more space or tab characters). +.PP +When input is read from multiple files, a record separator is implicit +between two adjacent files. +.SH "ENVIRONMENT VARIABLES" +The standard locale environment, specifically its character set +setting, affects the interpretation of input and output as character +streams. +.SH "ASYNCHRONOUS EVENTS" +Standard Unix signals have their usual meaning. +.SH STDOUT +All output is sent to the standard output stream. +. +The output is a tabular representation of the input database restricted +to the specified fields. Logically, the output is a table; when the +.B \-d +option is used, this table is represented simply by separating columns +in each row by the specified delimiter; when the option is not used, a +frame is drawn around the table. The order of the columns is the same +as the order of the column specifications on the command line. +.SH "OUTPUT FILES" +There are no output files. +.SH "EXIT STATUS" +This utility exits with 0 when successful. It uses a nonzero exit +code inconsistently when an error is noticed (this is a bug). +.SH "CONSEQUENCES OF ERRORS" +In case of errors in the input, the output will be partially or +completely garbage. In case of errors in invocation, the program will +refuse to function. +.SH "EXAMPLES" +The following command line pipe outputs a table of all packages, with +their maintainer data, sorted by the maintainer data, that have no +content: +.nf +% grep\-available \-FInstalled\-Size \-\-eq 0 | sort\-dctrl \-kMaintainer \- | + tbl\-dctrl \-cPackage \-cMaintainer +.fi +.SH "SEE ALSO" +.BR ara (1), +.BR apt\-cache (1), +.BR dpkg (8), +.BR dpkg\-awk (1), +.BR dpkg\-query (1), +.BR grep\-dctrl (1), +.BR sort\-dctrl (1) +.SH AUTHOR +The +.B tbl\-dctrl +program and this manual page were written by Antti-Juhani Kaijanaho. + diff --git a/misc.c b/misc.c deleted file mode 100644 index 1b299b6..0000000 --- a/misc.c +++ /dev/null @@ -1,71 +0,0 @@ -/* 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 -#include -#include -#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/misc.h b/misc.h deleted file mode 100644 index 674f2b9..0000000 --- a/misc.h +++ /dev/null @@ -1,28 +0,0 @@ -/* 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/msg.c b/msg.c deleted file mode 100644 index 85e8382..0000000 --- a/msg.c +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 - -#include -#include -#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/msg.h b/msg.h deleted file mode 100644 index bda8531..0000000 --- a/msg.h +++ /dev/null @@ -1,173 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#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/para_bundle.c b/para_bundle.c deleted file mode 100644 index d52edbf..0000000 --- a/para_bundle.c +++ /dev/null @@ -1,67 +0,0 @@ -/* 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/para_bundle.h b/para_bundle.h deleted file mode 100644 index 2cb8a63..0000000 --- a/para_bundle.h +++ /dev/null @@ -1,70 +0,0 @@ -/* 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/para_pool.c b/para_pool.c deleted file mode 100644 index eb907ac..0000000 --- a/para_pool.c +++ /dev/null @@ -1,29 +0,0 @@ -/* 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/para_pool.h b/para_pool.h deleted file mode 100644 index 9ec37f5..0000000 --- a/para_pool.h +++ /dev/null @@ -1,93 +0,0 @@ -/* 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 -#include -#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/paragraph.c b/paragraph.c deleted file mode 100644 index 5dc06a4..0000000 --- a/paragraph.c +++ /dev/null @@ -1,209 +0,0 @@ -/* 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 -#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/paragraph.h b/paragraph.h deleted file mode 100644 index b3daafc..0000000 --- a/paragraph.h +++ /dev/null @@ -1,90 +0,0 @@ -/* 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 -#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/predicate.c b/predicate.c deleted file mode 100644 index 67614cc..0000000 --- a/predicate.c +++ /dev/null @@ -1,241 +0,0 @@ -/* 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 -#include -#include -#include -#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/predicate.h b/predicate.h deleted file mode 100644 index 5860b6f..0000000 --- a/predicate.h +++ /dev/null @@ -1,102 +0,0 @@ -/* 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 -#include -#include -#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/rc.c b/rc.c deleted file mode 100644 index b94b669..0000000 --- a/rc.c +++ /dev/null @@ -1,206 +0,0 @@ -/* grep-dctrl - grep Debian control files - 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; 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. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "msg.h" -#include "fnutil.h" -#include "getaline.h" -#include "rc.h" -#include "strutil.h" -#include "util.h" - -char const * const ifile_modes[] = { [m_error] = "m_error", - [m_read] = "m_read", - [m_exec] = "m_exec" }; - -static struct ifile parse(char * s) -{ - assert(s != 0); - s = (char*)left_trimmed(s); - trim_right(s); - if (*s == 0) return (struct ifile) { .mode = m_read, .s = "-" }; - static const char at_exec[] = "@exec "; - if (s[0] != '@') return (struct ifile){ .mode = m_read, .s = s }; - assert(s[0] == '@'); - if (s[1] == '@') return (struct ifile){ .mode = m_read, .s = s+1 }; - if (strncmp(s, at_exec, sizeof at_exec - 1) == 0) { - return (struct ifile){ .mode = m_exec, - .s = s + sizeof at_exec - 1 }; - } - debug_message(s, 0); - message(L_IMPORTANT, _("Malformed default input file name"), 0); - return (struct ifile){ .mode = m_error, .s = 0 }; -} - -static bool perms_ok(char const * fname, int fd) -{ - struct stat stat; - int r = fstat(fd, &stat); - if (r == -1) { - errno_msg(L_IMPORTANT, fname); - return false; - } - if (stat.st_uid != 0 && stat.st_uid != getuid()) { - message(L_IMPORTANT, _("not owned by you or root, ignoring"), - fname); - return false; - } - if ((stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) { - message(L_IMPORTANT, _("write permissions for " - "group or others, ignoring"), - fname); - return false; - } - return true; -} - -struct ifile find_ifile_by_exename(const char * exename, const char * rcfname) -{ - static const char * default_rcfiles [] = { - /* Order is significant: earlier files override later ones. */ - "~/.grep-dctrlrc", - SYSCONF "/grep-dctrl.rc", - 0 }; - int i; - char * fname; - int lineno; - FILE * f; - - assert(exename != 0); - - if (rcfname == 0) { - struct ifile rv = { .mode = m_error, .s = 0 }; - for (i = 0; - rv.mode == m_error && default_rcfiles [i] != 0; - i++) { - rv = find_ifile_by_exename(exename, default_rcfiles [i]); - } - return rv; - } - - assert(rcfname != 0); - - fname = fnqualify(rcfname); - if (fname == 0) { - errno_msg(L_IMPORTANT, rcfname); - return (struct ifile){ .mode = m_error, .s = 0 }; - } - - message(L_INFORMATIONAL, _("reading config file"), fname); - - f = fopen(fname, "r"); - if (f == 0) { - message(L_INFORMATIONAL, strerror(errno), fname); - return (struct ifile){ .mode = m_error, .s = 0 }; - } - - // check permissions - if (!perms_ok(fname, fileno(f))) { - return (struct ifile){ .mode = m_error, .s = 0 }; - } - - lineno = 0; - char * rv = 0; - while (1) { - static char * line = 0; - char * line_exe; - char * line_ifile; - - /* If this is not the first call, line may be non-null - and must be freed. It must be freed on all - non-first iterations, too. */ - free(line); - - line = getaline (f); - if (line == 0) { - message(L_FATAL, _("read failure or out of memory"), - fname); - fail(); - } - - ++lineno; - - if (*line == 0) { - rv = 0; - break; - } - - chop_comment(line, '#'); - chomp(line); - - if (left_trimmed(line) [0] == 0) { - continue; - } - - line_exe = strtok(line, " \t"); - if (line_exe == 0) { - line_message(L_IMPORTANT, - _("syntax error: need a executable name"), - fname, lineno); - continue; - } - - line_ifile = strtok(0, "\n\t"); - if (line_ifile == 0) { - line_message(L_IMPORTANT, - _("syntax error: need an input file name"), - fname, lineno); - continue; - } - - message(L_INFORMATIONAL, _("considering executable name"), line_exe); - if (strcmp (exename, line_exe) == 0) { - message(L_INFORMATIONAL, _("yes, will use executable name"), line_exe); - rv = line_ifile; - message(L_INFORMATIONAL, _("default input file"), rv); - break; - } - } - - fclose(f); - free(fname); - - if (rv != 0) { - return parse(rv); - } else { - message(L_IMPORTANT, _("executable name not found; " - "reading from standard input"), 0); - return (struct ifile) { .mode = m_read, .s = "-" }; - } -} - diff --git a/rc.h b/rc.h deleted file mode 100644 index e112424..0000000 --- a/rc.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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 RC_H__ -#define RC_H__ - -#include "ifile.h" - -extern char const * const ifile_modes[]; - -/* Search for exename (only basename, though) in rcfname (or if it is - null, in default rc files) and return the found corresponding - default input file name. The mode will be m_error, if not found or - other error occurred, m_read, if it is intended to be interpreted - as a file name, and m_exec, if the string is to be interpreted as a - shell command whose stdout should be used. The returned pointer - will be invalidated by the next call to this function. */ -struct ifile -find_ifile_by_exename (const char * exename, const char * rcfname); - -#endif diff --git a/sort-dctrl.1.cp b/sort-dctrl.1.cp deleted file mode 100644 index 225cbd3..0000000 --- a/sort-dctrl.1.cp +++ /dev/null @@ -1,129 +0,0 @@ -.TH SORT-DCTRL 1 2005-06-08 "Debian Project" "Debian user's manual" -\" Copyright (C) 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; see the file COPYING. If not, write to -\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -\" Boston, MA 02111-1307, USA. -.SH NAME -sort\-dctrl \- sort Debian control files -.SH SYNOPSIS -.B sort\-dctrl -[options] -[ -.IR file " ..." -] -.sp -.B sort\-dctrl -\-\-copying | \-\-help | \-\-version | \-ChV -.SH DESCRIPTION -The -.B sort\-dctrl -program sorts Debian control files according to specified criteria. -.PP -A Debian control (dctrl) file is a semistructured single-table -database stored in a machine-parseable text file. Such a database -consists of a set of records; each record is a mapping from field -names to field content. Textually, records are separated by empty -lines, while each field is encoded as one or more nonempty lines -inside a record. A field starts with its name, followed by a colon, -followed by the field content. The colon must reside on the first -line of the field, and the first line must start with no whitespace. -Subsequent lines, in contrast, always start with linear whitespace -(one or more space or tab characters). -.PP -The -.B sort\-dctrl -program recognizes two field types: string fields and version fields. -Version fields act also as numeric fields. String fields are compared -according to strict lexicographical octet-by-octet comparison, after -ignoring any initial whitespace after the colon. Version fields are -parsed and compared as Debian version numbers. When comparing version -numbers, if a field content does not in fact conform to the version -number syntax, it compares less than any field content that does -conform, and equal to any other nonconforming field content. The -.B sort\-dctrl -program assumes all fields are string fields unless told otherwise. -.PP -You can specify arbitrary number of keys for sorting, using the \-k -option. The keys are interpreted in a descending order of priority: -the first key specified is primary, the second key specified is -secondary, and so on. If two records compare equal under the primary -key, then they are compared under the secondary key, and so on. If no -keys are specified, a default key is assumed (the "Package" field with -no modifiers). -.SH OPTIONS -.IP "-k KEYSPEC, --key-spec=KEYSPEC" -Specify one or more keys for sorting. You may specify this option any -number of times. The KEYSPEC argument consists of a comma-separated -list of key specifications. Each key specification consists of the -name of the field that serves as the key, optionally followed by a -colon and key modifiers. The following key modifiers are supported: -.RS -.TP -.B r -Invert the comparison for this key, reversing the sorting order. -.TP -.B v -Treat this field as a version number field. -.TP -.B n -Treat this field as numeric, which currently is synonymous with -.BR v . -.RE -.IP "\-q, \-\-quiet, \-\-silent" -Output nothing to the standard output stream. Instead, exit -immediately after finding the first match. -.IP "\-l LEVEL, \-\-errorlevel=LEVEL" -Set debugging level to LEVEL. LEVEL is one of "fatal", "important", -"informational" and "debug", but the last may not be available, -depending on the compile-time options. These categories are given -here in order; every message that is emitted when "fatal" is in -effect, will be emitted in the "important" error level, and so on. -The default is "important". -.IP "\-V, \-\-version" -Print out version information. -.IP "\-C, \-\-copying" -Print out the copyright license. This produces much output; be sure -to redirect or pipe it somewhere (such as your favourite pager). -.IP "\-h, \-\-help" -Print out a help summary. -.SH EXAMPLES -Here are some sample invocations of the program: -.IP "sort\-dctrl /var/lib/dpkg/available" -Output the dpkg available file sorted by the package name. -.IP "sort\-dctrl -k Version:v /var/lib/dpkg/available" -Output the dpkg available file sorted in ascending order of -version numbers. -.IP "sort\-dctrl -k Version:vr /var/lib/dpkg/available" -Output the dpkg available file sorted in descending order of -version numbers. -.IP "sort\-dctrl -k Package,Version:v /var/lib/dpkg/available" -Output the dpkg available file sorted primarily in ascending order of -package names and secondarily in descending order of version numbers. -.IP "sort\-dctrl -k Installed-Size:n,Size:nr /var/lib/dpkg/available" -Output the dpkg available file sorted primarily in ascending order of -installation sizes and secondarily in descending order of package -sizes. -.SH AUTHOR -The program and this manual page were written by Antti-Juhani -Kaijanaho . -.SH "SEE ALSO" -Ian Jackson et al.: Debian Policy Manual. Published as the Debian -package debian\-policy. Also available in the Debian website. The -Debian project, 2004. -.PP -.BR grep-dctrl (1) -\" Local variables: -\" mode: nroff -\" End: - diff --git a/sort-dctrl.c b/sort-dctrl.c deleted file mode 100644 index 725687c..0000000 --- a/sort-dctrl.c +++ /dev/null @@ -1,177 +0,0 @@ -/* 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 -#include -#include -#include "misc.h" -#include "msg.h" -#include "i18n.h" -#include "ifile.h" -#include "para_bundle.h" -#include "sorter.h" - -enum { - OPT_SILENT = 256, - OPT_MMAP -}; - -const char * argp_program_version = "sort-dctrl (dctrl-tools) " VERSION; -const char * argp_program_bug_address = MAINTAINER; - -static struct argp_option options[] = { - { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, - { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, - { "key-spec", 'k', N_("KEYSPEC"), 0, N_("Specify sort keys.") }, - { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, - { 0 } -}; - -#define MAX_FNAMES 4096 - -struct arguments { - /* Sort key specifications */ - keys_t keys; - /* Number of file names seen. */ - size_t num_fnames; - /* File names seen on the command line. */ - struct ifile fname[MAX_FNAMES]; -}; - -static error_t parse_opt (int key, char * arg, struct argp_state * state) -{ - struct arguments * args = state->input; - switch (key) { - case 'C': - if (!to_stdout (COPYING)) fail(); - exit(0); - case 'k': - { - char *carg = strdup(arg); - if (carg == 0) fatal_enomem(0); - for (char *s = strtok(carg, ","); s != 0; s = strtok(0, ",")) { - char *flags = strrchr(s, ':'); - if (flags == 0) { - flags = ""; - } else { - *(flags++) = '\0'; - } - struct key key; - key.field_inx = fieldtrie_insert(carg); - key.type = FT_STRING; - key.reverse = false; - for (char *p = flags; *p != '\0'; p++) { - switch (*p) { - case 'r': - key.reverse = true; - break; - case 'v': case 'n': - key.type = FT_VERSION; - break; - default: - message(L_FATAL, _("invalid key flag"), - 0); - fail(); - } - } - keys_append(&args->keys, key); - } - break; - } - case 'l': - { - int ll = str2loglevel(arg); - if (ll < 0) - { - message(L_FATAL, _("no such log level"), arg); - fail(); - } - set_loglevel(ll); - debug_message("parse_opt: l", 0); - } - break; - case OPT_MMAP: - debug_message("parse_opt: mmap", 0); - fsaf_mmap = 1; - break; - case ARGP_KEY_ARG: - debug_message("parse_opt: argument", 0); - { - char const * s; - if (args->num_fnames >= MAX_FNAMES) { - message(L_FATAL, _("too many file names"), 0); - fail(); - } - s = strdup(arg); - if (s == 0) fatal_enomem(0); - args->fname[args->num_fnames++] = - (struct ifile){ .mode = m_read, .s = s }; - } - break; - case ARGP_KEY_END: - case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: - case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: - debug_message("parse_opt: ignored", 0); - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static char progdoc [] = N_("sort-dctrl -- sort Debian control files"); - -static struct argp argp = { options, parse_opt, 0, progdoc }; - -int main(int argc, char * argv[]) -{ - setlocale(LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - fieldtrie_init(); - - static struct arguments args; - keys_init(&args.keys); - msg_set_progname(argv[0]); - argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); - - if (args.keys.nks == 0) { - size_t inx = fieldtrie_insert("Package"); - keys_append(&args.keys, (struct key){ .field_inx = inx, - .type = FT_STRING, - .reverse = false }); - } - - struct para_bundle pb; - bundle_init(&pb); - - for (size_t i = 0; i < args.num_fnames; i++) { - bundle_slurp(&pb, args.fname[i]); - } - - sort_bundle(&args.keys, &pb); - size_t num_paras = bundle_size(&pb); - para_t ** paras = bundle_vec(&pb); - for (size_t i = 0; i < num_paras; i++) { - struct fsaf_read_rv r = get_whole_para(paras[i]); - fwrite(r.b, 1, r.len, stdout); - putchar('\n'); - } - return 0; -} diff --git a/sort-dctrl/sort-dctrl.c b/sort-dctrl/sort-dctrl.c new file mode 100644 index 0000000..725687c --- /dev/null +++ b/sort-dctrl/sort-dctrl.c @@ -0,0 +1,177 @@ +/* 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 +#include +#include +#include "misc.h" +#include "msg.h" +#include "i18n.h" +#include "ifile.h" +#include "para_bundle.h" +#include "sorter.h" + +enum { + OPT_SILENT = 256, + OPT_MMAP +}; + +const char * argp_program_version = "sort-dctrl (dctrl-tools) " VERSION; +const char * argp_program_bug_address = MAINTAINER; + +static struct argp_option options[] = { + { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, + { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, + { "key-spec", 'k', N_("KEYSPEC"), 0, N_("Specify sort keys.") }, + { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, + { 0 } +}; + +#define MAX_FNAMES 4096 + +struct arguments { + /* Sort key specifications */ + keys_t keys; + /* Number of file names seen. */ + size_t num_fnames; + /* File names seen on the command line. */ + struct ifile fname[MAX_FNAMES]; +}; + +static error_t parse_opt (int key, char * arg, struct argp_state * state) +{ + struct arguments * args = state->input; + switch (key) { + case 'C': + if (!to_stdout (COPYING)) fail(); + exit(0); + case 'k': + { + char *carg = strdup(arg); + if (carg == 0) fatal_enomem(0); + for (char *s = strtok(carg, ","); s != 0; s = strtok(0, ",")) { + char *flags = strrchr(s, ':'); + if (flags == 0) { + flags = ""; + } else { + *(flags++) = '\0'; + } + struct key key; + key.field_inx = fieldtrie_insert(carg); + key.type = FT_STRING; + key.reverse = false; + for (char *p = flags; *p != '\0'; p++) { + switch (*p) { + case 'r': + key.reverse = true; + break; + case 'v': case 'n': + key.type = FT_VERSION; + break; + default: + message(L_FATAL, _("invalid key flag"), + 0); + fail(); + } + } + keys_append(&args->keys, key); + } + break; + } + case 'l': + { + int ll = str2loglevel(arg); + if (ll < 0) + { + message(L_FATAL, _("no such log level"), arg); + fail(); + } + set_loglevel(ll); + debug_message("parse_opt: l", 0); + } + break; + case OPT_MMAP: + debug_message("parse_opt: mmap", 0); + fsaf_mmap = 1; + break; + case ARGP_KEY_ARG: + debug_message("parse_opt: argument", 0); + { + char const * s; + if (args->num_fnames >= MAX_FNAMES) { + message(L_FATAL, _("too many file names"), 0); + fail(); + } + s = strdup(arg); + if (s == 0) fatal_enomem(0); + args->fname[args->num_fnames++] = + (struct ifile){ .mode = m_read, .s = s }; + } + break; + case ARGP_KEY_END: + case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: + debug_message("parse_opt: ignored", 0); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static char progdoc [] = N_("sort-dctrl -- sort Debian control files"); + +static struct argp argp = { options, parse_opt, 0, progdoc }; + +int main(int argc, char * argv[]) +{ + setlocale(LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + fieldtrie_init(); + + static struct arguments args; + keys_init(&args.keys); + msg_set_progname(argv[0]); + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + + if (args.keys.nks == 0) { + size_t inx = fieldtrie_insert("Package"); + keys_append(&args.keys, (struct key){ .field_inx = inx, + .type = FT_STRING, + .reverse = false }); + } + + struct para_bundle pb; + bundle_init(&pb); + + for (size_t i = 0; i < args.num_fnames; i++) { + bundle_slurp(&pb, args.fname[i]); + } + + sort_bundle(&args.keys, &pb); + size_t num_paras = bundle_size(&pb); + para_t ** paras = bundle_vec(&pb); + for (size_t i = 0; i < num_paras; i++) { + struct fsaf_read_rv r = get_whole_para(paras[i]); + fwrite(r.b, 1, r.len, stdout); + putchar('\n'); + } + return 0; +} diff --git a/sorter.c b/sorter.c deleted file mode 100644 index c8ae301..0000000 --- a/sorter.c +++ /dev/null @@ -1,85 +0,0 @@ -/* 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 - -#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/sorter.h b/sorter.h deleted file mode 100644 index 7b3ada2..0000000 --- a/sorter.h +++ /dev/null @@ -1,77 +0,0 @@ -/* 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 -#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/strutil.c b/strutil.c deleted file mode 100644 index 585abc1..0000000 --- a/strutil.c +++ /dev/null @@ -1,157 +0,0 @@ -/* 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 -#include -#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 -#include -#include - -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/strutil.h b/strutil.h deleted file mode 100644 index 431e9e1..0000000 --- a/strutil.h +++ /dev/null @@ -1,54 +0,0 @@ -/* 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 -#include -#include -#include - -/* 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/sync-available.8 b/sync-available.8 deleted file mode 100644 index a8fbbf9..0000000 --- a/sync-available.8 +++ /dev/null @@ -1,60 +0,0 @@ -.TH SYNC\-AVAILABLE 8 2005-07-11 "Debian Project" "Debian administrator's manual" -\" 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; see the file COPYING. If not, write to -\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -\" Boston, MA 02111-1307, USA. -.SH NAME -sync\-available \- sync dpkg's available database with apt's database -.SH SYNOPSIS -.B sync\-available -.sp -.B sync\-available --version -.sp -.B sync\-available --help -.SH DESCRIPTION -This program updates the -.BR dpkg (8) -available database with the data in the -.BR apt (8) -package database. This is required for -.BR grep\-available (1) -and other similar programs to give up-to-date results. -.PP -The same functionality is available through -.BR dselect (8), -when it is configured to use -.BR apt (8) -as its acquisition method. However, this program does not require that -.BR dselect (8) -is installed. -.SH "EXIT STATUS" -The program exits with status code 0 if all went well, and a non-zero -status code otherwise. -.SH OPTIONS -.IP --version -Output the name and version of the program onto standard output stream. -.IP --help -Give a brief usage statement on the standard output stream. -.SH ENVIRONMENT -.IP TMPDIR -The name of the directory where temporary files are created. Default is -.IR /tmp . -.SH AUTHOR -This program and this manual page were written by Antti-Juhani -Kaijanaho . -.SH SEE ALSO -.BR apt (8), -.BR dpkg (8), -.BR dselect (8), -.BR grep\-available (1) diff --git a/sync-available.cp b/sync-available.cp deleted file mode 100644 index c833ac2..0000000 --- a/sync-available.cp +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# 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 - -set -e - -aptcache=/usr/bin/apt-cache - -if [ $# -ge 1 ] -then - if [ "x$@" = "x--version" ] - then - echo "update-avail (dctrl-tools) VERSION" - else - echo "Usage:" - echo " $0" - echo " $0 --version" - echo " $0 --help" - fi - exit 0 -fi - -if [ "$EUID" -ne "0" ] -then - echo "$0: root privileges are required" - exit 1 -fi - -if [ ! -x $aptcache ] -then - echo "$0: $aptcache not executable, aborting" 1>&2 - exit 1 -fi - -tf=`mktemp -t apt-available.XXXXXX` || exit 1 -echo -n "Merging available database in $tf..." -$aptcache dumpavail >> $tf -echo "done." -dpkg --update-avail $tf -rm $tf diff --git a/sync-available/sync-available.cp b/sync-available/sync-available.cp new file mode 100644 index 0000000..c833ac2 --- /dev/null +++ b/sync-available/sync-available.cp @@ -0,0 +1,54 @@ +#!/bin/sh +# 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 + +set -e + +aptcache=/usr/bin/apt-cache + +if [ $# -ge 1 ] +then + if [ "x$@" = "x--version" ] + then + echo "update-avail (dctrl-tools) VERSION" + else + echo "Usage:" + echo " $0" + echo " $0 --version" + echo " $0 --help" + fi + exit 0 +fi + +if [ "$EUID" -ne "0" ] +then + echo "$0: root privileges are required" + exit 1 +fi + +if [ ! -x $aptcache ] +then + echo "$0: $aptcache not executable, aborting" 1>&2 + exit 1 +fi + +tf=`mktemp -t apt-available.XXXXXX` || exit 1 +echo -n "Merging available database in $tf..." +$aptcache dumpavail >> $tf +echo "done." +dpkg --update-avail $tf +rm $tf diff --git a/tbl-dctrl.1 b/tbl-dctrl.1 deleted file mode 100644 index cca74ee..0000000 --- a/tbl-dctrl.1 +++ /dev/null @@ -1,203 +0,0 @@ -.TH tbl\-dctrl 1 2006-04-02 "Debian Project" "Debian administrator's manual" -\" Copyright (C) 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; see the file COPYING. If not, write to -\" the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -\" Boston, MA 02111-1307, USA. -.SH NAME -tbl\-dctrl \- generate tabular representations of data in dctrl format -.SH SYNOPSIS -.B tbl\-dctrl -[ -.I options -] -.B \-c -.IR column\-specification " ..." -[ -.I filename -] ... -.sp -.B tbl\-dctrl -.B \-\-version -.sp -.B tbl\-dctrl -.B \-\-help -.SH DESCRIPTION -.B tbl\-dctrl -creates tabular representations of data given to it in Debian control -file format. -.PP -By default, -.B tbl\-dctrl -reads the whole database, looking for the longest entry in each -requested column; it then outputs a table, with borders and column -titles, where each column is just wide enough to fit the longest -entry. -. -Most of this behaviour can be customized as described below. -.PP -A column is requested by specifying the -.BR \-c " (" \-\-column ) -switch with a column specification. -. -The simplest kind of a column specification consists solely of the name -of a field. -. -In such a case, -.B tbl\-dctrl -will include in the output a column whose title is the literal column -specification and whose data is drawn from fields with that name. -. -At least one column must be requested. -.PP -There are two optional additions one can make to a column -specification. -. -Prefixing the field name with some text followed by an equality sign -(for example, -.BR "\-c 'Package name=Package'" ) -modifies the column in such a way that the text before the equality -sign is used as the column title, while the text after the equality -sign is used as the name of the field from which data is drawn. -. -One can also append a colon followed by a positive whole number to the -field name. In such a case, the number after the colon specifies the -width of the column. -. -These two additions can be used separately or together. -. -If there are more than one colon, the last one is significant. -. -If there are more than one equals sign, the first one is significant. -. -Other colons and equals signs are used simply as data. -. -Note that the whole column specification must be given to -.B tbl\-dctrl -as one argument, so if it contains spaces, it must be quoted for the -shell. -.PP -If all requested columns have a specified width, -.B tbl\-dctrl -will produce output immediately, not waiting for the whole input to be -read in. -.SH OPTIONS -.TP -.BI \-d " DELIMITER, " \-\-delimiter= DELIMITER -Instead of drawing nice borders to the table, use the specified -delimiter string to delimit columns in a row. -.TP -.BI \-l " LEVEL, " \-\-errorlevel= LEVEL -Set debugging level to LEVEL. LEVEL is one of "fatal", "important", -"informational" and "debug", but the last may not be available, -depending on the compile-time options. These categories are given -here in order; every message that is emitted when "fatal" is in -effect, will be emitted in the "important" error level, and so on. -The default is "important". -.TP -.BI \-V ", " \-\-version -Print out version information. -.TP -.BI \-C ", " \-\-copying -Print out the copyright license. This produces much output; be sure -to redirect or pipe it somewhere (such as your favourite pager). -.TP -.Bi \-h ", " \-\-help -Print out a help summary. -.SH OPERANDS -.B tbl\-dctrl -will read its input from the files named on the command line, -in the specified order. -. -A file called -.B \- -represents the program's standard input stream. -. -If no files are named, the program behaves as if -.B \- -alone had been named, that is, input is read from the standard input -stream. -.SH STDIN -The standard input stream may be used as input as specified above in -the OPERANDS section. -.SH "INPUT FILES" -All input to -.B tbl\-dctrl -is in the format of a Debian control file. -.PP -A Debian control (dctrl) file is a semistructured single-table -database stored in a machine-parseable text file. -. -Such a database consists of a set of records; each record is a mapping -from field names to field content. -. -Textually, records are separated by empty lines, while each field is -encoded as one or more nonempty lines inside a record. -. -A field starts with its name, followed by a colon, followed by the -field content. -. -The colon must reside on the first line of the field, and the first -line must start with no whitespace. -. -Subsequent lines, in contrast, always start with linear whitespace -(one or more space or tab characters). -.PP -When input is read from multiple files, a record separator is implicit -between two adjacent files. -.SH "ENVIRONMENT VARIABLES" -The standard locale environment, specifically its character set -setting, affects the interpretation of input and output as character -streams. -.SH "ASYNCHRONOUS EVENTS" -Standard Unix signals have their usual meaning. -.SH STDOUT -All output is sent to the standard output stream. -. -The output is a tabular representation of the input database restricted -to the specified fields. Logically, the output is a table; when the -.B \-d -option is used, this table is represented simply by separating columns -in each row by the specified delimiter; when the option is not used, a -frame is drawn around the table. The order of the columns is the same -as the order of the column specifications on the command line. -.SH "OUTPUT FILES" -There are no output files. -.SH "EXIT STATUS" -This utility exits with 0 when successful. It uses a nonzero exit -code inconsistently when an error is noticed (this is a bug). -.SH "CONSEQUENCES OF ERRORS" -In case of errors in the input, the output will be partially or -completely garbage. In case of errors in invocation, the program will -refuse to function. -.SH "EXAMPLES" -The following command line pipe outputs a table of all packages, with -their maintainer data, sorted by the maintainer data, that have no -content: -.nf -% grep\-available \-FInstalled\-Size \-\-eq 0 | sort\-dctrl \-kMaintainer \- | - tbl\-dctrl \-cPackage \-cMaintainer -.fi -.SH "SEE ALSO" -.BR ara (1), -.BR apt\-cache (1), -.BR dpkg (8), -.BR dpkg\-awk (1), -.BR dpkg\-query (1), -.BR grep\-dctrl (1), -.BR sort\-dctrl (1) -.SH AUTHOR -The -.B tbl\-dctrl -program and this manual page were written by Antti-Juhani Kaijanaho. - diff --git a/tbl-dctrl.c b/tbl-dctrl.c deleted file mode 100644 index 0fca2ea..0000000 --- a/tbl-dctrl.c +++ /dev/null @@ -1,426 +0,0 @@ -/* dctrl-tools - Debian control file inspection tools - Copyright (C) 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 -#include -#include -#include "misc.h" -#include "msg.h" -#include "i18n.h" -#include "ifile.h" -#include "para_bundle.h" -#include "sorter.h" - -enum { - OPT_SILENT = 256, - OPT_MMAP -}; - -const char * argp_program_version = "tbl-dctrl (dctrl-tools) " VERSION; -const char * argp_program_bug_address = MAINTAINER; - -static struct argp_option options[] = { - { "delimiter", 'd', N_("DELIM"), 0, N_("Specify a delimiter.") }, - { "column", 'c', N_("SPEC"), 0, N_("Append the specified column.") }, - { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, - { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, - { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, - { 0 } -}; - -#define MAX_FNAMES 4096 -#define MAX_COLUMNS 4096 - -struct arguments { - char *delim; // NULL if not specced - /* True iff some column lengths are unknown and need to be - * calculated. */ - _Bool need_preprocessing; - /* Number of columns specified */ - size_t num_columns; - /* Number of file names seen. */ - size_t num_fnames; - /* Columns specified on the command line. */ - struct column { - char *heading; - size_t field_inx; - size_t column_width; - } columns[MAX_COLUMNS]; - /* File names seen on the command line. */ - struct ifile fname[MAX_FNAMES]; -}; - -#define INSERT(c) do { \ - if (len >= max) { \ - max *= 2; \ - rv = realloc(rv, max); \ - if (rv == NULL) fatal_enomem(NULL); \ - } \ - rv[len++] = (c); \ -} while (0) - -size_t linewrap(char **res, char const *orig, size_t orig_len, - size_t col_width) -{ - assert(col_width > 0); - - size_t max = 16; - char *rv = malloc(max); - size_t len = 0; - - size_t ll = 0; - size_t bpo = 0, bpr = 0; - - size_t num_lines = 1; - - mblen(NULL, 0); - for (size_t i = 0; i < orig_len; /**/) { - if (ll == col_width) { - if (rv[bpr] == '\n') { - // no suitable breakpoint on this line, - // break here - } else { - assert(bpr < len); - len = bpr; - assert(bpo < i); - i = bpo; - } - ll = 0; - INSERT('\n'); - num_lines++; - continue; - } - if (orig[i] == ' ' || orig[i] == '\t') { - // record this breakpoint - bpo = i; - bpr = len; - } - int n = mblen(orig + i, orig_len - i); - if (n <= 0) break; - for (size_t j = 0; j < n; j++) INSERT(orig[i+j]); - i += n; - ll++; - } - INSERT('\0'); - *res = rv; - return num_lines; -} - - -void print_line(struct arguments *args, - struct fsaf_read_rv const columns[/*args->num_columns*/]) -{ - struct print_line_data { - char *wrapped; - size_t wrapped_len; - size_t inx; - } data[args->num_columns]; - size_t num_lines = 0; - for (size_t i = 0; i < args->num_columns; i++) { - size_t n = linewrap(&data[i].wrapped, - columns[i].b, columns[i].len, - args->columns[i].column_width); - if (n > num_lines) num_lines = n; - data[i].inx = 0; - data[i].wrapped_len = strlen(data[i].wrapped); - } - for (size_t i = 0; i < num_lines; i++) { - for (size_t j = 0; j < args->num_columns; j++) { - if (args->delim == NULL) { - fputs("| ", stdout); - } else if (j > 0) { - fputs(args->delim, stdout); - } - mblen(NULL, 0); - size_t k; - size_t m = 0; - for (k = data[j].inx; - k < data[j].wrapped_len; - /**/) { - if (data[j].wrapped[k] == '\n') { - k++; - break; - } - int n = mblen(data[j].wrapped + k, - data[j].wrapped_len - k); - for (int l = 0; l < n; l++) { - putchar(data[j].wrapped[k+l]); - } - if (n <= 0) n = 1; - k += n; - m++; - } - - if (args->delim == NULL) { - while (m < args->columns[j].column_width) { - m++; - putchar(' '); - } - putchar(' '); - } - data[j].inx = k; - } - if (args->delim == NULL) fputs("|", stdout); - putchar('\n'); - } - for (size_t i = 0; i < args->num_columns; i++) free(data[i].wrapped); -} - -void print_head(struct arguments *args) -{ - if (args->delim == NULL) { - for (size_t i = 0; i < args->num_columns; i++) { - putchar('+'); - for (size_t j = 0; - j < args->columns[i].column_width + 2; - j++) { - putchar('='); - } - } - putchar('+'); - putchar('\n'); - } - - struct fsaf_read_rv columns[args->num_columns]; - for (size_t i = 0; i < args->num_columns; i++) { - columns[i].b = args->columns[i].heading; - columns[i].len = strlen(columns[i].b); - } - print_line(args, columns); - - if (args->delim == NULL) { - for (size_t i = 0; i < args->num_columns; i++) { - putchar('+'); - for (size_t j = 0; j < args->columns[i].column_width + 2; j++) { - putchar('-'); - } - } - putchar('+'); - putchar('\n'); - } -} - -void print_foot(struct arguments *args) -{ - if (args->delim != NULL) return; - for (size_t i = 0; i < args->num_columns; i++) { - putchar('+'); - for (size_t j = 0; j < args->columns[i].column_width + 2; j++) { - putchar('='); - } - } - putchar('+'); - putchar('\n'); -} - -void handle_para(struct arguments *args, struct paragraph *para) -{ - struct fsaf_read_rv columns[args->num_columns]; - for (size_t i = 0; i < args->num_columns; i++) { - columns[i] = get_field(para, args->columns[i].field_inx, -1); - } - print_line(args, columns); -} - -static error_t parse_opt (int key, char * arg, struct argp_state * state) -{ - struct arguments * args = state->input; - switch (key) { - case 'C': - if (!to_stdout (COPYING)) fail(); - exit(0); - case 'c': { - char *carg = strdup(arg); - if (carg == NULL) fatal_enomem(0); - char *lens = strrchr(carg, ':'); - if (lens != NULL) *(lens++) = '\0'; else lens = ""; - char *fn = strchr(carg, '='); - if (fn != NULL) *(fn++) = '\0'; - struct column *col = &args->columns[args->num_columns]; - col->heading = carg; - col->field_inx = fieldtrie_insert(fn == NULL ? carg : fn); - size_t n = 0; - _Bool err = 0; - for (char const *p = lens; *p != '\0'; p++) { - if (!('0' <= *p && *p <= '9')) { - err = 1; - continue; - } - n = n * 10 + (*p - '0'); - } - if (err) message(L_IMPORTANT, _("invalid column length"), NULL); - col->column_width = n > 0 ? n : -1; - if (n == 0) args->need_preprocessing = 1; - args->num_columns++; - } - break; - case 'd': - args->delim = strdup(arg); - if (args->delim == NULL) fatal_enomem(NULL); - break; - case 'l': { - int ll = str2loglevel(arg); - if (ll < 0) - { - message(L_FATAL, _("no such log level"), arg); - fail(); - } - set_loglevel(ll); - debug_message("parse_opt: l", 0); - } - break; - case ARGP_KEY_ARG: - debug_message("parse_opt: argument", 0); - { - char const * s; - if (args->num_fnames >= MAX_FNAMES) { - message(L_FATAL, _("too many file names"), 0); - fail(); - } - s = strdup(arg); - if (s == 0) fatal_enomem(0); - args->fname[args->num_fnames++] = - (struct ifile){ .mode = m_read, .s = s }; - } - break; - case OPT_MMAP: - debug_message("parse_opt: mmap", 0); - fsaf_mmap = 1; - break; - case ARGP_KEY_END: - case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: - case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: - debug_message("parse_opt: ignored", 0); - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static char progdoc [] = -N_("tbl-dctrl -- tabularize Debian control files"); - -static struct argp argp = { options, parse_opt, 0, progdoc }; - -static size_t mbs_len(char const *mbs, size_t n, char const *fname) -{ - if (n == (size_t)(-1)) n = strlen(mbs); - size_t len = 0; - mblen(NULL, 0); - for (size_t k = 0; k < n;/**/) { - if (mbs[k] == '\n') break; - len++; - int delta = mblen(mbs + k, n - k); - if (delta <= 0) { - message(L_IMPORTANT, _("bad multibyte character"), - fname); - k++; - } else { - k += delta; - } - } - return len; -} - -int main(int argc, char * argv[]) -{ - setlocale(LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - fieldtrie_init(); - - static struct arguments args; - - //keys_init(&args.keys); - msg_set_progname(argv[0]); - argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); - - if (args.num_fnames == 0) { - args.fname[args.num_fnames++] = (struct ifile){ - .mode = m_read, .s = "-" - }; - } - - if (args.need_preprocessing) { - struct para_bundle pb; - - bundle_init(&pb); - - for (size_t i = 0; i < args.num_fnames; i++) { - bundle_slurp(&pb, args.fname[i]); - } - - size_t n = fieldtrie_count(); - size_t lens[n]; - for (size_t i = 0; i < n; i++) lens[i] = 0; - - for (size_t i = 0; i < pb.num_paras; i++) { - struct paragraph *p = pb.paras[i]; - assert(p->nfields == n); - for (size_t j = 0; j < p->nfields; j++) { - struct fsaf_read_rv r = get_field(p, j, -1); - size_t len = mbs_len(r.b, r.len, - p->common->fp->fname); - if (len > lens[j]) lens[j] = len; - } - } - for (size_t i = 0; i < args.num_columns; i++) { - size_t width = args.columns[i].column_width; - size_t headw = mbs_len(args.columns[i].heading, -1, 0); - if (width == (size_t)(-1)) width = lens[i]; - if (headw > width) width = headw; - args.columns[i].column_width = width; - } - print_head(&args); - for (size_t i = 0; i < pb.num_paras; i++) { - handle_para(&args, pb.paras[i]); - } - print_foot(&args); - } else { - print_head(&args); - for (size_t i = 0; i < args.num_fnames; ++i) { - int fd; - struct ifile fname = args.fname[i]; - - if (fname.mode == m_error) continue; - - fd = open_ifile(fname); - if (fd == -1) break; - - if (!chk_ifile(fname, fd)) break; - - FSAF * fp = fsaf_fdopen(fd, fname.s); - para_parser_t pp; - para_parser_init(&pp, fp, true, false); - para_t para; - para_init(&pp, ¶); - while (1) { - para_parse_next(¶); - if (para_eof(&pp)) break; - handle_para(&args, ¶); - } - - close_ifile(fname, fd); - } - print_foot(&args); - } - - return 0; -} diff --git a/tbl-dctrl/tbl-dctrl.c b/tbl-dctrl/tbl-dctrl.c new file mode 100644 index 0000000..0fca2ea --- /dev/null +++ b/tbl-dctrl/tbl-dctrl.c @@ -0,0 +1,426 @@ +/* dctrl-tools - Debian control file inspection tools + Copyright (C) 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 +#include +#include +#include "misc.h" +#include "msg.h" +#include "i18n.h" +#include "ifile.h" +#include "para_bundle.h" +#include "sorter.h" + +enum { + OPT_SILENT = 256, + OPT_MMAP +}; + +const char * argp_program_version = "tbl-dctrl (dctrl-tools) " VERSION; +const char * argp_program_bug_address = MAINTAINER; + +static struct argp_option options[] = { + { "delimiter", 'd', N_("DELIM"), 0, N_("Specify a delimiter.") }, + { "column", 'c', N_("SPEC"), 0, N_("Append the specified column.") }, + { "copying", 'C', 0, 0, N_("Print out the copyright license.") }, + { "errorlevel", 'l', N_("LEVEL"), 0, N_("Set debugging level to LEVEL.") }, + { "mmap", OPT_MMAP, 0, 0, N_("Attempt mmapping input files") }, + { 0 } +}; + +#define MAX_FNAMES 4096 +#define MAX_COLUMNS 4096 + +struct arguments { + char *delim; // NULL if not specced + /* True iff some column lengths are unknown and need to be + * calculated. */ + _Bool need_preprocessing; + /* Number of columns specified */ + size_t num_columns; + /* Number of file names seen. */ + size_t num_fnames; + /* Columns specified on the command line. */ + struct column { + char *heading; + size_t field_inx; + size_t column_width; + } columns[MAX_COLUMNS]; + /* File names seen on the command line. */ + struct ifile fname[MAX_FNAMES]; +}; + +#define INSERT(c) do { \ + if (len >= max) { \ + max *= 2; \ + rv = realloc(rv, max); \ + if (rv == NULL) fatal_enomem(NULL); \ + } \ + rv[len++] = (c); \ +} while (0) + +size_t linewrap(char **res, char const *orig, size_t orig_len, + size_t col_width) +{ + assert(col_width > 0); + + size_t max = 16; + char *rv = malloc(max); + size_t len = 0; + + size_t ll = 0; + size_t bpo = 0, bpr = 0; + + size_t num_lines = 1; + + mblen(NULL, 0); + for (size_t i = 0; i < orig_len; /**/) { + if (ll == col_width) { + if (rv[bpr] == '\n') { + // no suitable breakpoint on this line, + // break here + } else { + assert(bpr < len); + len = bpr; + assert(bpo < i); + i = bpo; + } + ll = 0; + INSERT('\n'); + num_lines++; + continue; + } + if (orig[i] == ' ' || orig[i] == '\t') { + // record this breakpoint + bpo = i; + bpr = len; + } + int n = mblen(orig + i, orig_len - i); + if (n <= 0) break; + for (size_t j = 0; j < n; j++) INSERT(orig[i+j]); + i += n; + ll++; + } + INSERT('\0'); + *res = rv; + return num_lines; +} + + +void print_line(struct arguments *args, + struct fsaf_read_rv const columns[/*args->num_columns*/]) +{ + struct print_line_data { + char *wrapped; + size_t wrapped_len; + size_t inx; + } data[args->num_columns]; + size_t num_lines = 0; + for (size_t i = 0; i < args->num_columns; i++) { + size_t n = linewrap(&data[i].wrapped, + columns[i].b, columns[i].len, + args->columns[i].column_width); + if (n > num_lines) num_lines = n; + data[i].inx = 0; + data[i].wrapped_len = strlen(data[i].wrapped); + } + for (size_t i = 0; i < num_lines; i++) { + for (size_t j = 0; j < args->num_columns; j++) { + if (args->delim == NULL) { + fputs("| ", stdout); + } else if (j > 0) { + fputs(args->delim, stdout); + } + mblen(NULL, 0); + size_t k; + size_t m = 0; + for (k = data[j].inx; + k < data[j].wrapped_len; + /**/) { + if (data[j].wrapped[k] == '\n') { + k++; + break; + } + int n = mblen(data[j].wrapped + k, + data[j].wrapped_len - k); + for (int l = 0; l < n; l++) { + putchar(data[j].wrapped[k+l]); + } + if (n <= 0) n = 1; + k += n; + m++; + } + + if (args->delim == NULL) { + while (m < args->columns[j].column_width) { + m++; + putchar(' '); + } + putchar(' '); + } + data[j].inx = k; + } + if (args->delim == NULL) fputs("|", stdout); + putchar('\n'); + } + for (size_t i = 0; i < args->num_columns; i++) free(data[i].wrapped); +} + +void print_head(struct arguments *args) +{ + if (args->delim == NULL) { + for (size_t i = 0; i < args->num_columns; i++) { + putchar('+'); + for (size_t j = 0; + j < args->columns[i].column_width + 2; + j++) { + putchar('='); + } + } + putchar('+'); + putchar('\n'); + } + + struct fsaf_read_rv columns[args->num_columns]; + for (size_t i = 0; i < args->num_columns; i++) { + columns[i].b = args->columns[i].heading; + columns[i].len = strlen(columns[i].b); + } + print_line(args, columns); + + if (args->delim == NULL) { + for (size_t i = 0; i < args->num_columns; i++) { + putchar('+'); + for (size_t j = 0; j < args->columns[i].column_width + 2; j++) { + putchar('-'); + } + } + putchar('+'); + putchar('\n'); + } +} + +void print_foot(struct arguments *args) +{ + if (args->delim != NULL) return; + for (size_t i = 0; i < args->num_columns; i++) { + putchar('+'); + for (size_t j = 0; j < args->columns[i].column_width + 2; j++) { + putchar('='); + } + } + putchar('+'); + putchar('\n'); +} + +void handle_para(struct arguments *args, struct paragraph *para) +{ + struct fsaf_read_rv columns[args->num_columns]; + for (size_t i = 0; i < args->num_columns; i++) { + columns[i] = get_field(para, args->columns[i].field_inx, -1); + } + print_line(args, columns); +} + +static error_t parse_opt (int key, char * arg, struct argp_state * state) +{ + struct arguments * args = state->input; + switch (key) { + case 'C': + if (!to_stdout (COPYING)) fail(); + exit(0); + case 'c': { + char *carg = strdup(arg); + if (carg == NULL) fatal_enomem(0); + char *lens = strrchr(carg, ':'); + if (lens != NULL) *(lens++) = '\0'; else lens = ""; + char *fn = strchr(carg, '='); + if (fn != NULL) *(fn++) = '\0'; + struct column *col = &args->columns[args->num_columns]; + col->heading = carg; + col->field_inx = fieldtrie_insert(fn == NULL ? carg : fn); + size_t n = 0; + _Bool err = 0; + for (char const *p = lens; *p != '\0'; p++) { + if (!('0' <= *p && *p <= '9')) { + err = 1; + continue; + } + n = n * 10 + (*p - '0'); + } + if (err) message(L_IMPORTANT, _("invalid column length"), NULL); + col->column_width = n > 0 ? n : -1; + if (n == 0) args->need_preprocessing = 1; + args->num_columns++; + } + break; + case 'd': + args->delim = strdup(arg); + if (args->delim == NULL) fatal_enomem(NULL); + break; + case 'l': { + int ll = str2loglevel(arg); + if (ll < 0) + { + message(L_FATAL, _("no such log level"), arg); + fail(); + } + set_loglevel(ll); + debug_message("parse_opt: l", 0); + } + break; + case ARGP_KEY_ARG: + debug_message("parse_opt: argument", 0); + { + char const * s; + if (args->num_fnames >= MAX_FNAMES) { + message(L_FATAL, _("too many file names"), 0); + fail(); + } + s = strdup(arg); + if (s == 0) fatal_enomem(0); + args->fname[args->num_fnames++] = + (struct ifile){ .mode = m_read, .s = s }; + } + break; + case OPT_MMAP: + debug_message("parse_opt: mmap", 0); + fsaf_mmap = 1; + break; + case ARGP_KEY_END: + case ARGP_KEY_ARGS: case ARGP_KEY_INIT: case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: case ARGP_KEY_FINI: case ARGP_KEY_NO_ARGS: + debug_message("parse_opt: ignored", 0); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static char progdoc [] = +N_("tbl-dctrl -- tabularize Debian control files"); + +static struct argp argp = { options, parse_opt, 0, progdoc }; + +static size_t mbs_len(char const *mbs, size_t n, char const *fname) +{ + if (n == (size_t)(-1)) n = strlen(mbs); + size_t len = 0; + mblen(NULL, 0); + for (size_t k = 0; k < n;/**/) { + if (mbs[k] == '\n') break; + len++; + int delta = mblen(mbs + k, n - k); + if (delta <= 0) { + message(L_IMPORTANT, _("bad multibyte character"), + fname); + k++; + } else { + k += delta; + } + } + return len; +} + +int main(int argc, char * argv[]) +{ + setlocale(LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + fieldtrie_init(); + + static struct arguments args; + + //keys_init(&args.keys); + msg_set_progname(argv[0]); + argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + + if (args.num_fnames == 0) { + args.fname[args.num_fnames++] = (struct ifile){ + .mode = m_read, .s = "-" + }; + } + + if (args.need_preprocessing) { + struct para_bundle pb; + + bundle_init(&pb); + + for (size_t i = 0; i < args.num_fnames; i++) { + bundle_slurp(&pb, args.fname[i]); + } + + size_t n = fieldtrie_count(); + size_t lens[n]; + for (size_t i = 0; i < n; i++) lens[i] = 0; + + for (size_t i = 0; i < pb.num_paras; i++) { + struct paragraph *p = pb.paras[i]; + assert(p->nfields == n); + for (size_t j = 0; j < p->nfields; j++) { + struct fsaf_read_rv r = get_field(p, j, -1); + size_t len = mbs_len(r.b, r.len, + p->common->fp->fname); + if (len > lens[j]) lens[j] = len; + } + } + for (size_t i = 0; i < args.num_columns; i++) { + size_t width = args.columns[i].column_width; + size_t headw = mbs_len(args.columns[i].heading, -1, 0); + if (width == (size_t)(-1)) width = lens[i]; + if (headw > width) width = headw; + args.columns[i].column_width = width; + } + print_head(&args); + for (size_t i = 0; i < pb.num_paras; i++) { + handle_para(&args, pb.paras[i]); + } + print_foot(&args); + } else { + print_head(&args); + for (size_t i = 0; i < args.num_fnames; ++i) { + int fd; + struct ifile fname = args.fname[i]; + + if (fname.mode == m_error) continue; + + fd = open_ifile(fname); + if (fd == -1) break; + + if (!chk_ifile(fname, fd)) break; + + FSAF * fp = fsaf_fdopen(fd, fname.s); + para_parser_t pp; + para_parser_init(&pp, fp, true, false); + para_t para; + para_init(&pp, ¶); + while (1) { + para_parse_next(¶); + if (para_eof(&pp)) break; + handle_para(&args, ¶); + } + + close_ifile(fname, fd); + } + print_foot(&args); + } + + return 0; +} diff --git a/util.c b/util.c deleted file mode 100644 index bb69764..0000000 --- a/util.c +++ /dev/null @@ -1,50 +0,0 @@ -/* 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 -#include -#include -#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/util.h b/util.h deleted file mode 100644 index 36293ae..0000000 --- a/util.h +++ /dev/null @@ -1,27 +0,0 @@ -/* 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 - -char * get_regerror (int errcode, regex_t *compiled); - - -#endif /* UTIL_H */ diff --git a/version.c b/version.c deleted file mode 100644 index 624e089..0000000 --- a/version.c +++ /dev/null @@ -1,158 +0,0 @@ -/* 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 - * Copyright (C) 2001 Wichert Akkerman - * - * 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 -#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/version.h b/version.h deleted file mode 100644 index 90aaf9d..0000000 --- a/version.h +++ /dev/null @@ -1,62 +0,0 @@ -/* 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 - * - * 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 -#include - -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 */ -- cgit v1.2.3