Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -12,11 +12,11 @@ ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.508 2008/10/12 00:27:53 shane Exp $ +** $Id: main.c,v 1.509 2008/10/30 15:03:16 drh Exp $ */ #include "sqliteInt.h" #include #ifdef SQLITE_ENABLE_FTS3 @@ -1263,10 +1263,19 @@ } if( !db || db->mallocFailed ){ return SQLITE_NOMEM; } return db->errCode & db->errMask; +} +int sqlite3_extended_errcode(sqlite3 *db){ + if( db && !sqlite3SafetyCheckSickOrOk(db) ){ + return SQLITE_MISUSE; + } + if( !db || db->mallocFailed ){ + return SQLITE_NOMEM; + } + return db->errCode; } /* ** Create a new collating function for database "db". The name is zName ** and the encoding is enc. Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -28,11 +28,11 @@ ** The name of this file under configuration management is "sqlite.h.in". ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.405 2008/10/17 15:10:37 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.406 2008/10/30 15:03:16 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ #include /* Needed for the definition of va_list */ @@ -376,16 +376,18 @@ ** ** {H12134} The [sqlite3_exec(D,S,C,A,E)] routine shall set the value of ** *E to NULL if E is not NULL and there are no errors. ** ** {H12137} The [sqlite3_exec(D,S,C,A,E)] function shall set the [error code] -** and message accessible via [sqlite3_errcode()], +** and message accessible via [sqlite3_errcode()], +** [sqlite3_extended_errcode()], ** [sqlite3_errmsg()], and [sqlite3_errmsg16()]. ** ** {H12138} If the S parameter to [sqlite3_exec(D,S,C,A,E)] is NULL or an ** empty string or contains nothing other than whitespace, comments, ** and/or semicolons, then results of [sqlite3_errcode()], +** [sqlite3_extended_errcode()], ** [sqlite3_errmsg()], and [sqlite3_errmsg16()] ** shall reset to indicate no errors. ** ** ASSUMPTIONS: ** @@ -2656,11 +2658,14 @@ ** ** The sqlite3_errcode() interface returns the numeric [result code] or ** [extended result code] for the most recent failed sqlite3_* API call ** associated with a [database connection]. If a prior API call failed ** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. +** sqlite3_errcode() is undefined. The sqlite3_extended_errcode() +** interface is the same except that it always returns the +** [extended result code] even when extended result codes are +** disabled. ** ** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. ** Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. @@ -2675,10 +2680,14 @@ ** ** {H12801} The [sqlite3_errcode(D)] interface returns the numeric ** [result code] or [extended result code] for the most recently ** failed interface call associated with the [database connection] D. ** +** {H12802} The [sqlite3_extended_errcode(D)] interface returns the numeric +** [extended result code] for the most recently +** failed interface call associated with the [database connection] D. +** ** {H12803} The [sqlite3_errmsg(D)] and [sqlite3_errmsg16(D)] ** interfaces return English-language text that describes ** the error in the mostly recently failed interface call, ** encoded as either UTF-8 or UTF-16 respectively. ** @@ -2686,19 +2695,22 @@ ** are valid until the next SQLite interface call. ** ** {H12808} Calls to API routines that do not return an error code ** (example: [sqlite3_data_count()]) do not ** change the error code or message returned by -** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()]. +** [sqlite3_errcode()], [sqlite3_extended_errcode()], +** [sqlite3_errmsg()], or [sqlite3_errmsg16()]. ** ** {H12809} Interfaces that are not associated with a specific ** [database connection] (examples: ** [sqlite3_mprintf()] or [sqlite3_enable_shared_cache()] ** do not change the values returned by -** [sqlite3_errcode()], [sqlite3_errmsg()], or [sqlite3_errmsg16()]. +** [sqlite3_errcode()], [sqlite3_extended_errcode()], +** [sqlite3_errmsg()], or [sqlite3_errmsg16()]. */ int sqlite3_errcode(sqlite3 *db); +int sqlite3_extended_errcode(sqlite3 *db); const char *sqlite3_errmsg(sqlite3*); const void *sqlite3_errmsg16(sqlite3*); /* ** CAPI3REF: SQL Statement Object {H13000} @@ -5763,10 +5775,11 @@ ** {H17819} The [sqlite3_blob_open()] interface shall return [SQLITE_OK] on ** success and an appropriate [error code] on failure. ** ** {H17821} If an error occurs during evaluation of [sqlite3_blob_open(D,...)] ** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_extended_errcode()], ** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return ** information appropriate for that error. ** ** {H17824} If any column in the row that a [sqlite3_blob] has open is ** changed by a separate [UPDATE] or [DELETE] statement or by @@ -5876,10 +5889,11 @@ ** the [sqlite3_blob_read(P,Z,N,X)] interface shall return an ** appropriate [error code] or [extended error code]. ** ** {H17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)] ** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_extended_errcode()], ** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return ** information appropriate for that error, where D is the ** [database connection] that was used to open the [BLOB handle] P. */ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); @@ -5945,10 +5959,11 @@ ** the [sqlite3_blob_write(P,Z,N,X)] interface shall return an ** appropriate [error code] or [extended error code]. ** ** {H17888} If an error occurs during evaluation of [sqlite3_blob_write(D,...)] ** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_extended_errcode()], ** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] shall return ** information appropriate for that error. */ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.328 2008/10/12 00:27:54 shane Exp $ +** $Id: test1.c,v 1.329 2008/10/30 15:03:16 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" #include #include @@ -115,39 +115,57 @@ } const char *sqlite3TestErrorName(int rc){ const char *zName = 0; - switch( rc & 0xff ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; - case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; - case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; - case SQLITE_ROW: zName = "SQLITE_ROW"; break; - case SQLITE_DONE: zName = "SQLITE_DONE"; break; - case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break; - case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; - default: zName = "SQLITE_Unknown"; break; + switch( rc ){ + case SQLITE_OK: zName = "SQLITE_OK"; break; + case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; + case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; + case SQLITE_PERM: zName = "SQLITE_PERM"; break; + case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; + case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; + case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; + case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; + case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; + case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; + case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; + case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; + case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; + case SQLITE_FULL: zName = "SQLITE_FULL"; break; + case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; + case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; + case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; + case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; + case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; + case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; + case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; + case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; + case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; + case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; + case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; + case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; + case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break; + case SQLITE_ROW: zName = "SQLITE_ROW"; break; + case SQLITE_DONE: zName = "SQLITE_DONE"; break; + case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break; + case SQLITE_IOERR_SHORT_READ: zName = "SQLITE_IOERR_SHORT_READ"; break; + case SQLITE_IOERR_WRITE: zName = "SQLITE_IOERR_WRITE"; break; + case SQLITE_IOERR_FSYNC: zName = "SQLITE_IOERR_FSYNC"; break; + case SQLITE_IOERR_DIR_FSYNC: zName = "SQLITE_IOERR_DIR_FSYNC"; break; + case SQLITE_IOERR_TRUNCATE: zName = "SQLITE_IOERR_TRUNCATE"; break; + case SQLITE_IOERR_FSTAT: zName = "SQLITE_IOERR_FSTAT"; break; + case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break; + case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break; + case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break; + case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break; + case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break; + case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break; + case SQLITE_IOERR_CHECKRESERVEDLOCK: + zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; + case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break; + default: zName = "SQLITE_Unknown"; break; } return zName; } #define t1ErrorName sqlite3TestErrorName @@ -3061,10 +3079,37 @@ return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_sleep(ms))); return TCL_OK; } + +/* +** Usage: sqlite3_extended_errcode DB +** +** Return the string representation of the most recent sqlite3_* API +** error code. e.g. "SQLITE_ERROR". +*/ +static int test_ex_errcode( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + int rc; + + if( objc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", + Tcl_GetString(objv[0]), " DB", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + rc = sqlite3_extended_errcode(db); + Tcl_AppendResult(interp, (char *)t1ErrorName(rc), 0); + return TCL_OK; +} + /* ** Usage: sqlite3_errcode DB ** ** Return the string representation of the most recent sqlite3_* API @@ -3076,25 +3121,19 @@ int objc, Tcl_Obj *CONST objv[] ){ sqlite3 *db; int rc; - char zBuf[30]; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_errcode(db); - if( (rc&0xff)==rc ){ - zBuf[0] = 0; - }else{ - sprintf(zBuf,"+%d", rc>>8); - } - Tcl_AppendResult(interp, (char *)t1ErrorName(rc), zBuf, 0); + Tcl_AppendResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } /* ** Usage: test_errmsg DB @@ -4665,10 +4704,11 @@ { "sqlite3_bind_parameter_name", test_bind_parameter_name, 0}, { "sqlite3_bind_parameter_index", test_bind_parameter_index, 0}, { "sqlite3_clear_bindings", test_clear_bindings, 0}, { "sqlite3_sleep", test_sleep, 0}, { "sqlite3_errcode", test_errcode ,0 }, + { "sqlite3_extended_errcode", test_ex_errcode ,0 }, { "sqlite3_errmsg", test_errmsg ,0 }, { "sqlite3_errmsg16", test_errmsg16 ,0 }, { "sqlite3_open", test_open ,0 }, { "sqlite3_open16", test_open16 ,0 }, { "sqlite3_complete16", test_complete16 ,0 }, Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.783 2008/10/27 13:59:34 danielk1977 Exp $ +** $Id: vdbe.c,v 1.784 2008/10/30 15:03:16 drh Exp $ */ #include "sqliteInt.h" #include #include "vdbeInt.h" @@ -2409,11 +2409,11 @@ ** that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot %s transaction - " "SQL statements in progress", rollback ? "rollback" : "commit"); - rc = SQLITE_ERROR; + rc = SQLITE_BUSY; }else if( i!=db->autoCommit ){ if( pOp->p2 ){ assert( i==1 ); sqlite3RollbackAll(db); db->autoCommit = 1; Index: test/capi3.test ================================================================== --- test/capi3.test +++ test/capi3.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script testing the callback-free C/C++ API. # -# $Id: capi3.test,v 1.67 2008/07/12 15:55:55 danielk1977 Exp $ +# $Id: capi3.test,v 1.68 2008/10/30 15:03:16 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -59,13 +59,16 @@ do_test capi3-1.1 { set STMT [sqlite3_prepare $DB {SELECT name FROM sqlite_master} -1 TAIL] sqlite3_finalize $STMT set TAIL } {} -do_test capi3-1.2 { +do_test capi3-1.2.1 { sqlite3_errcode $DB } {SQLITE_OK} +do_test capi3-1.2.2 { + sqlite3_extended_errcode $DB +} {SQLITE_OK} do_test capi3-1.3 { sqlite3_errmsg $DB } {not an error} do_test capi3-1.4 { set sql {SELECT name FROM sqlite_master;SELECT 10} @@ -90,13 +93,16 @@ set sql {SELECT namex FROM sqlite_master} catch { set STMT [sqlite3_prepare $DB $sql -1 TAIL] } } {1} -do_test capi3-1.8 { +do_test capi3-1.8.1 { sqlite3_errcode $DB } {SQLITE_ERROR} +do_test capi3-1.8.2 { + sqlite3_extended_errcode $DB +} {SQLITE_ERROR} do_test capi3-1.9 { sqlite3_errmsg $DB } {no such column: namex} ifcapable {utf16} { @@ -116,13 +122,16 @@ set sql [utf16 {SELECT namex FROM sqlite_master}] catch { set STMT [sqlite3_prepare16 $DB $sql -1 TAIL] } } {1} - do_test capi3-2.4 { + do_test capi3-2.4.1 { sqlite3_errcode $DB } {SQLITE_ERROR} + do_test capi3-2.4.2 { + sqlite3_extended_errcode $DB + } {SQLITE_ERROR} do_test capi3-2.5 { sqlite3_errmsg $DB } {no such column: namex} ifcapable schema_pragmas { @@ -155,11 +164,11 @@ } {SQLITE_OK} do_test capi3-3.3 { catch { set db2 [sqlite3_open /bogus/path/test.db {}] } - sqlite3_errcode $db2 + sqlite3_extended_errcode $db2 } {SQLITE_CANTOPEN} do_test capi3-3.4 { sqlite3_errmsg $db2 } {unable to open database file} do_test capi3-3.5 { @@ -822,16 +831,19 @@ } 0 do_test capi3-11.2 { set STMT [sqlite3_prepare $DB "SELECT func(b, a) FROM t1" -1 TAIL] sqlite3_step $STMT } {SQLITE_ROW} -do_test capi3-11.3 { +do_test capi3-11.3.1 { catchsql { COMMIT; } } {1 {cannot commit transaction - SQL statements in progress}} -do_test capi3-11.3.1 { +do_test capi3-11.3.2 { + sqlite3_extended_errcode $DB +} {SQLITE_BUSY} +do_test capi3-11.3.3 { sqlite3_get_autocommit $DB } 0 do_test capi3-11.4 { sqlite3_step $STMT } {SQLITE_ERROR} Index: test/capi3c.test ================================================================== --- test/capi3c.test +++ test/capi3c.test @@ -11,11 +11,11 @@ # This file implements regression tests for SQLite library. # # This is a copy of the capi3.test file that has been adapted to # test the new sqlite3_prepare_v2 interface. # -# $Id: capi3c.test,v 1.20 2008/10/12 00:27:54 shane Exp $ +# $Id: capi3c.test,v 1.21 2008/10/30 15:03:16 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -61,13 +61,16 @@ do_test capi3c-1.1 { set STMT [sqlite3_prepare_v2 $DB {SELECT name FROM sqlite_master} -1 TAIL] sqlite3_finalize $STMT set TAIL } {} -do_test capi3c-1.2 { +do_test capi3c-1.2.1 { sqlite3_errcode $DB } {SQLITE_OK} +do_test capi3c-1.2.2 { + sqlite3_extended_errcode $DB +} {SQLITE_OK} do_test capi3c-1.3 { sqlite3_errmsg $DB } {not an error} do_test capi3c-1.4 { set sql {SELECT name FROM sqlite_master;SELECT 10} @@ -79,13 +82,16 @@ set sql {SELECT namex FROM sqlite_master} catch { set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL] } } {1} -do_test capi3c-1.6 { +do_test capi3c-1.6.1 { sqlite3_errcode $DB } {SQLITE_ERROR} +do_test capi3c-1.6.2 { + sqlite3_extended_errcode $DB +} {SQLITE_ERROR} do_test capi3c-1.7 { sqlite3_errmsg $DB } {no such column: namex} @@ -106,13 +112,16 @@ set sql [utf16 {SELECT namex FROM sqlite_master}] catch { set STMT [sqlite3_prepare16_v2 $DB $sql -1 TAIL] } } {1} - do_test capi3c-2.4 { + do_test capi3c-2.4.1 { sqlite3_errcode $DB } {SQLITE_ERROR} + do_test capi3c-2.4.2 { + sqlite3_extended_errcode $DB + } {SQLITE_ERROR} do_test capi3c-2.5 { sqlite3_errmsg $DB } {no such column: namex} ifcapable schema_pragmas { @@ -1229,11 +1238,11 @@ set STMT [sqlite3_prepare_v2 $DB {SELECT * FROM t3} -1 TAIL] db progress 5 "expr 1" sqlite3_step $STMT } {SQLITE_INTERRUPT} do_test capi3c-21.2 { - sqlite3_errcode $DB + sqlite3_extended_errcode $DB } {SQLITE_INTERRUPT} do_test capi3c-21.3 { sqlite3_finalize $STMT } {SQLITE_INTERRUPT} do_test capi3c-21.4 { @@ -1248,10 +1257,13 @@ sqlite3_finalize $STMT } {SQLITE_INTERRUPT} do_test capi3c-21.7 { sqlite3_errcode $DB } {SQLITE_INTERRUPT} + do_test capi3c-21.8 { + sqlite3_extended_errcode $DB + } {SQLITE_INTERRUPT} } # Make sure sqlite3_result_error_code() returns the correct error code. # See ticket #2940 # Index: test/misc7.test ================================================================== --- test/misc7.test +++ test/misc7.test @@ -8,11 +8,11 @@ # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # -# $Id: misc7.test,v 1.24 2008/08/22 13:57:39 pweilbacher Exp $ +# $Id: misc7.test,v 1.25 2008/10/30 15:03:16 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl do_test misc7-1-misuse { @@ -395,11 +395,11 @@ catch {file attributes test.db-journal -permissions r--------} catch {file attributes test.db-journal -readonly 1} catchsql { SELECT count(*) FROM t3; } - } {1 {database is locked}} + } {1 {unable to open database file}} do_test misc7-17.2 { # Note that the -readonly flag must be cleared before the -permissions # are set. Otherwise, when using tcl 8.5 on mac, the fact that the # -readonly flag is set causes the attempt to set the permissions # to fail.