0

我有以下程序:

rename proc _proc
_proc proc {name args body} {
    global pass_log_trace

    set g_log_trace "0"
    if {[info exists pass_log_trace]} {
        set g_log_trace $pass_log_trace
    }

    # simple check if we have double declaration of the same procedure
    if {[info procs $name] != ""} {
        puts "\nERROR: redeclaration of procedure: $name"
    }

    _proc $name $args $body

    if {$g_log_trace != 0} {
        trace add execution $name enter trace_report_enter
        trace add execution $name leave trace_report_leave
    }
}

它是从使用 Tcl 解释器C 库构建的 C shell 调用的。shell的代码如下:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <tcl.h>

#include <readline/readline.h>
#include <readline/history.h>


/* Global variables */

static char init_file[256];
static char history_file[256];
static pid_t sfg_pid;
static Tcl_Interp *tcl_interp = NULL;

static int help(char *prog);

/**
 * Print the application help.
 * @param prog
 * @return
 */
static int
help(char *prog)
{
    printf("Usage: %s [OPTIONS]\n", prog);
    printf("\n");
    printf("  -h|-?                   Print this message and exit.\n");
    printf("  --init/-i file          Source this file when tcl is started.\n");
    printf("  --history/-f file       Read/Save history using this existing file.\n");
    printf("  --log/-l file           Save the Tcl log to the specified file.\n");
    printf("\n");

    exit(EXIT_SUCCESS);
}

int
main(int argc, char ** argv)
{
    const int buf_size = 1024;
    const useconds_t sfg_init_tmo_usec = 100000;
    char buf[buf_size+1];
    int  rc;
    char *inp = NULL;
    char pwd[buf_size+1];
    int  hfile;
    char *prompt = NULL;

    int c;
    int option_index = 0;
    struct option long_options[] = {
        /*name            arg     flag    val */
        {"help",          0,      0,      'h'},
        {"init",          1,      0,      'i'},
        {"log",           1,      0,      'l'},
        {"configuration", 1,      0,      'c'},
        {0,               0,      0,      0}
    };

    /* default values */
    strcpy(init_file, "log_init.tcl");
    sfg_pid = 0;

    /**
     * Options processing...
     */

    while ((c = getopt_long (argc, argv, "?hi:f:s:t:p:b:l:c:er",
                             long_options, &option_index)) != -1) {
        switch (c) {
        case 'h':
        case '?':
            help(argv[0]);
            break;

        case 'i':
            strncpy(init_file, optarg, sizeof(init_file)-1);
            break;

        default:
            printf ("?? getopt returned character code %c ??\n", c);
        }
    }

    if (optind < argc) {
        printf ("non-option ARGV-elements: ");
        while (optind < argc)
            printf ("%s ", argv[optind++]);
        printf ("\n");
        exit(EXIT_FAILURE);
    }

    /**
     * Start and configure tcl interpreter
     */
    if ((tcl_interp = Tcl_CreateInterp()) == NULL) {
        printf("Could not create Tcl interpreter: %s\n", Tcl_ErrnoMsg(Tcl_GetErrno()));
        exit(EXIT_FAILURE);
    }

    /* allocate a prompt string, default to diag_tcl> , link to TCL variable */
    if ((prompt = Tcl_Alloc(256)) == NULL) {
        printf("Cannot allocate a prompt variable: %s\n", tcl_interp->result);
        exit(EXIT_FAILURE);
    }
    strncpy(prompt, "diag_tcl> ", 256);
    if (Tcl_LinkVar(tcl_interp, "g_shell_prompt", (char *)&prompt, TCL_LINK_STRING) != TCL_OK) {
        printf("Unable to link to a prompt global variable: %s\n", tcl_interp->result);
    }

    /* Source an init file if specified */
    if (init_file[0]) {
        strcpy(buf, "source ");
        strncat(buf, init_file, (buf_size - strlen(buf)));
        if ((rc = Tcl_Eval(tcl_interp, buf)) != TCL_OK) {
            printf("Tcl Interpreter Error: %s\n", tcl_interp->result);
        }
    }

    /**
     * Main single command loop
     */
    while (1) {
        if (inp) {
            free(inp);
            inp = NULL;
        }

        inp = readline(prompt);
        if (inp == NULL)
            break;

        if (*inp == '\n' || *inp == '\r' || *inp == 0) {
            continue;
        }
        if (feof(stdin))
            break;

        if ((rc = Tcl_Eval(tcl_interp, inp)) != TCL_OK) {
            printf("Tcl Interpreter Error: %s\n",
                    Tcl_GetVar(tcl_interp, "errorInfo", TCL_GLOBAL_ONLY));
        }
    }

    return 0;
}

生成文件:

INC=-I/net/tools/include
LIB=-L/net/tools/lib -L/lib32 -L/usr/lib -m32
BIN=diag.lin

GCC                 = gcc

all: diag_tclsh

diag_tclsh: diag_tclsh.c
    $(GCC) $^ $(INC) $(LIB) -ltcl8.4 -lreadline -lncurses -ltermcap -o $@

install:
    cp -f strad /net/tools/bin/$(BIN)

clean:
    -rm -f diag_tclsh

此过程的目的主要是为代码中的所有过程添加入口和出口点跟踪器。但是,由于某种原因,它也删除了命名空间范围。例如,这样的代码:

namespace eval bob {
    namespace eval joe {
        proc proc1 {} {}
    }
    proc proc2 {} {
        puts "proc2"
    }
}

puts "Namespace calling [info procs ::bob\::*]"

不会在bob命名空间中创建过程,而是在全局命名空间中。调用namespace current总是返回::

有任何想法吗?

4

1 回答 1

2

问题是标准proc创建相对于当前名称空间的命令(当然,除非您使用绝对名称),而您的替换推送具有全局名称空间 ( ::) 作为其当前 NS 的堆栈框架。这意味着当您调用 时_proc,您使用的是错误的命名空间。

修复方法是用于在uplevel 1调用_proc者的上下文中调用,或者在必要时使用调用者的命名空间(可通过 发现uplevel 1 namespace current)限定名称。在您的情况下,您最好使用第二种技术,因为您还需要将名称用于其他目的(进行存在检查,添加执行跟踪):

rename proc _proc
_proc proc {name args body} {
    global pass_log_trace

    set g_log_trace "0"
    if {[info exists pass_log_trace]} {
        set g_log_trace $pass_log_trace
    }

    ######## ADDED CODE STARTS ########
    # Qualify the name if necessary:
    if {![string match "::*" $name]} {
        set name [uplevel 1 namespace current]::$name
    }
    ######## ADDED CODE ENDS ########

    # simple check if we have double declaration of the same procedure
    if {[info procs $name] != ""} {
        puts "\nERROR: redeclaration of procedure: $name"
    }

    _proc $name $args $body

    if {$g_log_trace != 0} {
        trace add execution $name enter trace_report_enter
        trace add execution $name leave trace_report_leave
    }
}
于 2012-09-26T08:58:01.897 回答