Add new ASCII mode to the shell capable of importing and exporting using the official unit and record separators (i.e. 0x1F and 0x1E, respectively).

FossilOrigin-Name: 7fe601ead0d0ae26cb09d0dbc7d6367785376567
This commit is contained in:
mistachkin
2014-07-19 20:15:16 +00:00
parent 857745c089
commit 636bf9f768
5 changed files with 328 additions and 124 deletions
+243 -101
View File
@@ -457,7 +457,8 @@ struct callback_data {
int writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */
char *zDestTable; /* Name of destination table when MODE_Insert */
char separator[20]; /* Separator character for MODE_List */
char colSeparator[20]; /* Column separator character for several modes */
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
int colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */
char nullvalue[20]; /* The text to print when a NULL comes back from
@@ -488,6 +489,7 @@ struct callback_data {
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
#define MODE_Csv 7 /* Quote strings, numbers are plain */
#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */
#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */
static const char *modeDescr[] = {
"line",
@@ -499,8 +501,16 @@ static const char *modeDescr[] = {
"tcl",
"csv",
"explain",
"ascii",
};
/*
** These are the column/row separators used by the ASCII mode.
*/
#define SEP_Line "\n"
#define SEP_Column "\x1F"
#define SEP_Row "\x1E"
/*
** Number of elements in an array
*/
@@ -667,11 +677,11 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
fprintf(out,"%s",p->nullvalue);
}else{
int i;
int nSep = strlen30(p->separator);
int nSep = strlen30(p->colSeparator);
for(i=0; z[i]; i++){
if( needCsvQuote[((unsigned char*)z)[i]]
|| (z[i]==p->separator[0] &&
(nSep==1 || memcmp(z, p->separator, nSep)==0)) ){
|| (z[i]==p->colSeparator[0] &&
(nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){
i = 0;
break;
}
@@ -688,7 +698,7 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
}
}
if( bSep ){
fprintf(p->out, "%s", p->separator);
fprintf(p->out, "%s", p->colSeparator);
}
}
@@ -720,10 +730,10 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
if( p->cnt++>0 ) fprintf(p->out,"\n");
if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator);
for(i=0; i<nArg; i++){
fprintf(p->out,"%*s = %s\n", w, azCol[i],
azArg[i] ? azArg[i] : p->nullvalue);
fprintf(p->out,"%*s = %s%s", w, azCol[i],
azArg[i] ? azArg[i] : p->nullvalue, p->rowSeparator);
}
break;
}
@@ -748,9 +758,11 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
}
if( p->showHeader ){
if( w<0 ){
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " ");
fprintf(p->out,"%*.*s%s",-w,-w,azCol[i],
i==nArg-1 ? p->rowSeparator : " ");
}else{
fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
fprintf(p->out,"%-*.*s%s",w,w,azCol[i],
i==nArg-1 ? p->rowSeparator : " ");
}
}
}
@@ -765,7 +777,7 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
}
fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
"----------------------------------------------------------",
i==nArg-1 ? "\n": " ");
i==nArg-1 ? p->rowSeparator : " ");
}
}
}
@@ -788,10 +800,12 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
}
if( w<0 ){
fprintf(p->out,"%*.*s%s",-w,-w,
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
azArg[i] ? azArg[i] : p->nullvalue,
i==nArg-1 ? p->rowSeparator : " ");
}else{
fprintf(p->out,"%-*.*s%s",w,w,
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
azArg[i] ? azArg[i] : p->nullvalue,
i==nArg-1 ? p->rowSeparator : " ");
}
}
break;
@@ -800,7 +814,8 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
fprintf(p->out,"%s%s",azCol[i],
i==nArg-1 ? p->rowSeparator : p->colSeparator);
}
}
if( azArg==0 ) break;
@@ -809,11 +824,11 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
if( z==0 ) z = p->nullvalue;
fprintf(p->out, "%s", z);
if( i<nArg-1 ){
fprintf(p->out, "%s", p->separator);
fprintf(p->out, "%s", p->colSeparator);
}else if( p->mode==MODE_Semi ){
fprintf(p->out, ";\n");
fprintf(p->out, ";%s", p->rowSeparator);
}else{
fprintf(p->out, "\n");
fprintf(p->out, "%s", p->rowSeparator);
}
}
break;
@@ -842,16 +857,16 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_c_string(p->out,azCol[i] ? azCol[i] : "");
if(i<nArg-1) fprintf(p->out, "%s", p->separator);
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator);
}
fprintf(p->out,"\n");
fprintf(p->out, "%s", p->rowSeparator);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
if(i<nArg-1) fprintf(p->out, "%s", p->separator);
if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator);
}
fprintf(p->out,"\n");
fprintf(p->out, "%s", p->rowSeparator);
break;
}
case MODE_Csv: {
@@ -859,13 +874,13 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
}
fprintf(p->out,"\n");
fprintf(p->out, "%s", p->rowSeparator);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
output_csv(p, azArg[i], i<nArg-1);
}
fprintf(p->out,"\n");
fprintf(p->out, "%s", p->rowSeparator);
break;
}
case MODE_Insert: {
@@ -897,6 +912,22 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
fprintf(p->out,");\n");
break;
}
case MODE_Ascii: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
if( i>0 ) fprintf(p->out, "%s", p->colSeparator);
fprintf(p->out,"%s",azCol[i] ? azCol[i] : "");
}
fprintf(p->out, "%s", p->rowSeparator);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
if( i>0 ) fprintf(p->out, "%s", p->colSeparator);
fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullvalue);
}
fprintf(p->out, "%s", p->rowSeparator);
break;
}
}
return 0;
}
@@ -1574,6 +1605,7 @@ static char zHelp[] =
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
".bail on|off Stop after hitting an error. Default OFF\n"
".clone NEWDB Clone data into NEWDB from the existing database\n"
".colseparator STRING This is an alias for .separator\n"
".databases List names and files of attached databases\n"
".dump ?TABLE? ... Dump the database in an SQL text format\n"
" If TABLE specified, only dump tables matching\n"
@@ -1598,6 +1630,7 @@ static char zHelp[] =
#endif
".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n"
".mode MODE ?TABLE? Set output mode where MODE is one of:\n"
" ascii Columns/rows delimited with 0x1F and 0x1E\n"
" csv Comma-separated values\n"
" column Left-aligned columns. (See .width)\n"
" html HTML <table> code\n"
@@ -1615,11 +1648,12 @@ static char zHelp[] =
".quit Exit this program\n"
".read FILENAME Execute SQL in FILENAME\n"
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
".rowseparator STRING Change row separator for output mode and .import\n"
".save FILE Write in-memory database into FILE\n"
".schema ?TABLE? Show the CREATE statements\n"
" If TABLE specified, only show tables matching\n"
" LIKE pattern TABLE.\n"
".separator STRING Change separator used by output mode and .import\n"
".separator STRING Change column separator for output mode and .import\n"
".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\n"
".stats on|off Turn stats on or off\n"
@@ -1832,10 +1866,10 @@ static void test_breakpoint(void){
}
/*
** An object used to read a CSV file
** An object used to read a CSV and other files for import.
*/
typedef struct CSVReader CSVReader;
struct CSVReader {
typedef struct ImportCtx ImportCtx;
struct ImportCtx {
const char *zFile; /* Name of the input file */
FILE *in; /* Read the CSV text from this input stream */
char *z; /* Accumulated text for a field */
@@ -1843,11 +1877,12 @@ struct CSVReader {
int nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int cTerm; /* Character that terminated the most recent field */
int cSeparator; /* The separator character. (Usually ",") */
int cColSep; /* The column separator character. (Usually ",") */
int cRowSep; /* The row separator character. (Usually "\n") */
};
/* Append a single byte to z[] */
static void csv_append_char(CSVReader *p, int c){
static void import_append_char(ImportCtx *p, int c){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
p->z = sqlite3_realloc(p->z, p->nAlloc);
@@ -1865,15 +1900,17 @@ static void csv_append_char(CSVReader *p, int c){
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc().
** + Use p->cSep as the separator. The default is ",".
** + Use p->cSep as the column separator. The default is ",".
** + Use p->rSep as the row separator. The default is "\n".
** + Keep track of the line number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *csv_read_one_field(CSVReader *p){
int c, pc, ppc;
int cSep = p->cSeparator;
static char *csv_read_one_field(ImportCtx *p){
int c;
int cSep = p->cColSep;
int rSep = p->cRowSep;
p->n = 0;
c = fgetc(p->in);
if( c==EOF || seenInterrupt ){
@@ -1881,12 +1918,13 @@ static char *csv_read_one_field(CSVReader *p){
return 0;
}
if( c=='"' ){
int pc, ppc;
int startLine = p->nLine;
int cQuote = c;
pc = ppc = 0;
while( 1 ){
c = fgetc(p->in);
if( c=='\n' ) p->nLine++;
if( c==rSep ) p->nLine++;
if( c==cQuote ){
if( pc==cQuote ){
pc = 0;
@@ -1894,8 +1932,8 @@ static char *csv_read_one_field(CSVReader *p){
}
}
if( (c==cSep && pc==cQuote)
|| (c=='\n' && pc==cQuote)
|| (c=='\n' && pc=='\r' && ppc==cQuote)
|| (c==rSep && pc==cQuote)
|| (c==rSep && pc=='\r' && ppc==cQuote)
|| (c==EOF && pc==cQuote)
){
do{ p->n--; }while( p->z[p->n]!=cQuote );
@@ -1909,19 +1947,19 @@ static char *csv_read_one_field(CSVReader *p){
if( c==EOF ){
fprintf(stderr, "%s:%d: unterminated %c-quoted field\n",
p->zFile, startLine, cQuote);
p->cTerm = EOF;
p->cTerm = c;
break;
}
csv_append_char(p, c);
import_append_char(p, c);
ppc = pc;
pc = c;
}
}else{
while( c!=EOF && c!=cSep && c!='\n' ){
csv_append_char(p, c);
while( c!=EOF && c!=cSep && c!=rSep ){
import_append_char(p, c);
c = fgetc(p->in);
}
if( c=='\n' ){
if( c==rSep ){
p->nLine++;
if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
}
@@ -1931,6 +1969,40 @@ static char *csv_read_one_field(CSVReader *p){
return p->z;
}
/* Read a single field of ASCII delimited text.
**
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc().
** + Use p->cSep as the column separator. The default is "\x1F".
** + Use p->rSep as the row separator. The default is "\x1E".
** + Keep track of the row number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *ascii_read_one_field(ImportCtx *p){
int c;
int cSep = p->cColSep;
int rSep = p->cRowSep;
p->n = 0;
c = fgetc(p->in);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
}
while( c!=EOF && c!=cSep && c!=rSep ){
import_append_char(p, c);
c = fgetc(p->in);
}
if( c==rSep ){
p->nLine++;
}
p->cTerm = c;
if( p->z ) p->z[p->n] = 0;
return p->z;
}
/*
** Try to transfer data for table zTable. If an error is seen while
** moving forward, try to go backwards. The backwards movement won't
@@ -2485,9 +2557,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
int nByte; /* Number of bytes in an SQL string */
int i, j; /* Loop counters */
int needCommit; /* True to COMMIT or ROLLBACK at end */
int nSep; /* Number of bytes in p->separator[] */
int nSep; /* Number of bytes in p->colSeparator[] */
char *zSql; /* An SQL statement */
CSVReader sCsv; /* Reader context */
ImportCtx sCtx; /* Reader context */
char *(*xRead)(ImportCtx*); /* Procecure to read one value */
int (*xCloser)(FILE*); /* Procedure to close th3 connection */
if( nArg!=3 ){
@@ -2497,55 +2570,71 @@ static int do_meta_command(char *zLine, struct callback_data *p){
zFile = azArg[1];
zTable = azArg[2];
seenInterrupt = 0;
memset(&sCsv, 0, sizeof(sCsv));
memset(&sCtx, 0, sizeof(sCtx));
open_db(p, 0);
nSep = strlen30(p->separator);
nSep = strlen30(p->colSeparator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null separator required for import\n");
fprintf(stderr, "Error: non-null column separator required for import\n");
return 1;
}
if( nSep>1 ){
fprintf(stderr, "Error: multi-character separators not allowed"
fprintf(stderr, "Error: multi-character column separators not allowed"
" for import\n");
return 1;
}
sCsv.zFile = zFile;
sCsv.nLine = 1;
if( sCsv.zFile[0]=='|' ){
sCsv.in = popen(sCsv.zFile+1, "r");
sCsv.zFile = "<pipe>";
nSep = strlen30(p->rowSeparator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null row separator required for import\n");
return 1;
}
if( nSep>1 ){
fprintf(stderr, "Error: multi-character row separators not allowed"
" for import\n");
return 1;
}
sCtx.zFile = zFile;
sCtx.nLine = 1;
if( sCtx.zFile[0]=='|' ){
sCtx.in = popen(sCtx.zFile+1, "r");
sCtx.zFile = "<pipe>";
xCloser = pclose;
}else{
sCsv.in = fopen(sCsv.zFile, "rb");
sCtx.in = fopen(sCtx.zFile, "rb");
xCloser = fclose;
}
if( sCsv.in==0 ){
if( p->mode==MODE_Ascii ){
xRead = ascii_read_one_field;
}else{
xRead = csv_read_one_field;
}
if( sCtx.in==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
return 1;
}
sCsv.cSeparator = p->separator[0];
sCtx.cColSep = p->colSeparator[0];
sCtx.cRowSep = p->rowSeparator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
xCloser(sCsv.in);
xCloser(sCtx.in);
return 1;
}
nByte = strlen30(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
char cSep = '(';
while( csv_read_one_field(&sCsv) ){
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z);
while( xRead(&sCtx) ){
zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z);
cSep = ',';
if( sCsv.cTerm!=sCsv.cSeparator ) break;
if( sCtx.cTerm!=sCtx.cColSep ) break;
}
if( cSep=='(' ){
sqlite3_free(zCreate);
sqlite3_free(sCsv.z);
xCloser(sCsv.in);
fprintf(stderr,"%s: empty file\n", sCsv.zFile);
sqlite3_free(sCtx.z);
xCloser(sCtx.in);
fprintf(stderr,"%s: empty file\n", sCtx.zFile);
return 1;
}
zCreate = sqlite3_mprintf("%z\n)", zCreate);
@@ -2554,8 +2643,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( rc ){
fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
sqlite3_errmsg(db));
sqlite3_free(sCsv.z);
xCloser(sCsv.in);
sqlite3_free(sCtx.z);
xCloser(sCtx.in);
return 1;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
@@ -2564,7 +2653,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
xCloser(sCsv.in);
xCloser(sCtx.in);
return 1;
}
nCol = sqlite3_column_count(pStmt);
@@ -2574,7 +2663,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
xCloser(sCsv.in);
xCloser(sCtx.in);
return 1;
}
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
@@ -2590,46 +2679,56 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if (pStmt) sqlite3_finalize(pStmt);
xCloser(sCsv.in);
xCloser(sCtx.in);
return 1;
}
needCommit = sqlite3_get_autocommit(db);
if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
do{
int startLine = sCsv.nLine;
int startLine = sCtx.nLine;
for(i=0; i<nCol; i++){
char *z = csv_read_one_field(&sCsv);
char *z = xRead(&sCtx);
/*
** Did we reach end-of-file before finding any columns?
** If so, stop instead of NULL filling the remaining columns.
*/
if( z==0 && i==0 ) break;
/*
** Did we reach end-of-file OR end-of-line before finding any
** columns in ASCII mode? If so, stop instead of NULL filling
** the remaining columns.
*/
if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
fprintf(stderr, "%s:%d: expected %d columns but found %d - "
"filling the rest with NULL\n",
sCsv.zFile, startLine, nCol, i+1);
sCtx.zFile, startLine, nCol, i+1);
i++;
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
}
if( sCsv.cTerm==sCsv.cSeparator ){
if( sCtx.cTerm==sCtx.cColSep ){
do{
csv_read_one_field(&sCsv);
xRead(&sCtx);
i++;
}while( sCsv.cTerm==sCsv.cSeparator );
}while( sCtx.cTerm==sCtx.cColSep );
fprintf(stderr, "%s:%d: expected %d columns but found %d - "
"extras ignored\n",
sCsv.zFile, startLine, nCol, i);
sCtx.zFile, startLine, nCol, i);
}
if( i>=nCol ){
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
if( rc!=SQLITE_OK ){
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine,
fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine,
sqlite3_errmsg(db));
}
}
}while( sCsv.cTerm!=EOF );
}while( sCtx.cTerm!=EOF );
xCloser(sCsv.in);
sqlite3_free(sCsv.z);
xCloser(sCtx.in);
sqlite3_free(sCtx.z);
sqlite3_finalize(pStmt);
if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
}else
@@ -2747,19 +2846,26 @@ static int do_meta_command(char *zLine, struct callback_data *p){
p->mode = MODE_Html;
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
p->mode = MODE_Tcl;
sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, " ");
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Line);
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, ",");
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Line);
}else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, "\t");
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Line);
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert;
set_table_name(p, nArg>=3 ? azArg[2] : "table");
}else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
p->mode = MODE_Ascii;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
}else {
fprintf(stderr,"Error: mode should be one of: "
"column csv html insert line list tabs tcl\n");
"ascii column csv html insert line list tabs tcl\n");
rc = 1;
}
}else
@@ -3028,10 +3134,30 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
#endif
if( c=='r' && strncmp(azArg[0], "rowseparator", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
"%.*s", (int)sizeof(p->rowSeparator)-1, azArg[1]);
}else{
fprintf(stderr, "Usage: .rowseparator STRING\n");
rc = 1;
}
}else
if( c=='c' && strncmp(azArg[0], "colseparator", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator,
"%.*s", (int)sizeof(p->colSeparator)-1, azArg[1]);
}else{
fprintf(stderr, "Usage: .colseparator STRING\n");
rc = 1;
}
}else
if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
if( nArg==2 ){
sqlite3_snprintf(sizeof(p->separator), p->separator,
"%.*s", (int)sizeof(p->separator)-1, azArg[1]);
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator,
"%.*s", (int)sizeof(p->colSeparator)-1, azArg[1]);
}else{
fprintf(stderr, "Usage: .separator STRING\n");
rc = 1;
@@ -3064,21 +3190,24 @@ static int do_meta_command(char *zLine, struct callback_data *p){
rc = 1;
goto meta_command_exit;
}
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
fprintf(p->out,"%9.9s: ", "nullvalue");
fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off");
fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off");
fprintf(p->out,"%12.12s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]);
fprintf(p->out,"%12.12s: ", "nullvalue");
output_c_string(p->out, p->nullvalue);
fprintf(p->out, "\n");
fprintf(p->out,"%9.9s: %s\n","output",
fprintf(p->out,"%12.12s: %s\n","output",
strlen30(p->outfile) ? p->outfile : "stdout");
fprintf(p->out,"%9.9s: ", "separator");
output_c_string(p->out, p->separator);
fprintf(p->out,"%12.12s: ", "colseparator");
output_c_string(p->out, p->colSeparator);
fprintf(p->out, "\n");
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
fprintf(p->out,"%9.9s: ","width");
fprintf(p->out,"%12.12s: ", "rowseparator");
output_c_string(p->out, p->rowSeparator);
fprintf(p->out, "\n");
fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off");
fprintf(p->out,"%12.12s: ","width");
for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
fprintf(p->out,"%d ",p->colWidth[i]);
}
@@ -3672,8 +3801,10 @@ static int process_sqliterc(
** Show available command line options
*/
static const char zOptions[] =
" -ascii set output mode to 'ascii'\n"
" -bail stop after hitting an error\n"
" -batch force batch I/O\n"
" -colseparator SEP same as -separator\n"
" -column set output mode to 'column'\n"
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
" -csv set output mode to 'csv'\n"
@@ -3693,6 +3824,7 @@ static const char zOptions[] =
" -multiplex enable the multiplexor VFS\n"
#endif
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -rowseparator SEP set output line separator. Default: '\\n'\n"
" -separator SEP set output field separator. Default: '|'\n"
" -stats print memory stats before each finalize\n"
" -version show SQLite version\n"
@@ -3720,7 +3852,8 @@ static void usage(int showDetail){
static void main_init(struct callback_data *data) {
memset(data, 0, sizeof(*data));
data->mode = MODE_List;
memcpy(data->separator,"|", 2);
memcpy(data->colSeparator,"|", 2);
memcpy(data->rowSeparator,"\n", 2);
data->showHeader = 0;
sqlite3_config(SQLITE_CONFIG_URI, 1);
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
@@ -3918,9 +4051,18 @@ int main(int argc, char **argv){
data.mode = MODE_Column;
}else if( strcmp(z,"-csv")==0 ){
data.mode = MODE_Csv;
memcpy(data.separator,",",2);
}else if( strcmp(z,"-separator")==0 ){
sqlite3_snprintf(sizeof(data.separator), data.separator,
memcpy(data.colSeparator,",",2);
}else if( strcmp(z,"-ascii")==0 ){
data.mode = MODE_Ascii;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
SEP_Column);
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
SEP_Row);
}else if( strcmp(z,"-separator")==0 || strcmp(z,"-colseparator")==0 ){
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-rowseparator")==0 ){
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
"%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,