Tcl Source Code

Artifact [534107d055]
Login

Artifact 534107d055e6d0271b8a5a362f30a13f4fefb98c:

Attachment "None" to ticket [403531ffff] added by dgp 2001-02-01 06:10:00.
? load.patch
? solaris
? unix/httpd
Index: doc/StaticPkg.3
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/StaticPkg.3,v
retrieving revision 1.3
diff -u -r1.3 StaticPkg.3
--- doc/StaticPkg.3	2000/04/14 23:01:54	1.3
+++ doc/StaticPkg.3	2001/01/31 22:47:43
@@ -24,8 +24,8 @@
 appropriate initialization procedure).  NULL means the package
 hasn't yet been incorporated into any interpreter.
 .AP char *pkgName in
-Name of the package;  should be properly capitalized (first letter
-upper-case, all others lower-case).
+Name of the package.  Should be the same string as passed
+by \fIinitProc\fR and \fIsafeInitProc\fR to \fBTcl_PkgProvide\fR.
 .AP Tcl_PackageInitProc *initProc in
 Procedure to invoke to incorporate this package into a trusted
 interpreter.
@@ -42,6 +42,23 @@
 has already been loaded into an interpreter.
 Once \fBTcl_StaticPackage\fR has been invoked for a package, it
 may be loaded into interpreters using the \fBload\fR command.
