]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added the _printf() interface. (CVS 150)
authordrh <drh@noemail.net>
Sun, 8 Oct 2000 22:20:57 +0000 (22:20 +0000)
committerdrh <drh@noemail.net>
Sun, 8 Oct 2000 22:20:57 +0000 (22:20 +0000)
FossilOrigin-Name: f9372072a6d6caa15fa14ec722523944470fe155

Makefile.in
VERSION
manifest
manifest.uuid
src/printf.c [new file with mode: 0644]
src/shell.c
src/sqlite.h.in
www/c_interface.tcl
www/changes.tcl
www/tclsqlite.tcl

index 37c7dab7a65bd16b4acc0536e592b685dc4cb652..08bf85c7ba4d4f87061b8d0937c890b32c5cb74a 100644 (file)
@@ -48,7 +48,7 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
 # Object files for the SQLite library.
 #
 LIBOBJ = build.o dbbe.o delete.o expr.o insert.o \
-         main.o parse.o select.o table.o tokenize.o update.o \
+         main.o parse.o printf.o select.o table.o tokenize.o update.o \
          util.o vdbe.o where.o tclsqlite.o
 
 # All of the source code files.
@@ -62,6 +62,7 @@ SRC = \
   $(TOP)/src/insert.c \
   $(TOP)/src/main.c \
   $(TOP)/src/parse.y \
+  $(TOP)/src/printf.c \
   $(TOP)/src/select.c \
   $(TOP)/src/shell.c \
   $(TOP)/src/sqlite.h.in \
@@ -164,6 +165,9 @@ update.o:   $(TOP)/src/update.c $(HDR)
 tclsqlite.o:   $(TOP)/src/tclsqlite.c $(HDR)
        $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/tclsqlite.c
 
+printf.o:      $(TOP)/src/printf.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) $(TCL_FLAGS) -c $(TOP)/src/printf.c
+
 gdbmdump:      $(TOP)/tool/gdbmdump.c
        $(TCC) $(GDBM_FLAGS) -o gdbmdump $(TOP)/tool/gdbmdump.c $(LIBGDBM)
 
diff --git a/VERSION b/VERSION
index b0f3d96f877256ed9ae03858ecc5185a989b1d1b..66c4c2263e5a2dde8e964a2bfbb94fa38b60d1f7 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.8
+1.0.9
index e51b544bb557c4a8f645a00a90bb42fd29327cd8..1c703f59aa6dff4155c0c332fde42c2f9b710911 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Version\s1.0.8\s(CVS\s494)
-D 2000-09-30T23:00:00
+C Added\sthe\s_printf()\sinterface.\s(CVS\s150)
+D 2000-10-08T22:20:57
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
-F Makefile.in 2fe404a488607712e569d69974d7929bae06ad1e
+F Makefile.in f0b70aaa6717f9454c787dc74e5504c98ae7ea18
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
-F VERSION c2e64b60cb74d152b5b5a0822f0e8cb673c928da
+F VERSION 1fe894c9b80001e9dbc3b40ef1f4f984c9b624f3
 F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
 F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
@@ -15,10 +15,11 @@ F src/expr.c e8e350d7baa33bd9ed8701c159eaba5e912e0adb
 F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
 F src/main.c 9a89579b40e498920f86e89878f52185457b9c2c
 F src/parse.y 5d199034de5d29ebedb42c1c51f34db4df40cbe5
+F src/printf.c 534954b942046761e0860c30a9031fa9d803ca3c
 F src/select.c d382e96c2221d08367cc87976f2b574537c9de97
-F src/shell.c 3d25f6709c2794cd910733a48a14f105eed6d7f3
+F src/shell.c ef5d12129c824cb98238763e9e86ca1847e0c7bd
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
-F src/sqlite.h.in 1e0e4495172f752935ad534871ff726ae509d2f0
+F src/sqlite.h.in 8d87c93de1b43c3cce984a4204d8de84e0a626b6
 F src/sqliteInt.h b65fdecac7281aafb4c9ff3e79ea1b5546478385
 F src/table.c 12f0165b47178b54a675d25ed373ee7e798d6ff0
 F src/tclsqlite.c 7ccccae67fb36ed60ec98282953bf5dad0f9c16f
@@ -62,8 +63,8 @@ F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
 F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
 F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
 F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
-F www/c_interface.tcl 73b5c1354e250a12ceaaccc376611351c867146a
-F www/changes.tcl d62039b5387cc0871ec1bd3b15065a7c7d421cb1
+F www/c_interface.tcl 47d6b9b66510be10d3b482ce978a317a183477e5
+F www/changes.tcl c4f4584325dd55f7f4146bb9f5c5f9f7ce385302
 F www/crosscompile.tcl 19734ce7f18b16ff2ed8479412abf8aca56e1dcc
 F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
 F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
@@ -71,9 +72,9 @@ F www/lang.tcl 9192e114b19987e630a41e879585b87006eb84a1
 F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
 F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
-F www/tclsqlite.tcl 21ecd82eaea3ce3d08593a9a2d2bfdb3c1f7b547
+F www/tclsqlite.tcl ae101d5f7c07dcc59770e2a84aae09025fab2dad
 F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520
-P 7e0bacedf928095b29e7166eacd2356e8169d6dd
-R 3e751dc5aea8f130e00211562c95fd7d
+P 384909e50f4ae2e84534f9a6505cfb5cc9f4d6ca
+R 20b739a1a421bb63bda38b88ca775180
 U drh
