From: Jonathan Leffler Date: Tue, 20 Oct 1998 09:26:09 -0700 (PDT) On Tue, 20 Oct 1998, John H. Frantz wrote: > finderr 100 shows me the duplicate value message. > finderr -100 also shows me this. Unless you put an explicit + in front of the number, finderr interprets numbers as error numbers, and hence as negative. > finderr +100 shows me a no matching record message (i.e. not found). It could, equivalently, have shown the duplicate value message. C-ISAM can return iserrno = +100 for the duplicate value error. Note that finderr simply (well, not all that simply) greps through a text file to produce the text of an error message. By contrast, rgetmsg() goes to the actual iem-file for the raw message. > Apparently finderr assumes a negative value unless otherwise specified. Yes. > I'm not sure about rgetmsg() but if it accepts the message number as an > integer then it may always assume a negative number, dealing only with > messages categorized as errors. No! rgetmsg() interprets the number you supply as the number you supply. Internally, Informix code uses positive numbers for messages (including things like menu options), and negative numbers for errors. > Art S. Kagel wrote: > > Henry A.L. Wollman wrote: > > > How come rgetmsg returns the wrong description for an error code 100? It > > > should be "not found" but instead returns something about a duplicate > > > value on insert or update of a unique key. > > > > > > Are there other error codes for which rgetmsg returns the wrong > > > description? It seems that even the manual as far back as several years > > > ago has the wrong message for a 100. Aren't these things ever fixed? Is > > > rgetmsg not a reliable function? Must I write my own? > > > > There is nothing to fix since nothing is broken! > > > > Rgetmsg does not return the wrong message. SQLNOTFOUND, or 100 is NOT > > an error, however, there IS an ISAM error -100 which is described as: > > > > -100 ISAM error: duplicate value for a record with a unique key. > > > > It is a convenience that finderr and rgetmsg return the message whether > > you enter the message number as positive or negative, but remember that > > ALL error message numbers are negative! SQLNOTFOUND is an INDICATOR > > that there is no more data to fetch NOT and error caused by fetching > > beyond the end of the select set. I threatened to send a chkmsg() program to c.d.i; I'm now carrying out that threat. I'm not all that keen on the way it uses getopt(), but it more or less works. To compile: esql -o chkmsg chkmsg.ec range2.c stderr.c To use: chkmsg 100 chkmsg +100 chkmsg -100 chkmsg -100:+100 chkmsg -100..+100 This is a direct interface to rgetmsg(), so you can see what messages are really in the message files. You might care to note that both +100 and -100 produce the same message text: -100: ISAM error: duplicate value for a record with unique key. 100: ISAM error: duplicate value for a record with unique key. Yours, Jonathan Leffler (jleffler@informix.com) #include Guardian of DBD::Informix v0.60 -- http://www.perl.com/CPAN Informix IDN for D4GL & Linux -- http://www.informix.com/idn PS: I think this might be useful in the IIUG software archives. : "@(#): 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: Tue Oct 20 09:20:00 PDT 1998 # Created by: jleffler at Informix Software Inc. # # Files archived in this archive: # chkmsg.ec # getopt.h # range.h # range2.c # stderr.c # stderr.h # #-------------------- if [ -f chkmsg.ec -a "$1" != "-c" ] then echo shar: chkmsg.ec already exists else echo 'x - chkmsg.ec (2190 characters)' sed -e 's/^X//' >chkmsg.ec <<'SHAR-EOF' X/* X@(#)File: $RCSfile: chkmsg.ec,v $ X@(#)Version: $Revision: 1.1 $ X@(#)Last changed: $Date: 1997/03/28 14:28:12 $ X@(#)Purpose: Check specified message numbers X@(#)Author: J Leffler X@(#)Copyright: (C) JLSS 1997 X*/ X X/*TABSTOP=4*/ X X#define MAIN_PROGRAM X#include X#include X#include "stderr.h" X#include "getopt.h" X#include "range.h" X X#define NIL(x) ((x)0) X X/* -- Declarations */ X Xstatic const char usestr[] = "[-qV] lo[-hi] ..."; X X#ifndef lint Xstatic const char sccs[] = "@(#)$Id: chkmsg.ec,v 1.1 1997/03/28 14:28:12 johnl Exp $"; X#endif X Xstatic int chkmsg(char *arg, int qflag) X{ X int estat = EXIT_SUCCESS; X long lo; X long hi; X char *endp; X long msg; X X endp = numeric_range(arg, &lo, &hi); X if (endp == arg || *endp != '\0') X err_report(ERR_ERR, ERR_STAT, X "Non-numeric character after range '%s'\n", arg); X X for (msg = lo; msg <= hi; msg++) X { X int actlen; X long err; X char buffer[BUFSIZ]; X if ((err = rgetlmsg(msg, buffer, sizeof(buffer), &actlen)) != 0) X { X err_report(ERR_REM, ERR_STAT, X "cannot locate message %ld -- error %ld\n", msg, err); X estat = EXIT_FAILURE; X } X else if (qflag == 0) X { X printf("%ld: %s", msg, buffer); X if (actlen >= sizeof(buffer)) X putchar('\n'); X } X } X return(estat); X} X Xint main(int argc, char **argv) X{ X int i; X int opt; X int nstat; X int estat = EXIT_SUCCESS; X int qflag = 0; X int nflag = 0; X X setarg0(argv[0]); X X while ((opt = getopt(argc, argv, "qV0:1:2:3:4:5:6:7:8:9:")) != EOF) X { X switch (opt) X { X case 'q': X qflag = 1; X break; X case 'V': X version("CHKMSG", "$Revision: 1.1 $ ($Date: 1997/03/28 14:28:12 $)"); X break; X X case '0': X case '1': X case '2': X case '3': X case '4': X case '5': X case '6': X case '7': X case '8': X case '9': X /* GETOPT() is probably not the right tool for this job! */ X nstat = chkmsg(optarg-2, qflag); X if (estat == EXIT_SUCCESS) X estat = nstat; X nflag = 1; X break; X X default: X usage(usestr); X break; X } X } X X if (optind >= argc && nflag == 0) X usage(usestr); X X for (i = optind; i < argc; i++) X { X nstat = chkmsg(argv[i], qflag); X if (estat == EXIT_SUCCESS) X estat = nstat; X } X X return(estat); X} SHAR-EOF chmod 444 chkmsg.ec 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.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 range2.c <<'SHAR-EOF' X/* X@(#)File: $RCSfile: range2.c,v $ X@(#)Version: $Revision: 1.2 $ 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 -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** 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: range2.c,v 1.2 1997/06/02 16:41:57 johnl Exp $"; X#endif X X/* X** Parse numeric range. X** Return pointer to trailing '\0' if OK, else pointer to input string X*/ Xchar *numeric_range(char *str, long *lo, long *hi) X{ X register char *s = str; X char *t; X long l; X long h; X X l = strtol(s, &t, 10); X if (*t == '\0') X { X /* Just one number */ X *lo = *hi = l; X return(t); X } X X if (*t == ':') X t += 1; X else if (t[0] == '.' && t[1] == '.') X t += 2; X else X { X /* Format error */ X return(str); X } X X h = strtol(t, &t, 10); X if (*t != '\0') X { X /* Format error */ X return(str); X } X X if (h < l) X { X long x = h; X h = l; X l = x; X } X X *lo = l; X *hi = h; 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 = numeric_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 range2.c 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