From: larrybr Date: Tue, 8 Mar 2022 01:02:37 +0000 (+0000) Subject: CLI regularization and code cleanup X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1cdb1fa7e399f15c77dc74b3900cf7758611eb99;p=thirdparty%2Fsqlite.git CLI regularization and code cleanup FossilOrigin-Name: 16af0e4560349f720d4e2fbb4c08211e6a9a573c1fb4eb287d8bc2aefc3c7ce0 --- diff --git a/manifest b/manifest index 29bc1cb75c..8e4ca1a7ac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Sync\swith\strunk -D 2022-03-07T19:00:25.488 +C CLI\sregularization\sand\scode\scleanup +D 2022-03-08T01:02:37.922 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -555,8 +555,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 4890a3cfee0bc60ff231c3a44db37968859ab0be156983dbcc0c096109832cdd -F src/shell.c.in 92f8c98677e6e9b192ee77927ecd2ab1af2c8e3e5daca06e52f06d451037f705 -F src/shext_linkage.h bff86090e82d1df3a6dd0c566324f38ac67cb75d6e72697d9a8a3065545b4f0c +F src/shell.c.in 52936caf57e2765e5647c70ccc8932fe510879f0abf2c331c1e83459e7778959 +F src/shext_linkage.h 76a8deb0ffc3b5ed4396f71fd7beb6c63477457d457cab873d7e2f6469819b13 F src/sqlite.h.in e82ac380b307659d0892f502b742f825504e78729f4edaadce946003b9c00816 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6 @@ -1883,7 +1883,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61 F tool/mkopcodeh.tcl 5dab48c49a25452257494e9601702ab63adaba6bd54a9b382615fa52661c8f8c F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d -F tool/mkshellc.tcl 8b59d0312c708acb00b9242a2eb79cdf3a84d972c06bb09447599ebadc1d5cbf +F tool/mkshellc.tcl 173780dab3cd26a7c58a05e401af6d2fa2d12187f349b86cec3e2cafe7526678 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f @@ -1949,8 +1949,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a88983ecb7e2905219898e17d11de3598ee801c57b8ba32c51cb2790456a0e5b 0021bebc162e001b788786703ce634e7b8fcd3976f7047a5956e82140791e765 -R 2e72e0cc424e9311255d66424a9824eb +P 1194093a58978b99c7afee08483d0590ce9aff4647a46dc9a338f022647fdb42 +R 1cbe480cc9660306a182a58b06b04872 U larrybr -Z d510d7fd6f6e96bbcf87e6ed60b7095d +Z eab8f0e1073c744044f30e201854ce42 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2b431c2ffb..b0eb5bb581 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1194093a58978b99c7afee08483d0590ce9aff4647a46dc9a338f022647fdb42 \ No newline at end of file +16af0e4560349f720d4e2fbb4c08211e6a9a573c1fb4eb287d8bc2aefc3c7ce0 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 5cd6093e2b..cd2e731c10 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1269,7 +1269,6 @@ typedef struct OutputModeSave{ #define DBX(psx) (psx)->dbUser typedef struct ShellInState { - /* sqlite3 *db; user database moved to ShellExState.dbUser */ int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */ u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ sqlite3_int64 szMax; /* --maxsize argument to .open */ @@ -1298,27 +1297,18 @@ typedef struct ShellInState { /* Output mode state-keep for certain ad-hoc save/restore ops: */ u8 cMode; /* temporary output mode for the current query */ u8 normalMode; /* Output mode before ".explain on" */ -#if 0 /* moved to ShellExState and renamed */ - int *colWidth; /* Requested width of each column in columnar modes */ - int *actualWidth; /* Actual width of each column */ - int nWidth; /* Number of slots in colWidth[] and actualWidth[] */ -#endif char nullValue[20]; /* Text to print for NULL retrieved from database */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ int outCount; /* Revert to stdout when reaching zero */ -#if 0 /* moved to ShellExState as resultCount */ - int cnt; /* Number of records displayed so far */ -#endif + int inputNesting; /* Track nesting level of .read and other redirects */ InSource *pInSource; /* Read commands and SQL from this stream source */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ -#if 0 /* moved to ShellExState */ - int abruptExit; /* Flag for immediate shell exit, exit code */ -#endif + int nErr; /* Number of errors seen */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int nCheck; /* Number of ".check" commands run */ @@ -1348,6 +1338,7 @@ typedef struct ShellInState { char *zNonce; /* Nonce for temporary safe-mode suspension */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ + ShellExState *pSXS; /* Pointer to companion, exposed shell state */ } ShellInState; @@ -4616,9 +4607,10 @@ static int run_schema_dump_query( DISPATCH_CONFIG[ HELP_COALESCE=1 ]; -#define HELP_TEXT_FMT "%s" +#define HELP_TEXT_FMTP ".%s" +#define HELP_TEXT_FMTS "%s" /* Above HELP_COALESCE config and HELP_TEXT_FMT PP vars must track. - * Alternative is 0 and "%s\n" . + * Alternative is 0, ".%s\n" and "%s\n" . */ /* Forward references */ @@ -7871,7 +7863,7 @@ CONDITION_COMMAND(seeargs !defined(SHELL_OMIT_SEEARGS)); * The .seeargs command */ COLLECT_HELP_TEXT[ - ".seeargs Echo arguments suffixed with |", + ",seeargs Echo arguments suffixed with |", ]; DISPATCHABLE_COMMAND( seeargs ? 0 0 azArg nArg p ){ int rc = 0; @@ -8011,7 +8003,7 @@ DISPATCHABLE_COMMAND( breakpoint 3 1 1 ){ */ COLLECT_HELP_TEXT[ ".changes on|off Show number of rows changed by SQL", - ".check GLOB Fail if output since .testcase does not match", + ",check GLOB Fail if output since .testcase does not match", ".clone NEWDB Clone data into NEWDB from the existing database", ".connection [close] [#] Open or close an auxiliary database connection", ]; @@ -8369,6 +8361,24 @@ DISPATCHABLE_COMMAND( eqp ? 0 0 ){ } } +/***************** + * The .exit and .quit commands + * These are together so that their subtle differences are apparent. + */ +COLLECT_HELP_TEXT[ + ".exit ?CODE? Exit this program with return-code CODE", + ".quit Exit this program or script", +]; +DISPATCHABLE_COMMAND( exit 3 1 0 ){ + int rc; + if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) + p->shellAbruptExit = rc; + return 2; +} +DISPATCHABLE_COMMAND( quit 1 1 0 ){ + return 2; +} + /***************** * The .expert and .explain commands */ @@ -8769,24 +8779,30 @@ DISPATCHABLE_COMMAND( headers 6 2 2 ){ /***************** * The .help command */ + +/* This literal's value AND address are used for help's workings. */ +static const char *zHelpAll = "-all"; + COLLECT_HELP_TEXT[ ".help ?PATTERN?|?-all? Show help for PATTERN or everything, or summarize", " Repeat -all to see undocumented commands", ]; -DISPATCHABLE_COMMAND( help 3 1 2 ){ +DISPATCHABLE_COMMAND( help 3 1 3 ){ const char *zPat = 0; + FILE *out = ISS(p)->out; if( nArg>1 ){ char *z = azArg[1]; - if( strcmp(z,"-a")==0 - || strcmp(z,"-all")==0 - || strcmp(z,"--all")==0 ){ + if( nArg==3 && strcmp(z, zHelpAll)==0 && strcmp(azArg[2], zHelpAll)==0 ){ + /* Show the undocumented command help */ + zPat = zHelpAll; + }else if( strcmp(z,"-a")==0 || strcmp(z,"-all")==0 || strcmp(z,"--all")==0 ){ zPat = ""; }else{ zPat = z; } } - if( showHelp(ISS(p)->out, zPat, p)==0 ){ - utf8_printf(ISS(p)->out, "Nothing matches '%s'\n", azArg[1]); + if( showHelp(out, zPat, p)==0 && nArg>1 ){ + utf8_printf(out, "Nothing matches '%s'\n", azArg[1]); } /* Help pleas never fail! */ return 0; @@ -11548,16 +11564,20 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){ } /***************** - * The .selftest, .shell, .show, .shxopts, .stats and .system commands + * The .selftest*, .shell, .show, .shxopts, .stats and .system commands */ +CONDITION_COMMAND( selftest_bool defined(SQLITE_DEBUG) ); +CONDITION_COMMAND( selftest_int defined(SQLITE_DEBUG) ); CONDITION_COMMAND( shell !defined(SQLITE_NOHAVE_SYSTEM) ); CONDITION_COMMAND( shxopts (SHELL_EXTENSIONS)!=0 ); CONDITION_COMMAND( system !defined(SQLITE_NOHAVE_SYSTEM) ); COLLECT_HELP_TEXT[ - ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", + ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", " Options:", " --init Create a new SELFTEST table", " -v Verbose output", + ",selftest_bool ?ARGS? Show boolean values of ARGS as flag tokens", + ",selftest_int ?ARGS? Show integer values of ARGS as integer tokens", ".shell CMD ARGS... Run CMD ARGS... in a system shell", ".show Show the current values for various settings", ".shxopts ?SIGNED_OPTS? Show or alter shell extension options", @@ -11569,6 +11589,26 @@ COLLECT_HELP_TEXT[ " vmstep Show the virtual machine step count only", ".system CMD ARGS... Run CMD ARGS... in a system shell", ]; + +DISPATCHABLE_COMMAND( selftest_bool 10 0 0 ){ + int i, v; + for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); + } + return 0; +} +DISPATCHABLE_COMMAND( selftest_int 10 0 0 ){ + int i; sqlite3_int64 v; + for(i=1; iout, "%s", zBuf); + } + return 0; +} + DISPATCHABLE_COMMAND( selftest 4 0 0 ){ int rc; ShellInState *psi = ISS(p); @@ -11994,8 +12034,8 @@ DISPATCHABLE_COMMAND( indices 3 1 2 ){ CONDITION_COMMAND( testctrl !defined(SQLITE_UNTESTABLE) ); CONDITION_COMMAND( trace !defined(SQLITE_OMIT_TRACE) ); COLLECT_HELP_TEXT[ - ".testcase NAME Begin redirecting output to 'testcase-out.txt'", - ".testctrl CMD ... Run various sqlite3_test_control() operations", + ",testcase NAME Begin redirecting output to 'testcase-out.txt'", + ",testctrl CMD ... Run various sqlite3_test_control() operations", " Run \".testctrl\" with no arguments for details", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", @@ -12801,7 +12841,7 @@ static void showPrimaryHelp(FILE *out, const char *zCmd, ShellExState *psx){ MetaCommand *pmc = nextMatchingMetaCmd(&mmi); if( pmc!=0 ){ const char *zH = pmc->pMethods->help(pmc, 0); - if( zH!=0 ) utf8_printf(out, HELP_TEXT_FMT, zH); + if( zH!=0 && *zH ) utf8_printf(out, HELP_TEXT_FMTP, zH+1); } freeMetaMatchIter(&mmi); } @@ -12813,33 +12853,42 @@ static void showPrimaryHelp(FILE *out, const char *zCmd, ShellExState *psx){ ** 3. For multiple commands matching a pattern, primary help text only. ** 4. For a single matched command, complete help text. ** 5. For commands whose help contains a pattern, complete help text. +** 6. For the set of "undocumented" (without normal help) commands. ** These variations are indicated thusly: ** 1. zPattern is NULL ** 2. zPattern is "" ** 3. zPattern is a prefix matching more than one command ** 4. zPattern is a word or prefix matching just one command ** 5. zPattern is neither case 3 or 4 but is found in complete help text +** 6. zPattern is exactly the pointer known locally as zHelpAll. ** ** Return the number of matches. */ static int showHelp(FILE *out, const char *zPattern, ShellExState *psx){ u8 bNullPattern = zPattern==0; - u8 bEmptyPattern = (bNullPattern)? 0 : *zPattern==0; + u8 bShowUndoc = zPattern==zHelpAll; + u8 bEmptyPattern = !bNullPattern && (*zPattern==0 || bShowUndoc); int npm = 0; /* track how many matches found */ - MetaMatchIter mmi = findMatchingMetaCmds(zPattern, psx); + MetaMatchIter mmi = findMatchingMetaCmds(bShowUndoc? "" : zPattern, psx); MetaCommand *pmc, *pmcLastShown = 0; char *zPat = 0; + char cLead = (bShowUndoc)? ',' : '.'; + if( bShowUndoc ){ + utf8_printf(out, "%s\n%s\n", + "The following commands are used for internal SQLite testing.", + "They are undocumented and subject to change without notice."); + } while( 0 != (pmc = nextMatchingMetaCmd(&mmi)) ){ const char *zH = pmc->pMethods->help(pmc, 0); - if( zH!=0 ){ + if( zH!=0 && *zH==cLead){ ++npm; pmcLastShown = pmc; - utf8_printf(out, HELP_TEXT_FMT, zH); + utf8_printf(out, HELP_TEXT_FMTP, zH+1); if( bEmptyPattern ){ zH = pmc->pMethods->help(pmc, 1); if( zH!=0 ){ - utf8_printf(out, HELP_TEXT_FMT, zH); + utf8_printf(out, HELP_TEXT_FMTS, zH); } } } @@ -12850,7 +12899,7 @@ static int showHelp(FILE *out, const char *zPattern, ShellExState *psx){ * the secondary help of that command, even if not requested, * unless it was already emitted. */ const char *zH = pmcLastShown->pMethods->help(pmcLastShown, 1); - if( zH!=0 ) utf8_printf(out, HELP_TEXT_FMT, zH); + if( zH!=0 ) utf8_printf(out, HELP_TEXT_FMTS, zH); return npm; } /* If found anything with provided (or NULL or empty) pattern, it's done. */ @@ -12863,10 +12912,11 @@ static int showHelp(FILE *out, const char *zPattern, ShellExState *psx){ while( 0 != (pmc = nextMatchingMetaCmd(&mmi)) ){ const char *zHp = pmc->pMethods->help(pmc, 0); const char *zHs = pmc->pMethods->help(pmc, 1); - if( (zHp!=0 && sqlite3_strlike(zPat, zHp, 0)==0) + if( zHp==0 || *zHp!='.' ) continue; + if( sqlite3_strlike(zPat, zHp, 0)==0 || (zHs!=0 && sqlite3_strlike(zPat, zHs, 0)==0) ){ - if( zHp ) utf8_printf(out, HELP_TEXT_FMT, zHp); - if( zHs ) utf8_printf(out, HELP_TEXT_FMT, zHs); + utf8_printf(out, HELP_TEXT_FMTP, zHp+1); + if( zHs ) utf8_printf(out, HELP_TEXT_FMTS, zHs); ++npm; } } @@ -12887,6 +12937,8 @@ static int do_meta_command(char *zLine, ShellExState *psx){ int n, c; int rc = 0; char *azArg[52]; + char *zErr = 0; + int dispatchResult; #if SHELL_VARIABLE_EXPANSION int ncLineIn = strlen30(zLine); u8 bExpVars = SHEXT_VAREXP(ISS(psx)); @@ -12930,91 +12982,52 @@ static int do_meta_command(char *zLine, ShellExState *psx){ c = azArg[0][0]; clearTempFile(ISS(psx)); - /* Check for the special, non-dispatched meta-commands. - */ - - if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ - if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) - psx->shellAbruptExit = rc; - rc = 2; - }else - - if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ - rc = 2; - }else - -#ifdef SQLITE_DEBUG - /* Undocumented commands for internal testing. - * Subject to change without notice. - * These are not dispatched via lookup because the command word varies. - */ - if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ - if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ - int i, v; - for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); + dispatchResult = dispatchCommand(azArg, nArg, psx, &zErr); + if( psx->shellAbruptExit!=0 ){ + dispatchResult = SHELL_FORBIDDEN_OP; + } + switch( dispatchResult ){ + case NO_SUCH_COMMAND: + utf8_printf(STD_ERR, "Error: unknown command: \"%s\"\n", azArg[0]); + if( stdin_is_interactive ) + utf8_printf(STD_ERR, " Enter \".help\" for a list of commands.\n"); + rc = 1; + break; + case SHELL_INVALID_ARGS: + utf8_printf(STD_ERR, "Error: invalid arguments for \".%s\"\n", azArg[0]); + if( stdin_is_interactive ){ + if( zErr!=0 ){ + utf8_printf(STD_ERR, " %s\n", zErr); + }else{ + utf8_printf(STD_ERR, "Usage: "); + showPrimaryHelp(STD_ERR, azArg[0], psx); } } - if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ - int i; sqlite3_int64 v; - for(i=1; iout, "%s", zBuf); - } + rc = 1; + break; + case SHELL_FORBIDDEN_OP: + if( zErr!=0 ){ + utf8_printf + (STD_ERR, + "Error: \".%s\" may not %s in --safe mode\n", azArg[0], zErr); + sqlite3_free(zErr); + }else { + utf8_printf(STD_ERR, + "Error: \".%s\" forbidden in --safe mode\n", azArg[0]); } - }else -#endif - /* The meta-command is not among the specially handled ones. Dispatch it. */ - { - char *zErr = 0; - int dispatchResult = dispatchCommand(azArg, nArg, psx, &zErr); - if( psx->shellAbruptExit!=0 ){ - dispatchResult = SHELL_FORBIDDEN_OP; - } - switch( dispatchResult ){ - case NO_SUCH_COMMAND: - utf8_printf(STD_ERR, "Error: unknown command: \"%s\"\n", azArg[0]); - if( stdin_is_interactive ) - utf8_printf(STD_ERR, " Enter \".help\" for a list of commands.\n"); - rc = 1; - break; - case SHELL_INVALID_ARGS: - utf8_printf(STD_ERR, "Error: invalid arguments for \".%s\"\n", azArg[0]); - if( stdin_is_interactive ){ - if( zErr!=0 ){ - utf8_printf(STD_ERR, " %s\n", zErr); - }else{ - utf8_printf(STD_ERR, "Usage: "); - showPrimaryHelp(STD_ERR, azArg[0], psx); - } - } - rc = 1; - break; - case SHELL_FORBIDDEN_OP: - if( zErr!=0 ){ - utf8_printf - (STD_ERR, - "Error: \".%s\" may not %s in --safe mode\n", azArg[0], zErr); - sqlite3_free(zErr); - }else { - utf8_printf(STD_ERR, - "Error: \".%s\" forbidden in --safe mode\n", azArg[0]); - } - psx->shellAbruptExit = 3; - rc = 2; - default: - if( 0!=dispatchResult ) rc = 1; - if( zErr!=0 ){ - utf8_printf(STD_ERR, "%s", zErr); - sqlite3_free(zErr); - } + psx->shellAbruptExit = 3; + /* fall thru */ + case 2: + rc = 2; + /* fall thru */ + default: + if( dispatchResult>2 ) rc = 1; + if( zErr!=0 ){ + utf8_printf(STD_ERR, "%s", zErr); + sqlite3_free(zErr); } } -meta_command_exit: if( ISS(psx)->outCount ){ ISS(psx)->outCount--; if( ISS(psx)->outCount==0 ) output_reset(ISS(psx)); diff --git a/src/shext_linkage.h b/src/shext_linkage.h index a8496f4a44..8be1f3a959 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -21,8 +21,11 @@ typedef struct ShellExState { /* A semi-transient holder of arbitrary data used during operations * not interrupted by meta-command invocations. Any not-null pointer * left after a meta-command has completed is, by contract, to be - * freeable using sqlite3_free(). It is otherwise unconstrained. */ + * freeable using sqlite3_free(), unless freeHandlerData is non-zero, + * in which case it is used for the free, then zeroed too. This + * pointer's use is otherwise unconstrained. */ void *pvHandlerData; + void (*freeHandlerData)(void *); /* The user's currently open and primary DB connection * Extensions may use this DB, but must not modify this pointer. @@ -72,6 +75,7 @@ typedef struct ShellExState { int *pSpecWidths; /* The column widths last observed in query results, read-only */ int *pHaveWidths; + /* Internal and opaque shell state, not for use by extensions */ struct ShellInState *pSIS; /* Offset of this member is NOT STABLE. */ } ShellExState; diff --git a/tool/mkshellc.tcl b/tool/mkshellc.tcl index 2a1b8a93a0..dce7caa6ec 100644 --- a/tool/mkshellc.tcl +++ b/tool/mkshellc.tcl @@ -269,7 +269,7 @@ proc emit_conditionally {cmd lines inSrc ostrm {indent ""} {cmdTagStore {}}} { proc coalesce_help {htin} { set htrv {} foreach hl $htin { - if {[regexp {^\s*"\.\w+} $hl]} { ;# " + if {[regexp {^\s*"[\.,/>