-Z 96e782ab6ce077a32cb4132dcba512bf
+Z 26c391a6fcfb2f70f6b3966c96a404f9
index 43132758618a80da54fc34e42d03ac513ccc3e36..52b6b1b04295eff5c2eeca94784a1e5a8edf3b40 100644 (file)
@@ -1 +1 @@
-384909e50f4ae2e84534f9a6505cfb5cc9f4d6ca
\ No newline at end of file
+f9372072a6d6caa15fa14ec722523944470fe155
\ No newline at end of file
diff --git a/src/printf.c b/src/printf.c
new file mode 100644 (file)
index 0000000..ed3854e
--- /dev/null
@@ -0,0 +1,788 @@
+/*
+** The "printf" code that follows dates from the 1980's.  It is in
+** the public domain.  The original comments are included here for
+** completeness.  They are slightly out-of-date.
+**
+** The following modules is an enhanced replacement for the "printf" programs
+** found in the standard library.  The following enhancements are
+** supported:
+**
+**      +  Additional functions.  The standard set of "printf" functions
+**         includes printf, fprintf, sprintf, vprintf, vfprintf, and
+**         vsprintf.  This module adds the following:
+**
+**           *  snprintf -- Works like sprintf, but has an extra argument
+**                          which is the size of the buffer written to.
+**
+**           *  mprintf --  Similar to sprintf.  Writes output to memory
+**                          obtained from malloc.
+**
+**           *  xprintf --  Calls a function to dispose of output.
+**
+**           *  nprintf --  No output, but returns the number of characters
+**                          that would have been output by printf.
+**
+**           *  A v- version (ex: vsnprintf) of every function is also
+**              supplied.
+**
+**      +  A few extensions to the formatting notation are supported:
+**
+**           *  The "=" flag (similar to "-") causes the output to be
+**              be centered in the appropriately sized field.
+**
+**           *  The %b field outputs an integer in binary notation.
+**
+**           *  The %c field now accepts a precision.  The character output
+**              is repeated by the number of times the precision specifies.
+**
+**           *  The %' field works like %c, but takes as its character the
+**              next character of the format string, instead of the next
+**              argument.  For example,  printf("%.78'-")  prints 78 minus
+**              signs, the same as  printf("%.78c",'-').
+**
+**      +  When compiled using GCC on a SPARC, this version of printf is
+**         faster than the library printf for SUN OS 4.1.
+**
+**      +  All functions are fully reentrant.
+**
+*/
+#include "sqliteInt.h"
+/*
+** Undefine COMPATIBILITY to make some slight changes in the way things
+** work.  I think the changes are an improvement, but they are not
+** backwards compatible.
+*/
+/* #define COMPATIBILITY       / * Compatible with SUN OS 4.1 */
+
+/*
+** Conversion types fall into various categories as defined by the
+** following enumeration.
+*/
+enum et_type {    /* The type of the format field */
+   etRADIX,            /* Integer types.  %d, %x, %o, and so forth */
+   etFLOAT,            /* Floating point.  %f */
+   etEXP,              /* Exponentional notation. %e and %E */
+   etGENERIC,          /* Floating or exponential, depending on exponent. %g */
+   etSIZE,             /* Return number of characters processed so far. %n */
+   etSTRING,           /* Strings. %s */
+   etPERCENT,          /* Percent symbol. %% */
+   etCHARX,            /* Characters. %c */
+   etERROR,            /* Used to indicate no such conversion type */
+/* The rest are extensions, not normally found in printf() */
+   etCHARLIT,          /* Literal characters.  %' */
+   etSQLESCAPE,        /* Strings with '\'' doubled.  %q */
+   etORDINAL           /* 1st, 2nd, 3rd and so forth */
+};
+
+/*
+** Each builtin conversion character (ex: the 'd' in "%d") is described
+** by an instance of the following structure
+*/
+typedef struct et_info {   /* Information about each format field */
+  int  fmttype;              /* The format field code letter */
+  int  base;                 /* The base for radix conversion */
+  char *charset;             /* The character set for conversion */
+  int  flag_signed;          /* Is the quantity signed? */
+  char *prefix;              /* Prefix on non-zero values in alt format */
+  enum et_type type;          /* Conversion paradigm */
+} et_info;
+
+/*
+** The following table is searched linearly, so it is good to put the
+** most frequently used conversion types first.
+*/
+static et_info fmtinfo[] = {
+  { 'd',  10,  "0123456789",       1,    0, etRADIX,      },
+  { 's',   0,  0,                  0,    0, etSTRING,     }, 
+  { 'q',   0,  0,                  0,    0, etSQLESCAPE,  },
+  { 'c',   0,  0,                  0,    0, etCHARX,      },
+  { 'o',   8,  "01234567",         0,  "0", etRADIX,      },
+  { 'u',  10,  "0123456789",       0,    0, etRADIX,      },
+  { 'x',  16,  "0123456789abcdef", 0, "x0", etRADIX,      },
+  { 'X',  16,  "0123456789ABCDEF", 0, "X0", etRADIX,      },
+  { 'r',  10,  "0123456789",       0,    0, etORDINAL,    },
+  { 'f',   0,  0,                  1,    0, etFLOAT,      },
+  { 'e',   0,  "e",                1,    0, etEXP,        },
+  { 'E',   0,  "E",                1,    0, etEXP,        },
+  { 'g',   0,  "e",                1,    0, etGENERIC,    },
+  { 'G',   0,  "E",                1,    0, etGENERIC,    },
+  { 'i',  10,  "0123456789",       1,    0, etRADIX,      },
+  { 'n',   0,  0,                  0,    0, etSIZE,       },
+  { '%',   0,  0,                  0,    0, etPERCENT,    },
+  { 'b',   2,  "01",               0, "b0", etRADIX,      }, /* Binary */
+  { 'p',  10,  "0123456789",       0,    0, etRADIX,      }, /* Pointers */
+  { '\'',  0,  0,                  0,    0, etCHARLIT,    }, /* Literal char */
+};
+#define etNINFO  (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
+
+/*
+** If NOFLOATINGPOINT is defined, then none of the floating point
+** conversions will work.
+*/
+#ifndef etNOFLOATINGPOINT
+/*
+** "*val" is a double such that 0.1 <= *val < 10.0
+** Return the ascii code for the leading digit of *val, then
+** multiply "*val" by 10.0 to renormalize.
+**
+** Example:
+**     input:     *val = 3.14159
+**     output:    *val = 1.4159    function return = '3'
+**
+** The counter *cnt is incremented each time.  After counter exceeds
+** 16 (the number of significant digits in a 64-bit float) '0' is
+** always returned.
+*/
+static int et_getdigit(double *val, int *cnt){
+  int digit;
+  double d;
+  if( (*cnt)++ >= 16 ) return '0';
+  digit = (int)*val;
+  d = digit;
+  digit += '0';
+  *val = (*val - d)*10.0;
+  return digit;
+}
+#endif
+
+#define etBUFSIZE 1000  /* Size of the output buffer */
+
+/*
+** The root program.  All variations call this core.
+**
+** INPUTS:
+**   func   This is a pointer to a function taking three arguments
+**            1. A pointer to anything.  Same as the "arg" parameter.
+**            2. A pointer to the list of characters to be output
+**               (Note, this list is NOT null terminated.)
+**            3. An integer number of characters to be output.
+**               (Note: This number might be zero.)
+**
+**   arg    This is the pointer to anything which will be passed as the
+**          first argument to "func".  Use it for whatever you like.
+**
+**   fmt    This is the format string, as in the usual print.
+**
+**   ap     This is a pointer to a list of arguments.  Same as in
+**          vfprint.
+**
+** OUTPUTS:
+**          The return value is the total number of characters sent to
+**          the function "func".  Returns -1 on a error.
+**
+** Note that the order in which automatic variables are declared below
+** seems to make a big difference in determining how fast this beast
+** will run.
+*/
+static int vxprintf(
+  void (*func)(void*,char*,int),
+  void *arg,
+  const char *format,
+  va_list ap
+){
+  register const char *fmt; /* The format string. */
+  register int c;           /* Next character in the format string */
+  register char *bufpt;     /* Pointer to the conversion buffer */
+  register int  precision;  /* Precision of the current field */
+  register int  length;     /* Length of the field */
+  register int  idx;        /* A general purpose loop counter */
+  int count;                /* Total number of characters output */
+  int width;                /* Width of the current field */
+  int flag_leftjustify;     /* True if "-" flag is present */
+  int flag_plussign;        /* True if "+" flag is present */
+  int flag_blanksign;       /* True if " " flag is present */
+  int flag_alternateform;   /* True if "#" flag is present */
+  int flag_zeropad;         /* True if field width constant starts with zero */
+  int flag_long;            /* True if "l" flag is present */
+  int flag_center;          /* True if "=" flag is present */
+  unsigned long longvalue;  /* Value for integer types */
+  double realvalue;         /* Value for real types */
+  et_info *infop;           /* Pointer to the appropriate info structure */
+  char buf[etBUFSIZE];      /* Conversion buffer */
+  char prefix;              /* Prefix character.  "+" or "-" or " " or '\0'. */
+  int  errorflag = 0;       /* True if an error is encountered */
+  enum et_type xtype;       /* Conversion paradigm */
+  char *zMem;               /* String to be freed */
+  char *zExtra;             /* Extra memory used for etTCLESCAPE conversions */
+  static char spaces[] = "                                                  "
+     "                                                                      ";
+#define etSPACESIZE (sizeof(spaces)-1)
+#ifndef etNOFLOATINGPOINT
+  int  exp;                 /* exponent of real numbers */
+  double rounder;           /* Used for rounding floating point values */
+  int flag_dp;              /* True if decimal point should be shown */
+  int flag_rtz;             /* True if trailing zeros should be removed */
+  int flag_exp;             /* True to force display of the exponent */
+  int nsd;                  /* Number of significant digits returned */
+#endif
+
+  fmt = format;                     /* Put in a register for speed */
+  count = length = 0;
+  bufpt = 0;
+  for(; (c=(*fmt))!=0; ++fmt){
+    if( c!='%' ){
+      register int amt;
+      bufpt = (char *)fmt;
+      amt = 1;
+      while( (c=(*++fmt))!='%' && c!=0 ) amt++;
+      (*func)(arg,bufpt,amt);
+      count += amt;
+      if( c==0 ) break;
+    }
+    if( (c=(*++fmt))==0 ){
+      errorflag = 1;
+      (*func)(arg,"%",1);
+      count++;
+      break;
+    }
+    /* Find out what flags are present */
+    flag_leftjustify = flag_plussign = flag_blanksign = 
+     flag_alternateform = flag_zeropad = flag_center = 0;
+    do{
+      switch( c ){
+        case '-':   flag_leftjustify = 1;     c = 0;   break;
+        case '+':   flag_plussign = 1;        c = 0;   break;
+        case ' ':   flag_blanksign = 1;       c = 0;   break;
+        case '#':   flag_alternateform = 1;   c = 0;   break;
+        case '0':   flag_zeropad = 1;         c = 0;   break;
+        case '=':   flag_center = 1;          c = 0;   break;
+        default:                                       break;
+      }
+    }while( c==0 && (c=(*++fmt))!=0 );
+    if( flag_center ) flag_leftjustify = 0;
+    /* Get the field width */
+    width = 0;
+    if( c=='*' ){
+      width = va_arg(ap,int);
+      if( width<0 ){
+        flag_leftjustify = 1;
+        width = -width;
+      }
+      c = *++fmt;
+    }else{
+      while( isdigit(c) ){
+        width = width*10 + c - '0';
+        c = *++fmt;
+      }
+    }
+    if( width > etBUFSIZE-10 ){
+      width = etBUFSIZE-10;
+    }
+    /* Get the precision */
+    if( c=='.' ){
+      precision = 0;
+      c = *++fmt;
+      if( c=='*' ){
+        precision = va_arg(ap,int);
+#ifndef etCOMPATIBILITY
+        /* This is sensible, but SUN OS 4.1 doesn't do it. */
+        if( precision<0 ) precision = -precision;
+#endif
+        c = *++fmt;
+      }else{
+        while( isdigit(c) ){
+          precision = precision*10 + c - '0';
+          c = *++fmt;
+        }
+      }
+      /* Limit the precision to prevent overflowing buf[] during conversion */
+      if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40;
+    }else{
+      precision = -1;
+    }
+    /* Get the conversion type modifier */
+    if( c=='l' ){
+      flag_long = 1;
+      c = *++fmt;
+    }else{
+      flag_long = 0;
+    }
+    /* Fetch the info entry for the field */
+    infop = 0;
+    for(idx=0; idx<etNINFO; idx++){
+      if( c==fmtinfo[idx].fmttype ){
+        infop = &fmtinfo[idx];
+        break;
+      }
+    }
+    /* No info entry found.  It must be an error. */
+    if( infop==0 ){
+      xtype = etERROR;
+    }else{
+      xtype = infop->type;
+    }
+    zExtra = 0;
+
+    /*
+    ** At this point, variables are initialized as follows:
+    **
+    **   flag_alternateform          TRUE if a '#' is present.
+    **   flag_plussign               TRUE if a '+' is present.
+    **   flag_leftjustify            TRUE if a '-' is present or if the
+    **                               field width was negative.
+    **   flag_zeropad                TRUE if the width began with 0.
+    **   flag_long                   TRUE if the letter 'l' (ell) prefixed
+    **                               the conversion character.
+    **   flag_blanksign              TRUE if a ' ' is present.
+    **   width                       The specified field width.  This is
+    **                               always non-negative.  Zero is the default.
+    **   precision                   The specified precision.  The default
+    **                               is -1.
+    **   xtype                       The class of the conversion.
+    **   infop                       Pointer to the appropriate info struct.
+    */
+    switch( xtype ){
+      case etORDINAL:
+      case etRADIX:
+        if( flag_long )  longvalue = va_arg(ap,long);
+       else             longvalue = va_arg(ap,int);
+#ifdef etCOMPATIBILITY
+        /* For the format %#x, the value zero is printed "0" not "0x0".
+        ** I think this is stupid. */
+        if( longvalue==0 ) flag_alternateform = 0;
+#else
+        /* More sensible: turn off the prefix for octal (to prevent "00"),
+        ** but leave the prefix for hex. */
+        if( longvalue==0 && infop->base==8 ) flag_alternateform = 0;
+#endif
+        if( infop->flag_signed ){
+          if( *(long*)&longvalue<0 ){
+            longvalue = -*(long*)&longvalue;
+            prefix = '-';
+          }else if( flag_plussign )  prefix = '+';
+          else if( flag_blanksign )  prefix = ' ';
+          else                       prefix = 0;
+        }else                        prefix = 0;
+        if( flag_zeropad && precision<width-(prefix!=0) ){
+          precision = width-(prefix!=0);
+       }
+        bufpt = &buf[etBUFSIZE];
+        if( xtype==etORDINAL ){
+          long a,b;
+          a = longvalue%10;
+          b = longvalue%100;
+          bufpt -= 2;
+          if( a==0 || a>3 || (b>10 && b<14) ){
+            bufpt[0] = 't';
+            bufpt[1] = 'h';
+          }else if( a==1 ){
+            bufpt[0] = 's';
+            bufpt[1] = 't';
+          }else if( a==2 ){
+            bufpt[0] = 'n';
+            bufpt[1] = 'd';
+          }else if( a==3 ){
+            bufpt[0] = 'r';
+            bufpt[1] = 'd';
+          }
+        }
+        {
+          register char *cset;      /* Use registers for speed */
+          register int base;
+          cset = infop->charset;
+          base = infop->base;
+          do{                                           /* Convert to ascii */
+            *(--bufpt) = cset[longvalue%base];
+            longvalue = longvalue/base;
+          }while( longvalue>0 );
+       }
+        length = (long)&buf[etBUFSIZE]-(long)bufpt;
+        for(idx=precision-length; idx>0; idx--){
+          *(--bufpt) = '0';                             /* Zero pad */
+       }
+        if( prefix ) *(--bufpt) = prefix;               /* Add sign */
+        if( flag_alternateform && infop->prefix ){      /* Add "0" or "0x" */
+          char *pre, x;
+          pre = infop->prefix;
+          if( *bufpt!=pre[0] ){
+            for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x;
+         }
+        }
+        length = (long)&buf[etBUFSIZE]-(long)bufpt;
+        break;
+      case etFLOAT:
+      case etEXP:
+      case etGENERIC:
+        realvalue = va_arg(ap,double);
+#ifndef etNOFLOATINGPOINT
+        if( precision<0 ) precision = 6;         /* Set default precision */
+        if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10;
+        if( realvalue<0.0 ){
+          realvalue = -realvalue;
+          prefix = '-';
+       }else{
+          if( flag_plussign )          prefix = '+';
+          else if( flag_blanksign )    prefix = ' ';
+          else                         prefix = 0;
+       }
+        if( infop->type==etGENERIC && precision>0 ) precision--;
+        rounder = 0.0;
+#ifdef COMPATIBILITY
+        /* Rounding works like BSD when the constant 0.4999 is used.  Wierd! */
+        for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
+#else
+        /* It makes more sense to use 0.5 */
+        for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
+#endif
+        if( infop->type==etFLOAT ) realvalue += rounder;
+        /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+        exp = 0;
+        if( realvalue>0.0 ){
+          int k = 0;
+          while( realvalue>=1e8 && k++<100 ){ realvalue *= 1e-8; exp+=8; }
+          while( realvalue>=10.0 && k++<100 ){ realvalue *= 0.1; exp++; }
+          while( realvalue<1e-8 && k++<100 ){ realvalue *= 1e8; exp-=8; }
+          while( realvalue<1.0 && k++<100 ){ realvalue *= 10.0; exp--; }
+          if( k>=100 ){
+            bufpt = "NaN";
+            length = 3;
+            break;
+          }
+       }
+        bufpt = buf;
+        /*
+        ** If the field type is etGENERIC, then convert to either etEXP
+        ** or etFLOAT, as appropriate.
+        */
+        flag_exp = xtype==etEXP;
+        if( xtype!=etFLOAT ){
+          realvalue += rounder;
+          if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+        }
+        if( xtype==etGENERIC ){
+          flag_rtz = !flag_alternateform;
+          if( exp<-4 || exp>precision ){
+            xtype = etEXP;
+          }else{
+            precision = precision - exp;
+            xtype = etFLOAT;
+          }
+       }else{
+          flag_rtz = 0;
+       }
+        /*
+        ** The "exp+precision" test causes output to be of type etEXP if
+        ** the precision is too large to fit in buf[].
+        */
+        nsd = 0;
+        if( xtype==etFLOAT && exp+precision<etBUFSIZE-30 ){
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(bufpt++) = prefix;         /* Sign */
+          if( exp<0 )  *(bufpt++) = '0';            /* Digits before "." */
+          else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+          if( flag_dp ) *(bufpt++) = '.';           /* The decimal point */
+          for(exp++; exp<0 && precision>0; precision--, exp++){
+            *(bufpt++) = '0';
+          }
+          while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+          *(bufpt--) = 0;                           /* Null terminate */
+          if( flag_rtz && flag_dp ){     /* Remove trailing zeros and "." */
+            while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+            if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+          }
+          bufpt++;                            /* point to next free slot */
+       }else{    /* etEXP or etGENERIC */
+          flag_dp = (precision>0 || flag_alternateform);
+          if( prefix ) *(bufpt++) = prefix;   /* Sign */
+          *(bufpt++) = et_getdigit(&realvalue,&nsd);  /* First digit */
+          if( flag_dp ) *(bufpt++) = '.';     /* Decimal point */
+          while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+          bufpt--;                            /* point to last digit */
+          if( flag_rtz && flag_dp ){          /* Remove tail zeros */
+            while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+            if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+          }
+          bufpt++;                            /* point to next free slot */
+          if( exp || flag_exp ){
+            *(bufpt++) = infop->charset[0];
+            if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
+            else       { *(bufpt++) = '+'; }
+            if( exp>=100 ){
+              *(bufpt++) = (exp/100)+'0';                /* 100's digit */
+              exp %= 100;
+           }
+            *(bufpt++) = exp/10+'0';                     /* 10's digit */
+            *(bufpt++) = exp%10+'0';                     /* 1's digit */
+          }
+       }
+        /* The converted number is in buf[] and zero terminated. Output it.
+        ** Note that the number is in the usual order, not reversed as with
+        ** integer conversions. */
+        length = (long)bufpt-(long)buf;
+        bufpt = buf;
+
+        /* Special case:  Add leading zeros if the flag_zeropad flag is
+        ** set and we are not left justified */
+        if( flag_zeropad && !flag_leftjustify && length < width){
+          int i;
+          int nPad = width - length;
+          for(i=width; i>=nPad; i--){
+            bufpt[i] = bufpt[i-nPad];
+          }
+          i = prefix!=0;
+          while( nPad-- ) bufpt[i++] = '0';
+          length = width;
+        }
+#endif
+        break;
+      case etSIZE:
+        *(va_arg(ap,int*)) = count;
+        length = width = 0;
+        break;
+      case etPERCENT:
+        buf[0] = '%';
+        bufpt = buf;
+        length = 1;
+        break;
+      case etCHARLIT:
+      case etCHARX:
+        c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
+        if( precision>=0 ){
+          for(idx=1; idx<precision; idx++) buf[idx] = c;
+          length = precision;
+       }else{
+          length =1;
+       }
+        bufpt = buf;
+        break;
+      case etSTRING:
+        zMem = bufpt = va_arg(ap,char*);
+        if( bufpt==0 ) bufpt = "(null)";
+        length = strlen(bufpt);
+        if( precision>=0 && precision<length ) length = precision;
+        break;
+      case etSQLESCAPE:
+        {
+          int i, j, n, c;
+          char *arg = va_arg(ap,char*);
+          if( arg==0 ) arg = "(NULL)";
+          for(i=n=0; (c=arg[i])!=0; i++){
+            if( c=='\'' )  n++;
+          }
+          n += i;
+          if( n>etBUFSIZE ){
+            bufpt = zExtra = sqliteMalloc( n );
+          }else{
+            bufpt = buf;
+          }
+          for(i=j=0; (c=arg[i])!=0; i++){
+            bufpt[j++] = c;
+            if( c=='\'' ) bufpt[j++] = c;
+          }
+          bufpt[j] = 0;
+          length = j;
+          if( precision>=0 && precision<length ) length = precision;
+        }
+        break;
+      case etERROR:
+        buf[0] = '%';
+        buf[1] = c;
+        errorflag = 0;
+        idx = 1+(c!=0);
+        (*func)(arg,"%",idx);
+        count += idx;
+        if( c==0 ) fmt--;
+        break;
+    }/* End switch over the format type */
+    /*
+    ** The text of the conversion is pointed to by "bufpt" and is
+    ** "length" characters long.  The field width is "width".  Do
+    ** the output.
+    */
+    if( !flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        if( flag_center ){
+          nspace = nspace/2;
+          width -= nspace;
+          flag_leftjustify = 1;
+       }
+        count += nspace;
+        while( nspace>=etSPACESIZE ){
+          (*func)(arg,spaces,etSPACESIZE);
+          nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ) (*func)(arg,spaces,nspace);
+      }
+    }
+    if( length>0 ){
+      (*func)(arg,bufpt,length);
+      count += length;
+    }
+    if( flag_leftjustify ){
+      register int nspace;
+      nspace = width-length;
+      if( nspace>0 ){
+        count += nspace;
+        while( nspace>=etSPACESIZE ){
+          (*func)(arg,spaces,etSPACESIZE);
+          nspace -= etSPACESIZE;
+        }
+        if( nspace>0 ) (*func)(arg,spaces,nspace);
+      }
+    }
+    if( zExtra ){
+      sqliteFree(zExtra);
+    }
+  }/* End for loop over the format string */
+  return errorflag ? -1 : count;
+} /* End of function */
+
+
+/* This structure is used to store state information about the
+** write to memory that is currently in progress.
+*/
+struct sgMprintf {
+  char *zBase;     /* A base allocation */
+  char *zText;     /* The string collected so far */
+  int  nChar;      /* Length of the string so far */
+  int  nAlloc;     /* Amount of space allocated in zText */
+};
+
+/* 
+** This function implements the callback from vxprintf. 
+**
+** This routine add nNewChar characters of text in zNewText to
+** the sgMprintf structure pointed to by "arg".
+*/
+static void mout(void *arg, char *zNewText, int nNewChar){
+  struct sgMprintf *pM = (struct sgMprintf*)arg;
+  if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
+    pM->nAlloc = pM->nChar + nNewChar*2 + 1;
+    if( pM->zText==pM->zBase ){
+      pM->zText = sqliteMalloc(pM->nAlloc);
+      if( pM->zText && pM->nChar ) memcpy(pM->zText,pM->zBase,pM->nChar);
+    }else{
+      pM->zText = sqliteRealloc(pM->zText, pM->nAlloc);
+    }
+  }
+  if( pM->zText ){
+    memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
+    pM->nChar += nNewChar;
+    pM->zText[pM->nChar] = 0;
+  }
+}
+
+/*
+** sqlite_mprintf() works like printf(), but allocations memory to hold the
+** resulting string and returns a pointer to the allocated memory.  Use
+** sqliteFree() to release the memory allocated.
+*/
+char *sqlite_mprintf(const char *zFormat, ...){
+  va_list ap;
+  struct sgMprintf sMprintf;
+  char *zNew;
+  char zBuf[200];
+
+  sMprintf.nChar = 0;
+  sMprintf.nAlloc = sizeof(zBuf);
+  sMprintf.zText = zBuf;
+  sMprintf.zBase = zBuf;
+  va_start(ap,zFormat);
+  vxprintf(mout,&sMprintf,zFormat,ap);
+  va_end(ap);
+  sMprintf.zText[sMprintf.nChar] = 0;
+  if( sMprintf.zText==sMprintf.zBase ){
+    zNew = sqliteMalloc( sMprintf.nChar+1 );
+    if( zNew ) strcpy(zNew,zBuf);
+  }else{
+    zNew = sqliteRealloc(sMprintf.zText,sMprintf.nChar+1);
+  }
+  return zNew;
+}
+
+/* This is the varargs version of sqlite_mprintf.  
+*/
+char *sqlite_vmprintf(const char *zFormat, va_list ap){
+  struct sgMprintf sMprintf;
+  char zBuf[200];
+  sMprintf.nChar = 0;
+  sMprintf.zText = zBuf;
+  sMprintf.nAlloc = sizeof(zBuf);
+  sMprintf.zBase = zBuf;
+  vxprintf(mout,&sMprintf,zFormat,ap);
+  sMprintf.zText[sMprintf.nChar] = 0;
+  if( sMprintf.zText==sMprintf.zBase ){
+    sMprintf.zText = sqliteMalloc( strlen(zBuf)+1 );
+    if( sMprintf.zText ) strcpy(sMprintf.zText,zBuf);
+  }else{
+    sMprintf.zText = sqliteRealloc(sMprintf.zText,sMprintf.nChar+1);
+  }
+  return sMprintf.zText;
+}
+
+/*
+** The following four routines implement the varargs versions of the
+** sqlite_exec() and sqlite_get_table() interfaces.  See the sqlite.h
+** header files for a more detailed description of how these interfaces
+** work.
+**
+** These routines are all just simple wrappers.
+*/
+int sqlite_exec_printf(
+  sqlite *db,                   /* An open database */
+  char *sqlFormat,              /* printf-style format string for the SQL */
+  sqlite_callback xCallback,    /* Callback function */
+  void *pArg,                   /* 1st argument to callback function */
+  char **errmsg,                /* Error msg written here */
+  ...                           /* Arguments to the format string. */
+){
+  va_list ap;
+  int rc;
+
+  va_start(ap, errmsg);
+  rc = sqlite_exec_vprintf(db, sqlFormat, xCallback, pArg, errmsg, ap);
+  va_end(ap);
+  return rc;
+}
+int sqlite_exec_vprintf(
+  sqlite *db,                   /* An open database */
+  char *sqlFormat,              /* printf-style format string for the SQL */
+  sqlite_callback xCallback,    /* Callback function */
+  void *pArg,                   /* 1st argument to callback function */
+  char **errmsg,                /* Error msg written here */
+  va_list ap                    /* Arguments to the format string. */
+){
+  char *zSql;
+  int rc;
+
+  zSql = sqlite_vmprintf(sqlFormat, ap);
+  rc = sqlite_exec(db, zSql, xCallback, pArg, errmsg);
+  sqliteFree(zSql);
+  return rc;
+}
+int sqlite_get_table_printf(
+  sqlite *db,            /* An open database */
+  char *sqlFormat,       /* printf-style format string for the SQL */
+  char ***resultp,       /* Result written to a char *[]  that this points to */
+  int *nrow,             /* Number of result rows written here */
+  int *ncol,             /* Number of result columns written here */
+  char **errmsg,         /* Error msg written here */
+  ...                    /* Arguments to the format string */
+){
+  va_list ap;
+  int rc;
+
+  va_start(ap, errmsg);
+  rc = sqlite_get_table_vprintf(db, sqlFormat, resultp, nrow, ncol, errmsg, ap);
+  va_end(ap);
+  return rc;
+}
+int sqlite_get_table_vprintf(
+  sqlite *db,            /* An open database */
+  char *sqlFormat,       /* printf-style format string for the SQL */
+  char ***resultp,       /* Result written to a char *[]  that this points to */
+  int *nrow,             /* Number of result rows written here */
+  int *ncolumn,          /* Number of result columns written here */
+  char **errmsg,         /* Error msg written here */
+  va_list ap             /* Arguments to the format string */
+){
+  char *zSql;
+  int rc;
+
+  zSql = sqlite_vmprintf(sqlFormat, ap);
+  rc = sqlite_get_table(db, zSql, resultp, nrow, ncolumn, errmsg);
+  sqliteFree(zSql);
+  return rc;
+}
index 9ef5896337e20634e73228367cc862e9f4fb4aae..0fedfebf60e2c375113f8c76dd329e5c578fbe34 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains code to implement the "sqlite" command line
 ** utility for accessing SQLite databases.
 **
