diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /lib/util | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'lib/util')
-rw-r--r-- | lib/util/LICENSE.txt | 121 | ||||
-rw-r--r-- | lib/util/Makefile | 29 | ||||
-rw-r--r-- | lib/util/bitreverse.h | 46 | ||||
-rw-r--r-- | lib/util/filename.c | 26 | ||||
-rw-r--r-- | lib/util/filename.h | 7 | ||||
-rw-r--r-- | lib/util/intcode.h | 86 | ||||
-rw-r--r-- | lib/util/lexheader | 9 | ||||
-rw-r--r-- | lib/util/mallocvar.h | 107 | ||||
-rw-r--r-- | lib/util/nstring.c | 906 | ||||
-rw-r--r-- | lib/util/nstring.h | 157 | ||||
-rw-r--r-- | lib/util/pm_c_util.h | 62 | ||||
-rw-r--r-- | lib/util/shhopt-1.1.6.lsm | 16 | ||||
-rw-r--r-- | lib/util/shhopt.README | 200 | ||||
-rw-r--r-- | lib/util/shhopt.c | 939 | ||||
-rw-r--r-- | lib/util/shhopt.h | 240 | ||||
-rw-r--r-- | lib/util/testnstring.c | 30 | ||||
-rw-r--r-- | lib/util/wordaccess.h | 65 | ||||
-rw-r--r-- | lib/util/wordaccess_64_le.h | 52 | ||||
-rw-r--r-- | lib/util/wordaccess_gcc3_be.h | 40 | ||||
-rw-r--r-- | lib/util/wordaccess_gcc3_le.h | 54 | ||||
-rw-r--r-- | lib/util/wordaccess_generic.h | 89 |
21 files changed, 3281 insertions, 0 deletions
diff --git a/lib/util/LICENSE.txt b/lib/util/LICENSE.txt new file mode 100644 index 00000000..aeb06a7f --- /dev/null +++ b/lib/util/LICENSE.txt @@ -0,0 +1,121 @@ +The Frontier Artistic License Version 1.0 +Derived from the Artistic License at OpenSource.org. +Submitted to OpenSource.org for Open Source Initiative certification. + +Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to +make reasonable modifications. + +Definitions + + "Package" refers to the script, suite, file, or collection of + scripts, suites, and/or files distributed by the Copyright Holder, + and to derivatives of that Package created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes of + the Copyright Holder. + + "Copyright Holder" is whoever is named in the copyright statement + or statements for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Reasonable copying fee" is whatever you can justify on the basis + of media cost, duplication charges, time of people involved, and + so on. (You will not be required to justify it to the Copyright + Holder, but only to the computing community at large as a market + that must bear the fee.) + + "Freely Available" means that no fee is charged for the item + itself, though there may be fees involved in handling the item. + It also means that recipients of the item may redistribute it under + the same conditions they received it. + + +Terms + +1. You may make and give away verbatim copies of the source form of +the Standard Version of this Package without restriction, provided +that you duplicate all of the original copyright notices and +associated disclaimers. + +2. You may apply bug fixes, portability fixes, and other modifications +derived from the Public Domain or from the Copyright Holder. A Package +modified in such a way shall still be considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, +provided that you insert a prominent notice in each changed script, +suite, or file stating how and when you changed that script, suite, +or file, and provided that you do at least ONE of the following: + + a) Use the modified Package only within your corporation or + organization, or retain the modified Package solely for personal use. + + b) Place your modifications in the Public Domain or otherwise make + them Freely Available, such as by posting said modifications to Usenet + or an equivalent medium, or placing the modifications on a major archive + site such as ftp.uu.net, or by allowing the Copyright Holder to include + your modifications in the Standard Version of the Package. + + c) Rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page (or equivalent) for each non-standard executable + that clearly documents how it differs from the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) Distribute a Standard Version of the executables and library + files, together with instructions (in the manual page or + equivalent) on where to get the Standard Version. + + b) Accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) Accompany any non-standard executables with their corresponding + Standard Version executables, give the non-standard executables + non-standard names, and clearly document the differences in manual + pages (or equivalent), together with instructions on where to get + the Standard Version. + + d) Make other distribution arrangements with the Copyright Holder. + +5. You may charge a reasonable copying fee for any distribution of +this Package. You may charge any fee you choose for support of this +Package. You may not charge a fee for this Package itself. However, +you may distribute this Package in aggregate with other (possibly +commercial) programs as part of a larger (possibly commercial) +software distribution provided that you do not advertise this Package +as a product of your own. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whomever generated +them, and may be sold commercially, and may be aggregated with this +Package. + +7. Scripts, suites, or programs supplied by you that depend on or +otherwise make use of this Package shall not be considered part of +this Package. + +8. The name of the Copyright Holder may not be used to endorse or +promote products derived from this software without specific prior +written permission. + +9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End + + +http://www.spinwardstars.com/frontier/fal.html diff --git a/lib/util/Makefile b/lib/util/Makefile new file mode 100644 index 00000000..8f461f28 --- /dev/null +++ b/lib/util/Makefile @@ -0,0 +1,29 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = lib/util +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/.. + +# nstring is required for asprintf(), etc. Also some systems don't have +# snprintf(), e.g. Solaris 2.5.1. 2002.03.29. +UTILOBJECTS = shhopt.o nstring.o filename.o + +MERGE_OBJECTS = + +all: $(UTILOBJECTS) + +include $(SRCDIR)/Makefile.common + +$(UTILOBJECTS):%.o:%.c importinc + $(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \ + $(CFLAGS_PERSONAL) $(CADD) -o $@ $< + +testnstring: test.c nstring.h nstring.o + $(CC) $(CFLAGS) $(CADD) -o $@ nstring.o $< + +include Makefile.depend diff --git a/lib/util/bitreverse.h b/lib/util/bitreverse.h new file mode 100644 index 00000000..b3f0ea13 --- /dev/null +++ b/lib/util/bitreverse.h @@ -0,0 +1,46 @@ +/* +** bitreverse.h +** +** This particular array seems to be useful in a lot of bitmap +** conversion programs. It's not used with standard libnetpbm PBM +** processing because bits are stored one per byte (or per word), for +** easier manipulation. But if you wanted to write, for example, a +** program to directly convert Sun raster format into X bitmaps, you +** could use this. It's also useful for the "packed PBM" libnetpbm +** functions. +** +** Of course, you could also use this fairly slick chunk of code: +** +** c = ((c >> 1) & 0x55) | ((c << 1) & 0xaa); +** c = ((c >> 2) & 0x33) | ((c << 2) & 0xcc); +** c = ((c >> 4) & 0x0f) | ((c << 4) & 0xf0); +*/ + +#ifndef _BITR_H_ +#define _BITR_H_ + +static unsigned char bitreverse[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, + 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, + 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, + 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, + 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, + 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, + 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, + 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, + 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, + 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, + 0x3f, 0xbf, 0x7f, 0xff}; + +#endif /*_BITR_H_*/ diff --git a/lib/util/filename.c b/lib/util/filename.c new file mode 100644 index 00000000..e3a9a89f --- /dev/null +++ b/lib/util/filename.c @@ -0,0 +1,26 @@ +#include "nstring.h" + +#include "filename.h" + +const char * +pm_basename(const char * const fileName) { +/*---------------------------------------------------------------------------- + Return the filename portion of a file name, e.g. "foo.ppm" from + "/home/bryanh/foo.ppm". + + Return it as a malloc'ed string. +-----------------------------------------------------------------------------*/ + unsigned int basenameStart; + unsigned int i; + const char * retval; + + basenameStart = 0; /* initial assumption */ + + for (i = 0; fileName[i]; ++i) { + if (fileName[i] == '/') + basenameStart = i+1; + } + asprintfN(&retval, "%s", &fileName[basenameStart]); + + return retval; +} diff --git a/lib/util/filename.h b/lib/util/filename.h new file mode 100644 index 00000000..f598fec1 --- /dev/null +++ b/lib/util/filename.h @@ -0,0 +1,7 @@ +#ifndef FILENAME_H_INCLUDED +#define FILENAME_H_INCLUDED + +const char * +pm_basename(const char * const fileName); + +#endif diff --git a/lib/util/intcode.h b/lib/util/intcode.h new file mode 100644 index 00000000..4d9c83aa --- /dev/null +++ b/lib/util/intcode.h @@ -0,0 +1,86 @@ +#ifndef INTCODE_H_INCLUDED +#define INTCODE_H_INCLUDED + +#include "pm_config.h" /* For uint32_t, BYTE_ORDER */ + +typedef struct { +/*---------------------------------------------------------------------------- + This is a big-endian representation of a 32 bit integer. I.e. + bytes[0] is the most significant 8 bits; bytes[3] is the least + significant 8 bits of the number in pure binary. + + On a big-endian machines, this is bit for bit identical to uint32_t. + On a little-endian machine, it isn't. + + This is an important data type because decent file formats use + big-endian -- they don't care if some CPU happens to use some other + code for its own work. +-----------------------------------------------------------------------------*/ + unsigned char bytes[4]; +} bigend32; + + +unsigned int const pm_byteOrder = BYTE_ORDER; + + +static __inline__ uint32_t +pm_uintFromBigend32(bigend32 const arg) { + + uint32_t retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend32 bigend; + uint32_t native; + } converter; + + converter.bigend = arg; + + retval = converter.native; + }; break; + case LITTLE_ENDIAN: { + retval = + (arg.bytes[0] << 24) | + (arg.bytes[1] << 16) | + (arg.bytes[2] << 8) | + (arg.bytes[3] << 0); + } break; + } + return retval; +} + + + +static __inline__ bigend32 +pm_bigendFromUint32(uint32_t const arg) { + + bigend32 retval; + + switch (pm_byteOrder) { + case BIG_ENDIAN: { + union { + bigend32 bigend; + uint32_t native; + } converter; + + converter.native = arg; + + retval = converter.bigend; + } break; + case LITTLE_ENDIAN: { + uint32_t shift; + shift = arg; + retval.bytes[3] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[2] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[1] = shift; /* Takes lower 8 bits */ + shift >>= 8; + retval.bytes[0] = shift; /* Takes lower 8 bits */ + } break; + } + return retval; +} + +#endif diff --git a/lib/util/lexheader b/lib/util/lexheader new file mode 100644 index 00000000..c5691660 --- /dev/null +++ b/lib/util/lexheader @@ -0,0 +1,9 @@ +/* This is the file 'lexheader'. It contains extra stuff needed by + parsers generated by lex. + + GNU Flex generates a parser that refers to the non-ansi C library + subroutine fileno(). In order to let it compile without warnings, + we define _XOPEN_SOURCE to declare that fact. +*/ +#define _XOPEN_SOURCE +/* END OF lexheader */ diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h new file mode 100644 index 00000000..a26d007b --- /dev/null +++ b/lib/util/mallocvar.h @@ -0,0 +1,107 @@ +/* These are some dynamic memory allocation facilities. They are essentially + an extension to C, as they do allocations with a cognizance of C + variables. You can use them to make C read more like a high level + language. + + Before including this, you must define an __inline__ macro if your + compiler doesn't recognize it as a keyword. +*/ + +#ifndef MALLOCVAR_INCLUDED +#define MALLOCVAR_INCLUDED + +#include "pm_config.h" + +#include <limits.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +static __inline__ void +mallocProduct(void ** const resultP, + unsigned int const factor1, + unsigned int const factor2) { +/*---------------------------------------------------------------------------- + malloc a space whose size in bytes is the product of 'factor1' and + 'factor2'. But if that size cannot be represented as an unsigned int, + return NULL without allocating anything. Also return NULL if the malloc + fails. + + If either factor is zero, malloc a single byte. + + Note that malloc() actually takes a size_t size argument, so the + proper test would be whether the size can be represented by size_t, + not unsigned int. But there is no reliable indication available to + us, like UINT_MAX, of what the limitations of size_t are. We + assume size_t is at least as expressive as unsigned int and that + nobody really needs to allocate more than 4GB of memory. +-----------------------------------------------------------------------------*/ + if (factor1 == 0 || factor2 == 0) + *resultP = malloc(1); + else { + if (UINT_MAX / factor2 < factor1) + *resultP = NULL; + else + *resultP = malloc(factor1 * factor2); + } +} + + + +static __inline__ void +reallocProduct(void ** const blockP, + unsigned int const factor1, + unsigned int const factor2) { + + if (UINT_MAX / factor2 < factor1) + *blockP = NULL; + else + *blockP = realloc(*blockP, factor1 * factor2); +} + + + +#define MALLOCARRAY(arrayName, nElements) do { \ + void * array; \ + mallocProduct(&array, nElements, sizeof(arrayName[0])); \ + arrayName = array; \ +} while (0) + +#define REALLOCARRAY(arrayName, nElements) { \ + void * array; \ + array = arrayName; \ + reallocProduct(&array, nElements, sizeof(arrayName[0])); \ + arrayName = array; \ +} while (0) + + +#define MALLOCARRAY_NOFAIL(arrayName, nElements) \ +do { \ + MALLOCARRAY(arrayName, nElements); \ + if ((arrayName) == NULL) \ + abort(); \ +} while(0) + +#define REALLOCARRAY_NOFAIL(arrayName, nElements) \ +do { \ + REALLOCARRAY(arrayName, nElements); \ + if ((arrayName) == NULL) \ + abort(); \ +} while(0) + + +#define MALLOCVAR(varName) \ + varName = malloc(sizeof(*varName)) + +#define MALLOCVAR_NOFAIL(varName) \ + do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/util/nstring.c b/lib/util/nstring.c new file mode 100644 index 00000000..702a3c44 --- /dev/null +++ b/lib/util/nstring.c @@ -0,0 +1,906 @@ +/* + * snprintf.c - a portable implementation of snprintf + * + + THIS MODULE WAS ADAPTED FOR NETPBM BY BRYAN HENDERSON ON 2002.03.24. + Bryan got the base from + http://www.ijs.si/software/snprintf/snprintf-2.2.tar.gz, but made + a lot of changes and additions. + + * AUTHOR + * Mark Martinec <mark.martinec@ijs.si>, April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * 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 Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * IMPLEMENTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf implements only the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is acceptable for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are implemented. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (you + * can't use data types wint_t and wchar_t). + * + * The following common synonyms for conversion characters are acceptable: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are accepted + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT implemented: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + */ + + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#include <sys/types.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <errno.h> + +#include "pm.h" +#include "pm_c_util.h" + +#include "nstring.h" + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + + + +void +vsnprintfN(char * const str, + size_t const str_m, + const char * const fmt, + va_list ap, + size_t * const sizeP) { + + size_t str_l = 0; + const char *p = fmt; + + /* In contrast with POSIX, the ISO C99 now says that str can be + NULL and str_m can be 0. This is more useful than the old: + if (str_m < 1) return -1; + */ + + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would + be sufficient but the following code achieves better + performance for cases * where format string is long and + contains few conversions + */ + const char *q = strchr(p + 1,'%'); + size_t n = !q ? strlen(p) : (q - p); + if (str_l < str_m) { + size_t avail = str_m - str_l; + fast_memcpy(str + str_l, p, (n > avail ? avail : n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; + /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. + */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32]; + /* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; + /* string address in case of string argument */ + size_t str_arg_l; + /* natural field width of arg without padding and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c + conversion. N.B. standard explicitly states the char + argument for the c conversion is unsigned. + */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric + conversions as required by the precision or minimal + field width + */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits; + /* just to make compiler happy (defined but not used) */ + str_arg = NULL; + starting_p = p; + ++p; /* skip '%' */ + + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; break; + /* If both the ' ' and '+' flags appear, the ' ' + flag should be ignored + */ + case '#': alternate_form = 1; break; + case '\'': break; + } + ++p; + } + /* If the '0' and '-' flags both appear, the '0' flag + should be ignored. + */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; make sure + we treat argument like common implementations do + */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) + uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: Solaris 2.6 man page claims that in + this case the precision should be set to 0. + Digital Unix 4.0, HPUX 10 and BSD man page + claim that this case should be treated as + unspecified precision, which is what we do + here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; make + sure we treat argument like common + implementations do + */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) + uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { + /* double l = long long */ + length_modifier = 'l'; /* treat it as a single 'l' */ + p++; + } + } + fmt_spec = *p; + + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': + /* % behaves similar to 's' regarding flags and field widths */ + case 'c': + /* c behaves similar to 's' regarding flags and field widths */ + case 's': + /* wint_t and wchar_t not handled */ + length_modifier = '\0'; + /* the result of zero padding flag with non-numeric + conversion specifier is undefined. Solaris and + HPUX 10 does zero padding in this case, Digital + Unix and Linux does not. + */ + + zero_padding = 0; + /* turn zero padding off for string conversions */ + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; + /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) + /* make sure not to address string beyond the + specified precision !!! + */ + str_arg_l = 0; + else if (!precision_specified) + /* truncate string if necessary as requested by + precision + */ + str_arg_l = strlen(str_arg); + else if (precision == 0) + str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char * q = + memchr(str_arg, '\0', + precision <= 0x7fffffff ? + precision : 0x7fffffff); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value + */ + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL + for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) + */ + + int int_arg = 0; + unsigned int uint_arg = 0; + /* defined only for length modifier h, or for no + length modifiers + */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other + conversion character (other than d, i, u, o, + x, or X) is ignored. + + Digital Unix: not specified, but seems to behave + as HPUX does. + + Solaris: If an h, l, or L appears before any + other conversion specifier (other than d, i, u, + o, x, or X), the behavior is + undefined. (Actually %hp converts only 16-bits + of address and %llp treats address as 64-bit + data which is incompatible with (void *) + argument on a 32-bit system). + */ + + length_modifier = '\0'; + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second + argument of char or short to va_arg, + because arguments seen by the called + function are not char or short. C converts + char and short arguments to int before + passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) + arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) + arg_sign = 1; + break; + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: For d, i, u, o, x, and X conversions, if + precision is specified, the '0' flag should be + ignored. This is so with Solaris 2.6, Digital UNIX + 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with + Perl. + */ + if (precision_specified) + zero_padding = 0; + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short + int)(-32768) + */ + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || + fmt_spec == 'X')) { + tmp[str_arg_l++] = '0'; + tmp[str_arg_l++] = fmt_spec; + } + /* alternate form should have no effect for p + conversion, but ... + */ + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) + precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0) { + /* converted to null string */ + /* When zero value is formatted with an explicit + precision 0, the resulting formatted string is + empty (d, i, u, o, x, X, p). + */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; + /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { + f[f_l++] = 'l'; f[f_l++] = 'l'; + } + else + f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') + str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); + break; + case 'l': + str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); + break; + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + str_arg_l += sprintf(tmp+str_arg_l, f, uint_arg); + break; + case 'l': + str_arg_l += sprintf(tmp+str_arg_l, f, ulong_arg); + break; + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point + */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { + size_t num_of_digits = + str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0')) { + /* assure leading zero for alternate-form + octal numbers + */ + if (!precision_specified || + precision < num_of_digits+1) { + /* precision is increased to force the + first character to be zero, except if a + zero value is formatted with an + explicit precision of zero + */ + precision = num_of_digits+1; + precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + int n = + min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + } break; + default: + /* unrecognized conversion specifier, keep format + string as-is + */ + zero_padding = 0; + /* turn zero padding off for non-numeric convers. */ + /* reset flags */ + justify_left = 1; + min_field_width = 0; + /* discard the unrecognized conversion, just keep the + unrecognized conversion character + */ + str_arg = p; + str_arg_l = 0; + if (*p) + /* include invalid conversion specifier unchanged + if not at end-of-string + */ + ++str_arg_l; + break; + } + if (*p) + p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by + min_field_width; this does not include the zero padding + in case of numerical conversions + */ + + if (!justify_left) { + /* left padding with blank or zero */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding ? '0' : ' '), + (n > avail ? avail : n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the + minimal field width for numeric conversions required? + */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, + force it to be copied later in its entirety + */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before + zero padding + */ + int n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision + or min field width + */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m - str_l; + fast_memset(str + str_l, '0', (n > avail ? avail : n)); + } + str_l += n; + } + } + /* insert formatted string (or as-is conversion specifier + for unknown conversions) + */ + { + int n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str + str_l, + str_arg + zero_padding_insertion_ind, + (n > avail ? avail : n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { + /* right blank padding to the field width */ + int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } + if (str_m > 0) { + /* make sure the string is null-terminated even at the expense + of overwriting the last character (shouldn't happen, but + just in case) + */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + *sizeP = str_l; +} + + + +int +snprintfN(char * const dest, + size_t const str_m, + const char * const fmt, + ...) { + + size_t size; + va_list ap; + + va_start(ap, fmt); + + vsnprintfN(dest, str_m, fmt, ap, &size); + + va_end(ap); + + assert(size <= INT_MAX); + + return size; +} + + + +/* When a function that is supposed to return a malloc'ed string cannot + get the memory for it, it should return 'strsol'. That has a much + better effect on the caller, if the caller doesn't explicitly allow for + the out of memory case, than returning NULL. Note that it is very + rare for the system not to have enough memory to return a small string, + so it's OK to have somewhat nonsensical behavior when it happens. We + just don't want catastrophic behavior. + + 'strsol' is an external symbol, so if Caller wants to detect the + out-of-memory failure, he certainly can. +*/ +const char * const strsol = "NO MEMORY TO CREATE STRING!"; + + + +/* We would like to have vasprintfN(), but it is difficult because you + can't run through a va_list twice, which we would want to do: once + to measure the length; once actually to build the string. On some + machines, you can simply make two copies of the va_list variable in + normal C fashion, but on others you need va_copy, which is a + relatively recent invention. In particular, the simple va_list copy + failed on an AMD64 Gcc Linux system in March 2006. +*/ + +void PM_GNU_PRINTF_ATTR(2,3) +asprintfN(const char ** const resultP, + const char * const fmt, + ...) { + + va_list varargs; + + size_t dryRunLen; + + va_start(varargs, fmt); + + vsnprintfN(NULL, 0, fmt, varargs, &dryRunLen); + + va_end(varargs); + + if (dryRunLen + 1 < dryRunLen) + /* arithmetic overflow */ + *resultP = strsol; + else { + size_t const allocSize = dryRunLen + 1; + char * result; + result = malloc(allocSize); + if (result == NULL) + *resultP = strsol; + else { + va_list varargs; + size_t realLen; + + va_start(varargs, fmt); + + vsnprintfN(result, allocSize, fmt, varargs, &realLen); + + assert(realLen == dryRunLen); + va_end(varargs); + + *resultP = result; + } + } +} + + + +void +strfree(const char * const string) { + + if (string != strsol) + free((void *) string); +} + + + +const char * +strsepN(char ** const stringP, const char * const delim) { + const char * retval; + + if (stringP == NULL || *stringP == NULL) + retval = NULL; + else { + char * p; + + retval = *stringP; + + for (p = *stringP; *p && strchr(delim, *p) == NULL; ++p); + + if (*p) { + /* We hit a delimiter, not end-of-string. So null out the + delimiter and advance user's pointer to the next token + */ + *p++ = '\0'; + *stringP = p; + } else { + /* We ran out of string. So the end-of-string delimiter is + already there, and we set the user's pointer to NULL to + indicate there are no more tokens. + */ + *stringP = NULL; + } + } + return retval; +} + + + +int +stripeq(const char * const comparand, + const char * const comparator) { +/*---------------------------------------------------------------------------- + Compare two strings, ignoring leading and trailing white space. + + Return 1 (true) if the strings are identical; 0 (false) otherwise. +-----------------------------------------------------------------------------*/ + char *p, *q, *px, *qx; + char equal; + + /* Make p and q point to the first non-blank character in each string. + If there are no non-blank characters, make them point to the terminating + NULL. + */ + + p = (char *) comparand; + while (ISSPACE(*p)) p++; + q = (char *) comparator; + while (ISSPACE(*q)) q++; + + /* Make px and qx point to the last non-blank character in each string. + If there are no nonblank characters (which implies the string is + null), make them point to the terminating NULL. + */ + + if (*p == '\0') px = p; + else { + px = p + strlen(p) - 1; + while (ISSPACE(*px)) px--; + } + + if (*q == '\0') qx = q; + else { + qx = q + strlen(q) - 1; + while (ISSPACE(*qx)) qx--; + } + + equal = 1; /* initial assumption */ + + /* If the stripped strings aren't the same length, + we know they aren't equal + */ + if (px - p != qx - q) equal = 0; + + + while (p <= px) { + if (*p != *q) equal = 0; + p++; q++; + } + return equal; +} + + + +const char * +memmemN(const char * const haystack, + size_t const haystacklen, + const char * const needle, + size_t const needlelen) { + + /* This does the same as the function of the same name in the GNU + C library + */ + const char * p; + + for (p = haystack; p <= haystack + haystacklen - needlelen; ++p) + if (MEMEQ(p, needle, needlelen)) + return p; + + return NULL; +} diff --git a/lib/util/nstring.h b/lib/util/nstring.h new file mode 100644 index 00000000..9ed20051 --- /dev/null +++ b/lib/util/nstring.h @@ -0,0 +1,157 @@ +#ifndef _NSTRING_H +#define _NSTRING_H + +#include <stdarg.h> +#include <string.h> +#include <ctype.h> + +#include "pm.h" /* For PM_GNU_PRINTF_ATTR, __inline__ */ + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +/* Here is are string functions that respect the size of the array + into which you are copying -- E.g. STRSCPY truncates the source string as + required so that it fits, with the terminating null, in the destination + array. +*/ +#define STRSCPY(A,B) \ + (strncpy((A), (B), sizeof(A)), *((A)+sizeof(A)-1) = '\0') +#define STRSCMP(A,B) \ + (strncmp((A), (B), sizeof(A))) +#define STRSCAT(A,B) \ + (strncpy(A+strlen(A), B, sizeof(A)-strlen(A)), *((A)+sizeof(A)-1) = '\0') + +#define STREQ(A, B) \ + (strcmp((A), (B)) == 0) +#define STRNEQ(A, B, C) \ + (strncmp((A), (B), (C)) == 0) +#define STRCASEEQ(A, B) \ + (strcasecmp((A), (B)) == 0) +#define STRNCASEEQ(A, B, C) \ + (strncasecmp((A), (B), (C)) == 0) +#define STRSEQ(A, B) \ + (strncmp((A), (B), sizeof(A)) == 0) + +#define MEMEQ(A, B, C) \ + (memcmp((A), (B), (C)) == 0) +#define MEMSZERO(A) \ + bzero((A), sizeof(A)) + + +static __inline__ int +streq(const char * const comparand, + const char * const comparator) { + + return strcmp(comparand, comparator) == 0; +} + + + +/* The standard C library routines isdigit(), for some weird + historical reason, does not take a character (type 'char') as its + argument. Instead it takes an integer. When the integer is a whole + number, it represents a character in the obvious way using the local + character set encoding. When the integer is negative, the results + are undefined. + + Passing a character to isdigit(), which expects an integer, results in + isdigit() sometimes getting a negative number. + + On some systems, when the integer is negative, it represents exactly + the character you want it to anyway (e.g. -1 is the character that is + encoded 0xFF). But on others, it does not. + + (The same is true of other routines like isdigit()). + + Therefore, we have the substitutes for isdigit() etc. that take an + actual character (type 'char') as an argument. +*/ + +#define ISALNUM(C) (isalnum((unsigned char)(C))) +#define ISALPHA(C) (isalpha((unsigned char)(C))) +#define ISCNTRL(C) (iscntrl((unsigned char)(C))) +#define ISDIGIT(C) (isdigit((unsigned char)(C))) +#define ISGRAPH(C) (isgraph((unsigned char)(C))) +#define ISLOWER(C) (islower((unsigned char)(C))) +#define ISPRINT(C) (isprint((unsigned char)(C))) +#define ISPUNCT(C) (ispunct((unsigned char)(C))) +#define ISSPACE(C) (isspace((unsigned char)(C))) +#define ISUPPER(C) (isupper((unsigned char)(C))) +#define ISXDIGIT(C) (isxdigit((unsigned char)(C))) +#define TOUPPER(C) ((char)toupper((unsigned char)(C))) + + +/* These are all private versions of commonly available standard C + library subroutines whose names are the same except with the N at + the end. Because not all standard C libraries have them all, + Netpbm must include them in its own libraries, and because some + standard C libraries have some of them, Netpbm must use different + names for them. + + The GNU C library has all of them. All but the oldest standard C libraries + have snprintf(). + + There are slight differences between the asprintf() family and that + found in other libraries: + + - There is no return value. + + - The returned string is a const char * instead of a char *. The + const is more correct. + + - If the function can't get the memory, it returns 'strsol', + which is a string that is in static memory that contains text + indicating an out of memory failure has occurred, intead of + NULL. This makes it much easier for programs to ignore this + possibility. + + strfree() is strictly a Netpbm invention, to allow proper type checking + when freeing storage allocated by the Netpbm asprintfN(). +*/ + +extern const char * const strsol; + +int +snprintfN(char * const dest, + size_t const str_m, + const char * const fmt, + ...) PM_GNU_PRINTF_ATTR(3,4); + +void +vsnprintfN(char * const str, + size_t const str_m, + const char * const fmt, + va_list ap, + size_t * const sizeP); + +void +asprintfN(const char ** const resultP, + const char * const fmt, + ...) PM_GNU_PRINTF_ATTR(2,3); + +void +strfree(const char * const string); + +const char * +strsepN(char ** const stringP, const char * const delim); + +int +stripeq(const char * const comparand, + const char * const comparator); + +const char * +memmemN(const char * const haystack, + size_t const haystacklen, + const char * const needle, + size_t const needlelen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h new file mode 100644 index 00000000..f21a2f82 --- /dev/null +++ b/lib/util/pm_c_util.h @@ -0,0 +1,62 @@ +#ifndef PM_C_UTIL_INCLUDED +#define PM_C_UTIL_INCLUDED + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#undef ABS +#define ABS(a) ((a) >= 0 ? (a) : -(a)) +#undef SGN +#define SGN(a) (((a)<0) ? -1 : 1) +#undef ODD +#define ODD(n) ((n) & 1) +#undef ROUND +#define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5)) +#undef ROUNDU +#define ROUNDU(X) ((unsigned int)((X)+0.5)) +#undef SQR +#define SQR(a) ((a)*(a)) + +/* NOTE: do not use "bool" as a type in an external interface. It could + have different definitions on either side of the interface. Even if both + sides include this interface header file, the conditional compilation + here means one side may use the typedef below and the other side may + use some other definition. For an external interface, be safe and just + use "int". +*/ + +/* We used to assume that if TRUE was defined, then bool was too. + However, we had a report on 2001.09.21 of a Tru64 system that had + TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was + likewise. So now we define bool all the time, unless the macro + HAVE_BOOL is defined. If someone is using the Netpbm libraries and + also another library that defines bool, he can either make the + other library define/respect HAVE_BOOL or just define HAVE_BOOL in + the file that includes pm_config.h or with a compiler option. Note + that C++ always has bool. + + A preferred way of getting booleans is <stdbool.h>. But it's not + available on all platforms, and it's easy to reproduce what it does + here. +*/ +#ifndef TRUE + #define TRUE 1 + #endif +#ifndef FALSE + #define FALSE 0 + #endif +/* C++ has a bool type and false and true constants built in. */ +#ifndef __cplusplus + #ifndef HAVE_BOOL + #define HAVE_BOOL 1 + typedef int bool; + #endif + #ifndef true + enum boolvalue {false=0, true=1}; + #endif + #endif + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +#endif diff --git a/lib/util/shhopt-1.1.6.lsm b/lib/util/shhopt-1.1.6.lsm new file mode 100644 index 00000000..7fba316e --- /dev/null +++ b/lib/util/shhopt-1.1.6.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: shhopt - library for parsing command line options. +Version: 1.1.6 +Entered-date: 10MAR00 +Description: C-functions for parsing command line options, both + traditional one-character options, and GNU'ish + --long-options. +Keywords: programming, library, lib, commandline, options +Author: sverrehu@online.no (Sverre H. Huseby) +Primary-site: http://home.sol.no/~sverrehu/pub-unix/ +Alternate-site: sunsite.unc.edu /pub/Linux/libs + shhopt-1.1.6.tar.gz +Platforms: Requires ANSI C-compiler. +Copying-policy: Artistic License + http://www.opensource.org/licenses/artistic-license.html +End diff --git a/lib/util/shhopt.README b/lib/util/shhopt.README new file mode 100644 index 00000000..2d241edf --- /dev/null +++ b/lib/util/shhopt.README @@ -0,0 +1,200 @@ +Shhopt was originally written by Sverre H. Huseby, and extended by +Bryan Henderson starting in March 2000 for use in Netpbm. + +Below is the README file from Huseby's package. + +The file LICENSE.TXT in this directory contains the license (the +Artistic License) under which Bryan took and redistributed Shhopt and +the license under which Bryan offers the modified Shhopt to others. + +Bryan made the following changes to shhopt for Netpbm. It is fully +backward compatible with the original. + +- OPT_FLOAT (floating point number) data type added + +- optParseOptions2() added. Advantages over optParseOptions(): You + can have a syntax where there is no such thing as a short option + (e.g. -a. Maybe stacked like -tanp). Then the long options can + have either 1 or 2 dashes (e.g. -width or --width). Of course, -w + could be an abbreviation of -width; that's not the same thing as a + short option. + +- optParseOptions3() added. Advantages over optParseOptions2(): + Tells you whether (how many times, actually) an option was + specified - no need to play games with defaults. Also, no need + to initialize an option value variable. + +- optStruct longName changed from char * to const char * to avoid + compiler warnings (with -Wwrite-strings) when you assign a string + literal to it (which is the normal case). + +- OPTENTRY/OPTENT3 macros added for declaring the option definition + array. + +- replace isdigit() with ISDIGIT() from Netpbm nstring.h so weird + 8-bit characters don't cause incorrect results. + +------------------------------------------------------------------------------ + + +shhopt - library for parsing command line options. +================================================== + +This is a set of functions for parsing command line options. Both +traditional one-character options, and GNU-style --long-options are +supported. + + +What separates this from traditional getopt? +-------------------------------------------- + +This library does more of the parsing for you. You set up a special +structure describing the names and types of the options you want your +program to support. In the structure you also give addresses of +variables to update or functions to call for the various +options. By calling optParseOptions, all options in argv are parsed +and removed from argv. What is left, are the non-optional arguments to +your program. + +The down-side of this, is that you won't be able to make a program +where the position of the options between the non-options are +significant. + +shhopt is distributed under the "Artistic license" (aka. the Perl +license), which IMHO gives more freedom than GPL or LGPL. For a copy +of the Artistic license, see + + http://www.opensource.org/licenses/artistic-license.html + +For more information on Open Source licenses, go to + + http://www.opensource.org/licenses/ + + +Usage +----- + +To see how to use this library, take a look at the sample program +example.c. + +A brief explanation: + +To parse your command line, you need to create and initialize an array +of optStruct's. Each element in the array describes a long and short +version of an option and specifies what type of option it is and how +to handle it. + +The structure fields (see also shhopt.h): + + `shortName' is the short option name without the leading '-'. + + `longName' is the long option name without the leading "--". + + `type' specifies what type of option this is. (Does it expect an + argument? Is it a flag? If it takes an argument, what type + should it be?) + + `arg' is either a function to be called with the argument from + the commandline, or a pointer to a location in which to store + the value. + + `flags' indicates whether `arg' points to a function or a storage + location. + +The different argument types: + + `OPT_END' flags this as the last element in the options array. + + `OPT_FLAG' indicates an option that takes no arguments. If `arg' is + not a function pointer, the value of `arg' will be set to 1 if + this flag is found on the command line. + + `OPT_STRING' expects a string argument. + + `OPT_INT' expects an int argument. + + `OPT_UINT' expects an unsigned int argument. + + `OPT_LONG' expects a long argument. + + `OPT_ULONG' expects an unsigned long argument. + +The different flag types: + + `OPT_CALLFUNC' indicates that `arg' is a function pointer. If this + is not given, `arg' is taken as a pointer to a variable. + + +Notes +----- + +* A dash (`-') by itself is not taken as any kind of an option, as + several programs use this to indicate the special files stdin and + stdout. It is thus left as a normal argument to the program. + +* Two dashes (`--') as an argument, is taken to mean that the rest of + the arguments should not be scanned for options. This simplifies + giving names of files that start with a dash. + +* Short (one-character) options accept parameters in two ways, either + directly following the option in the same argv-entry, or in the next + argv-entry: + + -sPARAMETER + -s PARAMETER + +* Long options accept parameters in two ways: + + --long-option=PARAMETER + --long-option PARAMETER + + To follow the GNU-tradition, your program documentation should use + the first form. + +* Several one-character options may be combined after a single + dash. If any of the options requires a parameter, the rest of the + string is taken as this parameter. If there is no "rest of the + string", the next argument is taken as the parameter. + +* There is no support for floating point (double) arguments to + options. This is to avoid unnecessary linking with the math + library. See example.c for how to get around this by writing a + function converting a string argument to a double (functions + strToDouble and doubleFunc). + + +Portability +----------- + +If your libc lacks strtoul, you will need to link with GNU's -liberty, +that may be found by anonymous ftp to ftp://ftp.gnu.org/pub/gnu/ + +The library has (more or less recently) been compiled and `tested' on +the following systems: + + IRIX Release 5.3 IP22 + GNU/Linux 2.2.11 + SunOS Release 4.1.3_U1 (-liberty needed) + ULTRIX V4.4 (Rev. 69) + +All compilations were done using GNU's gcc, and GNU's make. + + +Author +------ + +The program is written by + + Sverre H. Huseby sverrehu@online.no + Lofthusvn. 11 B http://home.sol.no/~sverrehu/ + N-0587 Oslo + Norway + +You can use and copy this for _free_, but I would be very happy if you +send me an E-mail and tell me that you use it. If you insist on paying +something, please donate some money to an organization that strives to +make the world a better place for everyone. + +I don't like bugs, so please help me removing them by reporting +whatever you find! + diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c new file mode 100644 index 00000000..7722b5d5 --- /dev/null +++ b/lib/util/shhopt.c @@ -0,0 +1,939 @@ +/*------------------------------------------------------------------------ + | FILE shhopt.c + | + | DESCRIPTION Functions for parsing command line arguments. Values + | of miscellaneous types may be stored in variables, + | or passed to functions as specified. + | + | REQUIREMENTS Some systems lack the ANSI C -function strtoul. If your + | system is one of those, you'll ned to write one yourself, + | or get the GNU liberty-library (from prep.ai.mit.edu). + | + | WRITTEN BY Sverre H. Huseby <sverrehu@online.no> + +----------------------------------------------------------------------*/ + +/************************************************************************* + This is based on work by Sverre H. Huseby <sverrehu@online.no>. + These functions are backward compatible with the 'shhopt' + distributed by Huseby. + + See the file README.shhopt for copy licensing information. +*************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <errno.h> +#include <assert.h> + +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" + +/*-----------------------------------------------------------------------+ +| PRIVATE DATA | ++-----------------------------------------------------------------------*/ + +static void optFatalFunc(const char *, ...); +static void (*optFatal)(const char *format, ...) = optFatalFunc; + +/*-----------------------------------------------------------------------+ +| PRIVATE FUNCTIONS | ++-----------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------ + | NAME optFatalFunc + | + | FUNCTION Show given message and abort the program. + | + | INPUT format, ... + | Arguments used as with printf(). + | + | RETURNS Never returns. The program is aborted. + */ +static void +optFatalFunc(const char *format, ...) +{ + va_list ap; + + fflush(stdout); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(99); +} + +/*------------------------------------------------------------------------ + | NAME optStructCount + | + | FUNCTION Get number of options in a optStruct. + | + | INPUT opt array of possible options. + | + | RETURNS Number of options in the given array. + | + | DESCRIPTION Count elements in an optStruct-array. The strcture must + | be ended using an element of type OPT_END. + */ +static int +optStructCount(const optEntry opt[]) +{ + int ret = 0; + + while (opt[ret].type != OPT_END && ret < 500) + ++ret; + return ret; +} + +/*------------------------------------------------------------------------ + | NAME optMatch + | + | FUNCTION Find a matching option. + | + | INPUT opt array of possible options. + | s string to match, without `-' or `--'. + | lng match long option, otherwise short. + | + | RETURNS Index to the option if found, -1 if not found. + | + | DESCRIPTION Short options are matched from the first character in + | the given string. + */ +static int +optMatch(const optEntry opt[], const char *s, int lng) +{ + int nopt, q, matchlen = 0; + const char *p; + + nopt = optStructCount(opt); + if (lng) { + if ((p = strchr(s, '=')) != NULL) + matchlen = p - s; + else + matchlen = strlen(s); + } + for (q = 0; q < nopt; q++) { + if (lng) { + if (!opt[q].longName) + continue; + if (strncmp(s, opt[q].longName, matchlen) == 0) + return q; + } else { + if (!opt[q].shortName) + continue; + if (*s == opt[q].shortName) + return q; + } + } + return -1; +} + +/*------------------------------------------------------------------------ + | NAME optString + | + | FUNCTION Return a (static) string with the option name. + | + | INPUT opt the option to stringify. + | lng is it a long option? + | + | RETURNS Pointer to static string. + */ +static char * +optString(const optEntry opte, int lng) +{ + static char ret[31]; + + if (lng) { + strcpy(ret, "--"); + strncpy(ret + 2, opte.longName, 28); + } else { + ret[0] = '-'; + ret[1] = opte.shortName; + ret[2] = '\0'; + } + return ret; +} + + + +static optEntry +optStructToEntry(const optStruct opt) { +/*---------------------------------------------------------------------------- + Return the information in 'opt' (an optStruct type) as an optEntry type. + optEntry is newer and has an additional field. +-----------------------------------------------------------------------------*/ + optEntry opte; + + opte.shortName = opt.shortName; + opte.longName = opt.longName; + opte.type = opt.type; + opte.arg = opt.arg; + opte.specified = NULL; + opte.flags = opt.flags; + + return(opte); +} + + + +static optEntry * +optStructTblToEntryTbl(const optStruct optStructTable[]) { +/*---------------------------------------------------------------------------- + Return a table of optEntry types containing the information in the + input table of optStruct types. + + Return it in newly malloc'ed storage. +-----------------------------------------------------------------------------*/ + int count; + /* Number of entries in input table, including OPT_END marker */ + int i; + + optEntry *optEntryTable; /* malloc'ed array */ + + /* Count the entries in optStructTable[] */ + for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++); + count = i+1; + + optEntryTable = (optEntry *) malloc(count * sizeof(optEntry)); + if (optEntryTable) { + int i; + for (i = 0; i < count; i++) + optEntryTable[i] = optStructToEntry(optStructTable[i]); + } + return(optEntryTable); +} + + + + +/*------------------------------------------------------------------------ + | NAME optNeedsArgument + | + | FUNCTION Check if an option requires an argument. + | + | INPUT opt the option to check. + | + | RETURNS Boolean value. + */ +static int +optNeedsArgument(const optEntry opt) +{ + return opt.type == OPT_STRING + || opt.type == OPT_INT + || opt.type == OPT_UINT + || opt.type == OPT_LONG + || opt.type == OPT_ULONG + || opt.type == OPT_FLOAT + || opt.type == OPT_NAMELIST + ; +} + +/*------------------------------------------------------------------------ + | NAME argvRemove + | + | FUNCTION Remove an entry from an argv-array. + | + | INPUT argc pointer to number of options. + | argv array of option-/argument-strings. + | i index of option to remove. + | + | OUTPUT argc new argument count. + | argv array with given argument removed. + */ +static void +argvRemove(int *argc, char *argv[], int i) +{ + if (i >= *argc) + return; + while (i++ < *argc) + argv[i - 1] = argv[i]; + --*argc; +} + + + +static void +getToken(const char * const tokenStart, + char const delimiter, + const char ** const tokenP, + const char ** const nextP) { +/*---------------------------------------------------------------------------- + Find the token starting at 'tokenStart' up to but not including + the first 'delimiter' character or end of string. Return it in newly + malloced memory as *tokenP, NUL-terminated. + + Make *nextP point just past the token, i.e. to the delimiter or + end of string NUL character. + + Note that if the string is empty, or starts with the delimiter, + we return an empty string and *nextP == tokenStart, i.e. *nextP + doesn't necessarily advance. +-----------------------------------------------------------------------------*/ + char * token; + const char * cursor; + unsigned int charCount; + + /* Run through the token, counting characters */ + + charCount = 0; + cursor = tokenStart; + + while (*cursor != delimiter && *cursor != '\0') { + if (*cursor == '\\') { + ++cursor; + if (*cursor == '\0') + optFatal("string ends with an escape character (\\)"); + } + ++cursor; + ++charCount; + } + + token = malloc(charCount + 1); + if (token == NULL) + optFatal("Could not allocate %u bytes of memory to parse a string", + charCount + 1); + + /* Go back and do it again, this time copying the characters */ + charCount = 0; + cursor = tokenStart; + + while (*cursor != delimiter && *cursor != '\0') { + if (*cursor == '\\') + ++cursor; + + assert(*cursor != '\0'); + + token[charCount++] = *cursor++; + } + token[charCount] = '\0'; + + *tokenP = token; + *nextP = cursor; +} + + + +static void +parseNameList(const char * const listText, + struct optNameValue ** const listP) { + + unsigned int const maxOptionCount = 100; + + const char * cursor; + unsigned int optionCount; + struct optNameValue * list; + + MALLOCARRAY_NOFAIL(list, maxOptionCount+1); + + cursor = &listText[0]; /* initial value */ + + optionCount = 0; /* initial value */ + + while (optionCount < maxOptionCount && *cursor != '\0') { + const char * next; + struct optNameValue pair; + + getToken(cursor, '=', &pair.name, &next); + + cursor = next; + + if (*cursor == '\0') + optFatal("name=value option value ends prematurely. An equal " + "sign was expected following name '%s'", pair.name); + + assert(*cursor == '='); + ++cursor; + + getToken(cursor, ',', &pair.value, &next); + + cursor = next; + + list[optionCount++] = pair; + + if (*cursor != '\0') { + assert(*cursor == ','); + ++cursor; + } + } + list[optionCount].name = NULL; + list[optionCount].value = NULL; + + *listP = list; +} + + + +/*------------------------------------------------------------------------ + | NAME optExecute + | + | FUNCTION Perform the action of an option. + | + | INPUT opt element in array of defined options that + | applies to this option + | arg argument to option, if it applies. + | lng was the option given as a long option? + | + | RETURNS Nothing. Aborts in case of error. + */ +static void +optExecute(optEntry const opt, char *arg, int lng) +{ + if (opt.specified) + (*(opt.specified))++; + + switch (opt.type) { + case OPT_FLAG: + if (opt.arg) + *((int *) opt.arg) = 1; + break; + + case OPT_STRING: + if (opt.arg) + *((char **) opt.arg) = arg; + break; + + case OPT_INT: + case OPT_LONG: { + long tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtol(arg, &e, 10); + if (*e) + optFatal("invalid number `%s'", arg); + if (errno == ERANGE + || (opt.type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN))) + optFatal("number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.type == OPT_INT) { + *((int *) opt.arg) = (int) tmp; + } else /* OPT_LONG */ { + if (opt.arg) + *((long *) opt.arg) = tmp; + } + } break; + + case OPT_UINT: + case OPT_ULONG: { + unsigned long tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtoul(arg, &e, 10); + if (*e) + optFatal("invalid number `%s'", arg); + if (errno == ERANGE + || (opt.type == OPT_UINT && tmp > UINT_MAX)) + optFatal("number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.type == OPT_UINT) { + if (opt.arg) + *((unsigned *) opt.arg) = (unsigned) tmp; + } else /* OPT_ULONG */ { + if (opt.arg) + *((unsigned long *) opt.arg) = tmp; + } + } break; + case OPT_FLOAT: { + float tmp; + char *e; + + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + tmp = strtod(arg, &e); + if (*e) + optFatal("invalid floating point number `%s'", arg); + if (errno == ERANGE) + optFatal("floating point number `%s' to `%s' out of range", + arg, optString(opt, lng)); + if (opt.arg) + *((float *) opt.arg) = tmp; + } break; + case OPT_NAMELIST: { + if (arg == NULL) + optFatal("internal error: optExecute() called with NULL argument " + "'%s'", optString(opt, lng)); + + if (opt.arg) + parseNameList(arg, (struct optNameValue **)opt.arg); + + } break; + default: + break; + } +} + + + +/*-----------------------------------------------------------------------+ +| PUBLIC FUNCTIONS | ++-----------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------ + | NAME optSetFatalFunc + | + | FUNCTION Set function used to display error message and exit. + | + | SYNOPSIS #include "shhopt.h" + | void optSetFatalFunc(void (*f)(const char *, ...)); + | + | INPUT f function accepting printf()'like parameters, + | that _must_ abort the program. + */ +void +optSetFatalFunc(void (*f)(const char *, ...)) +{ + optFatal = f; +} + + +/*------------------------------------------------------------------------ + | NAME optParseOptions + | + | FUNCTION Parse commandline options. + | + | SYNOPSIS #include "shhopt.h" + | void optParseOptions(int *argc, char *argv[], + | optStruct opt[], int allowNegNum); + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Array of possible options. + | allowNegNum + | a negative number is not to be taken as + | an option. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum) +{ + int ai, /* argv index. */ + optarg, /* argv index of option argument, or -1 if none. */ + mi, /* Match index in opt. */ + done; + char *arg, /* Pointer to argument to an option. */ + *o, /* pointer to an option character */ + *p; + + optEntry *opt_table; /* malloc'ed array */ + + opt_table = optStructTblToEntryTbl(opt); + if (opt_table == NULL) + optFatal("Memory allocation failed (trying to allocate space for " + "new-format option table)"); + + /* + * Loop through all arguments. + */ + for (ai = 0; ai < *argc; ) { + /* + * "--" indicates that the rest of the argv-array does not + * contain options. + */ + if (strcmp(argv[ai], "--") == 0) { + argvRemove(argc, argv, ai); + break; + } + + if (allowNegNum && argv[ai][0] == '-' && ISDIGIT(argv[ai][1])) { + ++ai; + continue; + } else if (strncmp(argv[ai], "--", 2) == 0) { + /* long option */ + /* find matching option */ + if ((mi = optMatch(opt_table, argv[ai] + 2, 1)) < 0) + optFatal("unrecognized option `%s'", argv[ai]); + + /* possibly locate the argument to this option. */ + arg = NULL; + if ((p = strchr(argv[ai], '=')) != NULL) + arg = p + 1; + + /* does this option take an argument? */ + optarg = -1; + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + if (!arg) { + if ((optarg = ai + 1) == *argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 1)); + arg = argv[optarg]; + } + } else { + if (arg) + optFatal("option `%s' doesn't allow an argument", + optString(opt_table[mi], 1)); + } + optExecute(opt_table[mi], arg, 1); + /* remove option and any argument from the argv-array. */ + if (optarg >= 0) + argvRemove(argc, argv, ai); + argvRemove(argc, argv, ai); + } else if (*argv[ai] == '-') { + /* A dash by itself is not considered an option. */ + if (argv[ai][1] == '\0') { + ++ai; + continue; + } + /* Short option(s) following */ + o = argv[ai] + 1; + done = 0; + optarg = -1; + while (*o && !done) { + /* find matching option */ + if ((mi = optMatch(opt_table, o, 0)) < 0) + optFatal("unrecognized option `-%c'", *o); + + /* does this option take an argument? */ + optarg = -1; + arg = NULL; + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + arg = o + 1; + if (!*arg) { + if ((optarg = ai + 1) == *argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 0)); + arg = argv[optarg]; + } + done = 1; + } + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 0); + ++o; + } + /* remove option and any argument from the argv-array. */ + if (optarg >= 0) + argvRemove(argc, argv, ai); + argvRemove(argc, argv, ai); + } else { + /* a non-option argument */ + ++ai; + } + } + free(opt_table); +} + + +static void +parse_short_option_token(char *argv[], const int argc, const int ai, + const optEntry opt_table[], + int * const tokens_consumed_p) { + + char *o; /* A short option character */ + char *arg; + int mi; /* index into option table */ + unsigned char processed_arg; /* boolean */ + /* We processed an argument to one of the one-character options. + This necessarily means there are no more options in this token + to process. + */ + + *tokens_consumed_p = 1; /* initial assumption */ + + o = argv[ai] + 1; + processed_arg = 0; /* initial value */ + while (*o && !processed_arg) { + /* find matching option */ + if ((mi = optMatch(opt_table, o, 0)) < 0) + optFatal("unrecognized option `-%c'", *o); + + /* does this option take an argument? */ + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + arg = o + 1; + if (!*arg) { + if (ai + 1 >= argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 0)); + arg = argv[ai+1]; + (*tokens_consumed_p)++; + } + processed_arg = 1; + } else + arg = NULL; + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 0); + ++o; + } +} + + + +static void +parse_long_option(char *argv[], const int argc, const int ai, + const int namepos, + const optEntry opt_table[], + int * const tokens_consumed_p) { + + char *equals_arg; + /* The argument of an option, included in the same token, after a + "=". NULL if no "=" in the token. + */ + char *arg; /* The argument of an option; NULL if none */ + int mi; /* index into option table */ + + /* The current token is an option, and its name starts at + Index 'namepos' in the argument. + */ + *tokens_consumed_p = 1; /* initial assumption */ + /* find matching option */ + if ((mi = optMatch(opt_table, &argv[ai][namepos], 1)) < 0) + optFatal("unrecognized option `%s'", argv[ai]); + + /* possibly locate the argument to this option. */ + { + char *p; + if ((p = strchr(argv[ai], '=')) != NULL) + equals_arg = p + 1; + else + equals_arg = NULL; + } + /* does this option take an argument? */ + if (optNeedsArgument(opt_table[mi])) { + /* option needs an argument. find it. */ + if (equals_arg) + arg = equals_arg; + else { + if (ai + 1 == argc) + optFatal("option `%s' requires an argument", + optString(opt_table[mi], 1)); + arg = argv[ai+1]; + (*tokens_consumed_p)++; + } + } else { + if (equals_arg) + optFatal("option `%s' doesn't allow an argument", + optString(opt_table[mi], 1)); + else + arg = NULL; + } + /* perform the action of this option. */ + optExecute(opt_table[mi], arg, 1); +} + + + +/*------------------------------------------------------------------------ + | NAME optParseOptions2 + | + | FUNCTION Parse commandline options. + | + | SYNOPSIS #include "shhopt.h" + | void optParseOptions2(int *argc, char *argv[], + | optStruct2 opt, unsigned long flags); + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Structure describing option syntax. + | flags Result is undefined if not zero. + | For future expansion. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | This differs from optParseOptions in that it accepts + | long options with just one hyphen and doesn't accept + | any short options. It also has accomodations for + | future expansion. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, + const unsigned long flags) +/*---------------------------------------------------------------------------- + This does the same thing as optParseOptions3(), except that there is no + "specified" return value. + + This function exists for backward compatibility. +-----------------------------------------------------------------------------*/ + +{ + optStruct3 opt3; + + opt3.short_allowed = opt.short_allowed; + opt3.allowNegNum = opt.allowNegNum; + opt3.opt_table = optStructTblToEntryTbl(opt.opt_table); + + if (opt3.opt_table == NULL) + optFatal("Memory allocation failed (trying to allocate space for " + "new-format option table)"); + + optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags); + + free(opt3.opt_table); +} + + + + +static void +zero_specified(optEntry opt_table[]) { +/*---------------------------------------------------------------------------- + Set all the "number of times specified" return values identified in the + option table opt_table[] to zero. +-----------------------------------------------------------------------------*/ + unsigned int i; + + for (i = 0; opt_table[i].type != OPT_END; i++) { + if (opt_table[i].specified) + *(opt_table[i].specified) = 0; + } +} + + + +/*------------------------------------------------------------------------ + | NAME optParseOptions3 + | + | FUNCTION Parse commandline options. + | + | INPUT argc Pointer to number of options. + | argv Array of option-/argument-strings. + | opt Structure describing option syntax. + | optStructSize + | Size of "opt" (since the caller may be older + | than this function, it may be using a structure + | with fewer fields than exist today. We use this + | parameter to handle those older callers). + | flags Result is undefined if not zero. + | For future expansion. + | + | OUTPUT argc new argument count. + | argv array with arguments removed. + | + | Areas pointed to by pointers in 'opt' get updated with + | option values and counts. + | + | RETURNS Nothing. Aborts in case of error. + | + | DESCRIPTION This function checks each option in the argv-array + | against strings in the opt-array, and `executes' any + | matching action. Any arguments to the options are + | extracted and stored in the variables or passed to + | functions pointed to by entries in opt. + | + | This differs from optParseOptions in that it accepts + | long options with just one hyphen and doesn't accept + | any short options. It also has accomodations for + | future expansion. + | + | Options and arguments used are removed from the argv- + | array, and argc is decreased accordingly. + | + | Any error leads to program abortion. + */ +void +optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, + const unsigned int optStructSize, const unsigned long flags) +{ + int ai; /* argv index. */ + int tokens_consumed; + unsigned char no_more_options; /* boolean */ + /* We've encountered the "no more options" token */ + + zero_specified(opt.opt_table); + + /* + * Loop through all arguments. + */ + no_more_options = 0; /* initial value */ + for (ai = 0; ai < *argc_p; ) { + if (no_more_options) + /* Can't be an option -- there aren't any more */ + ai++; + else if (argv[ai][0] != '-') + /* Can't be an option -- doesn't start with a dash */ + ai++; + else { + /* It starts with a dash -- could be an option */ + if (argv[ai][1] == '\0') { + /* A dash by itself is not considered an option. */ + ++ai; + tokens_consumed = 0; + } else if (opt.allowNegNum && ISDIGIT(argv[ai][1])) { + /* It's a negative number parameter, not an option */ + ++ai; + tokens_consumed = 0; + } else if (argv[ai][1] == '-') { + /* It starts with -- */ + if (argv[ai][2] == '\0') { + /* The entire thing is "--". That means no more options */ + tokens_consumed = 1; + no_more_options = 1; + } else + /* It's an option that starts with "--" */ + parse_long_option(argv, *argc_p, ai, 2, + opt.opt_table, &tokens_consumed); + } else { + if (opt.short_allowed) { + /* It's a cluster of (one or more) short options */ + parse_short_option_token(argv, *argc_p, ai, + opt.opt_table, &tokens_consumed); + } else { + /* It's a long option that starts with "-" */ + parse_long_option(argv, *argc_p, ai, 1, + opt.opt_table, &tokens_consumed); + } + + } + /* remove option and any argument from the argv-array. */ + { + int i; + for (i = 0; i < tokens_consumed; i++) + argvRemove(argc_p, argv, ai); + } + } + } +} + + + +void +optDestroyNameValueList(struct optNameValue * const list) { + + unsigned int i; + + for (i = 0; list[i].name; ++i) { + strfree(list[i].name); + strfree(list[i].value); + } + + free(list); +} diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h new file mode 100644 index 00000000..fd15b53c --- /dev/null +++ b/lib/util/shhopt.h @@ -0,0 +1,240 @@ +/*============================================================================== +HERE IS AN EXAMPLE OF THE USE OF SHHOPT: + + +#include <shhopt.h> +int +main ( int argc, char **argv ) { + + int help_flag = 7; + unsigned int help_spec =7; + unsigned int height_spec =7; + unsigned int name_spec= 7; + char *name= "initial"; + int height=7; + int verbose_flag=7; + int debug_flag=7; + struct optNameValue * optlist; + + optStruct3 opt; + unsigned int option_def_index = 0; + optEntry *option_def = malloc(100*sizeof(option_def[0])); + + OPTENT3(0, "help", OPT_FLAG, &help_flag, &help_spec, 0); + OPTENT3(0, "height", OPT_INT, &height, &height_spec, 0); + OPTENT3('n', "name", OPT_STRING, &name, &name_spec, 0); + OPTENT3('v', "verbose", OPT_FLAG, &verbose_flag, NULL, 0); + OPTENT3('g', "debug", OPT_FLAG, &debug_flag, NULL, 0); + OPTENT3(0, "options", OPT_NAMELIST, &optlist, NULL, 0); + + opt.opt_table = option_def; + opt.short_allowed = 1; + opt.allowNegNum = 1; + + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + + + printf("argc=%d\n", argc); + printf("help_flag=%d\n", help_flag); + printf("help_spec=%d\n", help_spec); + printf("height=%d\n", height); + printf("height_spec=%d\n", height_spec); + printf("name='%s'\n", name); + printf("name_spec=%d\n", name_spec); + printf("verbose_flag=%d\n", verbose_flag); + printf("debug_flag=%d\n", verbose_flag); + + if (optlist) { + unsigned int i; + while (optlist[i].name) { + printf("option '%s' = '%s'\n", optlist[i].name, optlist[i].value); + ++i; + } + optDestroyNameValueList(optlist); + } else + printf("No -options\n"); +} + +Now run this program with something like + + myprog -vg --name=Bryan --hei 4 "My first argument" --help + + or do it with opt.short_allowed=0 and + + myprog -v /etc/passwd -name=Bryan --hei=4 + + +========================================================================*/ + + +#ifndef SHHOPT_H +#define SHHOPT_H + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* to fake out automatic code indenters */ +#endif + +/* constants for recognized option types. */ +typedef enum { + OPT_END, /* nothing. used as ending element. */ + OPT_FLAG, /* no argument following. sets variable to 1. */ + OPT_STRING, /* string argument. */ + OPT_INT, /* signed integer argument. */ + OPT_UINT, /* unsigned integer argument. */ + OPT_LONG, /* signed long integer argument. */ + OPT_ULONG, /* unsigned long integer argument. */ + OPT_FLOAT, /* floating point argument. */ + OPT_NAMELIST /* list like "key1=val1,key2=val2" */ +} optArgType; + + +typedef struct { + /* This structure describes a single program option in a form for + use by the optParseOptions() or optParseOptions2() function. + */ + char shortName; /* short option name. */ + const char *longName; /* long option name, not including '--'. */ + optArgType type; /* option type. */ + void *arg; /* pointer to variable to fill with argument, + * or pointer to function if type == OPT_FUNC. */ + int flags; /* modifier flags. */ +} optStruct; + +typedef struct { + /* This structure describes a single program option in a form for + use by the optParseOptions3() function. + */ + char shortName; /* short option name. */ + const char *longName; /* long option name, not including '--' or '-' */ + optArgType type; /* option type. */ + void *arg; + /* pointer to variable in which to return option's argument (or TRUE + if it's a flag option), or pointer to function if + type == OPT_FUNC. If the option is specified multiple times, only + the rightmost one affects this return value. + */ + unsigned int *specified; + /* pointer to variable in which to return the number of times that + the option was specified. If NULL, don't return anything. + */ + int flags; /* modifier flags. */ +} optEntry; + + +typedef struct { + /* This structure describes the options of a program in a form for + use with the optParseOptions2() function. + */ + unsigned char short_allowed; /* boolean */ + /* The syntax may include short (i.e. one-character) options. + These options may be stacked within a single token (e.g. + -abc = -a -b -c). If this value is not true, the short option + member of the option table entry is meaningless and long + options may have either one or two dashes. + */ + unsigned char allowNegNum; /* boolean */ + /* Anything that starts with - and then a digit is a numeric + parameter, not an option + */ + optStruct *opt_table; +} optStruct2; + +typedef struct { + /* Same as optStruct2, but for optParseOptions3() */ + unsigned char short_allowed; + unsigned char allowNegNum; + optEntry *opt_table; +} optStruct3; + + +/* You can use OPTENTRY to assign a value to a dynamically or automatically + allocated optStruct structure with minimal typing and easy readability. + + Here is an example: + + unsigned int option_def_index = 0; + optStruct *option_def = malloc(100*sizeof(optStruct)); + OPTENTRY('h', "help", OPT_FLAG, &help_flag, 0); + OPTENTRY(0, "alphaout", OPT_STRING, &alpha_filename, 0); +*/ + +/* If you name your variables option_def and option_def_index like in the + example above, everything's easy. If you want to use OPTENTRY with other + variables, define macros OPTION_DEF and OPTION_DEF_INDEX before calling + OPTENTRY. +*/ + +#ifndef OPTION_DEF +#define OPTION_DEF option_def +#endif +#ifndef OPTION_DEF_INDEX +#define OPTION_DEF_INDEX option_def_index +#endif + +#define OPTENTRY(shortvalue,longvalue,typevalue,outputvalue,flagvalue) {\ + OPTION_DEF[OPTION_DEF_INDEX].shortName = (shortvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].longName = (longvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].type = (typevalue); \ + OPTION_DEF[OPTION_DEF_INDEX].arg = (outputvalue); \ + OPTION_DEF[OPTION_DEF_INDEX].flags = (flagvalue); \ + OPTION_DEF[++OPTION_DEF_INDEX].type = OPT_END; \ + } + +/* OPTENT3 is the same as OPTENTRY except that it also sets the "specified" + element of the table entry (so it assumes OPTION_DEF is a table of + optEntry instead of optStruct). + + Here is an example: + + unsigned int option_def_index = 0; + optEntry *option_def = malloc(100*sizeof(optEntry)); + OPTENT3('h', "help", OPT_FLAG, &help_flag, 0); + OPTENT3(0, "alphaout", OPT_STRING, &alpha_filename, 0); +*/ + +#define OPTENT3(shortvalue,longvalue,typevalue,outputvalue,specifiedvalue, \ + flagvalue) {\ + OPTION_DEF[OPTION_DEF_INDEX].specified = (specifiedvalue); \ + OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \ + } + + +struct optNameValue { + const char * name; + const char * value; +}; + + + +void optSetFatalFunc(void (*f)(const char *, ...)); +void optParseOptions(int *argc, char *argv[], + optStruct opt[], int allowNegNum); +void +optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, + const unsigned long flags); +void +optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, + const unsigned int optStructSize, const unsigned long flags); + +void +optDestroyNameValueList(struct optNameValue * const list); + +#ifdef __cplusplus +} +#endif + +/* Here's a hack to help us introduce pm_c_util.h. That should be + #included in nearly every Netpbm program, but we're too lazy to insert + it into hundreds of files right now. But shhopt.h is included into + most of those files, so we #include it here! + + Before 2005.12.03, the stuff that is now in pm_c_util.h was in pm.h, + but that's a bad place for it because pm.h is an external header file. +*/ +#include "pm_c_util.h" + +#endif diff --git a/lib/util/testnstring.c b/lib/util/testnstring.c new file mode 100644 index 00000000..87e6139e --- /dev/null +++ b/lib/util/testnstring.c @@ -0,0 +1,30 @@ +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include "nstring.h" + +#define true (1) +#define false (0) + +int +main(int argc, char **argv) { + + char snprintfNResult[80]; + const char * asprintfNResult; + + printf("Hello world.\n"); + + snprintfN(snprintfNResult, 19, "snprintfN test truncated"); + + printf("snprintfN result='%s'\n", snprintfNResult); + + asprintfN(&asprintfNResult, "asprintf'ed string %d %u %s", + 5, 89, "substring"); + + printf("asprintfN result='%s'\n", asprintfNResult); + + strfree(asprintfNResult); + return 0; +} diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h new file mode 100644 index 00000000..28963aee --- /dev/null +++ b/lib/util/wordaccess.h @@ -0,0 +1,65 @@ +#ifndef WORDACCESS_H_INCLUDED +#define WORDACCESS_H_INCLUDED + +/* These are facilities for accessing data in C programs in ways that + exploit the way the machine defines words in order to squeeze out + speed and CPU efficiency. + + In particular, routines in this file exploit the endianness of the + machine and use explicit machine instructions to access C + variables. + + A word is the amount of data that fits in a register; the amount of + data that a single machine instruction can process. For example, + on IA32, a word is 32 bits because a single load or store + instruction moves that many bits and a single add instruction + operates on that many bits. + + + These facilities revolve around two data types: wordInt and + wordIntBytes. + + wordint is an unsigned integer with precision (size) of one word. + It is just the number -- nothing is implied about how it is + represented in memory. + + wordintBytes is an array of bytes that represent a word-sized + unsigned integer. x[0] is the high order 8 digits of the binary + coding of the integer, x[1] the next highest 8 digits, etc. + Note that it has big-endian form, regardless of what endianness the + underlying machine uses. + + The actual size of word differs by machine. Usually it is 32 or 64 + bits. Logically it can be as small as one byte. Fixed bit sequences + in each program impose a lower limit of word width. For example, the + longest bit sequence in pbmtog3 has 13 bits, so an 8-bit word won't + work with that. + + We also assume that a char is 8 bits. +*/ +#if (!defined(WORDACCESS_GENERIC) \ + && defined(__GNUC__) && defined(__GLIBC__) \ + && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) ) + + #if BYTE_ORDER==BIG_ENDIAN /* defined by GCC */ + + #include "wordaccess_gcc3_be.h" + + #elif defined(__ia64__) || defined(__amd64__) || defined(__x86_64__) + /* all these macros are defined by GCC */ + + #include "wordaccess_64_le.h" + + #else + + #include "wordaccess_gcc3_le.h" + + #endif + +#else + + #include "wordaccess_generic.h" + +#endif + +#endif diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h new file mode 100644 index 00000000..2343b6d4 --- /dev/null +++ b/lib/util/wordaccess_64_le.h @@ -0,0 +1,52 @@ +/*============================================================================= + This file is the part of wordaccess.h for use under these + conditions: + + * GCC (>=3.4), GLIBC + * 64 bit Little-Endian machines (IA64, X86-64, AMD64) +=============================================================================*/ + +/* + 64 bit hton and ntoh do not exist. Here we use bswap_64. + + While bswap_64 works on 64 bit data, __builtin_clzl works on "long" which + may or may not be 64 bits. Code provided to find the right data type and + file off any extra when necessary. +*/ + +#include <byteswap.h> /* See note above on bswap_64 */ + +typedef uint64_t wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes bytes) { + return ((wordint) bswap_64(*(wordint *)bytes)); +} + + + +static __inline__ void +wordintToBytes(wordintBytes * const bytesP, + wordint const wordInt) { + *(wordint *)bytesP = bswap_64(wordInt); +} + + + +static __inline__ unsigned int +wordintClz(wordint const x){ + + unsigned int s; + + if (x == 0) + return sizeof(wordint) * 8; + + /* Find the data type closest to 64 bits, and file off any extra. */ + else if ((s=sizeof(long int)) >= 8) + return (__builtin_clzl((int)x << (s - 8) * 8)); + else if ((s=sizeof(long long int)) >= 8) + return (__builtin_clzll((long long int)x << (s - 8) * 8)); + else + pm_error("Long long int is less than 64 bits on this machine"); +} diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_gcc3_be.h new file mode 100644 index 00000000..6f5d86fc --- /dev/null +++ b/lib/util/wordaccess_gcc3_be.h @@ -0,0 +1,40 @@ +/*============================================================================= + This file is the part of wordaccess.h for use under these + conditions: + + * GCC (>=3.4), GLIBC + * Big-Endian machines + + __builtin_clz is available on GCC 3.4 and above + + Note that the clz scheme does not work and requires adjustment + if long type does not make use of all bits for data storage. + + This is unlikely. According to GNU MP (http://www.swox.com/gmp/), + in rare cases such as Cray, there are smaller data types that take up + the same space as long, but leave the higher bits silent. Currently, + there are no known such cases for data type long. +*===========================================================================*/ + +typedef unsigned long int wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes bytes) { + return *((wordint *)bytes); +} + + + +static __inline__ void +wordintToBytes(wordintBytes * const bytesP, + wordint const wordInt) { + *(wordint *)bytesP = wordInt; +} + + + +static __inline__ unsigned int +wordintClz(wordint const x) { + return (x==0 ? sizeof(wordint)*8 : __builtin_clzl(x)); +} diff --git a/lib/util/wordaccess_gcc3_le.h b/lib/util/wordaccess_gcc3_le.h new file mode 100644 index 00000000..7db218db --- /dev/null +++ b/lib/util/wordaccess_gcc3_le.h @@ -0,0 +1,54 @@ +/*============================================================================= + + This file is the part of wordaccess.h for use under these + conditions: + + * GCC (>=3.4), GLIBC + * 32 bit Little-Endian machines (intel MPUs 80386, Pentium, etc.) + * Other non-Big-Endian machines (very rare) + +=============================================================================*/ + +typedef uint32_t wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +#include <sys/types.h> +#include <netinet/in.h> + +/* + Here we use the more widely used functions htonl and ntohl instead of + bswap_32. This makes possible the handling of weird byte ordering + (neither Big-Endian nor Little-Endian) schemes, if any. +*/ + +static __inline__ wordint +bytesToWordint(wordintBytes const bytes) { + return (wordint) ntohl(*(wordint *)bytes); +} + + + +static __inline__ void +wordintToBytes(wordintBytes * const bytesP, + wordint const wordInt) { + + *(wordint *)bytesP = htonl(wordInt); +} + + + +static __inline__ unsigned int +wordintClz(wordint const x) { + + /* Find the data type closest to 32 bits, and file off any extra. */ + + if (x == 0) + return sizeof(wordint) * 8; + else if (sizeof(int) >= 4) + return __builtin_clz((int)x << (sizeof(int) - 4) * 8); + else if (sizeof(long int) >= 4) + return __builtin_clzl((long int)x << (sizeof(long int) - 4) * 8); + else + pm_error("Long int is less than 32 bits on this machine"); +} + diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h new file mode 100644 index 00000000..7f27ef74 --- /dev/null +++ b/lib/util/wordaccess_generic.h @@ -0,0 +1,89 @@ +/*============================================================================= + + This file is the part of wordaccess.h for use under these + conditions: + + * Compilers other than GCC + * GCC before version 3.4 + * c libraries other than Glibc + * Specified by the user with WORDACCESS_GENERIC +=============================================================================*/ + +typedef uint32_t wordint; +typedef unsigned char wordintBytes[sizeof(wordint)]; + +static __inline__ wordint +bytesToWordint(wordintBytes const bytes) { + wordint retval; + unsigned int i; + + /* Note that 'bytes' is a pointer, due to C array degeneration. + That means sizeof(bytes) isn't what you think it is. + */ + + for (i = 1, retval = bytes[0]; i < sizeof(wordint); ++i) { + retval = (retval << 8) + bytes[i]; + } + return retval; +} + + + +static __inline__ void +wordintToBytes(wordintBytes * const bytesP, + wordint const wordInt) { + + wordint buffer; + int i; + + for (i = sizeof(*bytesP)-1, buffer = wordInt; i >= 0; --i) { + (*bytesP)[i] = buffer & 0xFF; + buffer >>= 8; + } +} + +static unsigned char const clz8[256]= { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +static __inline__ unsigned int +clz16(wordint const x) { + if (x >> 8 != 0) + return clz8[x >> 8]; + else + return clz8[x] + 8; +} + + + +static __inline__ unsigned int +clz32(wordint const x) { + if (x >> 16 != 0) + return clz16(x >> 16); + else + return clz16(x) +16; +} + + + +static __inline__ unsigned int +wordintClz(wordint const x) { + return clz32(x); +} |