Check-in [929d28e358]
Not logged in
Overview

SHA1 Hash:929d28e358248b19de0186fe0e82519ad124d26f
Date: 2007-11-05 02:42:58
User: drh
Comment:Added the "e" capability for viewing ticket submitter email addresses. Additional tinkering toward the design of tickets. This check-in is only thinly tested.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/login.c from [f15d2f5703] to [457f0114fe].

@@ -291,11 +291,11 @@
     switch( zCap[i] ){
       case 's':   g.okSetup = 1;
       case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt = g.okQuery =
                               g.okRdWiki = g.okWrWiki = g.okNewWiki =
                               g.okApndWiki = g.okHistory = g.okClone =
-                              g.okNewTkt = g.okPassword = 1;
+                              g.okNewTkt = g.okPassword = g.okRdAddr = 1;
       case 'i':   g.okRead = g.okWrite = 1;                     break;
       case 'o':   g.okRead = 1;                                 break;
 
       case 'd':   g.okDelete = 1;                               break;
       case 'h':   g.okHistory = 1;                              break;
@@ -306,17 +306,53 @@
       case 'j':   g.okRdWiki = 1;                               break;
       case 'k':   g.okWrWiki = g.okRdWiki = g.okApndWiki =1;    break;
       case 'm':   g.okApndWiki = 1;                             break;
       case 'f':   g.okNewWiki = 1;                              break;
 
+      case 'e':   g.okRdAddr = 1;                               break;
       case 'r':   g.okRdTkt = 1;                                break;
       case 'n':   g.okNewTkt = 1;                               break;
       case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt =
                   g.okApndTkt = 1;                              break;
       case 'c':   g.okApndTkt = 1;                              break;
     }
   }
+}
+
+/*
+** If the current login lacks any of the capabilities listed in
+** the input, then return 0.  If all capabilities are present, then
+** return 1.
+*/
+int login_has_capability(const char *zCap, int nCap){
+  int i;
+  int rc = 1;
+  if( nCap<0 ) nCap = strlen(zCap);
+  for(i=0; i<nCap && rc && zCap[i]; i++){
+    switch( zCap[i] ){
+      case 'a':  rc = g.okAdmin;     break;
+      case 'c':  rc = g.okApndTkt;   break;
+      case 'd':  rc = g.okDelete;    break;
+      case 'e':  rc = g.okRdAddr;    break;
+      case 'f':  rc = g.okNewWiki;   break;
+      case 'g':  rc = g.okClone;     break;
+      case 'h':  rc = g.okHistory;   break;
+      case 'i':  rc = g.okWrite;     break;
+      case 'j':  rc = g.okRdWiki;    break;
+      case 'k':  rc = g.okWrWiki;    break;
+      case 'm':  rc = g.okApndWiki;  break;
+      case 'n':  rc = g.okNewTkt;    break;
+      case 'o':  rc = g.okRead;      break;
+      case 'p':  rc = g.okPassword;  break;
+      case 'q':  rc = g.okQuery;     break;
+      case 'r':  rc = g.okRdTkt;     break;
+      case 's':  rc = g.okSetup;     break;
+      case 'w':  rc = g.okWrTkt;     break;
+      default:   rc = 0;             break;
+    }
+  }
+  return rc;
 }
 
 /*
 ** Call this routine when the credential check fails.  It causes
 ** a redirect to the "login" page.

Modified src/main.c from [5992525fce] to [cfe3d68152].

@@ -103,10 +103,11 @@
   int okWrWiki;           /* k: edit wiki via web */
   int okRdTkt;            /* r: view tickets via web */
   int okNewTkt;           /* n: create new tickets */
   int okApndTkt;          /* c: append to tickets via the web */
   int okWrTkt;            /* w: make changes to tickets via web */