-** $Id: shell.c,v 1.25 2000/09/29 13:30:55 drh Exp $
+** $Id: shell.c,v 1.26 2000/10/08 22:20:58 drh Exp $
 */
 #include <stdlib.h>
 #include <string.h>
@@ -377,14 +377,15 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
   fprintf(pData->out, "%s;\n", azArg[2]);
   if( strcmp(azArg[1],"table")==0 ){
     struct callback_data d2;
-    char zSql[1000];
     d2 = *pData;
     d2.mode = MODE_List;
     d2.escape = '\t';
     strcpy(d2.separator,"\t");
     fprintf(pData->out, "COPY '%s' FROM STDIN;\n", azArg[0]);
-    sprintf(zSql, "SELECT * FROM '%s'", azArg[0]);
-    sqlite_exec(pData->db, zSql, callback, &d2, 0);
+    sqlite_exec_printf(pData->db, 
+       "SELECT * FROM '%q'",
+       callback, &d2, 0, azArg[0]
+    );
     fprintf(pData->out, "\\.\n");
   }
   fprintf(pData->out, "VACUUM '%s';\n", azArg[0]);
@@ -449,19 +450,22 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
  
   if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
     char *zErrMsg = 0;
-    char zSql[1000];
     if( nArg==1 ){
-      sprintf(zSql, "SELECT name, type, sql FROM sqlite_master "
-                    "WHERE type!='meta' "
-                    "ORDER BY tbl_name, type DESC, name");
-      sqlite_exec(db, zSql, dump_callback, p, &zErrMsg);
+      sqlite_exec(db,
+        "SELECT name, type, sql FROM sqlite_master "
+        "WHERE type!='meta' "
+        "ORDER BY tbl_name, type DESC, name",
+        dump_callback, p, &zErrMsg
+      );
     }else{
       int i;
       for(i=1; i<nArg && zErrMsg==0; i++){
-        sprintf(zSql, "SELECT name, type, sql FROM sqlite_master "
-                      "WHERE tbl_name LIKE '%.800s' AND type!='meta' "
-                      "ORDER BY type DESC, name", azArg[i]);
-        sqlite_exec(db, zSql, dump_callback, p, &zErrMsg);
+        sqlite_exec_printf(db, 
+          "SELECT name, type, sql FROM sqlite_master "
+          "WHERE tbl_name LIKE '%q' AND type!='meta' "
+          "ORDER BY type DESC, name",
+          dump_callback, p, &zErrMsg, azArg[i]
+        );
         
       }
     }