+.PP
+If \fIinterp\fR is not NULL, \fBTcl_StaticPackage\fR will check
+that package \fIpkgName\fR has been provided in \fIinterp\fR.
+If package \fIpkgName\fR has not been provided in \fIinterp\fR,
+\fBTcl_StaticPackage\fR will do nothing.  When used properly,
+a call to one of the initialization procedures, either
+.CS
+(*initProc)(\fIinterp\fR)
+.CE
+or
+.CS
+(*safeInitProc)(\fIinterp\fR)
+.CE
+should precede the call to \fBTcl_StaticPackage\fR.  Then package
+\fIpkgName\fR will be provided in \fIinterp\fR, and
+\fBTcl_StaticPackage\fR will do its work.
+.PP
 \fBTcl_StaticPackage\fR is normally invoked only by the \fBTcl_AppInit\fR
 procedure for the application, not by packages for themselves
 (\fBTcl_StaticPackage\fR should only be invoked for statically
@@ -59,11 +76,13 @@
 .CE
 The \fIinterp\fR argument identifies the interpreter in which the package
 is to be loaded.  The initialization procedure must return \fBTCL_OK\fR or
-\fBTCL_ERROR\fR to indicate whether or not it completed successfully; in
+\fBTCL_ERROR\fR to indicate whether or not it completed successfully.  In
 the event of an error it should set the interpreter's result to point to an
-error message.  The result or error from the initialization procedure will
-be returned as the result of the \fBload\fR command that caused the
-initialization procedure to be invoked.
+error message, which will be returned as the result of the \fBload\fR
+command that caused the initialization procedure to be invoked.  If
+the initialization procedure completes successfully, it must provide
+the package \fIpkgName\fR.  If it fails to do so, an error message
+reporting that failure will be returned as the result of \fBload\fR.
 
 .SH KEYWORDS
 initialization procedure, package, static linking
Index: doc/info.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/info.n,v
retrieving revision 1.5
diff -u -r1.5 info.n
--- doc/info.n	2000/09/07 14:27:48	1.5
+++ doc/info.n	2001/01/31 22:47:43
@@ -110,10 +110,11 @@
 .TP
 \fBinfo loaded \fR?\fIinterp\fR?
 Returns a list describing all of the packages that have been loaded into
-\fIinterp\fR with the \fBload\fR command.
+\fIinterp\fR with the \fBload\fR command, and all statically-loaded
+packages initialized in \fIinterp\fR by \fBTcl_StaticPackage\fR.
 Each list element is a sub-list with two elements consisting of the
 name of the file from which the package was loaded and the name of
-the package.
+the package provided when that file was loaded.
 For statically-loaded packages the file name will be an empty string.
 If \fIinterp\fR is omitted then information is returned for all packages
 loaded in any interpreter in the process.
Index: doc/load.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/load.n,v
retrieving revision 1.6
diff -u -r1.6 load.n
--- doc/load.n	2000/09/07 14:27:49	1.6
+++ doc/load.n	2001/01/31 22:47:43
@@ -61,12 +61,16 @@
 .CS
 typedef int Tcl_PackageInitProc(Tcl_Interp *\fIinterp\fR);
 .CE
-The \fIinterp\fR argument identifies the interpreter in which the
-package is to be loaded.  The initialization procedure must return
-\fBTCL_OK\fR or \fBTCL_ERROR\fR to indicate whether or not it completed
-successfully;  in the event of an error it should set the interpreter's result
-to point to an error message.  The result of the \fBload\fR command
-will be the result returned by the initialization procedure.
+The \fIinterp\fR argument identifies the interpreter in which the package
+is to be loaded.  The initialization procedure must return \fBTCL_OK\fR or
+\fBTCL_ERROR\fR to indicate whether or not it completed successfully.  In
+the event of an error it should set the interpreter's result to point to an
+error message, which will be returned as the result of \fBload\fR.
+If the initialization procedure completes successfully, it must provide
+the package \fIpackageName\fR.  If it fails to do so, \fBload\fR will
+return an error message reporting that failure.  If the initialization
+procedure successfully provides the package \fIpackageName\fR in
+\fIinterp\fR, \fBload\fR returns an empty string.
 .PP
 The actual loading of a file will only be done once for each \fIfileName\fR
 in an application.  If a given \fIfileName\fR is loaded into multiple
@@ -88,11 +92,14 @@
 take the last element of \fIfileName\fR, strip off the first
 three characters if they are \fBlib\fR, and use any following
 .VS
-alphabetic and underline characters as the module name.
+alphabetic and underline characters to create the package name.
+Finally the extracted alphabetic and underline characters have
+their case converted in the same manner used to determine the
+name of the initialization procedure.
 .VE
-For example, the command \fBload libxyz4.2.so\fR uses the module
-name \fBxyz\fR and the command \fBload bin/last.so {}\fR uses the
-module name \fBlast\fR.
+For example, the command \fBload libxyz4.2.so\fR uses the package
+name \fBXyz\fR and the command \fBload bin/last.so {}\fR uses the
+package name \fBLast\fR.
 .VS "" br
 .PP
 If \fIfileName\fR is an empty string, then \fIpackageName\fR must
Index: generic/tclLoad.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclLoad.c,v
retrieving revision 1.4
diff -u -r1.4 tclLoad.c
--- generic/tclLoad.c	1999/12/01 00:08:28	1.4
+++ generic/tclLoad.c	2001/01/31 22:47:44
@@ -27,9 +27,7 @@
 				 * package was loaded.  An empty string
 				 * means the package is loaded statically.
 				 * Malloc-ed. */
-    char *packageName;		/* Name of package prefix for the package,
-				 * properly capitalized (first letter UC,
-				 * others LC), no "_", as in "Net". 
+    char *packageName;		/* Name of package.
 				 * Malloc-ed. */
     ClientData clientData;	/* Token for the loaded file which should be
 				 * passed to TclpUnloadFile() when the file
@@ -68,7 +66,7 @@
  * The following structure represents a particular package that has
  * been incorporated into a particular interpreter (by calling its
  * initialization procedure).  There is a list of these structures for
- * each interpreter, with an AssocData value (key "load") for the
+ * each interpreter, with an AssocData value (key "tclLoad") for the
  * interpreter that points to the first package (if any).
  */
 
@@ -161,7 +159,8 @@
 	slaveIntName = Tcl_GetString(objv[3]);
 	target = Tcl_GetSlave(interp, slaveIntName);
 	if (target == NULL) {
-	    return TCL_ERROR;
+	    code = TCL_ERROR;
+	    goto done;
 	}
     }
 