+  int okRdAddr;           /* e: read email addresses on tickets */
 
   FILE *fDebug;           /* Write debug information here, if the file exists */
 };
 
 /*

Modified src/sample-config1.txt from [17dcdcf216] to [54c5c82c83].

@@ -79,49 +79,88 @@
   <td>After filling in the information above, press this button to create
   the new ticket</td>
   </tr>
   </table>
   [/status /Open default_value]
-} /new_template set
+} /new setpage
 ######################################################################
 {
+  [
+     # Extract the current information from the ticket table
+     {SELECT type, status, subsystem, priority,
+             severity, contact, title, comment
+        FROM ticket
+       WHERE tktid=:name} db_prepare
+     /name {} cgi_parameter {:name} db_bind
+     db_exec
+     if /title exists not {
+        {<i>No such ticket: } puts
+        /name {} cgi_parameter htmlize puts
+        {</i>} puts
+        return
+     } endif
+     /title title cgiparam /vtitle store
+     /status status cgiparam /vstatus store
+     /type type cgiparam /vtype store
+
+     if /submit cgiexists {
+       /name {} cgi_parameter login ticketchng_begin
+       if /apndcom cgiexists {
+         {+comment}
+         {<hr><i>Added by }
+         login htmlize concat
+         { on } concat
+         datetime concat
+         {:</i><br} concat
+         /apndcom {} cgi_parameter htmlize concat
+         ticketchng_field
+       } else {
+         if vcomment comment eq not {/comment vcomment ticketchng_field} endif
+       } endif
+       if vtitle title eq not {/title vtitle ticketchng_field} endif
+       ticketchng_submit
+       baseurl /tktview?name= concat /name {} cgi_parameter concat redirect
+     } endif
+  ]
+  <form method="POST" action="[baseurl]/tktedit">
   <table cellpadding="5">
   <tr><td align="right">Title:</td><td>
-  [/title 60 textedit]
+  <input type="text" name="title" value="[vtitle htmlize puts] size=60">
   </td></tr>
   <tr><td align="right">Status:</td><td>
-  [/status status_choices 20 combobox]
+  [vstatus /status status_choices 20 combobox]
   </td></tr>
   <tr><td align="right">Type:</td><td>
-  [/type type_choices 20 combobox]
+  [vtype /type type_choices 20 combobox]
   </td></tr>
   <tr><td align="right">Severity:</td><td>
-  [/severity {High Medium Low} 10 combobox]
+  [vseverity /severity {High Medium Low} 10 combobox]
   </td></tr>
   <tr><td align="right">Priority:</td><td>
-  [/priority {High Medium Low} 10 combobox]
+  [vpriority /priority {High Medium Low} 10 combobox]
   </td></tr>
   <tr><td align="right">Resolution:</td><td>
-  [/resolution resolution_choices 20 combobox]
+  [vresolution /resolution resolution_choices 20 combobox]
   </td></tr>
   <tr><td align="right">Subsystem:</td><td>
-  [/subsystem subsystem_choices 30 combobox]
-  </td></tr>
-  [is_anon not enable_output]
+  [vsubsystem /subsystem subsystem_choices 30 combobox]
+  </td></tr>
+  [{e} hascap enable_output]
     <tr><td align="right">Contact:</td><td>
-    [/contact 40 textedit]
+    <input type="text" name="contact" size="40" value="[vcontact htmlize puts]">
     </td></tr>
   [1 enable_output]
   <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
-  [/foundin 50 textedit]
+  <input type="text" name="foundin" size="50" value="[foundin htmlize puts]">
   </td></tr>
   <tr><td colspan="2">
-  [ok_wrtkt /eall 0 paramget and /eall store]
+  [w hascap /eall 0 paramget and /eall store]
   [eall enable_output]
     Description And Comments:<br>
-    [/comment 70 /comment linecount 15 max multilineedit]<br>
-    [/aonly {Append Remark} auxbutton]
+    <textarea name="comment" cols="80" rows="[comment linecount 15 max 10 min]"
+     wrap="virtual" class="wikiedit">[comment htmlize puts]</textarea><br>
+    <input type="submit" name="aonly" value="Append Remark">
   [eall not enable_output]
     Append Remark:<br>
     [/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br>
     [ok_wrtkt enable_output /eall {Edit All} auxbutton]
   [1 enable_output]
@@ -128,16 +167,16 @@
   </td></tr>
   <tr><td align="right"></td><td>
   [{Submit Changes} submitbutton]
   </td></tr>
   </table>
-} /edit_template set
+} /edit setpage
 ######################################################################
 {
   <table cellpadding="5">
   <tr><td align="right">Title:</td><td>
-  [/title textview]
+  [/title htmlize]
   </td></tr>
   <tr><td align="right">Status:</td><td>
   [/status textview]
   </td></tr>
   <tr><td align="right">Type:</td><td>
@@ -153,11 +192,11 @@
   [/priority textview]
   </td></tr>
   <tr><td align="right">Subsystem:</td><td>
   [/subsystem textview]
   </td></tr>
-  [is_anon not enable_output]
+  [{e} hascap enable_output]
     <tr><td align="right">Contact:</td><td>
     [/contact textview]
     </td></tr>
   [1 enable_output]
   <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
@@ -166,11 +205,11 @@
   <tr><td colspan="2">
   Description And Comments:<br>
   [/comment wikiview]
   </td></tr>
   </table>
-} /view_template set
+} /view setpage
 
 ##############
 # Verb list:
 #
 # CNEV

Modified src/setup.c from [44eafbc596] to [24c8846279].

@@ -126,10 +126,11 @@
   @ <li><p>The permission flags are as follows:</p>
   @ <ol type="a">
   @ <li value="1"><b>Admin</b>: Create and delete users</li>
   @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
   @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
+  @ <li value="5"><b>Email</b>: View EMail addresses on tickets</li>
   @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
   @ <li value="7"><b>Clone</b>: Clone the repository</li>
   @ <li value="8"><b>History</b>: View detail repository history</li>
   @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
   @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
@@ -160,12 +161,12 @@
 /*
 ** WEBPAGE: /setup_uedit
 */
 void user_edit(void){
   const char *zId, *zLogin, *zInfo, *zCap;
-  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ;
-  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag;
+  char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
+  char *oak, *oad, *oaq, *oac, *oaf, *oam, *oah, *oag, *oae;
   int doWrite;
   int uid;
   int higherUser = 0;  /* True if user being edited is SETUP and the */
                        /* user doing the editing is ADMIN.  Disallow editing */
 
@@ -200,10 +201,11 @@
     const char *zLogin;
     char zCap[30];
     int i = 0;
     int aa = P("aa")!=0;
     int ad = P("ad")!=0;
+    int ae = P("ae")!=0;
     int ai = P("ai")!=0;
     int aj = P("aj")!=0;
     int ak = P("ak")!=0;
     int an = P("an")!=0;
     int ao = P("ao")!=0;
@@ -218,10 +220,11 @@
     int ah = P("ah")!=0;
     int ag = P("ag")!=0;
     if( aa ){ zCap[i++] = 'a'; }
     if( ac ){ zCap[i++] = 'c'; }
     if( ad ){ zCap[i++] = 'd'; }
+    if( ae ){ zCap[i++] = 'e'; }
     if( af ){ zCap[i++] = 'f'; }
     if( ah ){ zCap[i++] = 'h'; }
     if( ag ){ zCap[i++] = 'g'; }
     if( ai ){ zCap[i++] = 'i'; }
     if( aj ){ zCap[i++] = 'j'; }
@@ -264,19 +267,20 @@
   /* Load the existing information about the user, if any
   */
   zLogin = "";
   zInfo = "";
   zCap = "";
-  oaa = oac = oad = oaf = oag = oah = oai = oaj = oak = oam =
+  oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
         oan = oao = oap = oaq = oar = oas = oaw = "";
   if( uid ){
     zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
     zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
     zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
     if( strchr(zCap, 'a') ) oaa = " checked";
     if( strchr(zCap, 'c') ) oac = " checked";
     if( strchr(zCap, 'd') ) oad = " checked";
+    if( strchr(zCap, 'e') ) oae = " checked";
     if( strchr(zCap, 'f') ) oaf = " checked";
     if( strchr(zCap, 'g') ) oag = " checked";
     if( strchr(zCap, 'h') ) oah = " checked";
     if( strchr(zCap, 'i') ) oai = " checked";
     if( strchr(zCap, 'j') ) oaj = " checked";
@@ -324,10 +328,11 @@
   if( g.okSetup ){
     @     <input type="checkbox" name="as"%s(oas)>Setup</input><br>
   }
   @     <input type="checkbox" name="aa"%s(oaa)>Admin</input><br>
   @     <input type="checkbox" name="ad"%s(oad)>Delete</input><br>
+  @     <input type="checkbox" name="ae"%s(oad)>Email</input><br>
   @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
   @     <input type="checkbox" name="aq"%s(oaq)>Query</input><br>
   @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
   @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
   @     <input type="checkbox" name="ah"%s(oah)>History</input><br>

Modified src/subscript.c from [a19913b6c5] to [3df34dd953].

@@ -82,10 +82,63 @@
 #define SBSTT_INCOMPLETE  6    /* Unterminated string token */
 #define SBSTT_UNKNOWN     7    /* Unknown token */
 #define SBSTT_EOF         8    /* End of input */
 
 /*
+** Values are stored in the hash table as instances of the following
+** structure.
+*/
+typedef struct SbSValue SbSValue;
+struct SbSValue {
+  int flags;        /* Bitmask of SBSVAL_* values */
+  union {
+    struct {
+      int size;        /* Number of bytes in string, not counting final zero */
+      char *z;         /* Pointer to string content */
+    } str;          /* Value if SBSVAL_STR */
+    struct {
+      int (*xVerb)(Subscript*, void*);     /* Function to do the work */
+      void *pArg;                          /* 2nd parameter to xVerb */
+    } verb;         /* Value if SBSVAL_VERB */
+  } u;
+};
+#define SBSVAL_VERB    0x0001      /* Value stored in u.verb */
+#define SBSVAL_STR     0x0002      /* Value stored in u.str */
+#define SBSVAL_DYN     0x0004      /* u.str.z is dynamically allocated */
+#define SBSVAL_EXEC    0x0008      /* u.str.z is a script */
+
+/*
+** An entry in the hash table is an instance of this structure.
+*/
+typedef struct SbsHashEntry SbsHashEntry;
+struct SbsHashEntry {
+  SbsHashEntry *pNext;     /* Next entry with the same hash on zKey */
+  SbSValue val;            /* The payload */
+  int nKey;               /* Length of the key */
+  char zKey[0];           /* The key */
+};
+
+/*
+** A hash table is an instance of the following structure.
+*/
+typedef struct SbsHashTab SbsHashTab;
+struct SbsHashTab {
+  SbsHashEntry *aHash[SBSCONFIG_NHASH];  /* The hash table */
+};
+
+/*
+** An instance of the Subscript interpreter
+*/
+struct Subscript {
+  int nStack;                      /* Number of entries on stack */
+  SbsHashTab symTab;                /* The symbol table */
+  char zErrMsg[SBSCONFIG_ERRSIZE];  /* Space to write an error message */
+  SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
+};
+
+
+/*
 ** Given an input string z of length n, identify the token that
 ** starts at z[0].  Write the token type into *pTokenType and
 ** return the length of the token.
 */
 static int sbs_next_token(const char *z, int n, int *pTokenType){
@@ -150,33 +203,10 @@
   return 1;
 }
 
 
 /*
-** Values are stored in the hash table as instances of the following
-** structure.
-*/
-typedef struct SbSValue SbSValue;
-struct SbSValue {
-  int flags;        /* Bitmask of SBSVAL_* values */
-  union {
-    struct {
-      int size;        /* Number of bytes in string, not counting final zero */
-      char *z;         /* Pointer to string content */
-    } str;          /* Value if SBSVAL_STR */
-    struct {
-      int (*xVerb)(Subscript*, void*);     /* Function to do the work */
-      void *pArg;                          /* 2nd parameter to xVerb */
-    } verb;         /* Value if SBSVAL_VERB */
-  } u;
-};
-#define SBSVAL_VERB    0x0001      /* Value stored in u.verb */
-#define SBSVAL_STR     0x0002      /* Value stored in u.str */
-#define SBSVAL_DYN     0x0004      /* u.str.z is dynamically allocated */
-#define SBSVAL_EXEC    0x0008      /* u.str.z is a script */
-
-/*
 ** Release any memory allocated by a value.
 */
 static void sbs_value_reset(SbSValue *p){
   if( p->flags & SBSVAL_DYN ){
     free(p->u.str.z);
@@ -183,30 +213,10 @@
     p->flags = SBSVAL_STR;
     p->u.str.z = "";
     p->u.str.size = 0;
   }
 }
-
-
-/*
-** An entry in the hash table is an instance of this structure.
-*/
-typedef struct SbsHashEntry SbsHashEntry;
-struct SbsHashEntry {
-  SbsHashEntry *pNext;     /* Next entry with the same hash on zKey */
-  SbSValue val;            /* The payload */
-  int nKey;               /* Length of the key */
-  char zKey[0];           /* The key */
-};
-
-/*
-** A hash table is an instance of the following structure.
-*/
-typedef struct SbsHashTab SbsHashTab;
-struct SbsHashTab {
-  SbsHashEntry *aHash[SBSCONFIG_NHASH];  /* The hash table */
-};
 
 /*
 ** Compute a hash on a string.
 */
 static int sbs_hash(const char *z, int n){
@@ -293,29 +303,32 @@
   }
   memset(pHash, 0, sizeof(*pHash));
 }
 
 /*
-** An instance of the Subscript interpreter
-*/
-struct Subscript {
-  int nStack;                      /* Number of entries on stack */
-  SbsHashTab symTab;                /* The symbol table */
-  char zErrMsg[SBSCONFIG_ERRSIZE];  /* Space to write an error message */
-  SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
-};
-
-/*
 ** Push a value onto the stack of an interpreter
 */
 static int sbs_push(Subscript *p, SbSValue *pVal){
   if( p->nStack>=SBSCONFIG_NSTACK ){
     sqlite3_snprintf(SBSCONFIG_ERRSIZE, p->zErrMsg, "stack overflow");
     return SBS_ERROR;
   }
   p->aStack[p->nStack++] = *pVal;
   return SBS_OK;
+}
+
+/*
+** Create a new subscript interpreter.  Return a pointer to the
+** new interpreter, or return NULL if malloc fails.
+*/
+struct Subscript *SbS_Create(void){
+  Subscript *p;
+  p = malloc( sizeof(*p) );
+  if( p ){
+    memset(p, 0, sizeof(*p));
+  }
+  return p;
 }
 
 /*
 ** Destroy an subscript interpreter
 */
@@ -570,17 +583,32 @@
     case SBSOP_ADD:  c = a+b;            break;
     case SBSOP_SUB:  c = a-b;            break;
     case SBSOP_MUL:  c = a*b;            break;
     case SBSOP_DIV:  c = b!=0 ? a/b : 0; break;
     case SBSOP_AND:  c = a && b;         break;
-    case SBSOP_OR:  c = a || b;          break;
+    case SBSOP_OR:   c = a || b;         break;
     case SBSOP_MIN:  c = a<b ? a : b;    break;
     case SBSOP_MAX:  c = a<b ? b : a;    break;
   }
   SbS_Pop(p, 2);
   SbS_PushInt(p, c);
   return 0;
