TIP:            83
Title:          Augment Tcl_EvalFile with Tcl_EvalChannel and Tcl_EvalUrl
Version:        $Revision: 1.6 $
Author:         Marian Szczepkowski <marian@mail.jozep.com.au>
Author:         <dgp@users.sf.net>
State:          Withdrawn
Type:           Project
Vote:           Pending
Created:        24-Jan-2002
Post-History:   
Tcl-Version:    8.5

~ Abstract

This TIP adds the ability to load Tcl files directly from URLs to the
core, together with a basic mechanism to simply evaluate a stream of
characters from a channel.

~ Proposal

I propose to split the ''Tcl_EvalFile'' function into two components
to enable the [[source]] command to use URL's to obtain source
material.

This will mean splitting ''Tcl_EvalFile'' into ''Tcl_EvalFile'' and
''Tcl_EvalChannel'' which are the two logical entities.  Maintaining
''Tcl_EvalFile'' will preserve backward compatability.

Creating ''Tcl_EvalChannel'' will provide generic functionality for
future use.

Adding ''Tcl_EvalUrl'' will enable handling standard URL format
strings.

This would enable this [[source http://anywhere.com/file.tcl]] to be
used.

Code will also need to be added to ''Tcl_SourceObjCmd'' to select
functionality requested.

~ Pro.

In a corporate environment where scripts are subject to change but the
interface is not, this allows scripts to be stored remotely on a
central server.

This also allows Tcl to interwork in a networked environment.

~ Con.

Security!!!!

This may mean in the long run adding a signing layer, but don't use it
if you don't want to.

~ Sample

I figure it looking something like this.
Snipped from 8.3 source.

~ Tcl_SourceObjCmd

|int
|Tcl_SourceObjCmd(dummy, interp, objc, objv)
|    ClientData dummy;		/* Not used. */
|    Tcl_Interp *interp;		/* Current interpreter. */
|    int objc;			/* Number of arguments. */
|    Tcl_Obj *CONST objv[];	/* Argument objects. */
|{
|    char *bytes;
|    int result;
|
|    if (objc != 2) {
|        Tcl_WrongNumArgs(interp, 1, objv, "fileName");
|        return TCL_ERROR;
|    }
|
|    bytes = Tcl_GetString(objv[1]);
|    if (strstr(ptr,"://")) {
|        result = Tcl_EvalFile(interp, bytes);
|    } else {
|        result = Tcl_EvalUrl(interp, bytes);
|    }
|    return result;
|}

~ Tcl_EvalFile

|int
|Tcl_EvalFile(interp, fileName)
|    Tcl_Interp *interp;         /* Interpreter in which to process file. */
|    char *fileName;             /* Name of file to process.  Tilde-substitution
|                                 * will be performed on this name. */
|{
|    int result, length;
|    struct stat statBuf;
|    Interp *iPtr;
|    Tcl_DString nameString;
|    char *name, *string;
|    Tcl_Channel chan;
|    Tcl_Obj *objPtr;
|
|    name = Tcl_TranslateFileName(interp, fileName, &nameString);
|    if (name == NULL) {
|        return TCL_ERROR;
|    }
|
|    result = TCL_ERROR;
|
|    if (TclStat(name, &statBuf) == -1) {
|        Tcl_SetErrno(errno);
|        Tcl_AppendResult(interp, "couldn't read file \"", fileName,
|                "\": ", Tcl_PosixError(interp), (char *) NULL);
|        goto end;
|    }
|
|    chan = Tcl_OpenFileChannel(interp, name, "r", 0644);
|    if (chan == (Tcl_Channel) NULL) {
|        Tcl_ResetResult(interp);
|        Tcl_AppendResult(interp, "couldn't read file \"", fileName,
|                "\": ", Tcl_PosixError(interp), (char *) NULL);
|        goto end;
|    }
|
|    result = Tcl_EvalChannel(interp, chan);
|
|  end:
|    Tcl_DStringFree(&nameString);
|    return result;
|}

~ Tcl_EvalUrl

|int
|Tcl_EvalUrl(interp, fileName)
|    Tcl_Interp *interp;	/* Interpreter in which to process file. */
|    char *fileName;	/* Name of URL to process. */
|{
|    return TCL_ERROR;
|}

~ Tcl_EvalChannel

|int
|Tcl_EvalChannel(interp, chan)
|    Tcl_Interp *interp; /* Interpreter in which to process file. */
|    Tcl_Channel chan;   /* Name of file to process. */
|{
|    int result, length;
|    struct stat statBuf;
|    char *oldScriptFile;
|    Interp *iPtr;
|    char *name, *string;
|    Tcl_Obj *objPtr;
|
|    result = TCL_ERROR;
|    objPtr = Tcl_NewObj();
|
|    if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
|        Tcl_Close(interp, chan);
|        Tcl_AppendResult(interp, "couldn't read file \"", fileName,
|                "\": ", Tcl_PosixError(interp), (char *) NULL);
|        goto end;
|    }
|    if (Tcl_Close(interp, chan) != TCL_OK) {
|        goto end;
|    }
|
|    iPtr = (Interp *) interp;
|    oldScriptFile = iPtr->scriptFile;
|    iPtr->scriptFile = fileName;
|    string = Tcl_GetStringFromObj(objPtr, &length);
|    result = Tcl_EvalEx(interp, string, length, 0);
|    iPtr->scriptFile = oldScriptFile;
|
|    if (result == TCL_RETURN) {
|        result = TclUpdateReturnInfo(iPtr);
|    } else if (result == TCL_ERROR) {
|        char msg[200 + TCL_INTEGER_SPACE];
|
|        /*
|         * Record information telling where the error occurred.
|         */
|
|        sprintf(msg, "\n    (file \"%.150s\" line %d)", fileName,
|                interp->errorLine);
|        Tcl_AddErrorInfo(interp, msg);
|    }
|
|  end:
|    Tcl_DecrRefCount(objPtr);
|    return result;
|}

~ Comments

The VFS extension interface of Tcl 8.4 plus the tclvfs and
vfs::http packages provide the ability to [[source]] an URL.
I believe that makes this proposal out of date.

~ Copyright

This document has been placed in the public domain.