@@ -171,8 +170,12 @@
      * it meets any of the following conditions:
      *  - Its name and file match the once we're looking for.
      *  - Its file matches, and we weren't given a name.
-     *  - Its name matches, the file name was specified as empty, and there
-     *    is only no statically loaded package with the same name.
+     *  - Its name matches, the file name was specified as empty, and the
+     *    loaded package was the first of all those loaded with the same
+     *    name.  Since statically loaded packages are loaded using
+     *    Tcl_StaticPackage() during application startup, a statically
+     *    loaded package is preferred over all dynamically loaded 
+     *    packages with the same name.
      */
     Tcl_MutexLock(&packageMutex);
 
@@ -185,8 +188,6 @@
 	    Tcl_DStringAppend(&pkgName, packageName, -1);
 	    Tcl_DStringSetLength(&tmp, 0);
 	    Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
-	    Tcl_UtfToLower(Tcl_DStringValue(&pkgName));
-	    Tcl_UtfToLower(Tcl_DStringValue(&tmp));
 	    if (strcmp(Tcl_DStringValue(&tmp),
 		    Tcl_DStringValue(&pkgName)) == 0) {
 		namesMatch = 1;
@@ -300,26 +301,31 @@
 		}
 		Tcl_DStringAppend(&pkgName, pkgGuess, (p - pkgGuess));
 		ckfree((char *)pargv);
+
+		/*
+		 * Normalize the guessed package name to have the
+		 * capitalization expected for the prefix of the
+		 * initialization procedures.
+		 */
+
+		Tcl_DStringSetLength(&pkgName,
+			Tcl_UtfToTitle(Tcl_DStringValue(&pkgName)));
 	    }
 	}
 
-	/*
-	 * Fix the capitalization in the package name so that the first
-	 * character is in caps (or title case) but the others are all
-	 * lower-case.
-	 */
-    
-	Tcl_DStringSetLength(&pkgName,
-		Tcl_UtfToTitle(Tcl_DStringValue(&pkgName)));
-
 	/*
-	 * Compute the names of the two initialization procedures,
-	 * based on the package name.
+	 * Compute the names of the two initialization procedures.
+	 * The common prefix for the names of both initialization
+	 * procedures is derived from the package name by forcing
+	 * the first character to upper case and all other characters
+	 * to lower case.  The package name itself is not changed.
 	 */
     
 	Tcl_DStringAppend(&initName, Tcl_DStringValue(&pkgName), -1);
+	Tcl_DStringSetLength(&initName,
+		Tcl_UtfToTitle(Tcl_DStringValue(&initName)));
+	Tcl_DStringAppend(&safeInitName, Tcl_DStringValue(&initName), -1);
 	Tcl_DStringAppend(&initName, "_Init", 5);
-	Tcl_DStringAppend(&safeInitName, Tcl_DStringValue(&pkgName), -1);
 	Tcl_DStringAppend(&safeInitName, "_SafeInit", 9);
 
 	/*
@@ -373,9 +379,13 @@
 	if (pkgPtr->safeInitProc != NULL) {
 	    code = (*pkgPtr->safeInitProc)(target);
 	} else {
+	    Tcl_DStringSetLength(&tmp, 0);
+	    Tcl_DStringAppend(&tmp, pkgPtr->packageName, -1);
+	    Tcl_DStringSetLength(&tmp,
+		    Tcl_UtfToTitle(Tcl_DStringValue(&tmp)));
 	    Tcl_AppendResult(interp,
 		    "can't use package in a safe interpreter: ",
-		    "no ", pkgPtr->packageName, "_SafeInit procedure",
+		    "no ", Tcl_DStringValue(&tmp), "_SafeInit procedure",
 		    (char *) NULL);
 	    code = TCL_ERROR;
 	    goto done;
@@ -385,27 +395,53 @@
     }
 
     /*
-     * Record the fact that the package has been loaded in the
-     * target interpreter.
+     * If the initialization procedure failed, return the error
+     * message, and don't register the load in the target interp.
      */