@@ -507,14 +511,15 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
   if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
     struct callback_data data;
     char *zErrMsg = 0;
-    char zSql[1000];
     memcpy(&data, p, sizeof(data));
     data.showHeader = 0;
     data.mode = MODE_List;
-    sprintf(zSql, "SELECT name FROM sqlite_master "
-                  "WHERE type='index' AND tbl_name LIKE '%.800s' "
-                  "ORDER BY name", azArg[1]);
-    sqlite_exec(db, zSql, callback, &data, &zErrMsg);
+    sqlite_exec_printf(db, 
+      "SELECT name FROM sqlite_master "
+      "WHERE type='index' AND tbl_name LIKE '%q' "
+      "ORDER BY name",
+      callback, &data, &zErrMsg, azArg[1]
+    );
     if( zErrMsg ){
       fprintf(stderr,"Error: %s\n", zErrMsg);
       free(zErrMsg);
@@ -559,21 +564,37 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
   if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
     struct callback_data data;
     char *zErrMsg = 0;
-    char zSql[1000];
     memcpy(&data, p, sizeof(data));
     data.showHeader = 0;
     data.mode = MODE_Semi;
     if( nArg>1 ){
-      sprintf(zSql, "SELECT sql FROM sqlite_master "
-                    "WHERE tbl_name LIKE '%.800s' AND type!='meta'"
-                    "ORDER BY type DESC, name",
-         azArg[1]);
+      if( sqliteStrICmp(azArg[1],"sqlite_master")==0 ){
+        char *new_argv[2], *new_colv[2];
+        new_argv[0] = "CREATE TABLE sqlite_master (\n"
+                      "  type text,\n"
+                      "  name text,\n"
+                      "  tbl_name text,\n"
+                      "  sql text\n"
+                      ")";
+        new_argv[1] = 0;
+        new_colv[0] = "sql";
+        new_colv[1] = 0;
+        callback(&data, 1, new_argv, new_colv);
+      }else{
+        sqlite_exec_printf(db,
+          "SELECT sql FROM sqlite_master "
+          "WHERE tbl_name LIKE '%q' AND type!='meta'"
+          "ORDER BY type DESC, name",
+          callback, &data, &zErrMsg, azArg[1]);
+      }
     }else{
-      sprintf(zSql, "SELECT sql FROM sqlite_master "
+      sqlite_exec(db,
+         "SELECT sql FROM sqlite_master "
          "WHERE type!='meta' "
-         "ORDER BY tbl_name, type DESC, name");
+         "ORDER BY tbl_name, type DESC, name",
+         callback, &data, &zErrMsg
+      );
     }
-    sqlite_exec(db, zSql, callback, &data, &zErrMsg);
     if( zErrMsg ){
       fprintf(stderr,"Error: %s\n", zErrMsg);
       free(zErrMsg);
@@ -588,19 +609,21 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
     char **azResult;
     int nRow, rc;
     char *zErrMsg;
-    char zSql[1000];
     if( nArg==1 ){
-      sprintf(zSql,
+      rc = sqlite_get_table(db,
         "SELECT name FROM sqlite_master "
         "WHERE type='table' "
-        "ORDER BY name");
+        "ORDER BY name",
+        &azResult, &nRow, 0, &zErrMsg
+      );
     }else{
-      sprintf(zSql,
+      rc = sqlite_get_table_printf(db,
         "SELECT name FROM sqlite_master "
-        "WHERE type='table' AND name LIKE '%%%.100s%%' "
-        "ORDER BY name", azArg[1]);
+        "WHERE type='table' AND name LIKE '%%%q%%' "
+        "ORDER BY name",
+        &azResult, &nRow, 0, &zErrMsg, azArg[1]
+      );
     }
-    rc = sqlite_get_table(db, zSql, &azResult, &nRow, 0, &zErrMsg);
     if( zErrMsg ){
       fprintf(stderr,"Error: %s\n", zErrMsg);
       free(zErrMsg);
index 9b06b21525b339bd932c8d1c27a7e669760597e4..3612f11e5f675bf509a442a30a9473bee05889ca 100644 (file)
 ** This header file defines the interface that the sqlite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.3 2000/09/29 13:30:55 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.4 2000/10/08 22:20:58 drh Exp $
 */
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
+#include <stdarg.h>     /* Needed for the definition of va_list */
 
 /*
 ** The version of the SQLite library.
@@ -187,12 +188,39 @@ void sqlite_busy_timeout(sqlite*, int ms);
 ** Instead of invoking a user-supplied callback for each row of the
 ** result, this routine remembers each row of the result in memory
 ** obtained from malloc(), then returns all of the result after the
-** query has finished.  After the calling function has finished using
-** the result, it should pass the result data pointer to 
-** sqlite_free_table() in order to release the memory that was malloc-ed.
-** Because of the way the malloc() happens, the calling function must
-** not try to call malloc() directly.  Only sqlite_free_table() is able
-** to release the memory properly and safely.
+** query has finished. 
+**
+** As an example, suppose the query result where this table:
+**
+**        Name        | Age
+**        -----------------------
+**        Alice       | 43
+**        Bob         | 28
+**        Cindy       | 21
+**
+** If the 3rd argument were &azResult then after the function returns
+** azResult would contain the following data:
+**
+**        azResult[0] = "Name";
+**        azResult[1] = "Age";
+**        azResult[2] = "Alice";
+**        azResult[3] = "43";
+**        azResult[4] = "Bob";
+**        azResult[5] = "28";
+**        azResult[6] = "Cindy";
+**        azResult[7] = "21";
+**
+** Notice that there is an extra row of data containing the column
+** headers.  But the *nrow return value is still 3.  *ncolumn is
+** set to 2.  In general, the number of values inserted into azResult
+** will be ((*nrow) + 1)*(*ncolumn).
+**
+** After the calling function has finished using the result, it should 
+** pass the result data pointer to sqlite_free_table() in order to 
+** release the memory that was malloc-ed.  Because of the way the 
+** malloc() happens, the calling function must not try to call 
+** malloc() directly.  Only sqlite_free_table() is able to release 
+** the memory properly and safely.
 **
 ** The return value of this routine is the same as from sqlite_exec().
 */
@@ -210,5 +238,78 @@ int sqlite_get_table(
 */
 void sqlite_free_table(char **result);
 
+/*
+** The following routines are wrappers around sqlite_exec() and
+** sqlite_get_table().  The only difference between the routine that
+** follow and the originals is that the second argument to the 
+** routines that follow is really a printf()-style format
+** string describing the SQL to be executed.  Arguments to the format
+** string appear at the end of the argument list.
+**
+** All of the usual printf formatting options apply.  In addition, there
+** is a "%q" option.  %q works like %s in that it substitutes a null-terminated
+** string from the argument list.  But %q also double every '\'' character.
+** %q is designed for use inside a string literal.  By doubling each '\''
+** character is escapes that character and allows it to be inserted into
+** the string.
+**
+** For example, so some string variable contains text as follows:
+**
+**      char *zText = "It's a happy day!";
+**
+** We can use this text in an SQL statement as follows:
+**
+**      sqlite_exec_printf(db, "INSERT INTO table VALUES('%q')",
+**          callback1, 0, 0, zText);
+**
+** Because the %q format string is used, the '\'' character in zText
+** is escaped and the SQL generated is as follows:
+**
+**      INSERT INTO table1 VALUES('It''s a happy day!')
+**
+** This is correct.  Had we used %s instead of %q, the generated SQL
+** would have looked like this:
+**
+**      INSERT INTO table1 VALUES('It's a happy day!');
+**
+** This second example is an SQL syntax error.  As a general rule you
+** should always use %q instead of %s when inserting text into a string 
+** literal.
+*/
+int sqlite_exec_printf(
+  sqlite*,                      /* An open database */
+  char *sqlFormat,              /* printf-style format string for the SQL */
+  sqlite_callback,              /* Callback function */
+  void *,                       /* 1st argument to callback function */
+  char **errmsg,                /* Error msg written here */
+  ...                           /* Arguments to the format string. */
+);
+int sqlite_exec_vprintf(
+  sqlite*,                      /* An open database */
+  char *sqlFormat,              /* printf-style format string for the SQL */
+  sqlite_callback,              /* Callback function */
+  void *,                       /* 1st argument to callback function */
+  char **errmsg,                /* Error msg written here */
+  va_list ap                    /* Arguments to the format string. */
+);
+int sqlite_get_table_printf(
+  sqlite*,               /* An open database */
+  char *sqlFormat,       /* printf-style format string for the SQL */
+  char ***resultp,       /* Result written to a char *[]  that this points to */
+  int *nrow,             /* Number of result rows written here */
+  int *ncolumn,          /* Number of result columns written here */
+  char **errmsg,         /* Error msg written here */
+  ...                    /* Arguments to the format string */
+);
+int sqlite_get_table_vprintf(
+  sqlite*,               /* An open database */
+  char *sqlFormat,       /* printf-style format string for the SQL */
+  char ***resultp,       /* Result written to a char *[]  that this points to */
+  int *nrow,             /* Number of result rows written here */
+  int *ncolumn,          /* Number of result columns written here */
+  char **errmsg,         /* Error msg written here */
+  va_list ap             /* Arguments to the format string */
+);
+
 
 #endif /* _SQLITE_H_ */
index 108214cdc4e96f26ca42799008db927e034f38c1..a33bded4c301560ba5a2787ed5c5db123f6963c7 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the sqlite.html file.
 #
-set rcsid {$Id: c_interface.tcl,v 1.8 2000/09/29 13:30:55 drh Exp $}
+set rcsid {$Id: c_interface.tcl,v 1.9 2000/10/08 22:20:58 drh Exp $}
 
 puts {<html>
 <head>
@@ -22,10 +22,9 @@ programming interface.</p>
 
 <h2>The API</h2>
 
-<p>The interface to the SQLite library consists of eight functions
-(only the first three of which are required),
-one opaque data structure, and some constants used as return
-values from sqlite_exec():</p>
+<p>The interface to the SQLite library consists of three core functions,
+one opaque data structure, and some constants used as return values.
+The core interface is as follows:</p>
 
 <blockquote><pre>
 typedef struct sqlite sqlite;
@@ -42,6 +41,22 @@ int sqlite_exec(
   char **errmsg
 );
 
+#define SQLITE_OK        0    /* Successful result */
+#define SQLITE_INTERNAL  1    /* An internal logic error in SQLite */
+#define SQLITE_ERROR     2    /* SQL error or missing database */
+#define SQLITE_PERM      3    /* Access permission denied */
+#define SQLITE_ABORT     4    /* Callback routine requested an abort */
+#define SQLITE_BUSY      5    /* One or more database files are locked */
+#define SQLITE_NOMEM     6    /* A malloc() failed */
+#define SQLITE_READONLY  7    /* Attempt to write a readonly database */
+</pre></blockquote>
+
+<p>Only the three core routines shown above are required to use
+SQLite.  But there are many other functions that provide 
+useful interfaces.  These extended routines are as follows:
+</p>
+
+<blockquote><pre>
 int sqlite_get_table(
   sqlite*,
   char *sql,
@@ -61,14 +76,44 @@ void sqlite_busy_timeout(sqlite*, int ms);
 
 const char sqlite_version[];
 
-#define SQLITE_OK        0    /* Successful result */
-#define SQLITE_INTERNAL  1    /* An internal logic error in SQLite */
-#define SQLITE_ERROR     2    /* SQL error or missing database */
-#define SQLITE_PERM      3    /* Access permission denied */
-#define SQLITE_ABORT     4    /* Callback routine requested an abort */
-#define SQLITE_BUSY      5    /* One or more database files are locked */
-#define SQLITE_NOMEM     6    /* A malloc() failed */
-#define SQLITE_READONLY  7    /* Attempt to write a readonly database */
+int sqlite_exec_printf(
+  sqlite*,
+  char *sql,
+  int (*)(void*,int,char**,char**),
+  void*,
+  char **errmsg,
+  ...
+);
+
+int sqlite_exec_vprintf(
+  sqlite*,
+  char *sql,
+  int (*)(void*,int,char**,char**),
+  void*,
+  char **errmsg,
+  va_list
+);
+
+int sqlite_get_table_printf(
+  sqlite*,
+  char *sql,
+  char ***result,
+  int *nrow,
+  int *ncolumn,
+  char **errmsg,
+  ...
+);
+
+int sqlite_get_table_vprintf(
+  sqlite*,
+  char *sql,
+  char ***result,
+  int *nrow,
+  int *ncolumn,
+  char **errmsg,
+  va_list
+);
+
 </pre></blockquote>
 
 <p>All of the above definitions are included in the "sqlite.h"
@@ -304,7 +349,7 @@ the SQLITE_VERSION macro against the <b>sqlite_version</b>
 string constant to verify that the version number of the
 header file and the library match.</p> 
 
-<h2>Changing the libraries reponse to locked files</h2>
+<h2>Changing the libraries response to locked files</h2>
 
 <p>The GDBM library supports database locks at the file level.
 If a GDBM database file is opened for reading, then that same
@@ -345,6 +390,85 @@ will wait for the lock to clear for at least the number of milliseconds
 specified before it returns SQLITE_BUSY.  Specifying zero milliseconds for
 the timeout restores the default behavior.</p>
 
+<h2>Using the <tt>_printf()</tt> wrapper functions</h2>
+
+<p>The four utility functions</p>
+
+<p>
+<ul>
+<li><b>sqlite_exec_printf()</b></li>
+<li><b>sqlite_exec_vprintf()</b></li>
+<li><b>sqlite_get_table_printf()</b></li>
+<li><b>sqlite_get_table_vprintf()</b></li>
+</ul>
+</p>
+
+<p>implement the same query functionality as <b>sqlite_exec()</b>
+and <b>sqlite_get_table()</b>.  But instead of taking a complete
+SQL statement as their second argument, the four <b>_printf</b>
+routines take a printf-style format string.  The SQL statement to
+be executed is generated from this format string and from whatever
+additional arguments are attached to the end of the function call.</p>
+
+<p>There are two advantages to using the SQLite printf
+functions instead of <b>sprintf()</b>.  First of all, with the
+SQLite printf routines, there is never a danger of overflowing a
+static buffer as there is with <b>sprintf()</b>.  The SQLite
+printf routines automatically allocate (and later free)
+as much memory as is 
+necessary to hold the SQL statements generated.</p>
+
+<p>The second advantage the SQLite printf routines have over
+<b>sprintf()</b> is a new formatting option specifically designed
+to support string literals in SQL.  Within the format string,
+the %q formatting option works very much like %s in that it
+reads a null-terminated string from the argument list and inserts
+it into the result.  But %q translates the inserted string by
+making two copies of every single-quote (') character in the
+substituted string.  This has the effect of escaping the end-of-string
+meaning of single-quote within a string literal.
+</p>
+
+<p>Consider an example.  Suppose you are trying to insert a string
+values into a database table where the string value was obtained from
+user input.  Suppose the string to be inserted is stored in a variable
+named zString.  The code to insert this string might look like this:</p>
+
+<blockquote><pre>
+sqlite_exec_printf(db,
+  "INSERT INTO table1 VALUES('%s')",
+  0, 0, 0, zString);
+</pre></blockquote>
+
+<p>If the zString variable holds text like "Hello", then this statement
+will work just fine.  But suppose the user enters a string like 
+"Hi y'all!".  The SQL statement generated reads as follows:
+
+<blockquote><pre>
+INSERT INTO table1 VALUES('Hi y'all')
+</pre></blockquote>
+
+<p>This is not valid SQL because of the apostrophy in the word "y'all".
+But if the %q formatting option is used instead of %s, like this:</p>
+
+<blockquote><pre>
+sqlite_exec_printf(db,
+  "INSERT INTO table1 VALUES('%q')",
+  0, 0, 0, zString);
+</pre></blockquote>
+
+<p>Then the generated SQL will look like the following:</p>
+
+<blockquote><pre>
+INSERT INTO table1 VALUES('Hi y''all')
+</pre></blockquote>
+
+<p>Here the apostrophy has been escaped and the SQL statement is well-formed.
+When generating SQL on-the-fly from data that might contain a
+single-quote character ('), it is always a good idea to use the
+SQLite printf routines and the %q formatting option instead of <b>sprintf</b>.
+</p>
+
 <h2>Usage Examples</h2>
 
 <p>For examples of how the SQLite C/C++ interface can be used,
index f2028309e9117545425c7f7ab346de4eea8e40e4..bd1b0860d7fb47d8d7237352ca371e0cb22b02e2 100644 (file)
@@ -17,6 +17,14 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chng {2000 Oct 8 (1.0.9)} {
+<li>Added the <b>sqlite_..._printf()</b> interface routines.</li>
+<li>Modified the <b>sqlite</b> shell program to use the new interface 
+    routines.</li>
+<li>Modified the <b>sqlite</b> shell program to print the schema for
+    the built-in SQLITE_MASTER table, if explicitly requested.</li>
+}
+
 chng {2000 Sep 30 (1.0.8)} {
 <li>Begin writing documentation on the TCL interface.</li>
 }
index 5c575b48f98aa222df4ed23d2f46c2994bd0d1d1..e3c7c2a1c31d1ad87241441b01e81d18daeba7b0 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the tclsqlite.html file.
 #
-set rcsid {$Id: tclsqlite.tcl,v 1.1 2000/09/30 22:46:07 drh Exp $}
+set rcsid {$Id: tclsqlite.tcl,v 1.2 2000/10/08 22:20:58 drh Exp $}
 
 puts {<html>
 <head>
@@ -108,7 +108,7 @@ db1 eval {CREATE TABLE t1(a int, b text)}</b>
 </blockquote>
 
 <p>The above code creates a new table named <b>t1</b> with columns
-<b>a</b> and <b>b</b>.  What could be simplier?</p>
+<b>a</b> and <b>b</b>.  What could be simpler?</p>
 
 <p>Query results are returned as a list of column values.  If a
 query requests 2 columns and there are 3 rows matching the query,
@@ -186,14 +186,58 @@ a=3 b=howdy!</b>
 </blockquote>
 
 <h2>The "complete" method</h2>
-<i>TBD</i>
+
+<p>
+The "complete" method takes a string of supposed SQL as its only argument.
+It returns TRUE if the string is a complete statement of SQL and FALSE if
+there is more to be entered.</p>
+
+<p>The "complete" method is useful when building interactive applications
+in order to know when the user has finished entering a line of SQL code.
+This is really just an interface to the <b>sqlite_complete()</b> C
+function.  Refer to the <a href="c_interface.html">C/C++ interface</a>
+specification for additional information.</p>
 
 <h2>The "timeout" method</h2>
-<i>TBD</i>
+
+<p>The "timeout" method is used to control how long the SQLite library
+will wait for locks to clear before giving up on a database transaction.
+The default timeout is 0 millisecond.  (In other words, the default behavior
+is not to wait at all.)</p>
+
+<p>The GDBM library the underlies SQLite allows multiple simultaneous
+readers or a single writer but not both.  If any process is writing to
+the database no other process is allows to read or write.  If any process
+is reading the database other processes are allowed to read but not write.
+Each GDBM file is locked separately.  Because each SQL table is stored as
+a separate file, it is possible for different processes to write to different
+database tables at the same time, just not the same table.</p>
+
+<p>When SQLite tries to open a GDBM file and finds that it is locked, it
+can optionally delay for a short while and try to open the file again.
+This process repeats until the query times out and SQLite returns a
+failure.  The timeout is adjustable.  It is set to 0 by default so that
+if a GDBM file is locked, the SQL statement fails immediately.  But you
+can use the "timeout" method to change the timeout value to a positive
+number.  For example:</p>
+
+<blockquote><b>db1 timeout 2000</b></blockquote>
+
+<p>The argument to the timeout method is the maximum number of milliseconds
+to wait for the lock to clear.  So in the example above, the maximum delay
+would be 2 seconds.</p>
 
 <h2>The "busy" method</h2>
-<i>TBD</i>
 
+<p>The "busy" method, like "timeout", only comes into play when a GDBM
+file is locked.  But the "busy" method gives the programmer much more
+control over what action to take.  The "busy" method specifies a callback
+Tcl procedure that is invoked whenever SQLite tries to open a locked
+GDBM file.  This callback can do whatever is desired.  Presumably, the
+callback will do some other useful work for a short while then return
+so that the lock can be tried again.  The callback procedure should
+return "0" if it wants SQLite to try again to open the GDBM file and
+should return "1" if it wants SQLite to abandon the current operation.
 
 }