#ifdef SHELL_MANAGE_TEXT
FRK_Text,
#endif
- FRK_AnyRef,
- FRK_CustomBase /* series of values for custom freers */
+ FRK_AnyRef, FRK_VdtorRef,
+ FRK_CountOf
} FreeableResourceKind;
#if defined(_WIN32) || defined(WIN32)
ShellText *p_text;
#endif
AnyResourceHolder *p_any_rh;
+ VirtualDtorNthObject *p_vdfo;
} held;
- FreeableResourceKind frk;
+ unsigned short frk; /* a FreeableResourceKind value */
+ unsigned short offset;
} ResourceHeld;
/* The held-resource stack. This is for single-threaded use only. */
static ResourceCount numResHold = 0;
static ResourceCount numResAlloc = 0;
-/* A small set of custom freers. It is linearly searched, used for
-** layered heap-allocated (and other-allocated) data structures, so
-** tends to have use limited to where slow things are happening.
-*/
-static ResourceCount numCustom = 0; /* number of the set */
-static ResourceCount numCustomAlloc = 0; /* allocated space */
-typedef void (*FreerFunction)(void *);
-static FreerFunction *aCustomFreers = 0; /* content of set */
-
const char *resmanage_oom_message = "out of memory, aborting";
/* Info recorded in support of quit_moan(...) and stack-ripping */
case FRK_AnyRef:
(pRH->held.p_any_rh->its_freer)(pRH->held.p_any_rh->pAny);
break;
- default:
+ case FRK_VdtorRef:
{
- int ck = pRH->frk - FRK_CustomBase;
- assert(ck>=0);
- if( ck < numCustom ){
- aCustomFreers[ck]( pRH->held.p_any );
- }else --rv;
+ VirtualDtorNthObject *po = pRH->held.p_vdfo;
+ (po->p_its_freer[pRH->offset])(po);
}
+ break;
+ default:
+ assert(pRH->frk < FRK_CountOf);
}
pRH->held.p_any = 0;
return rv;
/* Shared resource-stack pushing code */
static void res_hold(void *pv, FreeableResourceKind frk){
- ResourceHeld rh = { pv, frk };
+ ResourceHeld rh = { pv, frk, 0 };
if( numResHold == numResAlloc ){
ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
if( !more_holders_try(nrn) ){
res_hold(pt, FRK_Text);
}
#endif
-/* Hold anything together with arbitrary freeing function */
-void* any_holder(void *pm, void (*its_freer)(void*)){
- int i = 0;
- while( i < numCustom ){
- if( its_freer == aCustomFreers[i] ) break;
- ++i;
- }
- if( i == numCustom ){
- size_t ncf = numCustom + 2;
- FreerFunction *pcf;
- pcf = (FreerFunction *)realloc(aCustomFreers, ncf*sizeof(FreerFunction));
- if( pcf!=0 ){
- assert(ncf < (1<<16));
- numCustomAlloc = (ResourceCount)ncf;
- aCustomFreers = pcf;
- aCustomFreers[numCustom++] = its_freer;
- }else{
- quit_moan(resmanage_oom_message,1);
- }
- }
- res_hold(pm, i + FRK_CustomBase);
- return pm;
-}
+
/* Hold some SQLite-allocated memory */
void* smem_holder(void *pm){
res_hold(pm, FRK_DbMem);
assert(parh!=0 && parh->its_freer!=0);
res_hold(parh, FRK_AnyRef);
}
+/* Hold a reference to a VirtualDtorNthObject (in stack frame) */
+void dtor_ref_holder(VirtualDtorNthObject *pvdfo, unsigned char n){
+ ResourceHeld rh = { (void*)pvdfo, FRK_VdtorRef, n };
+ assert(pvdfo!=0 && pvdfo->p_its_freer!=0 && *(pvdfo->p_its_freer)!=0);
+ if( numResHold == numResAlloc ){
+ ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
+ if( !more_holders_try(nrn) ){
+ free_rk(&rh);
+ quit_moan(resmanage_oom_message,1);
+ }
+ }
+ pResHold[numResHold++] = rh;
+}
/* Free all held resources in excess of given resource stack mark,
** then return how many needed freeing. */
pResHold = 0;
numResAlloc = 0;
}
- if( numCustomAlloc>0 ){
- free(aCustomFreers);
- aCustomFreers = 0;
- numCustom = 0;
- numCustomAlloc = 0;
- }
}
return rv;
}
/*
** Arrange for shell input from either a FILE or a string.
** For applicable invariants, see strLineGet(...) which is
-** the only modifier of this struct. (3rd and 4th members)
+** the only modifier of this struct. (4rd and 5th members)
** All other members are simply initialized to select the
** input stream and track input sources, set by whatever
** routines redirect the input.
*/
typedef struct InSource {
+ struct InSource *pFrom; /* Aid redirect tracking and auto-unnesting. */
FILE *inFile; /* Will be 0 when input is to be taken from string. */
char *zStrIn; /* Will be 0 when no input is available from string. */
- int iReadOffset; /* Offset into zStrIn where next "read" to be done. */
+ int iReadOffset; /* Offset into zStrIn where next "read" to be done */
int lineno; /* How many lines have been read from this source */
- const char *zSourceSay; /* For complaints, keep a name for this source */
- struct InSource *pFrom; /* and redirect tracking to aid unraveling. */
+ const char *zSourceSay; /* For complaints, a name kept for this source */
+ union {
+ int (*stream)(FILE *);
+ void (*ptext)(char *);
+ } closer; /* Closer for the file or freer for the text, set by opener */
} InSource;
-#define INSOURCE_STR_REDIR(str, tagTo, isFrom) {0, str, 0, 0, tagTo, isFrom}
-#define INSOURCE_FILE_REDIR(fh, tagTo, isFrom) {fh, 0, 0, 0, tagTo, isFrom}
+#define INSOURCE_STR_REDIR(str, tagTo, isFrom) {isFrom, 0, str, 0, 0, tagTo }
+#define INSOURCE_FILE_REDIR(fh, tagTo, isFrom) {isFrom, fh, 0, 0, 0, tagTo }
#define INSOURCE_IS_INTERACTIVE(pIS) \
((pIS)==&termInSource && stdin_is_interactive )
#define INSOURCE_IS_INVOKEARG(pIS) \
((pIS)==&cmdInSource)
-/* This instance's address is taken as part of interactive input test. */
-static InSource termInSource = { 0, 0, 0, 0, "<terminal>", 0};
-static InSource stdInSource = { 0, 0, 0, 0, "<stdin>", 0};
-static InSource cmdInSource = { 0, 0, 0, 0, "<cmdLine>", 0};
+/* These instances' addresses are taken as part of interactive input test
+ * or test for other special handling as a command-line argument. */
+static InSource termInSource = { 0, 0, 0, 0, 0, "<terminal>", 0 };
+static InSource stdInSource = { 0, 0, 0, 0, 0, "<stdin>", 0 };
+static InSource cmdInSource = { 0, 0, 0, 0, 0, "<cmdLine>", 0 };
+
+/* Initializer for just above 3 InSource objects */
static void init_std_inputs(FILE *pIn ){
termInSource.inFile = pIn;
stdInSource.inFile = pIn;
cmdInSource.lineno = 0;
}
+/* Setup cmdInSource to supply given text as input. */
static void set_invocation_cmd(char *zDo){
cmdInSource.iReadOffset = 0;
cmdInSource.zStrIn = zDo;
++cmdInSource.lineno;
}
+/* Close an InSource object and unlink it from redirect nesting. */
+static void finish_InSource( InSource **ppIS ){
+ if( ppIS!=0 && *ppIS!=0 ){
+ InSource *pIS = *ppIS;
+ if( pIS->closer.stream != 0 ){
+ if( pIS->inFile != 0 ){
+ (pIS->closer.stream)(pIS->inFile);
+ pIS->inFile = 0;
+ }else if( pIS->zStrIn != 0 ){
+ (pIS->closer.ptext)(pIS->zStrIn);
+ pIS->zStrIn = 0;
+ }
+ }
+ *ppIS = pIS->pFrom;
+ pIS->pFrom = 0;
+ }
+}
+
+/* Similar to fgets(buffer, limit, file) but for InSource holding a string. */
static char *strLineGet(char *zBuf, int ncMax, InSource *pInSrc){
if( pInSrc->inFile!=0 ){
char *zRet = fgets(zBuf, ncMax, pInSrc->inFile );
const char *zDbFilename = psi->pAuxDb->zDbFilename;
/* Need next two objects only if redirecting input to get the hex. */
InSource inRedir = INSOURCE_FILE_REDIR(0, zDbFilename, psi->pInSource);
+ AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
unsigned int x[16];
char zLine[1000];
if( zDbFilename ){
return 0;
}
psi->pInSource = &inRedir;
+ inRedir.closer.stream = fclose;
+ any_ref_holder(&arh);
}else{
/* Will read hex DB lines inline from present input, without redirect. */
if( INSOURCE_IS_INTERACTIVE(psi->pInSource) ){
n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
a = sqlite3_malloc( n ? n : 1 );
shell_check_ooms(a);
- smem_holder(a); /* offset 0 */
+ smem_holder(a);
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
utf8_printf(STD_ERR, "invalid pagesize\n");
}
}
*pnData = n; /* Record success and size. */
- swap_held(mark, 0, 0);
+ pop_holder();
readHexDb_cleanup:
- if( psi->pInSource==&inRedir ){
- fclose( inRedir.inFile );
- psi->pInSource = inRedir.pFrom;
- }
holder_free(mark);
return a;
DISPATCHABLE_COMMAND( cd ? 2 2 ){
int rc=0;
if( ISS(p)->bSafeMode ) return DCR_AbortError;
+ else{
#if defined(_WIN32) || defined(WIN32)
- wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
- rc = !SetCurrentDirectoryW(z);
- sqlite3_free(z);
+ wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
+ rc = (z)? !SetCurrentDirectoryW(z) : 1;
+ sqlite3_free(z);
#else
- rc = chdir(azArg[1]);
+ rc = chdir(azArg[1]);
#endif
+ }
if( rc ){
utf8_printf(STD_ERR, "Cannot change to directory \"%s\"\n", azArg[1]);
rc = 1;
}else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
int i = azArg[1][0] - '0';
if( psi->pAuxDb != &psi->aAuxDb[i] && i>=0 && i<ArraySize(psi->aAuxDb) ){
- psi->pAuxDb->db = DBI(psi);
+ psi->pAuxDb->db = DBX(p);
psi->pAuxDb = &psi->aAuxDb[i];
#if SHELL_DYNAMIC_EXTENSION
- if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserVanishing, DBI(psi));
+ if( DBX(p)!=0 ) notify_subscribers(psi, NK_DbUserVanishing, DBX(p));
#endif
- globalDb = DBI(psi) = psi->pAuxDb->db;
+ globalDb = DBX(p) = psi->pAuxDb->db;
#if SHELL_DYNAMIC_EXTENSION
- if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserAppeared, DBI(psi));
+ if( DBX(p)!=0 ) notify_subscribers(psi, NK_DbUserAppeared, DBX(p));
#endif
psi->pAuxDb->db = 0;
}
open_db(p, 0);
db = DBX(p);
rc = sqlite3_prepare_v2(db, "PRAGMA database_list", -1, &pStmt, 0);
+ shell_check_nomem(rc);
if( rc ){
*pzErr = smprintf("%s\n", sqlite3_errmsg(db));
rc = 1;
ShellInState *psi = ISS(psx);
InSource inRedir
= INSOURCE_STR_REDIR(zIn, zName, psi->pInSource);
+ AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
psi->pInSource = &inRedir;
+ any_ref_holder(&arh);
rv = process_input(psi);
- psi->pInSource = inRedir.pFrom;
+ release_holder();
return rv;
}
DISPATCHABLE_COMMAND( read 3 2 2 ){
DotCmdRC rc = DCR_Ok;
+ ShellInState *psi = ISS(p);
FILE *inUse = 0;
int (*fCloser)(FILE *) = 0;
- if( ISS(p)->bSafeMode ) return DCR_AbortError;
+ if( psi->bSafeMode ) return DCR_AbortError;
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
*pzErr = smprintf("pipes are not supported in this OS\n");
}
if( inUse!=0 ){
InSource inSourceRedir
- = INSOURCE_FILE_REDIR(inUse, azArg[1], ISS(p)->pInSource);
- ISS(p)->pInSource = &inSourceRedir;
- rc = process_input(ISS(p));
+ = INSOURCE_FILE_REDIR(inUse, azArg[1], psi->pInSource);
+ AnyResourceHolder arh = { &(psi->pInSource),(GenericFreer)finish_InSource };
+ psi->pInSource = &inSourceRedir;
+ inSourceRedir.closer.stream = fCloser;
+ any_ref_holder(&arh);
+ rc = process_input(psi);
/* If error(s) occured during process, leave complaining to them. */
if( rc==DCR_Error ) rc = DCR_CmdErred;
- assert(fCloser!=0);
- fCloser(inUse);
- ISS(p)->pInSource = inSourceRedir.pFrom;
+ release_holder();
}
return rc;
}
ShellExState *psx){
char *zErr = 0;
DotCmdRC dcr = pmc->pMethods->argsCheck(pmc, &zErr, nArg, azArg);
+ ResourceMark mark = holder_mark();
+ sstr_ptr_holder(&zErr);
command_prep(ISS(psx));
if( dcr==DCR_Ok ){
dcr = pmc->pMethods->execute(pmc, psx, &zErr, nArg, azArg);
if( dcr!=DCR_Ok ){
dcr = dot_command_errors(zErr, azArg, nArg, dcr, psx);
}
- sqlite3_free(zErr);
+ holder_free(mark);
command_post(ISS(psx));
return dcr;
}
int ncLineIn = strlen30(zLine);
u8 bExpVars = SHEXT_VAREXP(ISS(psx));
#endif
+ RipStackDest dotRipDest = RIP_STACK_DEST_INIT;
/* Parse the input line into tokens which are 0-terminated and left in-place.
*/
/* Process the input line. If it was empty, do nothing and declare success.
* Note that "empty" includes a leading '.' followed by nothing else.
*/
- if( nArg>0 ){
- int nFound;
- DotCommand *pmc = findDotCommand(azArg[0], psx, &nFound);
- if( pmc==0 || nFound>1 ){
- if( nFound==0 ){
- dcr = DCR_Unknown;
+ register_exit_ripper(&dotRipDest);
+ if( 0==RIP_TO_HERE(dotRipDest) ){
+ if( nArg>0 ){
+ int nFound;
+ DotCommand *pmc = findDotCommand(azArg[0], psx, &nFound);
+ if( pmc==0 || nFound>1 ){
+ if( nFound==0 ){
+ dcr = DCR_Unknown;
#if SHELL_DYNAMIC_EXTENSION
- pmc = ISS(psx)->pUnknown;
- if( pmc ) dcr = runDotCommand(pmc, azArg, nArg, psx);
+ pmc = ISS(psx)->pUnknown;
+ if( pmc ) dcr = runDotCommand(pmc, azArg, nArg, psx);
#endif
- }else{
- dcr = DCR_Ambiguous;
+ }else{
+ dcr = DCR_Ambiguous;
+ }
+ if( dcr > DCR_ArgIxMask ){
+ /* Issue error for unknown or inadequately specified dot command. */
+ dcr = dot_command_errors(0, azArg, nArg, dcr, psx);
+ }
}
- if( dcr > DCR_ArgIxMask ){
- /* Issue error for unknown or inadequately specified dot command. */
- dcr = dot_command_errors(0, azArg, nArg, dcr, psx);
+ else{
+ char *arg0 = azArg[0];
+ azArg[0] = (char *)(pmc->pMethods->name(pmc));
+ /* Run found command and issue or handle any errors it may report. */
+ dcr = runDotCommand(pmc, azArg, nArg, psx);
+ azArg[0] = arg0;
}
}
- else{
- char *arg0 = azArg[0];
- azArg[0] = (char *)(pmc->pMethods->name(pmc));
- /* Run found command and issue or handle any errors it may report. */
- dcr = runDotCommand(pmc, azArg, nArg, psx);
- azArg[0] = arg0;
- }
+ }else{
+ dcr = DCR_Abort|DCR_Error;
}
-
#if SHELL_VARIABLE_EXPANSION
if( bExpVars ){
/* Free any arguments that are allocated rather than tokenized in place. */
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *inUse;
+ ResourceMark mark = holder_mark();
if( sqliterc == NULL ){
sqliterc = find_xdg_config();
zBuf = smprintf("%s/.sqliterc",home_dir);
shell_check_ooms(zBuf);
sqliterc = zBuf;
+ sstr_holder(zBuf);
}
inUse = fopen(sqliterc,"rb");
if( inUse!=0 ){
InSource inSourceRedir
= INSOURCE_FILE_REDIR(inUse, sqliterc, psi->pInSource);
+ AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
DotCmdRC rc;
psi->pInSource = &inSourceRedir;
+ inSourceRedir.closer.stream = fclose;
if( stdin_is_interactive ){
utf8_printf(STD_ERR,"-- Loading resources from %s\n",sqliterc);
}
+ any_ref_holder(&arh);
rc = process_input(psi);
- fclose(inUse);
- psi->pInSource = inSourceRedir.pFrom;
if( rc>DCR_Ok && bail_on_error ){
XSS(psi)->shellAbruptExit = 0x100|((rc>>1)&0x3);
}
}else if( sqliterc_override!=0 ){
utf8_printf(STD_ERR,"cannot open: \"%s\"\n", sqliterc);
- if( bail_on_error ) exit(1); /* Future: Exit shell rather than process. */
+ if( bail_on_error ){
+ XSS(psi)->shellAbruptExit = 0x102;
+ }
}
- sqlite3_free(zBuf);
+ holder_free(mark);
}
/*