-
-    if (code == TCL_OK) {
-	/*
-	 * Refetch ipFirstPtr: loading the package may have introduced
-	 * additional static packages at the head of the linked list!
-	 */
 
-	ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad",
-		(Tcl_InterpDeleteProc **) NULL);
-	ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
-	ipPtr->pkgPtr = pkgPtr;
-	ipPtr->nextPtr = ipFirstPtr;
-	Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
-		(ClientData) ipPtr);
-    } else {
+    if (code != TCL_OK) {
 	TclTransferResult(target, code, interp);
+	goto done;
+    }
+
+    /*
+     * If the initialization procedure did not provide the package we
+     * associate with loading the file, we want to report that as an
+     * error to the caller, and we want to not register that package
+     * as one loaded in target for later return by [info loaded target].
+     */
+
+    if ((Tcl_PkgPresent(target, pkgPtr->packageName, NULL, 0) == NULL) ) {
+	if (target == interp) {
+	    Tcl_ResetResult(interp);
+	}
+        Tcl_AppendResult(interp, "loading \"", fullFileName,
+		"\" did not provide package \"", pkgPtr->packageName, "\"",
+		(char *) NULL);
+	if (target != interp) {
+	    Tcl_AppendResult(interp, " in ", Tcl_GetString(objv[3]),
+		    (char *) NULL);
+	}
+        code = TCL_ERROR;
+        goto done;
     }
 
+    /*
+     * Record the fact that the package has been loaded in the
+     * target interpreter.
+     *
+     * Refetch ipFirstPtr: loading the package may have introduced
+     * additional static packages at the head of the linked list!
+     */
+
+    ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad",
+	    (Tcl_InterpDeleteProc **) NULL);
+    ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
+    ipPtr->pkgPtr = pkgPtr;
+    ipPtr->nextPtr = ipFirstPtr;
+    Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
+	    (ClientData) ipPtr);
+
     done:
     Tcl_DStringFree(&pkgName);
     Tcl_DStringFree(&initName);
@@ -439,9 +475,7 @@
 					 * package has already been loaded
 					 * into the given interpreter by
 					 * calling the appropriate init proc. */
-    char *pkgName;			/* Name of package (must be properly
-					 * capitalized: first letter upper
-					 * case, others lower case). */
+    char *pkgName;			/* Name of package */
     Tcl_PackageInitProc *initProc;	/* Procedure to call to incorporate
 					 * this package into a trusted
 					 * interpreter. */
@@ -454,6 +488,18 @@
 {
     LoadedPackage *pkgPtr;
     InterpPackage *ipPtr, *ipFirstPtr;
+
+    /*
+     * When used properly, Tcl_StaticPackage() should be called only
+     * after the appropriate init proc has been called to provide
+     * pkgName in interp.  If package pkgName has not been provided
+     * in interp, return immediately.  Do not register a false claim
+     * that package pkgName is loaded in interp.
+     */
+
+    if (interp != NULL && Tcl_PkgPresent(interp, pkgName, NULL, 0) == NULL) {
+	return;
+    }
 
     /*
      * Check to see if someone else has already reported this package as
Index: generic/tclTest.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclTest.c,v
retrieving revision 1.22
diff -u -r1.22 tclTest.c
--- generic/tclTest.c	2000/11/24 11:27:37	1.22
+++ generic/tclTest.c	2001/01/31 22:47:44
@@ -49,6 +49,13 @@
 static Tcl_Interp *delInterp;
 
 /*
+ * Dynamic string shared by TeststaticpkgCmd and StaticInitProc; used
+ * to covertly pass the package name.
+ */
+
+static Tcl_DString packageName;
+
+/*
  * One of the following structures exists for each asynchronous
  * handler created by the "testasync" command".
  */
@@ -3053,6 +3060,11 @@
     if (Tcl_GetInt(interp, argv[3], &loaded) != TCL_OK) {
 	return TCL_ERROR;
     }
+    Tcl_DStringInit(&packageName);
+    Tcl_DStringAppend(&packageName, argv[1], -1);
+    if (loaded) {
+	StaticInitProc(interp);
+    }
     Tcl_StaticPackage((loaded) ? interp : NULL, argv[1], StaticInitProc,
 	    (safe) ? StaticInitProc : NULL);
     return TCL_OK;
@@ -3063,6 +3075,7 @@
     Tcl_Interp *interp;			/* Interpreter in which package
 					 * is supposedly being loaded. */
 {
+    Tcl_PkgProvide(interp, Tcl_DStringValue(&packageName), "0");
     Tcl_SetVar(interp, "x", "loaded", TCL_GLOBAL_ONLY);
     return TCL_OK;
 }
Index: tests/load.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/load.test,v
retrieving revision 1.7
diff -u -r1.7 load.test
--- tests/load.test	2000/04/10 17:19:01	1.7
+++ tests/load.test	2001/01/31 22:47:44
@@ -66,9 +66,9 @@
     list [pkga_eq abc def] [info commands pkga_*]
 } {0 {pkga_eq pkga_quote}}
 interp create -safe child
