TIP 83: Augment Tcl<em>EvalFile with Tcl</em>EvalChannel and Tcl_EvalUrl

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
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.

History