: "@(#): shar.sh,v 2.1 1998/06/02 17:13:43 jleffler Exp $" #! /bin/sh # # This is a shell archive. # Remove everything above this line and run sh on the resulting file. # If this archive is complete, you will see this message at the end: # "All files extracted" # # Created on: Thu Oct 15 09:57:34 PDT 1998 # Created by: jleffler at Informix Software Inc. # # Files archived in this archive: # dbldfmt.1 # dbldfmt.c # dbldfmt.mk # dbldfmt.tst # debug.h # emalloc.c # emalloc.h # filter.c # filter.h # getopt.h # range.c # range.h # stderr.c # stderr.h # #-------------------- if [ -f dbldfmt.1 -a "$1" != "-c" ] then echo shar: dbldfmt.1 already exists else echo 'x - dbldfmt.1 (8624 characters)' sed -e 's/^X//' >dbldfmt.1 <<'SHAR-EOF' X.\" @(#)$Id: dbldfmt.1,v 3.3 1997/09/03 20:43:28 johnl Exp $ X'\" @(#)Manual page: Application -- Part of application X.ds fC "Version: $Revision: 3.3 $ ($Date: 1997/09/03 20:43:28 $) X.TH DBLDFMT 1S "JLSS UNIX Tools" X.SH NAME Xdbldfmt \(em convert fixed format LOAD file to delimited format X.SH SYNOPSIS X\fBdbldfmt\fP [-P p][-D d] -l len \e X.br X [ -c literal | -d lo-hi.dp | -r lo-hi[,...] | \e X.br X -f [c=const,r=lo-hi,d=lo-hi.dp,l=lo-hi,u=lo-hi,...] \e X.br X -t [r=lo-hi,i='in format',o='out format',...]] \e X.br X ... [file ...] X.SH DESCRIPTION X\fBdbldfmt\fP converts a file with fixed length records separated by Xnewlines into the delimited format used by the Informix LOAD statement Xand the \fBdbload\fP and dbimport commands. X.P XThe `\*c-l\*d' option is used to specify the line length (excluding the newline Xcharacter) and is mandatory. XIf any of the input lines turns out to have a different length from this, Xthe program stops immediately. X.P XThe `\*c-f\*d', `\*c-r\*d', `\*c-c\*d', `\*c-d\*d' and `\*c-t\*d' can occur Xmultiple times on the command line, and each occurrence generates a field Xin the output. X.P XThe `\*c-c\*d' option specifies the text for a constant field, which can be Xuseful to specify missing information. X.P XA range of columns is a value such as 13, or a column range such as 13-16, Xor a combination of values and column ranges separated by commas, or a Xcombination of values separated by literals. XRanges such as -16 are interpreted as 1-16, and ranges such as 17- are Xinterpreted as 16-len. X.P XThe `\*c-d\*d' option allows you to specify a range of columns which form Xthe number, and it implicitly inserts a decimal point at the position Xspecified by `\*c.dp\*d'. XThere is no default value for `\*c.dp\*d'; if you do not specify X`\*c.dp\*d' then no decimal point will be placed in the output. X.P XThe `\*c-r\*d' option is used to specify one or more ranges of columns to Xbe treated as a single field. XIt allows more or less arbitrary literal strings to be used as separators Xfor the ranges, but you cannot embed literal digits or dashes between Xranges, and you must have a valid range as the first part of the option Xargument. XIf you want something more complex, you have to use the more general X`\*c-f\*d' option. XThus `\*c-r\ 10-11/8-9/12-15\*d' specifies that columns 10 and 11 are to be Xoutput, followed by a slash, columns 8 and 9, another slash, and finally Xcolumn 12 to 15. XThis effectively converts a date written in American format with no Xpunctuation into British format with slashes separating the day, month and Xyear. X.P XThe `\*c-f\*d' option is the most general and allows you to concatenate Xranges, decimals and constants into a single, composite output field. XNote that with the `\*c-f\*d' option, the suboptions are interpreted by Xgetsubopt(3). XBecause \fBdbldfmt\fP uses sub-options, if you need a compound range Xequivalent to `\*c-r\ 10-15,18-20\*d', you have to use two range Xsub-options: `\*c-f\ r=10-15,r=18-20\*d'. XSince a comma always marks the end of a suboption, you cannot embed them Xdirectly in the option string for a `\*c-f\*d' field; use octal escape Xsequences instead (the ASCII octal value for a comma is \e054). XAn octal escape consists of a backslash followed by an arbitrary length Xoctal number (though values too large for an unsigned character generate an Xerror). XOctal escapes can be used for any character, though they are not strictly Xnecessary except for comma. XYou must escape a singleton backslash too; any character which is not an Xoctal digit that occurs immediately after a backslash is treated verbatim, X(but note that the interpretation of commas occurs before examining the Xstring for backslashes, so you still cannot escape a comma). XEscape sequences are also interpreted by the `\*c-c\*d' option. X.P XThe suboptions `\*cr=\*d', `\*cl=\*d' and `\*cu=\*d' are similar. XThe range on the right-hand side specifes the input range. XThe difference is that `\*cr=\*d' copies the data unchanged, but X`\*cl=\*d' converts the data to lower case and `\*du=\*d' converts the Xdata to upper case. XIf you need to put an initial capital on a field in columns 1-10, Xyou can do so using the notation `\*c-f\ u=1,r=2-10\*d'. XThe `\*cd=\*d' suboption is treated like the value for a `\*c-d\*d' option. XThe `\*cc=\*d' suboption is treated like the value for a `\*c-c\*d' option. X.P XThe `\*c-t\*d' option allows you to apply date conversions to the data. XIt takes 3 suboptions, of which only the `\*cr=\*d' option is mandatory. XThe `\*cr=\*d' suboption specifies the range of columns in which the input Xdata appears, just as in the `\*c-f\*d' option. XThe `\*ci=\*d' suboption specifies the format of the input in terms which Xwill be understood by the X/Open function, strptime(3). XThe `\*co=\*d' suboption specifies the format of the output in terms which Xwill be understood by the ANSI C function, strftime(3). XIf the input format is not specified, the input is assumed to be in the Xformat dictated by ISO 8601:1988, which is familiar to you as the format Xfor a DATETIME YEAR TO SECOND value: `\*c%Y-%m-%d\ %H:%M:%S\*d'. XSimilarly, if the output format is not specified, the output is printed in Xthe same format. XTherefore, in practice, you will always specify at least one of the two Xformats as otherwise you are using an expensive way of copying the input Xdata to the output, but \fBdbldfmt\fP does not stop you from doing that. XIf you try to print data which is not specified in the input format, the Xresult is indeterminate. X.P XThe `\*c-P\*d' option allows you to specify a single character which will Xbe used as the decimal point in all `\*c-d\*d' fields. XThe `\*c-D\*d' option allows you to override the default delimiter which is Xspecified by $DBDELIMITER and defaults to `\*c|\*d'. X.P XIf no input file is specified, the standard input is processed. XIf a file name argument `\*c-\*d' is specified, that too is treated as Xstandard input. X.SH EXAMPLES X.eS Xdbldfmt -P',' -D'*' -l 100 -d 1-10.3 -r11-24 \e X -r 22-24,26-37 -c widget -r 80-83/84-85/86-87 \e X -f r=40-50,c='\e'\e''",-',r=60-70 /some/file/or/other X.eE XThe output data will have fields delimited by '*'; ',' will be used as the Xdecimal point (presumably DBMONEY="DM," or something similar); the lines Xare 100 chars long excluding the newline; the first ten characters are a Xdecimal number with implicit decimal point between characters 7 and 8; Xcharacters 11-24 form the next field; characters 22-24 followed by Xcharacters 26-37 (22-37 omitting 25) form the next field; the next field is Xa constant string 'widget'; the next field is columns 80-87 with slashes in Xthe output between the designated columns; and the last field is columns X40-50 followed by back-slash, single-quote, double-quote, comma, dash and Xcolumns 60-70 (tested with Korn Shell -- that sequence of quotes is Xcontorted enough that it might make a difference). X.P XNote that the data in a decimal field is not validated for all digits. XThere is no built-in limit on the size of the lines or on the number of Xfields, nor on the number of components in a field. XFixed format files exclude blobs by definition! XFixed format files also exclude embedded newlines in (long) character Xfields. X.SH "BUILDING DBLDFMT" XIf you have an ANSI C compiler, you should be able to run just: X.eS Xmake -f dbldfmt.mk X.eE XIf you don't have an ANSI C compiler, it is time to get one. XIn case of doubt, get the GNU C Compiler, GCC, version 2.7 or later. XOther configuration options will need to be dug out of the headers as Xnecessary, but you are unlikely to run into any problems with a reasonably Xmodern C compiler. XThe code uses ANSI C functions, plus getopt(3C) which is available in Xthe public domain, and strptime(3) which is defined in the X/Open standard XSingle Unix Specification, Version 2 (aka UNIX 98), and earlier editions. X.SH "SEE ALSO" X/usr/pub/ascii for the octal values for ASCII characters. X.SH DIAGNOSTICS XShould be self-explanatory. X.SH VERSION XThis manual page is for DBLDFMT version 3.x. X.SH BUGS XNote that the meaning of the `\*c-f' option has changed significantly between Xversion 1.x (the previously released version) and version 3.x. XThe new `\*c-r' option is similar to the old `\*c-f' option, but it does much Xless complex processing on the non-range characters in between the ranges, Xand it is formally defined that you must have a range as the first Xcomponent of the argument. XThe new `\*c-f' option with sub-options is much more controllable. XNote that the old syntax will generate errors with the new version. X.SH AUTHOR XJonathan Leffler X.br XJLSS X.br XOriginal: 10th July 1995 X.br XUpdated: 3rd September 1997 SHAR-EOF chmod 444 dbldfmt.1 if [ `wc -c dbldfmt.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: dbldfmt.c,v $ X@(#)Version: $Revision: 3.13 $ X@(#)Last changed: $Date: 1998/07/10 18:25:02 $ X@(#)Purpose: Convert fixed-length records to delimited variable-length X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1991,1993,1995-97 X*/ X X/*TABSTOP=4*/ X X#define MAIN_PROGRAM X X/* X** This code uses strptime() defined in X/Open Portability Guide Version 4. X** The function should be declared in time.h . Note that defining X** _XOPEN_VERSION as 4 means that va_list is defined in on X** Solaris 2.6, but ANSI and POSIX do not require this. Hence, it is not X** possible to get a clean compile with both strptime() defined and no X** problems with va_list, unless we go for the back-door approaches of X** either defining NO_SYSTEM_DECLARATION_OF_STRPTIME or __EXTENSIONS__. X*/ X X#ifndef _XOPEN_SOURCE X#define _XOPEN_SOURCE 1 X#endif X#ifndef _XOPEN_VERSION X#define _XOPEN_VERSION 4 X#endif /* _XOPEN_VERSION */ X X#include X#include X#include X#include X#include X#include X#include X X#include "emalloc.h" X#include "filter.h" X#include "getopt.h" X#include "range.h" X#include "stderr.h" X X#ifdef NO_SYSTEM_DECLARATION_OF_STRPTIME Xextern char *strptime(const char *buffer, const char *format, struct tm *output); X#endif /* NO_SYSTEM_DECLARATION_OF_STRPTIME */ X X X#define NIL(x) ((x)0) X X/* X** Beware -- the return value from getsubopt using gen_subopts[] relies on X** F_CONST = 0, F_DECPT = 1, F_LRANGE = 2, F_RANGE = 3, F_URANGE = 4. X** Also note that the initializer list for an enum type cannot have a X** trailing comma in ANSI C (ISO/IEC 9899-1990 section 6.5.2.2). X*/ Xenum FieldType X{ X F_UNDEFINED = -1, X F_CONST = 0, X F_DECPT = 1, X F_LRANGE = 2, X F_RANGE = 3, X F_URANGE = 4, X F_TIME = 5 X}; Xtypedef enum FieldType FieldType; X X/* X** If this structure grows any bigger, it should be converted to X** a discriminated union, with f_type as the discriminator, and a X** collection of small sub-structures for the different types. X*/ Xstruct SubField X{ X char *f_const; /* F_CONST */ X long f_lo; /* F_*RANGE, F_DECPT, F_TIME */ X long f_hi; /* F_*RANGE, F_DECPT, F_TIME */ X long f_dp; /* F_DECPT */ X char *f_input; /* F_TIME */ X char *f_output; /* F_TIME */ X int f_type; /* Discriminator */ X}; Xtypedef struct SubField SubField; X Xstruct Field X{ X size_t f_nparts; X SubField *f_part; X}; Xtypedef struct Field Field; X Xtypedef unsigned char Uchar; X X/* -- Declarations */ X Xstatic size_t ibuflen; Xstatic char *ibuffer; Xstatic char *obuffer; Xstatic char *delim; Xstatic char *point; Xstatic Field *flist; Xstatic size_t nfl; Xstatic long linelength; X X/* ANSI string concatenation -- wonderful! */ Xstatic const char usestr[] = X{ X "[-V] -l length [-D delim] [-P decpt] \\\n" X "\t[-d lo-hi[.dp] | -r lo-hi[,...] | -c const] | \\\n" X "\t -f [c=const,r=lo-hi,d=lo-hi.dp,l=lo-hi,u=lo-hi,...]] \\\n" X "\t -t [r=lo-hi,i='in format',o='out format',...]] \\\n" X "\t... [file ...]" X}; X X/* X** Although I'd like to use type const char *const gen_subopt[], X** the prototype for getsubopt(3) prevents this. X*/ Xstatic char *const gen_subopt[] = X{ X "c", X "d", X "l", X "r", X "u", X 0 X}; X Xstatic char *const time_subopt[] = X{ X "i", X "o", X "r", X 0 X}; X X/* strftime() and strptime() manage to agree on this format */ X/* Note that handling fractional seconds is not supported */ Xstatic const char * const def_time_fmt = "%Y-%m-%d %H:%M:%S"; X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: dbldfmt.c,v 3.13 1998/07/10 18:25:02 jleffler Exp $"; X#endif X X/* Add an extra subfield */ Xstatic SubField *addsubfield(Field *fld) X{ X size_t nbytes; X SubField *new_sf; X X assert(fld != NIL(Field *)); X if (fld->f_nparts != 0) X { X assert(fld->f_part != NIL(SubField *)); X fld->f_nparts++; X nbytes = fld->f_nparts * sizeof(SubField); X fld->f_part = (SubField *)REALLOC(fld->f_part, nbytes); X } X else X { X fld->f_nparts = 1; X fld->f_part = (SubField *)MALLOC(sizeof(SubField)); X } X new_sf = &fld->f_part[fld->f_nparts - 1]; X /* Initialize new subfield to all invalid values */ X new_sf->f_const = (char *)0; X new_sf->f_dp = -1; X new_sf->f_hi = -1; X new_sf->f_input = (char *)0; X new_sf->f_lo = -1; X new_sf->f_output = (char *)0; X new_sf->f_type = F_UNDEFINED; X return(new_sf); X} X X/* isoctal is a name reserved by ; undefine any macro version */ X#undef isoctal Xstatic int isoctal(Uchar c) X{ X if (!isdigit(c) || c == '8' || c == '9') X return 0; X return 1; X} X X/* Copy string from start to finish, processing escapes */ Xstatic char *setstr(const char *start, const char *finish) X{ X char *result; X int len; X const char *src; X char *dst; X const char *end; X X assert(finish >= start); X len = finish - start; X result = (char *)MALLOC(len + 1); X dst = result; X src = start; X end = start + len; X while (src < end) X { X if (*src != '\\') X *dst++ = *src++; X else if (*++src == '\0') X { X FREE(result); X error2("backslash must be followed by a character:", start); X } X else if (!isoctal(*src)) X *dst++ = *src++; X else X { X int c; X X c = 0; X while (isoctal(*src)) X c = (8 * c) + *src++ - '0'; X if (c > UCHAR_MAX) X { X FREE(result); X err_report(ERR_ERR, ERR_STAT, "octal value too big \\%o\n", c); X } X *dst++ = c; X } X } X *dst = '\0'; X return(result); X} X X/* Parse a datetime/interval (-t) field */ Xstatic void datetime_field(Field *fld, char *arg) X{ X char *np; X char *str; X SubField *f; X long subopt; X X f = addsubfield(fld); X for (str = arg; *str != '\0'; arg = str) X { X subopt = getsubopt(&str, time_subopt, &np); X if (np == (char *)0 || *np == '\0') X error2("no argument for sub-option:", time_subopt[subopt]); X X switch (subopt) X { X case 0: /* 'i' for input format */ X if (f->f_input != (const char *)0) X error2("cannot specify 2 input formats in field:", arg); X f->f_input = setstr(np, np + strlen(np)); X break; X X case 1: /* 'o' for output format */ X if (f->f_output != (const char *)0) X error2("cannot specify 2 output formats in field:", arg); X f->f_output = setstr(np, np + strlen(np)); X break; X X case 2: /* 'r' for range of columns */ X { X long lo; X long hi; X char *endp; X endp = parse_range(np, &lo, &hi); X if (endp == (char *)0) X error2("non-numeric range subfield in field:", arg); X else if (*endp == '.') X error2("cannot specify decimal point in datetime field:", arg); X if (*endp != '\0') X error2("invalid character in range suboption:", arg); X f->f_lo = lo; X f->f_hi = hi; X if (f->f_lo <= 0) X f->f_lo = 1; X } X break; X X default: X error2("invalid suboption in argument:", arg); X break; X } X } X X if (f->f_lo == -1 || f->f_hi == -1) X error2("must specify range suboption in datetime field:", arg); X if (f->f_input == (const char *)0) X f->f_input = setstr(def_time_fmt, def_time_fmt + strlen(def_time_fmt)); X if (f->f_output == (const char *)0) X f->f_output = setstr(def_time_fmt, def_time_fmt + strlen(def_time_fmt)); X if (strchr(f->f_input, '%') == NIL(char *)) X error2("no conversion specifier '%' in input format:", f->f_input); X if (strchr(f->f_output, '%') == NIL(char *)) X error2("no conversion specifier '%' in output format:", f->f_output); X f->f_type = F_TIME; X} X X/* Parse a general (-f) field */ X/* X** Semantics of a general field X** 1. A general field consists of 1 or more subfields. X** 2. Each sub-field can be a constant string, a range, or a decimal X** field. X** 3. A constant sub-field consists of the suboption 'c=' followed by X** a string up to a comma or the end of the argument. The string X** may contain octal escape sequences (including \054 for comma). X** The output data consists of the characters in the string after X** the escapes have been translated. X** 4. A range sub-field consists of one of the suboptions 'r=', 'l=' or X** 'u=' and one of the following, where n1 and n2 are sequences of X** digits: X** n1-n2 (n1 <= n2) X** n1 (n1 == n2) X** -n2 (n1 == 1) X** n1- (n2 == linelength) X** The output data consists of columns n1..n2 in the input lines. X** If the suboption is 'l=', the output is converted to lower case. X** If the suboption is 'u=', the output is converted to upper case. X** 5. A decimal sub-field consists of the suboption 'd=' followed by a X** range sub-field followed by a '.' and another number, n3. The X** output data consists of the the same data as would be produced by X** the range, except that a decimal point '.' will be placed n3 X** characters before the end of the string. This is logically X** equivalent to an implied decimal point with n3 decimal places, but X** there is no validation that the data is actually all digits. X*/ Xstatic void general_field(Field *fld, char *arg) X{ X char *np; X char *str; X SubField *f; X long subopt; X X for (str = arg; *str != '\0'; arg = str) X { X subopt = getsubopt(&str, gen_subopt, &np); X if (np == (char *)0 || *np == '\0') X error2("no argument for sub-option:", gen_subopt[subopt]); X X switch (subopt) X { X case F_CONST: X f = addsubfield(fld); X f->f_const = setstr(np, np + strlen(np)); X f->f_type = F_CONST; X break; X X case F_DECPT: X { X long lo; X long hi; X long dp = -1; X char *endp; X endp = parse_range(np, &lo, &hi); X if (endp == np) X error2("non-numeric range in decimal subfield:", arg); X if (endp == (char *)0) X error2("invalid decimal subfield in field:", arg); X else if (*endp == '.') X { X char *mark; X dp = strtol(endp + 1, &mark, 10); X if (mark == endp + 1) X error2("non-numeric data after decimal point:", endp + 1); X if (*mark != '\0') X error2("extra data after decimal suboption:", arg); X if (dp > (hi - lo + 1)) X error2("too many decimal places in range:", arg); X endp = mark; X } X else if (*endp != '\0') X error2("invalid character in decimal suboption:", arg); X f = addsubfield(fld); X f->f_type = F_RANGE; X f->f_dp = dp; X f->f_lo = lo; X f->f_hi = hi; X if (f->f_lo <= 0) X f->f_lo = 1; X } X break; X X case F_LRANGE: X case F_RANGE: X case F_URANGE: X { X long lo; X long hi; X char *endp; X endp = parse_range(np, &lo, &hi); X if (endp == (char *)0) X error2("invalid range subfield in field:", arg); X else if (*endp == '.') X error2("use -d for implicit decimal fields:", arg); X if (*endp != '\0') X error2("invalid character in range suboption:", arg); X f = addsubfield(fld); X f->f_type = subopt; X f->f_dp = -1; X f->f_lo = lo; X f->f_hi = hi; X if (f->f_lo <= 0) X f->f_lo = 1; X } X break; X X default: X error2("invalid suboption in argument:", arg); X break; X } X } X} X X/* Parse a range (-r) field */ X/* Semantics of range field: X** 1. First element is a range of columns. X** 2. This may be separated from the next range by a comma (meaning no X** special separator) or some other character string not containing X** any digits or hyphens '-'. X** 3. There may be an arbitrary number of ranges followed by separators. X** X** NB: This is not as general as the previous version -- for the fully X** general field, use the -f option. X*/ Xstatic void range_field(Field *fld, char *arg) X{ X char *np; X char *dp; X char *fs; /* Field separator */ X SubField *f; X long lo; X long hi; X X dp = arg; X while (*dp != '\0' && (np = parse_range(dp, &lo, &hi)) != NIL(char *)) X { X f = addsubfield(fld); X f->f_lo = lo; X f->f_hi = hi; X f->f_type = F_RANGE; X if (f->f_lo <= 0) X f->f_lo = 1; X dp = np; X if (*dp == ',') X dp++; X else if (*dp != '\0' && !isdigit((Uchar)(*dp))) X { X assert(!isdigit((Uchar)(*dp))); X f = addsubfield(fld); X fs = dp; X /* Non-quoted within-field separator */ X while (*fs != '\0' && !isdigit((Uchar)(*fs)) && *fs != '-') X fs++; X f->f_const = setstr(dp, fs); X f->f_type = F_CONST; X dp = fs; X } X } X if (dp == arg) X error2("non-numeric range specifier:", arg); X} X X/* Parse a decimal (-d) field */ Xstatic void decimal_field(Field *fld, char *arg) X{ X SubField *f; X char *dp; X X f = addsubfield(fld); X f->f_type = F_RANGE; X f->f_dp = -1; X if ((dp = parse_range(arg, &f->f_lo, &f->f_hi)) == NIL(char *)) X error2("non-numeric decimal specifier:", arg); X if (f->f_lo <= 0) X f->f_lo = 1; X assert(f->f_hi >= f->f_lo); X if (*dp == '.') X { X char *mark; X f->f_dp = strtol(dp + 1, &mark, 10); X if (mark == dp + 1) X error2("non-numeric data after decimal point:", dp + 1); X if (f->f_dp > (f->f_hi - f->f_lo + 1)) X error2("too many decimal places in range:", arg); X dp = mark; X } X if (*dp != '\0') X error2("extra data after decimal field specifier:", arg); X} X X/* 'Parse' a constant (-c) field */ Xstatic void const_field(Field *fld, char *arg) X{ X SubField *f; X X f = addsubfield(fld); X f->f_const = setstr(arg, arg + strlen(arg)); X f->f_type = F_CONST; X} X X/* Compute the length of the output format for a datetime field */ Xstatic size_t dtlen(const char *format) X{ X size_t max = 4 * strlen(format); X char *buffer = (char *)MALLOC(max); X time_t now = time((time_t *)0); X size_t len = strftime(buffer, max, format, localtime(&now)) + 1; X FREE(buffer); X return(len); X} X X/* Compute length of output line */ X/* dlen = length of delimiter, plen = length of decimal point */ Xstatic size_t outlength(size_t dlen, size_t plen) X{ X size_t tlen; X size_t i; X size_t j; X SubField *f; X X tlen = 0; X for (i = 0; i < nfl; i++) X { X for (j = 0; j < flist[i].f_nparts; j++) X { X f = &flist[i].f_part[j]; X if (f->f_hi <= 0 && (f->f_type == F_RANGE || X f->f_type == F_LRANGE || f->f_type == F_URANGE)) X f->f_hi = linelength; X switch (f->f_type) X { X case F_CONST: X tlen += strlen(f->f_const) + dlen; X break; X case F_LRANGE: X case F_RANGE: X case F_URANGE: X if (f->f_hi > linelength) X error("range longer than line length"); X tlen += f->f_hi - f->f_lo + 1 + dlen; X if (f->f_dp > 0) X tlen += plen; X break; X case F_TIME: X tlen += dtlen(f->f_output) + dlen; X break; X default: X error("bug -- field type out of control"); X break; X } X } X } X tlen += 2; /* For '\n' and '\0' */ X return(tlen); X} X X/* Free resources for one field */ Xstatic void free_field(Field *fld) X{ X size_t i; X SubField *sf; X X assert(fld != NIL(Field *)); X assert(fld->f_nparts == 0 || fld->f_part != NIL(SubField *)); X for (i = 0; i < fld->f_nparts; i++) X { X sf = &fld->f_part[i]; X assert((sf->f_type != F_CONST) || (sf->f_const != NIL(char *))); X assert((sf->f_type != F_TIME) || (sf->f_input != NIL(char *))); X assert((sf->f_type != F_TIME) || (sf->f_output != NIL(char *))); X FREE(sf->f_const); X FREE(sf->f_input); X FREE(sf->f_output); X } X FREE(fld->f_part); X} X X/* Release allocated memory resources */ Xstatic void free_memory(void) X{ X size_t i; X X FREE(obuffer); X FREE(ibuffer); X for (i = 0; i < nfl; i++) X free_field(&flist[i]); X FREE(flist); X} X X/* Return 1 is string is all blanks, else 0 */ X#undef isblankstr /* Name is reserved by */ Xstatic int isblankstr(char *str) X{ X char c; X while ((c = *str++) != '\0') X { X if (!isspace((Uchar)c)) X return 0; X } X return 1; X} X X/* Convert string to time using ifmt, and reconvert time to string using ofmt */ Xstatic char *tmconvert(char *str, const char *ifmt, const char *ofmt) X{ X char buffer[BUFSIZ]; X struct tm t; X char *end; X size_t len; X X end = strptime(str, ifmt, &t); X if (end == NULL || !isblankstr(end)) X err_report(ERR_ERR, ERR_STAT, "field value <<%s>> cannot be" X " converted to time using format <<%s>>\n", str, ifmt); X len = strftime(buffer, sizeof(buffer), ofmt, &t); X if (len == 0) X err_report(ERR_ERR, ERR_STAT, "field value <<%s>> cannot be" X " reformatted using format <<%s>>\n", str, ofmt); X return(setstr(buffer, buffer + strlen(buffer))); X} X X/* Process one input file */ Xstatic void dbldfmt(FILE *fp, char *fn) X{ X size_t i; X size_t j; X register char *src; X register char *dst; X register char *end; X Field *fld; X SubField *f; X X while (fgets(ibuffer, ibuflen, fp) != NIL(char *)) X { X if (strlen(ibuffer) != linelength + 1) X { X fclose(fp); X error2("variable or incorrect length records in file:", fn); X } X dst = obuffer; X for (i = 0; i < nfl; i++) X { X fld = &flist[i]; X for (j = 0; j < fld->f_nparts; j++) X { X f = &fld->f_part[j]; X switch (f->f_type) X { X case F_CONST: X src = f->f_const; X while (*src) X *dst++ = *src++; X break; X case F_LRANGE: X case F_RANGE: X case F_URANGE: X assert(f->f_lo > 0); X src = &ibuffer[f->f_lo - 1]; X if (f->f_dp > 0) X { X /* Deal with implicit decimal point */ X end = &ibuffer[f->f_hi - f->f_dp - 1]; X while (src <= end) X *dst++ = *src++; X src = point; X while (*src) X *dst++ = *src++; X src = end + 1; X } X end = &ibuffer[f->f_hi - 1]; X switch (f->f_type) X { X case F_LRANGE: X while (src <= end) X *dst++ = tolower(*src++); X break; X case F_RANGE: X while (src <= end) X *dst++ = *src++; X break; X case F_URANGE: X while (src <= end) X *dst++ = toupper(*src++); X break; X } X break; X case F_TIME: X { X char *ival; X char *oval; X ival = setstr(&ibuffer[f->f_lo - 1], &ibuffer[f->f_hi]); X oval = tmconvert(ival, f->f_input, f->f_output); X src = oval; X while (*src != '\0') X *dst++ = *src++; X FREE(ival); X FREE(oval); X } X break; X default: X error("bug -- field type out of control"); X break; X } X } X src = delim; X while (*src) X *dst++ = *src++; X } X *dst = '\0'; X puts(obuffer); X } X} X Xint main(int argc, char **argv) X{ X size_t obuflen; X int opt; X char *dp; X X setarg0(argv[0]); X if (atexit(free_memory) != 0) X error("atexit() failed"); X X linelength = 0; X point = "."; X if ((delim = getenv("DBDELIMITER")) == NIL(char *)) X delim = "|"; X X /* Allocate too many Field structures */ X flist = (Field *)CALLOC(argc, sizeof(Field)); X nfl = 0; X X while ((opt = GETOPT(argc, argv, "D:P:Vl:c:d:f:r:t:")) != EOF) X { X switch (opt) X { X case 'D': X delim = optarg; X break; X X case 'P': X point = optarg; X break; X X case 'l': X linelength = strtol(optarg, &dp, 0); X if (dp == optarg) X error2("non-numeric value for length specifier:", optarg); X if (*dp != '\0') X error2("extra data after length specifier:", dp); X if (linelength <= 0) X error2("line length should be positive number:", optarg); X break; X X case 'c': X const_field(&flist[nfl++], optarg); X break; X X case 'd': X decimal_field(&flist[nfl++], optarg); X break; X X case 'f': X general_field(&flist[nfl++], optarg); X break; X X case 'r': X range_field(&flist[nfl++], optarg); X break; X X case 't': X datetime_field(&flist[nfl++], optarg); X break; X X case 'V': X version("DBLDFMT", "$Revision: 3.13 $ ($Date: 1998/07/10 18:25:02 $)"); X break; X X default: X usage(usestr); X break; X } X } X X if (nfl == 0) X { X remark("no fields specified"); X usage(usestr); X } X X if (linelength <= 0) X error("line length not specified (-l length)"); X X /* Make sure there's enough space for long and short lines to show up */ X ibuflen = linelength + 6; X ibuffer = (char *)MALLOC(ibuflen); X obuflen = outlength(strlen(delim), strlen(point)); X obuffer = (char *)MALLOC(obuflen); X X filter(argc, argv, optind, dbldfmt); X X return(0); X} SHAR-EOF chmod 440 dbldfmt.c if [ `wc -c dbldfmt.mk <<'SHAR-EOF' X# @(#)$Id: dbldfmt.mk,v 1.4 1997/09/03 20:49:18 johnl Exp $ X# X# Makefile for dbldfmt X XCCFLAGS = -O XCFLAGS = ${CPPFLAGS} ${CCFLAGS} XCPPFLAGS = XLDFLAGS = XLIBRARIES = XMAKEFILE = dbldfmt.mk XMKDEP = mkdep -f${MAKEFILE} XPROGRAM = dbldfmt XRM = rm -f X XFILES_C = \ X dbldfmt.c \ X emalloc.c \ X filter.c \ X range.c \ X stderr.c X XFILES_O = ${FILES_C:.c=.o} X XFILES_H = \ X debug.h \ X emalloc.h \ X filter.h \ X getopt.h \ X range.h \ X stderr.h X Xall: ${PROGRAM} X X${PROGRAM}: ${FILES_O} X ${CC} ${CFLAGS} -o $@ ${LDFLAGS} ${FILES_O} ${LIBRARIES} X Xdepend: ${FILES_C} X ${MKDEP} ${CPPFLAGS} ${FILES_C} X Xclean: X ${RM} ${FILES_O} ${PROGRAM} X X# DO NOT DELETE THIS LINE -- makedep depends on it X Xdbldfmt.o: dbldfmt.c emalloc.h filter.h getopt.h stderr.h range.h Xemalloc.o: debug.h emalloc.c emalloc.h stderr.h Xfilter.o: filter.c filter.h stderr.h Xrange.o: range.c range.h Xstderr.o: stderr.c stderr.h SHAR-EOF chmod 444 dbldfmt.mk if [ `wc -c dbldfmt.tst <<'SHAR-EOF' X: "@(#)$Id: dbldfmt.tst,v 1.7 1997/09/03 20:50:19 johnl Exp $" X# X# Test script to drive dbldfmt X Xdata="dbldfmt.data" Xtrap 'rm -f $data.?; exit 1' 0 1 2 3 13 15 X Xcat > $data.1 < $data.2 < $data.3 < $data.4 < $data.5 <debug.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: debug.h,v $ X@(#)Version: $Revision: 2.9 $ X@(#)Last changed: $Date: 1998/07/13 02:31:58 $ X@(#)Purpose: Definitions for the debugging system X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1990-93,1997-98 X@(#)Product: :PRODUCT: X*/ X X#ifndef DEBUG_H X#define DEBUG_H X X/* -- Macro Definitions */ X X/* X** Usage: TRACE((level, fmt, ...)) X** "level" is the debugging level which must be operational for the output X** to appear. "fmt" is a printf format string. "..." is whatever extra X** arguments fmt requires (possibly nothing). X*/ X#ifdef DEBUG X#define TRACE(x) db_print x X#else X#define TRACE(x) ((void)0) X#endif /* DEBUG */ X X/* -- Declarations */ X X#ifndef lint X#ifdef DEBUG Xstatic const char _debug_enabled[] = "@(#)*** DEBUG ***"; X#endif /* DEBUG */ X X#ifdef MAIN_PROGRAM Xstatic const char debug_h[] = "@(#)$Id: debug.h,v 2.9 1998/07/13 02:31:58 jleffler Exp $"; X#endif /* MAIN_PROGRAM */ X#endif /* lint */ X X#include X Xextern int db_getdebug(void); Xextern int db_newindent(void); Xextern int db_oldindent(void); Xextern int db_setdebug(int level); Xextern int db_setindent(int i); Xextern void db_print(int level, const char *fmt,...); Xextern void db_setfilename(const char *fn); Xextern void db_setfileptr(FILE *fp); Xextern FILE *db_getfileptr(void); X X/**************************************\ X** MULTIPLE DEBUGGING SUBSYSTEMS CODE ** X\**************************************/ X X#ifdef DB_SUBSYSTEMS X X/* X** Usage: MDTRACE((subsys, level, fmt, ...)) X** "subsys" is the debugging system to which this statement belongs. X** The significance of the subsystems is determined by the programmer, X** except that the functions such as db_print refer to subsystem 0. X** "level" is the debugging level which must be operational for the X** output to appear. "fmt" is a printf format string. "..." is X** whatever extra arguments fmt requires (possibly nothing). X*/ X#ifdef DEBUG X#define MDTRACE(x) db_mdprint x X#else X#define MDTRACE(x) ((void)0) X#endif /* DEBUG */ X Xextern int db_mdgetdebug(int subsys); Xextern int db_mdparsearg(char *arg); Xextern int db_mdsetdebug(int subsys, int level); Xextern void db_mdprint(int subsys, int level, const char *fmt,...); Xextern void db_mdsubsysnames(char *const *names); X X#endif /* DB_SUBSYSTEMS */ X X#endif /* DEBUG_H */ SHAR-EOF chmod 440 debug.h if [ `wc -c emalloc.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: emalloc.c,v $ X@(#)Version: $Revision: 5.3 $ X@(#)Last changed: $Date: 1997/06/02 16:41:57 $ X@(#)Purpose: More secure memory allocation X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1989-93,1996-97 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X/*LINTLIBRARY*/ X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: emalloc.c,v 5.3 1997/06/02 16:41:57 johnl Exp $"; X#endif X X#include X#include X#include "emalloc.h" X#include "debug.h" X#include "stderr.h" X X/* X** Note: under normal circumstances X** MALLOC <=> emalloc <=> malloc X** REALLOC <=> erealloc <=> realloc X** FREE <=> efree <=> free X** Under error conditions, the behaviour of these routines is different: X** 1. FREE does no damage when given a null pointer X** 2. REALLOC does no damage when given a null pointer X** 3. REALLOC does an error exit if it fails to allocate memory X** 4. MALLOC does an error exit if it fails to allocate memory X*/ X X#define NIL(x) ((x)0) X#define ITEMSIZE 8 X Xstatic const char no_memory[] = "out of memory"; X X#ifdef DEBUG Xstatic long counter = 1; Xstatic const char sz_format[] = "%07ld:%-17s: 0x%08X %8lu\n"; Xstatic const char pt_format[] = "%07ld:%-17s: 0x%08X\n"; X#endif /* DEBUG */ X X/* Secure memory allocation */ Xvoid *emalloc(size_t u) X{ X void *cp; X X if ((cp = malloc(u)) == NIL(void *)) X error(no_memory); X TRACE((10, sz_format, counter++, "emalloc", cp, u)); X return(cp); X} X X/* Secure allocation of zeroed memory */ Xvoid *ecalloc(size_t n, size_t s) X{ X void *cp; X X if ((cp = calloc(n, s)) == NIL(void *)) X error(no_memory); X TRACE((10, sz_format, counter++, "ecalloc", cp, n * s)); X return(cp); X} X X/* Secure memory re-allocation */ Xvoid *erealloc(void *s, size_t n) X{ X void *cp; X X if (s == NIL(void *)) X { X cp = malloc(n); X TRACE((10, sz_format, counter++, "erealloc-malloc", cp, n)); X } X else X { X TRACE((10, pt_format, counter++, "erealloc-realloc1", s)); X cp = realloc(s, n); X TRACE((10, sz_format, counter++, "erealloc-realloc2", cp, n)); X } X X if (cp == NIL(void *)) X error(no_memory); X X return(cp); X} X X/* Secure memory de-allocation */ Xvoid efree(void *s) X{ X if (s != NIL(void *)) X { X TRACE((10, pt_format, counter++, "efree", s)); X free(s); X } X} SHAR-EOF chmod 444 emalloc.c if [ `wc -c emalloc.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: emalloc.h,v $ X@(#)Version: $Revision: 5.4 $ X@(#)Last changed: $Date: 1997/06/17 18:53:28 $ X@(#)Purpose: Interfaces to routines in emalloc.c X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1990,1992-93,1996-97 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X X#ifndef EMALLOC_H X#define EMALLOC_H X X#ifdef MAIN_PROGRAM X#ifndef lint Xstatic const char emalloc_h[] = "@(#)$Id: emalloc.h,v 5.4 1997/06/17 18:53:28 johnl Exp $"; X#endif /* lint */ X#endif /* MAIN_PROGRAM */ X X#include X X/* -- Macro Definitions */ X X/* Make it easier to avoid using emalloc() et al */ X#ifdef USE_REAL_MALLOC X#define MALLOC(n) malloc((n)) X#define CALLOC(n, s) calloc((n), (s)) X#define REALLOC(s, n) realloc((s), (n)) X#define FREE(s) free((s)) X#endif /* USE_REAL_MALLOC */ X X#ifndef MALLOC X#define MALLOC(n) emalloc((size_t)(n)) X#endif /* MALLOC */ X X#ifndef CALLOC X#define CALLOC(n, s) ecalloc((size_t)(n), (size_t)(s)) X#endif /* CALLOC */ X X#ifndef REALLOC X#define REALLOC(s, n) erealloc((void *)(s), (size_t)(n)) X#endif /* REALLOC */ X X#ifndef FREE X#define FREE(s) efree((void *)(s)) X#endif /* FREE */ X X#ifndef NOSTRICT X#ifdef lint X#define NOSTRICT(type, exp) ((type)((exp) ? 0 : 0)) X#else X#define NOSTRICT(type, exp) ((type)(exp)) X#endif X#endif /* NOSTRICT */ X X/* -- Declarations */ X Xextern void *emalloc(size_t nbytes); Xextern void *ecalloc(size_t nitems, size_t size); Xextern void *erealloc(void *space, size_t nbytes); Xextern void efree(void *space); X X#endif /* EMALLOC_H */ SHAR-EOF chmod 444 emalloc.h if [ `wc -c filter.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: filter.c,v $ X@(#)Version: $Revision: 3.7 $ X@(#)Last changed: $Date: 1998/03/31 22:05:49 $ X@(#)Purpose: Standard File Filter X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1987-89,1991,1993,1996-98 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X/*LINTLIBRARY*/ X X#include X#include X#include X#include "stderr.h" X#include "filter.h" X Xstatic int continue_mode = 0; X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: filter.c,v 3.7 1998/03/31 22:05:49 jleffler Exp $"; X#endif X X/* Set program to continue with next file after a file-open error */ Xvoid filter_setcontinue(void) X{ X continue_mode = 1; X} X X/* X Purpose: Standard File Filter X X Maintenance Log X --------------- X 09/03/87 JL Original version stabilised X 18/04/88 JL Now pass fp and file name to function X 15/12/91 JL Upgrade for ANSI C X 29/06/93 JL Rename to filter and accept optind argument X 15/10/96 JL Remove non-ANSI support and ffilter(); rename file X 31/03/98 JL Add support for continuing on error opening a file X X Arguments X --------- X argc In: Number of arguments X argv In: Argument list of program X optind In: Offset in list to start at X function In: Function to process file X X Comments X -------- X 1. For every non-flag option in the argument list, or standard input X if there are no non-flag arguments, run 'function' on file. X 2. If a file name is '-', use standard input. X 3. The optnum argument should normally be the value of optind as X supplied by getopt(3). But it should be the index of the first X non-flag argument. X X*/ X Xvoid filter(int argc, char **argv, int optnum, Filter function) X{ X int i; X FILE *fp; X char *file; X X if (argc <= optnum) X { X /* Assumes argv[argc] == NIL(char *) and can be assigned to */ X argv[argc] = "-"; X argc++; X } X X for (i = optnum; i < argc; i++) X { X if (strcmp(argv[i], "-") == 0) X { X file = "(standard input)"; X (*function)(stdin, file); X } X else if ((fp = fopen(argv[i], "r")) != NULL) X { X file = argv[i]; X (*function)(fp, file); X fclose(fp); X } X else if (continue_mode) X err_remark("failed to open file %s (%s)\n", argv[i], strerror(errno)); X else X error2("failed to open file", argv[i]); X } X} X X#ifdef TEST X X/* X** Test program -- equivalent to: cat [-v] [file ...] X*/ X X#include X#define GETOPT gnu_getopt X#include "getopt.h" X Xstatic void fcopy(FILE *f1, FILE *f2) X{ X char buffer[BUFSIZ]; X int n; X X while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0) X { X if (fwrite(buffer, sizeof(char), n, f2) != n) X error("write failed"); X } X} X Xstatic void vis(FILE *fp, char *fn) X{ X int c; X X fprintf(stdout, "%s:\n", fn); X while ((c = getc(fp)) != EOF) X { X if (c == '\n' || c == '\t') X putchar(c); X else if (c > 127 || !isprint(c)) X printf("\\%03o", c); X else X putchar(c); X } X} X Xstatic void cat(FILE *fp, char *fn) X{ X fprintf(stdout, "%s:\n", fn); X fcopy(fp, stdout); X} X Xint main(int argc, char **argv) X{ X int opt; X Filter f = cat; X X setarg0(argv[0]); X opterr = 0; X while ((opt = GETOPT(argc, argv, "cv")) != EOF) X { X if (opt == 'c') X filter_setcontinue(); X else if (opt == 'v') X f = vis; X else X usage("[-v] [file ...]"); X } X filter(argc, argv, optind, f); X return(0); X} X X#endif /* TEST */ SHAR-EOF chmod 440 filter.c if [ `wc -c filter.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: filter.h,v $ X@(#)Version: $Revision: 1.7 $ X@(#)Last changed: $Date: 1998/03/31 22:06:20 $ X@(#)Purpose: Header for filter functions X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1993,1996-98 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X X#ifndef FILTER_H X#define FILTER_H X X#ifdef MAIN_PROGRAM X#ifndef lint Xstatic const char filter_h[] = "@(#)$Id: filter.h,v 1.7 1998/03/31 22:06:20 jleffler Exp $"; X#endif /* lint */ X#endif /* MAIN_PROGRAM */ X X#include X Xtypedef void (*Filter)(FILE *fp, char *fn); X Xextern void filter_setcontinue(void); X Xextern void filter(int argc, char **argv, int optnum, Filter function); X X/* Backwards compatability */ X#ifdef USE_FFILTER Xextern void ffilter(int argc, char **argv, Filter function); X#else X#define ffilter(argc, argv, function) filter(argc, argv, 1, function) X#endif X X#endif /* FILTER_H */ SHAR-EOF chmod 440 filter.h if [ `wc -c getopt.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: getopt.h,v $ X@(#)Version: $Revision: 1.10 $ X@(#)Last changed: $Date: 1998/04/09 18:48:20 $ X@(#)Purpose: Declarations for GETOPT(3) and GETSUBOPT(3) X@(#)Author: J Leffler X@(#)Copyright: JLSS (C) 1992-93,1996-97 X@(#)Product: :PRODUCT: X*/ X X#ifndef GETOPT_H X#define GETOPT_H X X#ifdef MAIN_PROGRAM X#ifndef lint Xstatic const char getopt_h[] = "@(#)$Id: getopt.h,v 1.10 1998/04/09 18:48:20 jleffler Exp $"; X#endif /* lint */ X#endif /* MAIN_PROGRAM */ X X/* X** GNU getopt provides facilities not available in standard getopt. X** Specifically, it will reorder all option arguments before all non-option X** arguments unless the environment variable _POSIX_OPTION_ORDER is X** defined. It can also handle optional arguments, which must be attached X** to option letter on the command line, indicated by two colons after the X** option letter. It can be told to return all arguments in order, with a X** value of '\0' indicating a file option by starting the options string X** with a '-'. It also has a different interface from standard getopt X** because the second (argv) argument is not const. X*/ X X#ifdef USE_GNU_GETOPT X#define GETOPT(argc, argv, opts) gnu_getopt(argc, argv, opts) X#define opterr gnu_opterr X#define optind gnu_optind X#define optarg gnu_optarg X#define optopt gnu_optopt X#else X#define GETOPT(argc, argv, opts) getopt(argc, argv, opts) X#endif /* USE_GNU_GETOPT */ X Xextern int optopt; Xextern int opterr; Xextern int optind; Xextern char *optarg; X Xextern int getopt(int argc, char *const*argv, const char *opts); Xextern int getsubopt(char **opt, char *const*names, char **value); Xextern int gnu_getopt(int argc, char **argv, const char *opts); X X#endif /* GETOPT_H */ SHAR-EOF chmod 440 getopt.h if [ `wc -c range.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: range.c,v $ X@(#)Version: $Revision: 1.5 $ X@(#)Last changed: $Date: 1997/06/02 16:41:57 $ X@(#)Purpose: Decode string into range of integers. X@(#)Author: J Leffler X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X/*LINTLIBRARY*/ X X/* X** Input: Low High X** 23 23 23 X** 23-25 23 25 X** 23- 23 0 X** -23 0 23 X** Any other delimiter after number (or before number) terminates input X*/ X X#include X#include "range.h" X X#define NIL(x) ((x)0) X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: range.c,v 1.5 1997/06/02 16:41:57 johnl Exp $"; X#endif X Xchar *parse_range(char *str, long *lo, long *hi) X{ X register char *s = str; X char *t; X long l; X X l = strtol(s, &t, 10); X if (l < 0) X { /* -n => lo = 0, hi = n */ X *lo = 0; X *hi = -l; X } X else if (t == s) X { /* No number found */ X *lo = *hi = 0; X t = NIL(char *); X } X else if (*t == ',') X { /* n, => lo = hi = n */ X t++; X *lo = *hi = l; X } X else if (*t == '-') X { /* n-m => lo = n, hi = m */ X /* n- => lo = n, hi = 0 */ X s = ++t; X *lo = l; X *hi = strtol(s, &t, 10); X if (t != NIL(char *) && *t == ',') X t++; X } X else X { /* n => lo = hi = n */ X *lo = *hi = l; X } X return(t); X} X X#ifdef TEST X#include X#include "stderr.h" X Xint main(int argc, char **argv) X{ X int i; X long lo; X long hi; X char *t; X X setarg0(argv[0]); X if (argc <= 1) X usage("range [...]"); X for (i = 1; i < argc; i++) X { X t = argv[i]; X while (t != NIL(char *) && *t != '\0') X { X printf("Parse: %15s (addr = 0x%08lX) ", t, t); X fflush(stdout); X t = parse_range(t, &lo, &hi); X printf("Range: %2d -> %2d (addr = 0x%08lX)\n", lo, hi, t); X fflush(stdout); X } X } X return(0); X} X#endif /* TEST */ SHAR-EOF chmod 444 range.c if [ `wc -c range.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: range.h,v $ X@(#)Version: $Revision: 1.3 $ X@(#)Last changed: $Date: 1997/06/02 16:24:26 $ X@(#)Purpose: Declaration of range parsing functions X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1997 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X X#ifndef RANGE_H X#define RANGE_H X X#ifdef MAIN_PROGRAM X#ifndef lint Xstatic const char range_h[] = "@(#)$Id: range.h,v 1.3 1997/06/02 16:24:26 johnl Exp $"; X#endif /* lint */ X#endif /* MAIN_PROGRAM */ X X/* X** parse_range(): parse range of positive numbers. X** X** Given a string, parse_range() returns the lo and hi values corresponding X** to the range specified by the string. For example: X** Input: Low High X** 23 23 23 X** 23-25 23 25 X** 23- 23 0 X** -23 0 23 X** Any delimiter other than '-' before or after a number terminates the X** scan. Returns pointer to character after last character parsed (which X** may or may not be '\0') if successful. Otherwise, returns pointer to X** str if it fails, and does not set *lo or *hi. X*/ Xextern char *parse_range(char *str, long *lo, long *hi); X X/* X** numeric_range(): parse range of numbers, positive or negative. X** X** Input: Low High X** 23 23 23 X** -23 -23 -23 X** 23:25 23 25 X** 23..25 23 25 X** -23..-25 -25 -23 X** -23..25 -23 25 X** 23..-25 -25 23 X** Returns pointer to '\0' at end of string if OK, sets *lo and *hi, X** and guarantees *lo <= *hi. X** Otherwise, returns pointer to start of string and does not set *lo or *hi. X*/ Xextern char *numeric_range(char *str, long *lo, long *hi); X X#endif /* RANGE_H */ SHAR-EOF chmod 444 range.h if [ `wc -c stderr.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: stderr.c,v $ X@(#)Version: $Revision: 6.21 $ X@(#)Last changed: $Date: 1998/04/07 19:09:04 $ X@(#)Purpose: Error reporting routines -- using stdio X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1988-91,1996-98 X@(#)Product: :PRODUCT: X*/ X X/*TABSTOP=4*/ X/*LINTLIBRARY*/ X X#include X#include X#include X#include X#include X#include X#include X#include X#include "stderr.h" X Xstatic char arg0[15] = "**undefined**"; Xstatic FILE *errout = stderr; X X#ifndef lint Xstatic const char rcs[] = "@(#)$Id: stderr.c,v 6.21 1998/04/07 19:09:04 jleffler Exp $"; X#endif /* lint */ X X/* Change the definition of 'stderr', reporting on the old one too */ X/* NB: using err_stderr((FILE *)0) simply reports the current 'stderr' */ XFILE *err_stderr(FILE *newerr) X{ X FILE *old = errout; X if (newerr != (FILE *)0) X errout = newerr; X return(old); X} X Xconst char *getarg0(void) X{ X return(arg0); X} X Xvoid remark2(const char *s1, const char *s2) X{ X err_report(ERR_REM, ERR_STAT, "%s %s\n", (s1), (s2)); X} X Xvoid remark(const char *s1) X{ X err_report(ERR_REM, ERR_STAT, "%s\n", (s1)); X} X Xvoid error2(const char *s1, const char *s2) X{ X err_report(ERR_ERR, ERR_STAT, "%s %s\n", (s1), (s2)); X} X Xvoid error(const char *s1) X{ X err_report(ERR_ERR, ERR_STAT, "%s\n", (s1)); X} X Xvoid stop(const char *s1) X{ X err_report(ERR_ABT, ERR_STAT, "%s\n", (s1)); X} X Xvoid usage(const char *s1) X{ X err_report(ERR_USE, ERR_STAT, (s1)); X} X Xconst char *err_rcs_string(const char *s2, char *buffer, size_t buflen) X{ X const char *src = s2; X char *dst = buffer; X char *end = buffer + buflen - 1; X X /* X ** Bother RCS! We've probably been given something like: X ** "$Revision: 6.21 $ ($Date: 1998/04/07 19:09:04 $)" X ** We only want to emit the revision number and the date/time. X ** Skip the components between '$' and ': ', copy up to ' $', X ** repeating as necessary. And we have to test for overflow! X */ X while (*src != '\0' && dst < end) X { X while (*src != '\0' && *src != '$') X { X *dst++ = *src++; X if (dst >= end) X break; X } X if (*src == '$') X src++; X while (*src != '\0' && *src != ':' && *src != '$') X src++; X if (*src == '\0') X break; X if (*src == '$') X { X /* Unexpanded keyword '$Keyword$' notation */ X src++; X continue; X } X if (*src == ':') X src++; X if (*src == ' ') X src++; X while (*src != '\0' && *src != '$') X { X *dst++ = *src++; X if (dst >= end) X break; X } X if (*src == '$') X { X if (*(dst-1) == ' ') X dst--; X src++; X } X } X *dst = '\0'; X return(buffer); X} X X/* Report version information, removing embedded RCS keyword strings (but not values) */ Xvoid version(const char *s1, const char *s2) X{ X char buffer[64]; X X if (strchr(s2, '$')) X s2 = err_rcs_string(s2, buffer, sizeof(buffer)); X err_logmsg(stdout, ERR_ERR, EXIT_SUCCESS, "%s Version %s\n", s1, s2); X} X X/* Store basename of command, excluding trailing slashes */ X/* Doesn't handle two pathological cases -- "/" and "" */ Xvoid setarg0(const char *argv0) X{ X const char *cp; X size_t nbytes = sizeof(arg0) - 1; X X if ((cp = strrchr(argv0, '/')) != (char *)0 && *(cp + 1) == '\0') X { X /* Skip backwards over trailing slashes */ X const char *ep = cp; X while (ep > argv0 && *ep == '/') X ep--; X /* Skip backwards over non-slashes */ X cp = ep; X while (cp > argv0 && *cp != '/') X cp--; X cp++; X nbytes = ep - cp + 1; X if (nbytes > sizeof(arg0) - 1) X nbytes = sizeof(arg0) - 1; X } X else if (cp != (char *)0) X { X /* Regular pathname containing slashes */ X cp++; X } X else X { X /* Basename of file only */ X cp = argv0; X } X strncpy(arg0, cp, nbytes); X arg0[nbytes] = '\0'; X} X X/* Format a time string for now (using ISO8601 format) */ X/* Allow for future settable time format with tm_format */ Xstatic char *err_time(void) X{ X static char buffer[32]; X static const char tm_format[] = "%Y-%m-%d %H:%M:%S"; X time_t now; X struct tm *tp; X X now = time((time_t *)0); X tp = localtime(&now); X strftime(buffer, sizeof(buffer), tm_format, tp); X return(buffer); X} X X/* Most fundamental (and flexible) error message printing routine */ X/* Not singularly convenient for ordinary mortals -- see err_logmsg() */ Xvoid err_fprint(FILE *fp, int flags, int estat, const char *string, va_list args) X{ X int errnum = errno; /* Capture errno before it is damaged! */ X if (flags & ERR_FLUSH) X (void)fflush(stdout); X if (flags & ERR_USAGE) X (void)fprintf(fp, "Usage: %s %s\n", arg0, string); X else if (flags & ERR_COMM) X { X if ((flags & ERR_NOARG0) == 0) X (void)fprintf(fp, "%s: ", arg0); X if (flags & ERR_STAMP) X (void)fprintf(fp, "%s - ", err_time()); X if (flags & ERR_PID) X (void)fprintf(fp, "pid=%d: ", (int)getpid()); X (void)vfprintf(fp, string, args); X if (flags & ERR_ERRNO) X (void)fprintf(fp, "error (%d) %s\n", errnum, strerror(errnum)); X } X (void)fflush(fp); X if (flags & ERR_ABORT) X abort(); X if (flags & ERR_EXIT) X exit(estat); X} X X/* Most convenient external interface to err_fprint() */ Xvoid err_logmsg(FILE *fp, int flags, int estat, const char *string, ...) X{ X va_list args; X X va_start(args, string); X err_fprint(fp, flags, estat, string, args); X va_end(args); X} X X/* Cover function for err_fprint() using default output file (normally stderr) */ Xvoid err_print(int flags, int estat, const char *string, va_list args) X{ X err_fprint(errout, flags, estat, string, args); X} X Xvoid err_remark(const char *format, ...) X{ X va_list args; X X va_start(args, format); X err_print(ERR_REM, ERR_STAT, format, args); X va_end(args); X} X Xvoid err_error(const char *format, ...) X{ X va_list args; X X va_start(args, format); X err_print(ERR_ERR, ERR_STAT, format, args); X va_end(args); X} X Xvoid err_report(int flags, int estat, const char *string, ...) X{ X va_list args; X X va_start(args, string); X err_print(flags, estat, string, args); X va_end(args); X} X X#ifdef TEST X Xstatic const char *list[] = X{ X "/usr/fred/bloggs", X "/usr/fred/bloggs/", X "/usr/fred/bloggs////", X "bloggs", X "/.", X ".", X "/", X "//", X "///", X "////", X "", X (char *)0 X}; X Xint main(int argc, char **argv) X{ X const char **name; X char *data; X X setarg0(argv[0]); X X err_logmsg(stdout, ERR_LOG, EXIT_SUCCESS, "testing ERR_LOG\n"); X err_logmsg(stdout, ERR_STAMP|ERR_REM|ERR_FLUSH, EXIT_SUCCESS, X "testing ERR_STAMP\n"); X err_logmsg(stdout, ERR_PID|ERR_REM|ERR_FLUSH, EXIT_SUCCESS, X "testing ERR_PID\n"); X errno = EXDEV; X err_logmsg(stdout, ERR_ERRNO|ERR_REM|ERR_FLUSH, EXIT_SUCCESS, X "testing ERR_ERRNO\n"); X X remark("testing values for argv[0]"); X X for (name = list; *name != (char *)0; name++) X { X data = malloc(strlen(*name) + 1); X strcpy(data, *name); X printf("name = <<%s>>; ", *name); X setarg0(*name); X printf(" (<<%s>>) arg0 = <<%s>>\n", *name, getarg0()); X free(data); X } X X setarg0(argv[0]); X remark("reporting arguments to program"); X while (*++argv != (char *)0) X remark2("next argument", *argv); X X remark("reporting on version!"); X version("STDERR", "$Revision: 6.21 $ ($Date: 1998/04/07 19:09:04 $)"); X return(0); X} X X#endif /* TEST */ SHAR-EOF chmod 440 stderr.c if [ `wc -c stderr.h <<'SHAR-EOF' X/* X@(#)File: $RCSfile: stderr.h,v $ X@(#)Version: $Revision: 6.17 $ X@(#)Last changed: $Date: 1998/04/07 19:02:25 $ X@(#)Purpose: Header file for standard error functions X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1989-93,1996-98 X@(#)Product: :PRODUCT: X*/ X X#ifndef STDERR_H X#define STDERR_H X X#ifdef MAIN_PROGRAM X#ifndef lint Xstatic const char stderr_h[] = "@(#)$Id: stderr.h,v 6.17 1998/04/07 19:02:25 jleffler Exp $"; X#endif X#endif X X#include X#include X X/* -- Definitions for error handling */ X X#define ERR_STAT (1) /* Default exit status */ X X#define ERR_COMM (0x0001) /* Print message on stderr */ X#define ERR_USAGE (0x0002) /* Print usage on stderr */ X#define ERR_EXIT (0x0004) /* Exit -- do not return */ X#define ERR_ABORT (0x0008) /* Abort -- do not return */ X#define ERR_FLUSH (0x0010) /* Flush stdout */ X#define ERR_STAMP (0x0020) /* Timestamp messages */ X#define ERR_NOARG0 (0x0040) /* Do not print arg0 prefix */ X#define ERR_PID (0x0080) /* Include pid=nnnnn info */ X#define ERR_ERRNO (0x0100) /* Include system error */ X X/* -- Standard combinations of flags */ X X#define ERR_USE (ERR_USAGE|ERR_EXIT|ERR_FLUSH) X#define ERR_REM (ERR_COMM|ERR_FLUSH) X#define ERR_ERR (ERR_COMM|ERR_EXIT|ERR_FLUSH) X#define ERR_ABT (ERR_COMM|ERR_ABORT|ERR_FLUSH) X#define ERR_LOG (ERR_STAMP|ERR_PID|ERR_COMM|ERR_FLUSH) X X/* -- Global definitions */ X Xextern const char *getarg0(void); Xextern void setarg0(const char *argv0); X Xextern FILE *err_stderr(FILE *fp); Xextern const char *err_rcs_string(const char *s, char *buffer, size_t buflen); X Xextern void err_error(const char *format, ...); Xextern void err_fprint(FILE *fp, int flags, int estat, const char *string, va_list args); Xextern void err_logmsg(FILE *fp, int flags, int estat, const char *string, ...); Xextern void err_print(int flags, int estat, const char *string, va_list args); Xextern void err_remark(const char *format, ...); Xextern void err_report(int flags, int estat, const char *string, ...); X Xextern void error(const char *s1); Xextern void error2(const char *s1, const char *s2); Xextern void remark(const char *s1); Xextern void remark2(const char *s1, const char *s2); Xextern void stop(const char *s1); Xextern void usage(const char *s1); Xextern void version(const char *s1, const char *s2); X X#endif /* STDERR_H */ SHAR-EOF chmod 440 stderr.h if [ `wc -c