Hello,

On 2017-03-07 18:16, Dan Kennedy wrote:
On 03/08/2017 12:03 AM, Rob Golsteijn wrote:

I want to report a minor issue for the Sqlite shell. It does not
handle multiline command line arguments in which the second line
contains a dot-command correctly.
If the same statements are passed via stdin they are handled fine.
Tested with Sqlite 3.15.2 on Ubuntu 14.04 using Bash.

Passing statements via stdin works fine:

echo "SELECT 1;
.mode csv
SELECT 1;" | sqlite3 mydb.sq3

Passing the statements via a command line argument gives an error:

sqlite3 mydb.sq3 "SELECT 1;
.mode csv
SELECT 1;"

Error: near ".": syntax error

A work around is:

  sqlite3 mydb.sq3 "SELECT 1" ".mode csv" "SELECT 1"

Fortunately most of work is done already. It is enough to dirty patch ``fgets'':

After ``static char mainPrompt[20];'':

======
typedef struct CHNFILE {
  int     nCmd;         /* nof elems in azCmd */
  char    **azCmd;      /* arr to scan */
  int     nCmdCur;      /* nof cur elem */
  char    *zCmdCur;   /* next char to scan */
  size_t  nLenCmdCur;   /* len of cur line */
} CHNFILE;

static CHNFILE chnfile = { 0 };

/*
 * nCmd > 0 or UDB (7.1.7)
 * azCmd != NULL or UDB (7.1.7)
 */
/* !!!! MT: NOT LOCKED !!!! */
static FILE *chnfileopen(int nCmd, char *azCmd[])
{
  assert(0 < nCmd);
  assert(NULL != azCmd);

  if ( 0 != chnfile.nCmd ) {
    return NULL;
  }

  chnfile.nCmd = nCmd;
  chnfile.azCmd = azCmd;
  chnfile.nCmdCur = 0;
  chnfile.zCmdCur = strchr(azCmd[0], '\n');
chnfile.nLenCmdCur = NULL == chnfile.zCmdCur ? strlen(azCmd[0]) : chnfile.zCmdCur - azCmd[0];
  chnfile.zCmdCur = azCmd[0];

  return (void *)&chnfile;
}

/* !!!! MT: NOT LOCKED !!!! */
static char *chnfilegets(char *s, int n, FILE *f)
{
  if ( 0 == chnfile.nCmd || (void *)&chnfile != f ) {
    return fgets(s, n, f);
  }

  /* ``shall'' -- UDB (7.1.7) */
  assert(NULL != s || 0 >= n || (int)chnfile.nCmd <= chnfile.nCmdCur);

  /* nothing to read or EOF */
  if ( 0 >= n || (int)chnfile.nCmd <= chnfile.nCmdCur ) {
    return NULL;
  }

  /* Trailing '\0' */
  --n;
  /* Advance to the next line? */
  if ( (size_t)n > chnfile.nLenCmdCur ) {
    /* YES: Advance to the next line? */
    char *p; /* Temp helper */
    memcpy(s, chnfile.zCmdCur, chnfile.nLenCmdCur);
    s[chnfile.nLenCmdCur] = '\n';
    s[chnfile.nLenCmdCur + 1] = 0;
    /* Next line */
if ( '\n' == chnfile.zCmdCur[chnfile.nLenCmdCur] && 0 != chnfile.zCmdCur[chnfile.nLenCmdCur + 1] ) {
      /* Ends with '\n' [not followed by '\0' ???] */
      chnfile.zCmdCur = &chnfile.zCmdCur[chnfile.nLenCmdCur + 1];
      p = strchr(chnfile.zCmdCur, '\n');
chnfile.nLenCmdCur = NULL == p ? strlen(chnfile.zCmdCur) : p - chnfile.zCmdCur;
    } else {
      /* Next element */
      if ( (int)++chnfile.nCmdCur < chnfile.nCmd ) {
        chnfile.zCmdCur = chnfile.azCmd[chnfile.nCmdCur];
        p = strchr(chnfile.zCmdCur, '\n');
chnfile.nLenCmdCur = NULL == p ? strlen(chnfile.zCmdCur) : p - chnfile.zCmdCur;
      }
    }
  } else {
    /* NO: Advance to the next line? */
    memcpy(s, chnfile.zCmdCur, n);
    s[n] = 0;
    chnfile.zCmdCur = &chnfile.zCmdCur[n];
    chnfile.nLenCmdCur -= n;
  }

  return s;
}

/* !!!! MT: NOT LOCKED !!!! */
static int chnfileclose(FILE *f)
{
  if ( 0 == chnfile.nCmd || &chnfile != (void *)f ) {
    return fclose(f);
  }

  chnfile.nCmd = 0;

  return 0;
}

#define fgets   chnfilegets
#define fclose  chnfileclose
======

And replace the final ``if( !readStdin ){'' block in ``main'':

======
    FILE *in;
    in = chnfileopen(nCmd, azCmd);
    if( in==0 ){
      utf8_printf(stderr, "Error: cannot process command line\n");
      rc = 1;
    } else {
      rc = process_input(&data, in);
      fclose(in);
    }
    free(azCmd);
======

A bit dirty, but works fine though not extremely tested. A cleaner solution probably requires a bit more work, but due to a shell's thrift it is quite easy (there is a simple, non--forking flow: main => process_input => one_input_line => local_getline => fgets).

-- best regards

Cezary H. Noweta
_______________________________________________
sqlite-users mailing list
sqlite-users@mailinglists.sqlite.org
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to