+}
+
+/*
+** Subscript command:     STRING hascap INTEGER
+**
+** Return true if the user has all of the capabilities listed.
+*/
+static int hascapCmd(struct Subscript *p, void *pNotUsed){
+  const char *z;
+  int i, n, a;
+  if( SbS_RequireStack(p, 1, "hascap") ) return 1;
+  z = SbS_StackValue(p, 0, &n);
+  a = login_has_capability(z, n);
+  SbS_Pop(p, 1);
+  SbS_PushInt(p, a);
 }
 
 /*
 ** Subscript command:      STRING puts
 */
@@ -587,11 +615,17 @@
 static int putsCmd(struct Subscript *p, void *pNotUsed){
   int size;
   const char *z;
   if( SbS_RequireStack(p, 1, "puts") ) return 1;
   z = SbS_StackValue(p, 0, &size);
-  printf("%.*s\n", size, z);
+  if( g.cgiPanic ){
+    char *zCopy = mprintf("%.*s", size, z);
+    cgi_printf("%h", zCopy);
+    free(zCopy);
+  }else{
+    printf("%.*s\n", size, z);
+  }
   SbS_Pop(p, 1);
   return 0;
 }
 
 
@@ -598,53 +632,38 @@
 /*
 ** A table of built-in commands
 */
 static const struct {
   const char *zCmd;
-  int nCmd;
   int (*xCmd)(Subscript*,void*);
   void *pArg;
 } aBuiltin[] = {
-  { "add",   3,    bopCmd,  (void*)SBSOP_AND    },
-  { "and",   3,    bopCmd,  (void*)SBSOP_AND    },
-  { "div",   3,    bopCmd,  (void*)SBSOP_DIV    },
-  { "max",   3,    bopCmd,  (void*)SBSOP_MAX    },
-  { "min",   3,    bopCmd,  (void*)SBSOP_MIN    },
-  { "mul",   3,    bopCmd,  (void*)SBSOP_MUL    },
-  { "not",   3,    notCmd,  0                   },
-  { "or",    2,    bopCmd,  (void*)SBSOP_OR     },
-  { "puts",  4,    putsCmd, 0                   },
-  { "set",   3,    setCmd,  0                   },
-  { "sub",   3,    bopCmd,  (void*)SBSOP_SUB    },
-};
-
-/*
-** A table of built-in string and integer values
-*/
-static const struct {
-  const char *zVar;
-  int nVar;
-  int *pI;
-  char *z;
-} aVars[] = {
-  { "okAdmin", 7,  &g.okAdmin,  0 },
-  { "okSetup", 7,  &g.okSetup,  0 },
+  { "add",    bopCmd,    (void*)SBSOP_AND    },
+  { "and",    bopCmd,    (void*)SBSOP_AND    },
+  { "div",    bopCmd,    (void*)SBSOP_DIV    },
+  { "hascap", hascapCmd, 0                },
+  { "max",    bopCmd,    (void*)SBSOP_MAX    },
+  { "min",    bopCmd,    (void*)SBSOP_MIN    },
+  { "mul",    bopCmd,    (void*)SBSOP_MUL    },
+  { "not",    notCmd,    0                   },
+  { "or",     bopCmd,    (void*)SBSOP_OR     },
+  { "puts",   putsCmd,   0                   },
+  { "set",    setCmd,    0                   },
+  { "sub",    bopCmd,    (void*)SBSOP_SUB    },
 };
 
 
