diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/Makefile.am | 13 | ||||
-rw-r--r-- | util/Makefile.in | 248 | ||||
-rw-r--r-- | util/argparse.c | 653 | ||||
-rw-r--r-- | util/errors.c | 69 | ||||
-rw-r--r-- | util/fileutil.c | 31 | ||||
-rw-r--r-- | util/iobuf.c | 762 | ||||
-rw-r--r-- | util/logger.c | 139 | ||||
-rw-r--r-- | util/memory.c | 460 | ||||
-rw-r--r-- | util/miscutil.c | 33 | ||||
-rw-r--r-- | util/strgutil.c | 63 | ||||
-rw-r--r-- | util/ttyio.c | 114 |
11 files changed, 2585 insertions, 0 deletions
diff --git a/util/Makefile.am b/util/Makefile.am new file mode 100644 index 000000000..5fd3e59ca --- /dev/null +++ b/util/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = -I$(top_srcdir)/include + +noinst_LIBRARIES = util + + +util_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ + ttyio.c argparse.c memory.c errors.c iobuf.c + + + + diff --git a/util/Makefile.in b/util/Makefile.in new file mode 100644 index 000000000..ca24380d6 --- /dev/null +++ b/util/Makefile.in @@ -0,0 +1,248 @@ +# Makefile.in generated automatically by automake 1.0 from Makefile.am + +# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + + +SHELL = /bin/sh + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +INCLUDES = -I$(top_srcdir)/include + +noinst_LIBRARIES = util + +util_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ + ttyio.c argparse.c memory.c errors.c iobuf.c +mkinstalldirs = $(top_srcdir)/scripts/mkinstalldirs +CONFIG_HEADER = ../config.h +LIBRARIES = $(noinst_LIBRARIES) + +noinst_LIBFILES = libutil.a + +CC = @CC@ +LEX = @LEX@ +YACC = @YACC@ + +DEFS = @DEFS@ -I. -I$(srcdir) -I.. +CPPFLAGS = @CPPFLAGS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) +LINK = $(CC) $(LDFLAGS) -o $@ +util_LIBADD = +util_OBJECTS = logger.o fileutil.o miscutil.o strgutil.o ttyio.o \ +argparse.o memory.o errors.o iobuf.o +EXTRA_util_SOURCES = +LIBFILES = libutil.a +AR = ar +RANLIB = @RANLIB@ +DIST_COMMON = Makefile.am Makefile.in + + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \ + $(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA) +DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \ + $(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA) + +TAR = tar +DEP_FILES = $(srcdir)/.deps/argparse.P $(srcdir)/.deps/errors.P \ +$(srcdir)/.deps/fileutil.P $(srcdir)/.deps/iobuf.P \ +$(srcdir)/.deps/logger.P $(srcdir)/.deps/memory.P \ +$(srcdir)/.deps/miscutil.P $(srcdir)/.deps/strgutil.P \ +$(srcdir)/.deps/ttyio.P +SOURCES = $(util_SOURCES) +OBJECTS = $(util_OBJECTS) + +default: all + + +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in + cd $(top_srcdir) && automake $(subdir)/Makefile + +Makefile: $(top_builddir)/config.status Makefile.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + rm -f $(noinst_LIBFILES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.c.o: + $(COMPILE) $< + +mostlyclean-compile: + rm -f *.o core + +clean-compile: + +distclean-compile: + rm -f *.tab.c + +maintainer-clean-compile: +$(util_OBJECTS): ../config.h + +libutil.a: $(util_OBJECTS) $(util_LIBADD) + rm -f libutil.a + $(AR) cru libutil.a $(util_OBJECTS) $(util_LIBADD) + $(RANLIB) libutil.a + +ID: $(HEADERS) $(SOURCES) + here=`pwd` && cd $(srcdir) && mkid -f$$here/ID $(SOURCES) $(HEADERS) + +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) + here=`pwd` && cd $(srcdir) && etags $(ETAGS_ARGS) $(SOURCES) $(HEADERS) -o $$here/TAGS + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + rm -f TAGS ID + +maintainer-clean-tags: + +subdir = util +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) +distdir: $(DEP_DISTFILES) + @for file in `cd $(srcdir) && echo $(DISTFILES)`; do \ + test -f $(distdir)/$$file \ + || ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $(srcdir)/$$file $(distdir)/$$file; \ + done + +# This fragment is probably only useful for maintainers. It relies on +# GNU make and gcc. It is only included in the generated Makefile.in +# if `automake' is not passed the `--include-deps' flag. + +MKDEP = gcc -MM $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) + +-include $(srcdir)/.deps/.P +$(srcdir)/.deps/.P: $(BUILT_SOURCES) + cd $(srcdir) && test -d .deps || mkdir .deps + echo > $@ + +-include $(DEP_FILES) +$(DEP_FILES): $(srcdir)/.deps/.P + +$(srcdir)/.deps/%.P: $(srcdir)/%.c + @echo "mkdeps $< > $@" + @re=`echo 's,^$(srcdir)//*,,g;s, $(srcdir)//*, ,g' | sed 's,\.,\\\\.,g'`; \ + $(MKDEP) $< | sed "$$re" > $@-tmp + @if test -n "$o"; then \ + sed 's/\.o:/$$o:/' $@-tmp > $@; \ + rm $@-tmp; \ + else \ + mv $@-tmp $@; \ + fi + +# End of maintainer-only section +info: + +dvi: + +check: all + +installcheck: + +install-exec: + +install-data: + +install: install-exec install-data all + @: + +uninstall: + +all: $(LIBFILES) Makefile + +install-strip: + $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install +installdirs: + + +mostlyclean-generic: + test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) + +clean-generic: + test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + rm -f Makefile $(DISTCLEANFILES) + rm -f config.cache config.log $(CONFIG_HEADER) stamp-h + +maintainer-clean-generic: + test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) + test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +mostlyclean: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +clean: clean-noinstLIBRARIES clean-compile clean-tags clean-generic \ + mostlyclean + +distclean: distclean-noinstLIBRARIES distclean-compile distclean-tags \ + distclean-generic clean + rm -f config.status + +maintainer-clean: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +.PHONY: default mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info dvi check installcheck \ +install-exec install-data install uninstall all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + +.SUFFIXES: +.SUFFIXES: .c .o + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/util/argparse.c b/util/argparse.c new file mode 100644 index 000000000..3d51d014c --- /dev/null +++ b/util/argparse.c @@ -0,0 +1,653 @@ +/* [argparse.c wk 17.06.97] Argument Parser for option handling + * Copyright (c) 1997 by Werner Koch (dd9jn) + * This file is part of WkLib. + * + * WkLib 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. + * + * WkLib 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 + * + * + * Note: This is an independent version of the one in WkLib + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +#ifdef DOCUMENTATION +@Summary arg_parse + #include <wk/lib.h> + + typedef struct { + char *argc; /* pointer to argc (value subject to change) */ + char ***argv; /* pointer to argv (value subject to change) */ + unsigned flags; /* Global flags (DO NOT CHANGE) */ + int err; /* print error about last option */ + /* 1 = warning, 2 = abort */ + int r_opt; /* return option */ + int r_type; /* type of return value (0 = no argument found)*/ + union { + int ret_int; + long ret_long + ulong ret_ulong; + char *ret_str; + } r; /* Return values */ + struct { + int index; + const char *last; + } internal; /* DO NOT CHANGE */ + } ARGPARSE_ARGS; + + typedef struct { + int short_opt; + const char *long_opt; + unsigned flags; + } ARGPARSE_OPTS; + + int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts ); + +@Description + This is my replacement for getopt(). See the example for a typical usage. + Global flags are: + Bit 0 : Do not remove options form argv + Bit 1 : Do not stop at last option but return other args + with r_opt set to -1. + Bit 2 : Assume options and real args are mixed. + Bit 3 : Do not use -- to stop option processing. + Bit 4 : Do not skip the first arg. + Bit 5 : allow usage of long option with only one dash + all other bits must be set to zero, this value is modified by the function + so assume this is write only. + Local flags (for each option): + Bit 2-0 : 0 = does not take an argument + 1 = takes int argument + 2 = takes string argument + 3 = takes long argument + 4 = takes ulong argument + Bit 3 : argument is optional (r_type will the be set to 0) + Bit 4 : allow 0x etc. prefixed values. + If can stop the option processing by setting opts to NULL, the function will + then return 0. +@Return Value + Returns the args.r_opt or 0 if ready + r_opt may be -2 to indicate an unknown option. +@See Also + ArgExpand +@Notes + You do not need to process the options 'h', '--help' or '--version' + because this function includes standard help processing; but if you + specify '-h', '--help' or '--version' you have to do it yourself. + The option '--' stops argument processing; if bit 1 is set the function + continues to return normal arguments. + To process float args or unsigned args you must use a string args and do + the conversion yourself. +@Example + + ARGPARSE_OPTS opts[] = { + { 'v', "verbose", 0 }, + { 'd', "debug", 0 }, + { 'o', "output", 2 }, + { 'c', "cross-ref", 2|8 }, + { 'm', "my-option", 1|8 }, + { 500, "have-no-short-option-for-this-long-option", 0 }, + {0} }; + ARGPARSE_ARGS pargs = { &argc, &argv, 0 } + + while( ArgParse( &pargs, &opts) ) { + switch( pargs.r_opt ) { + case 'v': opt.verbose++; break; + case 'd': opt.debug++; break; + case 'o': opt.outfile = pargs.r.ret_str; break; + case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; + case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; + case 500: opt.a_long_one++; break + default : pargs.err = 1; break; /* force warning output */ + } + } + if( argc > 1 ) + log_fatal( "Too many args"); + +#endif /*DOCUMENTATION*/ + + + +static void set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s); +static void show_help(ARGPARSE_OPTS *opts, unsigned flags); +static void show_version(void); + + +int +arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) +{ + int index; + int argc; + char **argv; + char *s, *s2; + int i; + + if( !(arg->flags & (1<<15)) ) { /* initialize this instance */ + arg->internal.index = 0; + arg->internal.last = NULL; + arg->internal.inarg = 0; + arg->internal.stopped= 0; + arg->err = 0; + arg->flags |= 1<<15; /* mark initialized */ + if( *arg->argc < 0 ) + log_bug("Invalid argument for ArgParse\n"); + } + argc = *arg->argc; + argv = *arg->argv; + index = arg->internal.index; + + if( arg->err ) { /* last option was erroneous */ + if( arg->r_opt == -3 ) + s = "Missing argument for option \"%.50s\"\n"; + else + s = "Invalid option \"%.50s\"\n"; + log_error(s, arg->internal.last? arg->internal.last:"[??]" ); + if( arg->err != 1 ) + exit(2); + arg->err = 0; + } + + if( !index && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */ + argc--; argv++; index++; + } + + next_one: + if( !argc ) { /* no more args */ + arg->r_opt = 0; + goto leave; /* ready */ + } + + s = *argv; + arg->internal.last = s; + + if( arg->internal.stopped && (arg->flags & (1<<1)) ) { + arg->r_opt = -1; /* not an option but a argument */ + arg->r_type = 2; + arg->r.ret_str = s; + argc--; argv++; index++; /* set to next one */ + } + else if( arg->internal.stopped ) { /* ready */ + arg->r_opt = 0; + goto leave; + } + else if( *s == '-' && s[1] == '-' ) { /* long option */ + arg->internal.inarg = 0; + if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */ + arg->internal.stopped = 1; + argc--; argv++; index++; + goto next_one; + } + + for(i=0; opts[i].short_opt; i++ ) + if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+2) ) + break; + + if( !opts[i].short_opt && !strcmp( "help", s+2) ) + show_help(opts, arg->flags); + else if( !opts[i].short_opt && !strcmp( "version", s+2) ) + show_version(); + else if( !opts[i].short_opt && !strcmp( "warranty", s+2) ) { + puts( strusage(10) ); + puts( strusage(31) ); + exit(0); + } + + arg->r_opt = opts[i].short_opt; + if( !opts[i].short_opt ) { + arg->r_opt = -2; /* unknown option */ + arg->r.ret_str = s+2; + } + else if( (opts[i].flags & 7) ) { + s2 = argv[1]; + if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/ + arg->r_type = 0; /* because it is optional */ + } + else if( !s2 ) { + arg->r_opt = -3; /* missing argument */ + } + else if( *s2 == '-' && (opts[i].flags & 8) ) { + /* the argument is optional and the next seems to be + * an option. We do not check this possible option + * but assume no argument */ + arg->r_type = 0; + } + else { + set_opt_arg(arg, opts[i].flags, s2); + argc--; argv++; index++; /* skip one */ + } + } + else { /* does not take an argument */ + arg->r_type = 0; + } + argc--; argv++; index++; /* set to next one */ + } + else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */ + int dash_kludge = 0; + i = 0; + if( !arg->internal.inarg ) { + arg->internal.inarg++; + if( arg->flags & (1<<5) ) { + for(i=0; opts[i].short_opt; i++ ) + if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) { + dash_kludge=1; + break; + } + } + } + s += arg->internal.inarg; + + if( !dash_kludge ) { + for(i=0; opts[i].short_opt; i++ ) + if( opts[i].short_opt == *s ) + break; + } + + if( !opts[i].short_opt && *s == 'h' ) + show_help(opts, arg->flags); + + arg->r_opt = opts[i].short_opt; + if( !opts[i].short_opt ) { + arg->r_opt = -2; /* unknown option */ + arg->internal.inarg++; /* point to the next arg */ + arg->r.ret_str = s; + } + else if( (opts[i].flags & 7) ) { + if( s[1] && !dash_kludge ) { + s2 = s+1; + set_opt_arg(arg, opts[i].flags, s2); + } + else { + s2 = argv[1]; + if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/ + arg->r_type = 0; /* because it is optional */ + } + else if( !s2 ) { + arg->r_opt = -3; /* missing argument */ + } + else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) { + /* the argument is optional and the next seems to be + * an option. We do not check this possible option + * but assume no argument */ + arg->r_type = 0; + } + else { + set_opt_arg(arg, opts[i].flags, s2); + argc--; argv++; index++; /* skip one */ + } + } + s = "x"; /* so that !s[1] yields false */ + } + else { /* does not take an argument */ + arg->r_type = 0; + arg->internal.inarg++; /* point to the next arg */ + } + if( !s[1] || dash_kludge ) { /* no more concatenated short options */ + arg->internal.inarg = 0; + argc--; argv++; index++; + } + } + else if( arg->flags & (1<<2) ) { + arg->r_opt = -1; /* not an option but a argument */ + arg->r_type = 2; + arg->r.ret_str = s; + argc--; argv++; index++; /* set to next one */ + } + else { + arg->internal.stopped = 1; /* stop option processing */ + goto next_one; + } + + leave: + *arg->argc = argc; + *arg->argv = argv; + arg->internal.index = index; + return arg->r_opt; +} + + + +static void +set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s) +{ + int base = (flags & 16)? 0 : 10; + + switch( arg->r_type = (flags & 7) ) { + case 1: /* takes int argument */ + arg->r.ret_int = (int)strtol(s,NULL,base); + break; + default: + case 2: /* takes string argument */ + arg->r.ret_str = s; + break; + case 3: /* takes long argument */ + arg->r.ret_long= strtol(s,NULL,base); + break; + case 4: /* takes ulong argument */ + arg->r.ret_ulong= strtoul(s,NULL,base); + break; + } +} + +static void +show_help( ARGPARSE_OPTS *opts, unsigned flags ) +{ + const char *s; + + puts( strusage(10) ); + s = strusage(12); + if( *s == '\n' ) + s++; + puts(s); + if( opts[0].description ) { /* auto format the option description */ + int i,j, indent; + /* get max. length of long options */ + for(i=indent=0; opts[i].short_opt; i++ ) { + if( opts[i].long_opt ) + if( (j=strlen(opts[i].long_opt)) > indent && j < 35 ) + indent = j; + } + /* example: " -v, --verbose Viele Sachen ausgeben" */ + indent += 10; + puts("Options:"); + for(i=0; opts[i].short_opt; i++ ) { + if( opts[i].short_opt < 256 ) + printf(" -%c", opts[i].short_opt ); + else + fputs(" ", stdout); + j = 3; + if( opts[i].long_opt ) + j += printf("%c --%s ", opts[i].short_opt < 256?',':' ', + opts[i].long_opt ); + for(;j < indent; j++ ) + putchar(' '); + if( (s = opts[i].description) ) { + for(; *s; s++ ) { + if( *s == '\n' ) { + if( s[1] ) { + putchar('\n'); + for(j=0;j < indent; j++ ) + putchar(' '); + } + } + else + putchar(*s); + } + } + putchar('\n'); + } + if( flags & 32 ) + puts("\n(A single dash may be used instead of the double ones)"); + } + fflush(stdout); + exit(0); +} + +static void +show_version() +{ + const char *s; + printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) ); + if( (s = strusage(24)) && *s ) { + #ifdef DEBUG + printf(", %s, dbg)\n", s); + #else + printf(", %s)\n", s); + #endif + } + else { + #ifdef DEBUG + printf(", dbg)\n"); + #else + printf(")\n"); + #endif + } + fflush(stdout); + exit(0); +} + + + +void +usage( int level ) +{ + static int sentinel=0; + + if( sentinel ) + return; + + sentinel++; + if( !level ) { + fputs( strusage(level), stderr ); putc( '\n', stderr ); + fputs( strusage(31), stderr); + #if DEBUG + fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) ); + #else + fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) ); + #endif + fflush(stderr); + } + else if( level == 1 ) { + fputs(strusage(level),stderr);putc('\n',stderr); + exit(2);} + else if( level == 2 ) { + puts(strusage(level)); exit(0);} + sentinel--; +} + + +const char * +default_strusage( int level ) +{ + const char *p; + switch( level ) { + case 0: p = strusage(10); break; + case 1: p = strusage(11); break; + case 2: p = strusage(12); break; + case 10: p = "WkLib" + #if DOS386 && __WATCOMC__ + " (DOS4G)" + #elif DOS386 + " (DOSX)" + #elif DOS16RM + " (DOS16RM)" + #elif M_I86VM + " (VCM)" + #elif UNIX || POSIX + " (Posix)" + #elif OS2 + " (OS/2)" + #elif WINNT && __CYGWIN32__ + " (CygWin)" + #elif WINNT + " (WinNT)" + #elif NETWARE + " (Netware)" + #elif VMS + " (VMS)" + #endif + "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break; + case 11: p = "usage: ?"; break; + case 16: + case 15: p = "[Untitled]"; break; + case 23: p = "[unknown]"; break; + case 24: p = ""; break; + case 12: p = + "This is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n\n" + "WkLib is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307," + " USA.\n" ; + break; + case 22: + #if MSDOS + #if USE_EMS + p = "MSDOS+EMS"; + #else + p = "MSDOS"; + #endif + #elif OS2 + p = "OS/2"; + #elif WINNT && __CYGWIN32__ + p = "CygWin"; + #elif WINNT + p = "WinNT"; + #elif DOS386 + p = "DOS386"; + #elif EMX + p = "EMX"; + #elif DOS16RM + p = "DOS16RM"; + #elif NETWARE + p = "Netware"; + #elif __linux__ + p = "Linux"; + #elif UNIX || M_UNIX || M_XENIX + p = "UNIX"; + #elif VMS + p = "VMS"; + #else + p = "UnknownOS"; + #endif + break; + case 31: p = + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions. See the file COPYING for details.\n"; + break; + case 32: p = "[" + #if MSDOS + "MSDOS Version" + #elif DOS386 && __ZTC__ + "32-Bit MSDOS Version (Zortech's DOSX)" + #elif DOS386 + "32-Bit MSDOS Version" + #elif OS20 && EMX + "OS/2 2.x EMX Version" + #elif OS20 + "OS/2 2.x Version" + #elif OS2 + "OS/2 1.x Version" + #elif WINNT && __CYGWIN32__ + "Cygnus WinAPI Version" + #elif WINNT + "Windoze NT Version" + #elif EMX + "EMX Version" + #elif NETWARE + "NLM Version" + #elif DOS16RM + "DOS16RM Version" + #elif __linux__ + "Linux Version" + #elif VMS + "OpenVMS Version" + #elif POSIX + "POSIX Version" + #elif M_UNIX || M_XENIX + "*IX Version" + #endif + "]"; + break; + case 33: p = + #ifdef MULTI_THREADED + "mt" + #else + "" + #endif + ; break; + case 42: + case 43: + case 44: + case 45: p = ""; break; + default: p = "?"; + } + + return p; +} + + + +#ifdef TEST +static struct { + int verbose; + int debug; + char *outfile; + char *crf; + int myopt; + int echo; + int a_long_one; +}opt; + +int +main(int argc, char **argv) +{ + ARGPARSE_OPTS opts[] = { + { 'v', "verbose", 0 , "Laut sein"}, + { 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"}, + { 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"}, + { 'o', "output", 2 }, + { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" }, + { 'm', "my-option", 1|8 }, + { 500, "a-long-option", 0 }, + {0} }; + ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 }; + int i; + + while( ArgParse( &pargs, opts) ) { + switch( pargs.r_opt ) { + case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break; + case 'v': opt.verbose++; break; + case 'e': opt.echo++; break; + case 'd': opt.debug++; break; + case 'o': opt.outfile = pargs.r.ret_str; break; + case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; + case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; + case 500: opt.a_long_one++; break; + default : pargs.err = 1; break; /* force warning output */ + } + } + for(i=0; i < argc; i++ ) + printf("%3d -> (%s)\n", i, argv[i] ); + puts("Options:"); + if( opt.verbose ) + printf(" verbose=%d\n", opt.verbose ); + if( opt.debug ) + printf(" debug=%d\n", opt.debug ); + if( opt.outfile ) + printf(" outfile='%s'\n", opt.outfile ); + if( opt.crf ) + printf(" crffile='%s'\n", opt.crf ); + if( opt.myopt ) + printf(" myopt=%d\n", opt.myopt ); + if( opt.a_long_one ) + printf(" a-long-one=%d\n", opt.a_long_one ); + if( opt.echo ) + printf(" echo=%d\n", opt.echo ); + return 0; +} +#endif + +/**** bottom of file ****/ diff --git a/util/errors.c b/util/errors.c new file mode 100644 index 000000000..1e4579fc4 --- /dev/null +++ b/util/errors.c @@ -0,0 +1,69 @@ +/* errors.c - error strings + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "errors.h" + +const char * +g10_errstr( int err ) +{ + static char buf[50]; + const char *p; + + #define X(n,s) case G10ERR_##n : p = s; break; + switch( err ) { + X(GENERAL, "General error") + X(UNKNOWN_PACKET, "Unknown packet type") + X(UNKNOWN_VERSION,"Unknown version") + X(PUBKEY_ALGO ,"Unknown pubkey algorithm") + X(DIGEST_ALGO ,"Unknown digest algorithm") + X(BAD_PUBKEY ,"Bad public key") + X(BAD_SECKEY ,"Bad secret key") + X(BAD_SIGN ,"Bad signature") + X(CHECKSUM , "Checksum error") + X(BAD_PASS , "Bad passphrase") + X(NO_PUBKEY ,"Public key not found") + X(CIPHER_ALGO ,"Unknown cipher algorithm") + X(KEYRING_OPEN ,"Can't open the keyring") + X(BAD_RING ,"Broken keyring") + X(NO_USER_ID ,"No such user id found") + X(NO_SECKEY ,"Secret key not available") + X(WRONG_SECKEY ,"Wrong secret key used") + X(UNSUPPORTED ,"Not supported") + X(BAD_KEY ,"Bad key") + X(READ_FILE ,"File read error") + X(WRITE_FILE ,"File write error") + X(COMPR_ALGO ,"Unknown compress algorithm") + X(OPEN_FILE ,"File open error") + X(CREATE_FILE ,"File create error") + X(PASSPHRASE ,"Invalid passphrase") + X(NI_PUBKEY ,"Unimplemented pubkey algorithm") + X(NI_CIPHER ,"Unimplemented cipher algorithm") + + default: p = buf; sprintf(buf, "Error code %d", err); break; + } + #undef X + return p; +} + diff --git a/util/fileutil.c b/util/fileutil.c new file mode 100644 index 000000000..e2ea9b20e --- /dev/null +++ b/util/fileutil.c @@ -0,0 +1,31 @@ +/* fileutil.c - file utilities + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include "util.h" +#include "memory.h" +#include "ttyio.h" + + diff --git a/util/iobuf.c b/util/iobuf.c new file mode 100644 index 000000000..12fc74ff6 --- /dev/null +++ b/util/iobuf.c @@ -0,0 +1,762 @@ +/* iobuf.c - file handling + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "memory.h" +#include "util.h" +#include "iobuf.h" + +typedef struct { + FILE *fp; /* open file handle */ + char fname[1]; /* name of the file */ +} file_filter_ctx_t ; + +typedef struct { + int usage; + size_t size; + size_t count; + int eof; +} block_filter_ctx_t; + +static int underflow(IOBUF a); + +/**************** + * Read data from a file into buf which has an allocated length of *LEN. + * return the number of read bytes in *LEN. OPAQUE is the FILE * of + * the stream. A is not used. + * control maybe: + * IOBUFCTRL_INIT: called just before the function is linked into the + * list of function. This can be used to prepare internal + * data structures of the function. + * IOBUFCTRL_FREE: called just before the function is removed from the + * list of functions and can be used to release internal + * data structures or close a file etc. + * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer + * with new stuff. *RET_LEN is the available size of the + * buffer, and should be set to the number of bytes + * which were put into the buffer. The function + * returns 0 to indicate success, -1 on EOF and + * G10ERR_xxxxx for other errors. + * + * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff. + * *RET_LAN is the number of bytes in BUF. + * + */ +static int +file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) +{ + file_filter_ctx_t *a = opaque; + FILE *fp = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int c, rc = 0; + char *p; + + if( control == IOBUFCTRL_UNDERFLOW ) { + assert( size ); /* need a buffer */ + for(; size; size-- ) { + if( (c=getc(fp)) == EOF ) { + if( ferror(fp) ) { + log_error("%s: read error: %s\n", + a->fname, strerror(errno)); + rc = G10ERR_READ_FILE; + } + else if( !nbytes ) + rc = -1; /* okay: we can return EOF now. */ + break; + } + buf[nbytes++] = c & 0xff; + } + *ret_len = nbytes; + } + else if( control == IOBUFCTRL_FLUSH ) { + for(p=buf; nbytes < size; nbytes++, p++ ) { + if( putc(*p, fp) == EOF ) { + log_error("%s: write error: %s\n", + a->fname, strerror(errno)); + rc = G10ERR_WRITE_FILE; + break; + } + } + *ret_len = nbytes; + } + else if( control == IOBUFCTRL_INIT ) { + } + else if( control == IOBUFCTRL_DESC ) { + *(char**)buf = "file_filter"; + } + else if( control == IOBUFCTRL_FREE ) { + if( fp != stdin && fp != stdout ) + fclose(fp); + fp = NULL; + m_free(a); /* we can free our context now */ + } + + return rc; +} + + +/**************** + * This is used to implement the block write mode. + * Block reading is done on a byte by byte basis in readbyte(), + * without a filter + */ +static int +block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len) +{ + block_filter_ctx_t *a = opaque; + size_t size = *ret_len; + int c, rc = 0; + char *p; + + if( control == IOBUFCTRL_UNDERFLOW ) { + size_t n=0; + + p = buf; + assert( size ); /* need a buffer */ + if( a->eof ) /* don't read any further */ + rc = -1; + while( !rc && size ) { + if( !a->size ) { /* get the length bytes */ + c = iobuf_get(chain); + a->size = c << 8; + c = iobuf_get(chain); + a->size |= c; + if( c == -1 ) { + log_error("block_filter: error reading length info\n"); + rc = G10ERR_READ_FILE; + } + if( !a->size ) { + a->eof = 1; + if( !n ) + rc = -1; + break; + } + } + + for(; !rc && size && a->size; size--, a->size-- ) { + if( (c=iobuf_get(chain)) == -1 ) { + log_error("block_filter %p: read error (size=%lu,a->size=%lu)\n", + a, (ulong)size, (ulong)a->size); + rc = G10ERR_READ_FILE; + } + else { + *p++ = c; + n++; + } + } + } + *ret_len = n; + } + else if( control == IOBUFCTRL_FLUSH ) { + size_t avail, n; + + for(p=buf; !rc && size; ) { + n = size; + avail = a->size - a->count; + if( !avail ) { + if( n > a->size ) { + iobuf_put( chain, (a->size >> 8) & 0xff ); + iobuf_put( chain, a->size & 0xff ); + avail = a->size; + a->count = 0; + } + else { + iobuf_put( chain, (n >> 8) & 0xff ); + iobuf_put( chain, n & 0xff ); + avail = n; + a->count = a->size - n; + } + } + if( n > avail ) + n = avail; + if( iobuf_write(chain, p, n ) ) + rc = G10ERR_WRITE_FILE; + a->count += n; + p += n; + size -= n; + } + } + else if( control == IOBUFCTRL_INIT ) { + if( DBG_IOBUF ) + log_debug("init block_filter %p\n", a ); + if( a->usage == 1 ) + a->count = a->size = 0; + else + a->count = a->size; /* force first length bytes */ + a->eof = 0; + } + else if( control == IOBUFCTRL_DESC ) { + *(char**)buf = "block_filter"; + } + else if( control == IOBUFCTRL_FREE ) { + if( a->usage == 2 ) { /* write the end markers */ + iobuf_writebyte(chain, 0); + iobuf_writebyte(chain, 0); + } + else if( a->size ) { + log_error("block_filter: pending bytes!\n"); + } + if( DBG_IOBUF ) + log_debug("free block_filter %p\n", a ); + m_free(a); /* we can free our context now */ + } + + return rc; +} + + + +/**************** + * Allocate a new io buffer, with no function assigned. + * Usage is the desired usage: 1 for input, 2 for output, 3 for temp buffer + * BUFSIZE is a suggested buffer size. + */ +IOBUF +iobuf_alloc(int usage, size_t bufsize) +{ + IOBUF a; + static int number=0; + + a = m_alloc_clear(sizeof *a); + a->usage = usage; + a->d.buf = m_alloc( bufsize ); + a->d.size = bufsize; + a->no = ++number; + a->subno = 0; + return a; +} + + +int +iobuf_close( IOBUF a ) +{ + IOBUF a2; + size_t dummy_len; + int rc=0; + + for( ; a; a = a2 ) { + a2 = a->chain; + if( a->usage == 2 && (rc=iobuf_flush(a)) ) + log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc)); + + if( DBG_IOBUF ) + log_debug("iobuf-%d.%d: close '%s'\n", a->no, a->subno, a->desc ); + if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, + a->chain, NULL, &dummy_len)) ) + log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) ); + m_free(a->recorder.buf); + m_free(a->d.buf); + m_free(a); + } + return rc; +} + +int +iobuf_cancel( IOBUF a ) +{ + /* FIXME: do an unlink if usage is 2 */ + return iobuf_close(a); +} + + +/**************** + * create a temporary iobuf, which can be used to collect stuff + * in an iobuf and later be written by iobuf_write_temp() to another + * iobuf. + */ +IOBUF +iobuf_temp() +{ + IOBUF a; + + a = iobuf_alloc(3, 8192 ); + + return a; +} + + +/**************** + * Create a head iobuf for reading from a file + * returns: NULL if an error occures and sets errno + */ +IOBUF +iobuf_open( const char *fname ) +{ + IOBUF a; + FILE *fp; + file_filter_ctx_t *fcx; + size_t len; + + if( !fname ) { + fp = stdin; /* fixme: set binary mode for msdoze */ + fname = "[stdin]"; + } + else if( !(fp = fopen(fname, "rb")) ) + return NULL; + a = iobuf_alloc(1, 8192 ); + fcx = m_alloc( sizeof *fcx + strlen(fname) ); + fcx->fp = fp; + strcpy(fcx->fname, fname ); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); + file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); + if( DBG_IOBUF ) + log_debug("iobuf-%d.%d: open '%s'\n", a->no, a->subno, fname ); + + return a; +} + +/**************** + * create a iobuf for writing to a file; the file will be created. + */ +IOBUF +iobuf_create( const char *fname ) +{ + IOBUF a; + FILE *fp; + file_filter_ctx_t *fcx; + size_t len; + + if( !fname ) { + fp = stdout; + fname = "[stdout]"; + } + else if( !(fp = fopen(fname, "wb")) ) + return NULL; + a = iobuf_alloc(2, 8192 ); + fcx = m_alloc( sizeof *fcx + strlen(fname) ); + fcx->fp = fp; + strcpy(fcx->fname, fname ); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len ); + file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len ); + if( DBG_IOBUF ) + log_debug("iobuf-%d.%d: create '%s'\n", a->no, a->subno, a->desc ); + + return a; +} + +/**************** + * Register an i/o filter. + */ +int +iobuf_push_filter( IOBUF a, + int (*f)(void *opaque, int control, + IOBUF chain, byte *buf, size_t *len), void *ov ) +{ + IOBUF b; + size_t dummy_len=0; + int rc=0; + + if( a->usage == 2 && (rc=iobuf_flush(a)) ) + return rc; + /* make a copy of the current stream, so that + * A is the new stream and B the original one. + * The contents of the buffers are transferred to the + * new stream. + */ + b = m_alloc(sizeof *b); + memcpy(b, a, sizeof *b ); + /* remove the filter stuff from the new stream */ + a->filter = NULL; + a->filter_ov = NULL; + if( a->usage == 2 ) { /* allocate a fresh buffer for the original stream */ + b->d.buf = m_alloc( a->d.size ); + b->d.len = 0; + b->d.start = 0; + } + else { /* allocate a fresh buffer for the new stream */ + a->d.buf = m_alloc( a->d.size ); + a->d.len = 0; + a->d.start = 0; + } + /* disable nlimit for the new stream */ + a->nlimit = a->nbytes = 0; + /* disable recorder for the original stream */ + b->recorder.buf = NULL; + /* make a link from the new stream to the original stream */ + a->chain = b; + + /* setup the function on the new stream */ + a->filter = f; + a->filter_ov = ov; + + a->subno = b->subno + 1; + f( ov, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &dummy_len ); + + if( DBG_IOBUF ) { + log_debug("iobuf-%d.%d: push '%s'\n", a->no, a->subno, a->desc ); + for(b=a; b; b = b->chain ) + log_debug("\tchain: %d.%d '%s'\n", b->no, b->subno, b->desc ); + } + + /* now we can initialize the new function if we have one */ + if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_INIT, a->chain, + NULL, &dummy_len)) ) + log_error("IOBUFCTRL_INIT failed: %s\n", g10_errstr(rc) ); + return rc; +} + +/**************** + * Remove an i/o filter. + */ +int +iobuf_pop_filter( IOBUF a, int (*f)(void *opaque, int control, + IOBUF chain, byte *buf, size_t *len), void *ov ) +{ + IOBUF b; + size_t dummy_len=0; + int rc=0; + + if( DBG_IOBUF ) + log_debug("iobuf-%d.%d: pop '%s'\n", a->no, a->subno, a->desc ); + if( !a->filter ) { /* this is simple */ + b = a->chain; + assert(b); + m_free(a->d.buf); + memcpy(a,b, sizeof *a); + m_free(b); + return 0; + } + for(b=a ; b; b = b->chain ) + if( b->filter == f && (!ov || b->filter_ov == ov) ) + break; + if( !b ) + log_bug("iobuf_pop_filter(): filter function not found\n"); + + /* flush this stream if it is an output stream */ + if( a->usage == 2 && (rc=iobuf_flush(b)) ) { + log_error("iobuf_flush failed in pop_filter: %s\n", g10_errstr(rc)); + return rc; + } + /* and tell the filter to free it self */ + if( (rc = b->filter(b->filter_ov, IOBUFCTRL_FREE, b->chain, + NULL, &dummy_len)) ) { + log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) ); + return rc; + } + + /* and look how to remove it */ + if( a == b && !b->chain ) + log_bug("can't remove the last filter from the chain\n"); + else if( a == b ) { /* remove the first iobuf from the chain */ + /* everything from b is copied to a. This is save because + * a flush has been done on the to be removed entry + */ + b = a->chain; + m_free(a->d.buf); + memcpy(a,b, sizeof *a); + m_free(b); + } + else if( !b->chain ) { /* remove the last iobuf from the chain */ + log_bug("Ohh jeee, trying to a head filter\n"); + } + else { /* remove an intermediate iobuf from the chain */ + log_bug("Ohh jeee, trying to remove an intermediate filter\n"); + } + + return rc; +} + + + +/**************** + * read underflow: read more bytes into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow(IOBUF a) +{ + size_t len; + int rc; + + /*log_debug("iobuf-%d.%d: underflow: start=%lu len=%lu\n", + a->no, a->subno, (ulong)a->d.start, (ulong)a->d.len );*/ + assert( a->d.start == a->d.len ); + if( a->usage == 3 ) + return -1; /* EOF because a temp buffer can't do an underflow */ + if( a->filter_eof ) { + if( DBG_IOBUF ) + log_debug("iobuf-%d.%d: filter eof\n", a->no, a->subno ); + return -1; + } + + if( a->filter ) { + len = a->d.size; + rc = a->filter( a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, + a->d.buf, &len ); + if( a->usage == 1 && rc == -1 ) { /* EOF: we can remove the filter */ + size_t dummy_len; + + /* and tell the filter to free it self */ + if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain, + NULL, &dummy_len)) ) + log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) ); + a->filter = NULL; + a->desc = NULL; + a->filter_ov = NULL; + a->filter_eof = 1; + } + + if( !len ) + return -1; + a->d.len = len; + a->d.start = 0; + return a->d.buf[a->d.start++]; + } + else + return -1; /* no filter; return EOF */ +} + + +void +iobuf_clear_eof(IOBUF a) +{ + assert(a->usage == 1); + + if( a->filter ) + log_info("iobuf-%d.%d: clear_eof '%s' with enabled filter\n", a->no, a->subno, a->desc ); + if( !a->filter_eof ) + log_info("iobuf-%d.%d: clear_eof '%s' with no EOF pending\n", a->no, a->subno, a->desc ); + iobuf_pop_filter(a, NULL, NULL); +} + + +int +iobuf_flush(IOBUF a) +{ + size_t len; + int rc; + + /*log_debug("iobuf-%d.%d: flush\n", a->no, a->subno );*/ + if( a->usage == 3 ) + log_bug("temp buffer too short\n"); + else if( a->usage != 2 ) + log_bug("flush on non-output iobuf\n"); + else if( !a->filter ) + log_bug("iobuf_flush: no filter\n"); + len = a->d.len; + rc = a->filter( a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len ); + if( !rc && len != a->d.len ) { + log_info("iobuf_flush did not write all!\n"); + rc = G10ERR_WRITE_FILE; + } + a->d.len = 0; + + return rc; +} + + +/**************** + * Read a byte from the iobuf; returns -1 on EOF + */ +int +iobuf_readbyte(IOBUF a) +{ + int c; + + if( a->nlimit && a->nbytes >= a->nlimit ) + return -1; /* forced EOF */ + + if( a->d.start < a->d.len ) { + c = a->d.buf[a->d.start++]; + } + else if( (c=underflow(a)) == -1 ) + return -1; /* EOF */ + + a->nbytes++; + + if( a->recorder.buf ) { + if( a->recorder.len >= a->recorder.size ) { + a->recorder.size += 500; + a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size ); + } + ((byte*)a->recorder.buf)[a->recorder.len++] = c; + } + return c; +} + + +int +iobuf_writebyte(IOBUF a, unsigned c) +{ + if( a->d.len == a->d.size ) + if( iobuf_flush(a) ) + return -1; + + assert( a->d.len < a->d.size ); + a->d.buf[a->d.len++] = c; + return 0; +} + + +int +iobuf_write(IOBUF a, byte *buf, unsigned buflen ) +{ + for( ; buflen; buflen--, buf++ ) + if( iobuf_writebyte(a, *buf) ) + return -1; + return 0; +} + + + +/**************** + * copy the contents of TEMP to A. + */ +int +iobuf_write_temp( IOBUF a, IOBUF temp ) +{ + return iobuf_write(a, temp->d.buf, temp->d.len ); +} + +/**************** + * copy the contents of the temp io stream to BUFFER. + */ +size_t +iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen ) +{ + size_t n = a->d.len; + + if( n > buflen ) + n = buflen; + memcpy( buffer, a->d.buf, n ); + return n; +} + + +/**************** + * Set a limit, how much bytes may be read from the input stream A. + * Setting the limit to 0 disables this feature. + */ +void +iobuf_set_limit( IOBUF a, unsigned long nlimit ) +{ + a->nlimit = nlimit; + a->nbytes = 0; +} + + + +void +iobuf_start_recorder( IOBUF a ) +{ + m_free(a->recorder.buf); + a->recorder.size = 500; + a->recorder.buf = m_alloc(a->recorder.size); + a->recorder.len = 0; +} + +void +iobuf_push_recorder( IOBUF a, int c ) +{ + if( a->recorder.buf ) { + if( a->recorder.len >= a->recorder.size ) { + a->recorder.size += 500; + a->recorder.buf = m_realloc( a->recorder.buf, a->recorder.size ); + } + ((byte*)a->recorder.buf)[a->recorder.len++] = c; + } +} + + +char * +iobuf_stop_recorder( IOBUF a, size_t *n ) +{ + char *p; + if( !a->recorder.buf ) + log_bug("iobuf_recorder not started\n"); + p = a->recorder.buf; + if( n ) + *n = a->recorder.len; + a->recorder.buf = NULL; + return p; +} + + +/**************** + * Return the length of an open file + */ +u32 +iobuf_get_filelength( IOBUF a ) +{ + struct stat st; + + for( ; a; a = a->chain ) + if( !a->chain && a->filter == file_filter ) { + file_filter_ctx_t *b = a->filter_ov; + FILE *fp = b->fp; + + if( !fstat(fileno(fp), &st) ) + return st.st_size; + log_error("fstat() failed: %s\n", strerror(errno) ); + break; + } + + return 0; +} + +/**************** + * Start the block write mode, see rfc1991.new for details. + * A value of 0 for N stops this mode (flushes and writes + * the end marker) + */ +void +iobuf_set_block_mode( IOBUF a, size_t n ) +{ + block_filter_ctx_t *ctx = m_alloc_clear( sizeof *ctx ); + + assert( a->usage == 1 || a->usage == 2 ); + ctx->usage = a->usage; + if( !n ) { + iobuf_pop_filter(a, block_filter, NULL ); + } + else { + ctx->size = n; /* only needed for usage 2 */ + iobuf_push_filter(a, block_filter, ctx ); + } +} + + +/**************** + * checks wether the stream is in block mode + */ +int +iobuf_in_block_mode( IOBUF a ) +{ + for(; a; a = a->chain ) + if( a->filter == block_filter ) + return 1; /* yes */ + return 0; /* no */ +} + + + diff --git a/util/logger.c b/util/logger.c new file mode 100644 index 000000000..803420cd8 --- /dev/null +++ b/util/logger.c @@ -0,0 +1,139 @@ +/* logger.c - log functions + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "util.h" + +/**************** + * General interface for printing a line + * level 0 := print to /dev/null + * 1 := print to stdout + * 2 := print as info to stderr + * 3 := ditto but as error + */ +void +printstr( int level, const char *fmt, ... ) +{ + va_list arg_ptr ; + + if( !level ) + return; + + if( !fmt ) { + putc('\n', level? stderr: stdout); + return; + } + + va_start( arg_ptr, fmt ) ; + if( level < 2 ) { + vfprintf(stdout,fmt,arg_ptr) ; + } + else { + fprintf(stderr, level==2? "%s: ": "%s: error: ", strusage(13) ) ; + vfprintf(stderr,fmt,arg_ptr) ; + } + va_end(arg_ptr); +} + + +void +log_info( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "info: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); +} + +void +log_error( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "error: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); +} + +void +log_fatal( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "Fatal: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); + exit(2); +} + +void +log_bug( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "\nInternal Error: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); + fflush(stderr); + abort(); +} + +void +log_debug( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "DBG: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); +} + + + +void +log_hexdump( const char *text, char *buf, size_t len ) +{ + int i; + + fprintf(stderr, "DBG: %s", text ); + for(i=0; i < len; i++ ) + fprintf(stderr, " %02X", ((byte*)buf)[i] ); + fputc('\n', stderr); +} + + +void +log_mpidump( const char *text, MPI a ) +{ + fprintf(stderr, "DBG: %s", text ); + mpi_print(stderr, a, 1 ); + fputc('\n', stderr); +} + diff --git a/util/memory.c b/util/memory.c new file mode 100644 index 000000000..6ad57f9b0 --- /dev/null +++ b/util/memory.c @@ -0,0 +1,460 @@ +/* memory.c - memory allocation + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * We use our own memory allocation functions instead of plain malloc(), + * so that we can provide some special enhancements: + * a) functions to provide memory from a secure memory. + * Don't know how to handle it yet, but it may be possible to + * use memory which can't be swapped out. + * b) By looking at the requested allocation size we + * can reuse memory very quickly (e.g. MPI storage) + * c) A controlbyte gives us the opportunity to use only one + * free() function and do some overflow checking. + * d) memory checking and reporting if compiled with M_DEBUG + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "types.h" +#include "memory.h" +#include "util.h" + + +#define MAGIC_NOR_BYTE 0x55 +#define MAGIC_SEC_BYTE 0xcc +#define MAGIC_END_BYTE 0xaa + +const void membug( const char *fmt, ... ); + +#ifdef M_DEBUG + #undef m_alloc + #undef m_alloc_clear + #undef m_alloc_secure + #undef m_alloc_secure_clear + #undef m_realloc + #undef m_free + #undef m_check + #define FNAME(a) m_debug_ ##a + #define FNAMEPRT , const char *info + #define FNAMEARG , info + #define store_len(p,n,m) do { add_entry(p,n,m, \ + info, __FUNCTION__); } while(0) +#else + #define FNAME(a) m_ ##a + #define FNAMEPRT + #define FNAMEARG + #define store_len(p,n,m) do { ((byte*))p[0] = n; \ + ((byte*))p[2] = n >> 8 ; \ + ((byte*))p[3] = n >> 16 ; \ + ((byte*))p[4] = m? MAGIC_SEC_BYTE \ + : MAGIC_NOR_BYTE; \ + } while(0) +#endif + + +#ifdef M_DEBUG /* stuff used for memory debuging */ + +struct info_entry { + struct info_entry *next; + unsigned count; /* call count */ + const char *info; /* the reference to the info string */ +}; + +struct memtbl_entry { + const void *user_p; /* for reference: the pointer given to the user */ + size_t user_n; /* length requested by the user */ + struct memtbl_entry *next; /* to build a list of unused entries */ + const struct info_entry *info; /* points into the table with */ + /* the info strings */ + unsigned inuse:1; /* this entry is in use */ + unsigned count:31; +}; + + +#define INFO_BUCKETS 53 +#define info_hash(p) ( *(u32*)((p)) % INFO_BUCKETS ) +static struct info_entry *info_strings[INFO_BUCKETS]; /* hash table */ + +static struct memtbl_entry *memtbl; /* the table with the memory infos */ +static unsigned memtbl_size; /* number of allocated entries */ +static unsigned memtbl_len; /* number of used entries */ +static struct memtbl_entry *memtbl_unused;/* to keep track of unused entries */ + +static void dump_table(void); +static void check_allmem( const char *info ); + +/**************** + * Put the new P into the debug table and return a pointer to the table entry. + * mode is true for security. BY is the name of the function which called us. + */ +static void +add_entry( byte *p, unsigned n, int mode, const char *info, const char *by ) +{ + unsigned index; + struct memtbl_entry *e; + struct info_entry *ie; + + if( memtbl_len < memtbl_size ) + index = memtbl_len++; + else { + struct memtbl_entry *e; + /* look for an used entry in the table. We take the first one, + * so that freed entries remain as long as possible in the table + * (free appends a new one) + */ + if( (e = memtbl_unused) ) { + index = e - memtbl; + memtbl_unused = e->next; + e->next = NULL; + } + else { /* no free entries in the table: extend the table */ + if( !memtbl_size ) { /* first time */ + memtbl_size = 100; + if( !(memtbl = calloc( memtbl_size, sizeof *memtbl )) ) + membug("memory debug table malloc failed\n"); + index = 0; + memtbl_len = 1; + if( DBG_MEMSTAT ) + atexit( dump_table ); + } + else { /* realloc */ + unsigned n = memtbl_size / 4; /* enlarge by 25% */ + if(!(memtbl = realloc(memtbl, (memtbl_size+n)*sizeof *memtbl))) + membug("memory debug table realloc failed\n"); + memset(memtbl+memtbl_size, 0, n*sizeof *memtbl ); + memtbl_size += n; + index = memtbl_len++; + } + } + } + e = memtbl+index; + if( e->inuse ) + membug("Ooops: entry %u is flagged as in use\n", index); + e->user_p = p + 4; + e->user_n = n; + e->count++; + if( e->next ) + membug("Ooops: entry is in free entry list\n"); + /* do we already have this info string */ + for( ie = info_strings[info_hash(info)]; ie; ie = ie->next ) + if( ie->info == info ) + break; + if( !ie ) { /* no: make a new entry */ + if( !(ie = malloc( sizeof *ie )) ) + membug("can't allocate info entry\n"); + ie->next = info_strings[info_hash(info)]; + info_strings[info_hash(info)] = ie; + ie->info = info; + ie->count = 0; + } + ie->count++; + e->info = ie; + e->inuse = 1; + + /* put the index at the start of the memory */ + p[0] = index; + p[1] = index >> 8 ; + p[2] = index >> 16 ; + p[3] = mode? MAGIC_SEC_BYTE : MAGIC_NOR_BYTE ; + if( DBG_MEMORY ) + log_debug( "%s allocates %u bytes using %s\n", info, e->user_n, by ); +} + + + +/**************** + * Check that the memory block is correct. The magic byte has already been + * checked. Checks which are done here: + * - see wether the index points into our memory table + * - see wether P is the same as the one stored in the table + * - see wether we have already freed this block. + */ +struct memtbl_entry * +check_mem( const byte *p, const char *info ) +{ + unsigned n; + struct memtbl_entry *e; + + n = p[0]; + n |= p[1] << 8; + n |= p[2] << 16; + + if( n >= memtbl_len ) + membug("memory at %p corrupted: index=%u table_len=%u (%s)\n", + p+4, n, memtbl_len, info ); + e = memtbl+n; + + if( e->user_p != p+4 ) + membug("memory at %p corrupted: reference mismatch (%s)\n", p+4, info ); + if( !e->inuse ) + membug("memory at %p corrupted: marked as free (%s)\n", p+4, info ); + + if( !(p[3] == MAGIC_NOR_BYTE || p[3] == MAGIC_SEC_BYTE) ) + membug("memory at %p corrupted: underflow=%02x (%s)\n", p+4, p[3], info ); + if( p[4+e->user_n] != MAGIC_END_BYTE ) + membug("memory at %p corrupted: overflow=%02x (%s)\n", p+4, p[4+e->user_n], info ); + if( e->info->count > 20000 ) + membug("memory at %p corrupted: count too high (%s)\n", p+4, info ); + return e; +} + + +/**************** + * free the entry and the memory (replaces free) + */ +static void +free_entry( byte *p, const char *info ) +{ + struct memtbl_entry *e, *e2; + + check_allmem("add_entry"); + + e = check_mem(p, info); + if( DBG_MEMORY ) + log_debug( "%s frees %u bytes alloced by %s\n", + info, e->user_n, e->info->info ); + if( !e->inuse ) { + if( e->user_p == p + 4 ) + membug("freeing an already freed pointer at %p\n", p+4 ); + else + membug("freeing pointer %p which is flagged as freed\n", p+4 ); + } + + e->inuse = 0; + e->next = NULL; + if( !memtbl_unused ) + memtbl_unused = e; + else { + for(e2=memtbl_unused; e2->next; e2 = e2->next ) + ; + e2->next = e; + } + memset(p,'f', e->user_n+5); + free(p); +} + +static void +dump_entry(struct memtbl_entry *e ) +{ + unsigned n = e - memtbl; + + fprintf(stderr, "mem %4u%c %5u %p %5u %s (%u)\n", + n, e->inuse?'a':'u', e->count, e->user_p, e->user_n, + e->info->info, e->info->count ); + + +} + +static void +dump_table(void) +{ + unsigned n; + struct memtbl_entry *e; + ulong sum = 0, chunks =0; + + for( e = memtbl, n = 0; n < memtbl_len; n++, e++ ) { + dump_entry(e); + if(e->inuse) { + sum += e->user_n; + chunks++; + } + } + fprintf(stderr, " memory used: %8lu bytes in %ld chunks\n", + sum, chunks ); +} + +static void +check_allmem( const char *info ) +{ + unsigned n; + struct memtbl_entry *e; + + for( e = memtbl, n = 0; n < memtbl_len; n++, e++ ) + if( e->inuse ) + check_mem(e->user_p-4, info); +} + +#endif /* M_DEBUG */ + +const void +membug( const char *fmt, ... ) +{ + va_list arg_ptr ; + + fprintf(stderr, "\nMemory Error: " ) ; + va_start( arg_ptr, fmt ) ; + vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); + fflush(stderr); + #ifdef M_DEBUG + if( DBG_MEMSTAT ) + dump_table(); + #endif + abort(); +} + + +static void +out_of_core(size_t n) +{ + log_fatal("out of memory while allocating %u bytes\n", (unsigned)n ); +} + +/**************** + * Allocate memory of size n. + * This function gives up if we do not have enough memory + */ +void * +FNAME(alloc)( size_t n FNAMEPRT ) +{ + char *p; + + if( !(p = malloc( n + 5 )) ) + out_of_core(n); + store_len(p,n,0); + p[4+n] = MAGIC_END_BYTE; /* need to add the length somewhere */ + return p+4; +} + +/**************** + * Allocate memory of size n from the secure memory pool. + * This function gives up if we do not have enough memory + */ +void * +FNAME(alloc_secure)( size_t n FNAMEPRT ) +{ + char *p; + + if( !(p = malloc( n + 5 )) ) /* fixme: should alloc from the secure heap*/ + out_of_core(n); + store_len(p,n,1); + p[4+n] = MAGIC_END_BYTE; + return p+4; +} + +void * +FNAME(alloc_clear)( size_t n FNAMEPRT ) +{ + void *p; + p = FNAME(alloc)( n FNAMEARG ); + memset(p, 0, n ); + return p; +} + +void * +FNAME(alloc_secure_clear)( size_t n FNAMEPRT) +{ + void *p; + p = FNAME(alloc_secure)( n FNAMEARG ); + memset(p, 0, n ); + return p; +} + + +/**************** + * realloc and clear the new space + */ +void * +FNAME(realloc)( void *a, size_t n FNAMEPRT ) +{ /* FIXME: should be optimized :-) */ + unsigned char *p = a; + void *b; + size_t len = m_size(a); + + if( len >= n ) /* we don't shrink for now */ + return a; + if( p[-1] == MAGIC_SEC_BYTE ) + b = FNAME(alloc_secure_clear)(n FNAMEARG); + else + b = FNAME(alloc_clear)(n FNAMEARG); + FNAME(check)(NULL FNAMEARG); + memcpy(b, a, len ); + FNAME(free)(p FNAMEARG); + return b; +} + + + +/**************** + * Free a pointer + */ +void +FNAME(free)( void *a FNAMEPRT ) +{ + byte *p = a; + + if( !p ) + return; + #ifdef M_DEBUG + free_entry(p-4, info); + #else + m_check(p); + free(p-4); + #endif +} + + +void +FNAME(check)( const void *a FNAMEPRT ) +{ + const byte *p = a; + + #ifdef M_DEBUG + if( p ) + check_mem(p-4, info); + else + check_allmem(info); + #else + if( !p ) + return; + if( !(p[-1] == MAGIC_NOR_BYTE || p[-1] == MAGIC_SEC_BYTE) ) + membug("memory at %p corrupted (underflow=%02x)\n", p, p[-1] ); + else if( p[m_size(p)] != MAGIC_END_BYTE ) + membug("memory at %p corrupted (overflow=%02x)\n", p, p[-1] ); + #endif +} + + +size_t +m_size( const void *a ) +{ + const byte *p = a; + size_t n; + + #ifdef M_DEBUG + n = check_mem(p-4, "m_size")->user_n; + #else + n = ((byte*)p[-4]; + n |= ((byte*)p[-3] << 8; + n |= ((byte*)p[-2] << 16; + #endif + return n; +} + + +int +m_is_secure( const void *p ) +{ + return p && ((byte*)p)[-1] == MAGIC_SEC_BYTE; +} + diff --git a/util/miscutil.c b/util/miscutil.c new file mode 100644 index 000000000..9fecf4488 --- /dev/null +++ b/util/miscutil.c @@ -0,0 +1,33 @@ +/* miscutil.c - miscellaneous utilities + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <time.h> +#include "types.h" +#include "util.h" + +u32 +make_timestamp() +{ + return time(NULL); +} + + diff --git a/util/strgutil.c b/util/strgutil.c new file mode 100644 index 000000000..b517ed5b6 --- /dev/null +++ b/util/strgutil.c @@ -0,0 +1,63 @@ +/* strgutil.c - miscellaneous utilities + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdlib.h> +#include <ctype.h> +#include "types.h" +#include "util.h" +#include "memory.h" + + +void +free_strlist( STRLIST sl ) +{ + STRLIST sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + m_free(sl); + } +} + +/**************** + * look for the substring SUB in buffer and return a pointer to that + * substring in BUF or NULL if not found. + * Comparison is case-in-sensitive. + */ +char * +memistr( char *buf, size_t buflen, const char *sub ) +{ + const byte *t, *s ; + size_t n; + + for( t=buf, n=buflen, s=sub ; n ; t++, n-- ) + if( toupper(*t) == toupper(*s) ) { + for( buf=(char*)t++, buflen = n--, s++; + n && toupper(*t) == toupper(*s); t++, s++, n-- ) + ; + if( !*s ) + return buf; + t = buf; n = buflen; s = sub ; + } + + return NULL ; +} + diff --git a/util/ttyio.c b/util/ttyio.c new file mode 100644 index 000000000..39ad5a666 --- /dev/null +++ b/util/ttyio.c @@ -0,0 +1,114 @@ +/* ttyio.c - tty i/O functions + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 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 <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> +#include "util.h" +#include "memory.h" +#include "ttyio.h" + +static int last_prompt_len; + +static FILE * +open_tty(void) +{ + FILE *tty = fopen("/dev/tty", "r"); + if( !tty ) + log_fatal("cannot open /dev/tty: %s\n", strerror(errno) ); + return tty; +} + +static void +close_tty( FILE *tty ) +{ + fclose(tty); +} + + + +void +tty_printf( const char *fmt, ... ) +{ + va_list arg_ptr; + + va_start( arg_ptr, fmt ) ; + last_prompt_len += vfprintf(stderr,fmt,arg_ptr) ; + va_end(arg_ptr); + fflush(stderr); +} + + +char * +tty_get( const char *prompt ) +{ + char *buf; + int c, n, i; + FILE *fp; + + last_prompt_len = 0; + tty_printf( prompt ); + buf = m_alloc(n=50); + i = 0; + fp = open_tty(); + while( (c=getc(fp)) != EOF && c != '\n' ) { + last_prompt_len++; + if( c == '\t' ) + c = ' '; + else if( iscntrl(c) ) + continue; + if( !(i < n-1) ) { + n += 50; + buf = m_realloc( buf, n ); + } + buf[i++] = c; + } + close_tty(fp); + buf[i] = 0; + return buf; +} + +char * +tty_get_hidden( const char *prompt ) +{ + return tty_get( prompt ); /* fixme */ +} + + +void +tty_kill_prompt() +{ + int i; +#if 0 + for(i=0; i < last_prompt_len; i ++ ) + fputc('\b', stderr); + for(i=0; i < last_prompt_len; i ++ ) + fputc(' ', stderr); + for(i=0; i < last_prompt_len; i ++ ) + fputc('\b', stderr); +#endif + last_prompt_len = 0; + fflush(stderr); +} + |