-test load-2.2 {loading into a safe interpreter, with package name conversion} \
+test load-2.2 {loading into a safe interpreter, with explicit package name} \
 	[list $dll $loaded] {
-    load [file join $testDir pkgb$ext] pKgB child
+    load [file join $testDir pkgb$ext] Pkgb child
     list [child eval pkgb_sub 44 13] [catch {child eval pkgb_unsafe} msg] $msg \
 	    [catch {pkgb_sub 12 10} msg2] $msg2
 } {31 1 {invalid command name "pkgb_unsafe"} 1 {invalid command name "pkgb_sub"}}
@@ -109,22 +109,35 @@
 "load [file join $testDir pkge$ext] pkge x"} {POSIX ENOENT {no such file or directory}}}
 
 test load-4.1 {reloading package into same interpreter} [list $dll $loaded] {
-    list [catch {load [file join $testDir pkga$ext] pkga} msg] $msg
+    list [catch {load [file join $testDir pkga$ext] Pkga} msg] $msg
 } {0 {}}
 test load-4.2 {reloading package into same interpreter} [list $dll $loaded] {
     list [catch {load [file join $testDir pkga$ext] pkgb} msg] $msg
 } "1 {file \"[file join $testDir pkga$ext\"] is already loaded for package \"Pkga\"}"
+test load-4.3 {reloading package into same interpreter} [list $dll $loaded] {
+    list [catch {load [file join $testDir pkga$ext] pkga} msg] $msg
+} "1 {file \"[file join $testDir pkga$ext\"] is already loaded for package \"Pkga\"}"
 
 test load-5.1 {file name not specified and no static package: pick default} \
 	[list $dll $loaded] {
     catch {interp delete x}
     interp create x
-    load [file join $testDir pkga$ext] pkga
-    load {} pkga x
+    load [file join $testDir pkga$ext] Pkga
+    load {} Pkga x
     set result [info loaded x]
     interp delete x
     set result
 } "{[file join $testDir pkga$ext] Pkga}"