-
 /*
-** Create a new subscript interpreter
+** Compare a zero-terminated string zPattern against
+** an unterminated string zStr of length nStr.
 */
-struct Subscript *SbS_Create(void){
-  Subscript *p;
-
-  p = malloc( sizeof(*p) );
-  if( p ){
-    memset(p, 0, sizeof(*p));
+static int compare_cmd(const char *zPattern, const char *zStr, int nStr){
+  int c = strncmp(zPattern, zStr, nStr);
+  if( c==0 && zPattern[nStr]!=0 ){
+    c = -1;
   }
-  return p;
+  return c;
 }
 
 /*
 ** Evaluate the script given by the first nScript bytes of zScript[].
 ** Return 0 on success and non-zero for an error.
@@ -691,41 +710,18 @@
           int upr = sizeof(aBuiltin)/sizeof(aBuiltin[0]) - 1;
           int lwr = 0;
           rc = SBS_ERROR;
           while( upr>=lwr ){
             int i = (upr+lwr)/2;
-            int c = strncmp(zScript, aBuiltin[i].zCmd, n);
+            int c = compare_cmd(aBuiltin[i].zCmd, zScript, n);
             if( c==0 ){
               rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
               break;
             }else if( c<0 ){
               upr = i-1;
             }else{
               lwr = i+1;
-            }
-          }
-          if( upr<lwr ){
-            /* If it is not a built-in command, look for a built-in
-            ** variable */
-            upr = sizeof(aVars)/sizeof(aVars[0]) - 1;
-            lwr = 0;
-            while( upr>=lwr ){
-              int i = (upr+lwr)/2;
-              int c = strncmp(zScript, aVars[i].zVar, n);
-              if( c==0 ){
-                if( aVars[i].pI ){
-                  SbS_PushInt(p, *aVars[i].pI);
-                }else{
-                  SbS_Push(p, aVars[i].z, -1, 0);
-                }
-                rc = SBS_OK;
-                break;
-              }else if( c<0 ){
-                upr = i-1;
-              }else{
-                lwr = i+1;
-              }
             }
           }
         }else if( pVal->flags & SBSVAL_VERB ){
           rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg);
         }else if( pVal->flags & SBSVAL_EXEC ){