From 8254c1a4fa38c6eab349e7e360429d433f00b701 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 14 Nov 2025 18:41:21 +0000 Subject: [PATCH] Add the --tag and --list options to the ".mode" command. FossilOrigin-Name: 9daf98c2f449688b7463b71dfa926cb4ae96d8c7ee34946df2172bb37f7c5616 --- manifest | 12 ++--- manifest.uuid | 2 +- src/shell.c.in | 133 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 105 insertions(+), 42 deletions(-) diff --git a/manifest b/manifest index ab709ab434..708085f66a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s--once\soption\sto\sthe\s.mode\scommand.\s\sImprovements\sto\shelp\stext. -D 2025-11-14T17:11:06.215 +C Add\sthe\s--tag\sand\s--list\soptions\sto\sthe\s".mode"\scommand. +D 2025-11-14T18:41:21.284 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -735,7 +735,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c ba9cd07ffa3277883c1986085f6ddc4320f4d35d5f212ab58df79a7ecc1a576a -F src/shell.c.in 80cddc5b2c3b9a2317ba673566b3a4091948ee640cf9e80daa432556418e61aa +F src/shell.c.in 84a30f8d3ca31ba31de3e823bd90f07de211bb7dd0ea770831d392e2b894d06c F src/sqlite.h.in 684c19c3b093cca7a38e3f6405d067777464285f17a58a78f7f89d6763e011e7 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 7f236ca1b175ffe03316d974ef57df79b3938466c28d2f95caef5e08c57f3a52 @@ -2175,8 +2175,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 046bfab4a01e8a7cc58d1bdf0756c90ba354562d79e5453c08202daf648e76a6 -R 617f8c7d32295d372a6a7a285c1648b3 +P 253980122a35f787423aaeedbec12ec94b31768f245fe1c1fcc7e08911855c60 +R 3310fe55886bfbe2545dc792f3febefa U drh -Z 8d9d41d539e868e42ddb8706f8074151 +Z 827696bc8e94178725eea170d53fd831 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8aacf80ba6..ffdc9ed804 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -253980122a35f787423aaeedbec12ec94b31768f245fe1c1fcc7e08911855c60 +9daf98c2f449688b7463b71dfa926cb4ae96d8c7ee34946df2172bb37f7c5616 diff --git a/src/shell.c.in b/src/shell.c.in index bb79a75681..a000e8f054 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1289,7 +1289,6 @@ struct ShellState { unsigned mxProgress; /* Maximum progress callbacks before failing */ unsigned flgProgress; /* Flags for the progress callback */ unsigned shellFlgs; /* Various flags */ - unsigned priorShFlgs; /* Saved copy of flags */ unsigned nTestRun; /* Number of test cases run */ unsigned nTestErr; /* Number of test cases that failed */ sqlite3_int64 szMax; /* --maxsize argument to .open */ @@ -1302,6 +1301,11 @@ struct ShellState { FILE *pLog; /* Write log output here */ Mode mode; /* Current display mode */ Mode modePrior; /* Backup */ + struct SavedMode { /* Ability to define custom mode configurations */ + char *zTag; /* Name of this saved mode */ + Mode mode; /* The saved mode */ + } *aSavedModes; /* Array of saved .mode settings. system malloc() */ + int nSavedModes; /* Number of saved .mode settings */ struct AuxDb { /* Storage space for auxiliary database connections */ sqlite3 *db; /* Connection pointer */ const char *zDbFilename; /* Filename used to open the connection */ @@ -1371,7 +1375,8 @@ static ShellState shellState; /* Names of values for Mode.spec.eEsc and Mode.spec.eText */ static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; -static const char *qrfQuoteNames[] = { "off","off","sql","hex", "tcl", "json"}; +static const char *qrfQuoteNames[] = + { "off","off","sql","hex","csv","tcl","json"}; /* ** These are the allowed shellFlgs values @@ -1574,18 +1579,23 @@ static void modeSetStr(char **az, const char *zNew){ /* ** Change the mode to eMode */ -static void modeChange(Mode *p, unsigned char eMode){ +static void modeChange(ShellState *p, unsigned char eMode){ const ModeInfo *pI; - assert( eModeeMode = eMode; - if( pI->eCSep ) modeSetStr(&p->spec.zColumnSep, aModeStr[pI->eCSep]); - if( pI->eRSep ) modeSetStr(&p->spec.zRowSep, aModeStr[pI->eRSep]); - if( pI->eNull ) modeSetStr(&p->spec.zNull, aModeStr[pI->eNull]); - p->spec.eText = pI->eText; - p->spec.eBlob = pI->eBlob; - p->spec.bTitles = pI->bHdr; - p->spec.eTitle = pI->eHdr; + if( eModemode; + pM->eMode = eMode; + if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); + if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); + if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); + pM->spec.eText = pI->eText; + pM->spec.eBlob = pI->eBlob; + pM->spec.bTitles = pI->bHdr; + pM->spec.eTitle = pI->eHdr; + }else if( eMode>=100 && eMode-100nSavedModes ){ + modeFree(&p->mode); + modeDup(&p->mode, &p->aSavedModes[eMode-100].mode); + } } /* @@ -1608,12 +1618,18 @@ static void modeInit(Mode *p){ /* ** Find the number of a display mode given its name. Return -1 if ** the name does not match any mode. +** +** Saved modes are also searched if p!=NULL. The number returned +** for a saved mode is the index into the p->aSavedModes[] array plus 100. */ -static int modeFind(const char *zName){ +static int modeFind(ShellState *p, const char *zName){ int i; for(i=0; inSavedModes; i++){ + if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+100; + } return -1; } @@ -7312,6 +7328,7 @@ static int modeTitleDsply(ShellState *p, int bAll){ ** any single SQL value to N. Longer values are ** truncated. Zero means "no limit". Only works ** in "line" mode and in columnar modes. +** --list List available modes ** --null STRING Render SQL NULL values as the given string ** --once Setting changes to the left are reverted after ** the next SQL command. @@ -7327,6 +7344,7 @@ static int modeTitleDsply(ShellState *p, int bAll){ ** means "no limit". Or N can be "auto" to set the ** width automatically. ** --tablename NAME Set the name of the table for "insert" mode. +** --tag NAME Save current mode as NAME. ** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. ** --title ARG Whether or not to show column headers, and if so ** how to encode them. ARG can be "off", "on", @@ -7357,11 +7375,11 @@ static int dotCmdMode(ShellState *p){ if( z[0]=='-' && z[1]=='-' ) z++; if( z[0]!='-' && iMode<0 - && (eMode = modeFind(azArg[i]))>=0 + && (eMode = modeFind(p, azArg[i]))>=0 && eMode!=MODE_Www ){ iMode = i; - modeChange(&p->mode, eMode); + modeChange(p, eMode); /* (Legacy) If the mode is MODE_Insert and the next argument ** is not an option, then the next argument must be the table ** name. @@ -7445,10 +7463,21 @@ static int dotCmdMode(ShellState *p){ } p->mode.spec.eEsc = k+1; chng = 1; + }else if( optionMatch(z,"list") ){ + int ii; + cli_puts("available modes:", p->out); + for(ii=0; iiout, " %s", aModeInfo[ii].zName); + } + for(ii=0; iinSavedModes; ii++){ + cli_printf(p->out, " %s", p->aSavedModes[ii].zTag); + } + cli_puts("\n", p->out); }else if( optionMatch(z,"quote") ){ if( i+10 || modeFind(azArg[i+1])<0) + && (iMode>0 || modeFind(p, azArg[i+1])<0) ){ /* --quote is followed by an argument other that is not an option ** or a mode name. See it must be a boolean or a keyword to describe @@ -7497,19 +7526,19 @@ static int dotCmdMode(ShellState *p){ break; } chng = 1; + }else if( optionMatch(z,"once") ){ + p->nPopMode = 0; + modePush(p); + p->nPopMode = 1; }else if( optionMatch(z,"noquote") ){ /* (undocumented legacy) --noquote always turns quoting off */ p->mode.spec.eText = QRF_TEXT_Plain; p->mode.spec.eBlob = QRF_BLOB_Text; chng = 1; - }else if( optionMatch(z,"once") ){ - p->nPopMode = 0; - modePush(p); - p->nPopMode = 1; }else if( optionMatch(z,"reset") ){ int saved_eMode = p->mode.eMode; modeFree(&p->mode); - modeChange(&p->mode, saved_eMode); + modeChange(p, saved_eMode); }else if( optionMatch(z,"screenwidth") ){ if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); @@ -7530,6 +7559,32 @@ static int dotCmdMode(ShellState *p){ } i++; chng = 1; + }else if( optionMatch(z,"tag") ){ + size_t nByte; + int n; + const char *zTag; + if( i+1>=nArg ){ + dotCmdError(p, i, "missing argument", 0); + return 1; + } + zTag = azArg[++i]; + if( modeFind(p, zTag)>=0 ){ + dotCmdError(p, i, "mode already exists", 0); + return 1; + } + if( p->nSavedModes > 100 ){ + dotCmdError(p, i-1, "cannot add more modes", 0); + return 1; + } + n = p->nSavedModes++; + nByte = sizeof(p->aSavedModes[0]); + nByte *= n+1; + p->aSavedModes = realloc( p->aSavedModes, nByte ); + shell_check_oom(p->aSavedModes); + p->aSavedModes[n].zTag = strdup(zTag); + shell_check_oom(p->aSavedModes[n].zTag); + modeDup(&p->aSavedModes[n].mode, &p->mode); + chng = 1; }else if( optionMatch(z,"textjsonb") ){ if( i+1>=nArg ){ dotCmdError(p, i, "missing argument", 0); @@ -7622,7 +7677,7 @@ static int dotCmdMode(ShellState *p){ return 1; } } - if( !chng || bAll ){ + if( chng || bAll ){ const ModeInfo *pI = aModeInfo + p->mode.eMode; sqlite3_str *pDesc = sqlite3_str_new(p->db); char *zDesc; @@ -12626,25 +12681,25 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ if( cli_strcmp(z,"-init")==0 ){ i++; }else if( cli_strcmp(z,"-html")==0 ){ - modeChange(&data.mode, MODE_Html); + modeChange(&data, MODE_Html); }else if( cli_strcmp(z,"-list")==0 ){ - modeChange(&data.mode, MODE_List); + modeChange(&data, MODE_List); }else if( cli_strcmp(z,"-quote")==0 ){ - modeChange(&data.mode, MODE_Quote); + modeChange(&data, MODE_Quote); }else if( cli_strcmp(z,"-line")==0 ){ - modeChange(&data.mode, MODE_Line); + modeChange(&data, MODE_Line); }else if( cli_strcmp(z,"-column")==0 ){ - modeChange(&data.mode, MODE_Column); + modeChange(&data, MODE_Column); }else if( cli_strcmp(z,"-json")==0 ){ - modeChange(&data.mode, MODE_Json); + modeChange(&data, MODE_Json); }else if( cli_strcmp(z,"-markdown")==0 ){ - modeChange(&data.mode, MODE_Markdown); + modeChange(&data, MODE_Markdown); }else if( cli_strcmp(z,"-table")==0 ){ - modeChange(&data.mode, MODE_Table); + modeChange(&data, MODE_Table); }else if( cli_strcmp(z,"-box")==0 ){ - modeChange(&data.mode, MODE_Box); + modeChange(&data, MODE_Box); }else if( cli_strcmp(z,"-csv")==0 ){ - modeChange(&data.mode, MODE_Csv); + modeChange(&data, MODE_Csv); }else if( cli_strcmp(z,"-escape")==0 && i+1