+test load-5.2 {file name not specified and no matching package} \
+	[list $dll $loaded] {
+    catch {interp delete x}
+    interp create x
+    load [file join $testDir pkga$ext] Pkga
+    set result [list [catch {load {} pkga x} msg] $msg]
+    lappend result [info loaded x]
+    interp delete x
+    set result
+} {1 {package "pkga" isn't loaded statically} {}}
 
 # On some platforms, like SunOS 4.1.3, these tests can't be run because
 # they cause the process to exit.
@@ -133,6 +146,13 @@
     catch {load foo foo}
 } {1}
 
+test load-6.2 {_Init procedure fails to provide package} {
+    list [catch {load [file join $testDir pkgd$ext] Pkgd} msg] $msg
+} "1 {loading \"[file join $testDir pkgd$ext]\" did not provide package \"Pkgd\"}"
+test load-6.3 {_SafeInit procedure fails to provide package} {
+    list [catch {load {} Pkgd child} msg] $msg
+} "1 {loading \"\" did not provide package \"Pkgd\" in child}"
+
 if {[info command teststaticpkg] != ""} {
     test load-7.1 {Tcl_StaticPackage procedure} [list $dll $loaded] {
 	set x "not loaded"
@@ -152,19 +172,21 @@
     test load-7.3 {Tcl_StaticPackage procedure} [list $dll $loaded] {
 	set x "not loaded"
 	teststaticpkg More 0 1
+	set result $x
+	set x "not loaded"
 	load {} More
-	set x
-    } {not loaded}
+	list $result $x
+    } {loaded {not loaded}}
     test load-7.4 {Tcl_StaticPackage procedure, redundant calls} \
 	    [list $dll $loaded] {
 	teststaticpkg Double 0 1
 	teststaticpkg Double 0 1
-	info loaded
-    } "{{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkge$ext] Pkge} {[file join $testDir pkgb$ext] Pkgb} {[file join $testDir pkga$ext] Pkga} $alreadyTotalLoaded"
+	info loaded {}
+    } "{{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkga$ext] Pkga} $alreadyTotalLoaded"
 
     test load-8.1 {TclGetLoadedPackages procedure} [list $dll $loaded] {
 	info loaded
-    } "{{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkge$ext] Pkge} {[file join $testDir pkgb$ext] Pkgb} {[file join $testDir pkga$ext] Pkga} $alreadyTotalLoaded"
+    } "{{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkgd$ext] Pkgd} {[file join $testDir pkge$ext] pkge} {[file join $testDir pkgb$ext] Pkgb} {[file join $testDir pkga$ext] Pkga} $alreadyTotalLoaded"
     test load-8.2 {TclGetLoadedPackages procedure} [list $dll $loaded] {
 	list [catch {info loaded gorp} msg] $msg
     } {1 {could not find interpreter "gorp"}}
@@ -172,7 +194,7 @@
 	list [info loaded {}] [info loaded child]
     } "{{{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkga$ext] Pkga} $alreadyLoaded} {{{} Test} {[file join $testDir pkgb$ext] Pkgb}}"
     test load-8.4 {TclGetLoadedPackages procedure} [list $dll $loaded] {
-	load [file join $testDir pkgb$ext] pkgb
+	load [file join $testDir pkgb$ext] Pkgb
 	list [info loaded {}] [lsort [info commands pkgb_*]]
     } "{{[file join $testDir pkgb$ext] Pkgb} {{} Double} {{} More} {{} Another} {{} Test} {[file join $testDir pkga$ext] Pkga} $alreadyLoaded} {pkgb_sub pkgb_unsafe}"
     interp delete child
Index: unix/dltest/pkgd.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/dltest/pkgd.c,v
retrieving revision 1.4
diff -u -r1.4 pkgd.c
--- unix/dltest/pkgd.c	2000/04/04 08:06:07	1.4
+++ unix/dltest/pkgd.c	2001/01/31 22:47:44
@@ -117,10 +117,15 @@
     if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) {
 	return TCL_ERROR;
     }
+/*
+ * We forget to provide the package "Pkgd"
+ *
     code = Tcl_PkgProvide(interp, "Pkgd", "7.3");
     if (code != TCL_OK) {
 	return code;
     }
+ *
+ */
     Tcl_CreateObjCommand(interp, "pkgd_sub", Pkgd_SubObjCmd,
 	    (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
     Tcl_CreateObjCommand(interp, "pkgd_unsafe", Pkgd_UnsafeObjCmd,
@@ -155,10 +160,15 @@
     if (Tcl_InitStubs(interp, TCL_VERSION, 1) == NULL) {
 	return TCL_ERROR;
     }
+/*
+ * We forget to provide the package "Pkgd"
+ *
     code = Tcl_PkgProvide(interp, "Pkgd", "7.3");
     if (code != TCL_OK) {
 	return code;
     }
+ *
+ */
     Tcl_CreateObjCommand(interp, "pkgd_sub", Pkgd_SubObjCmd, (ClientData) 0,
 	    (Tcl_CmdDeleteProc *) NULL);
     return TCL_OK;