Tcl / Tk 'invalid command name ""' when controlling through a pipe

I was trying to tweak a simple GUI for a C program by opening a C program and executing wish

in a child, then concatenating a group of tcl / tk commands from the parent into it. After creating the form, I would let the C program just keep reading the output of the tcl program and responding to it.

It mostly works for me, but in this example I keep getting the message from tcl:

invalid command name ""

      

It doesn't break anything, but I really don't understand what's causing it.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DIE do { perror(__FUNCTION__); exit(EXIT_FAILURE); } while (0)
#define LEN(a) (sizeof(a) / sizeof(*a))

int child(int p2c[2], int c2p[2]) {
    if (close(p2c[1])) DIE;
    if (close(c2p[0])) DIE;

    if (dup2(p2c[0], fileno(stdin)) < 0) DIE;
    if (dup2(c2p[1], fileno(stdout)) < 0) DIE;

    char * cmds[] = { "wish", NULL };
    execvp(cmds[0], cmds);
    DIE;
}

int parent(int p2c[2], int c2p[2]) {
    if (close(p2c[0])) DIE;
    if (close(c2p[1])) DIE;

    char init[] = "button .b -text {Print} -command {puts hi}\n"
                  "button .x -text {Exit} -command {exit}\n"
                  "grid .b -column 0 -row 0\n"
                  "grid .x -column 0 -row 1\n";
    if (write(p2c[1], init, LEN(init)) < LEN(init)) DIE;
    if (close(p2c[1])) DIE;

    char buf[1<<10];
    ssize_t s;

    while ((s = read(c2p[0], buf, LEN(buf))) > 0) {
        buf[s-1] = '\0';

        printf("i read '%s'\n", buf);
    }

    return 0;
}

int main(int argc, char ** argv) {
    int p2c[2]; // parent to child pipe
    int c2p[2];

    if (pipe(p2c)) DIE;
    if (pipe(c2p)) DIE;

    switch (fork()) {
        case -1: DIE;              break;
        case  0: child(p2c, c2p);  break;
        default: parent(p2c, c2p); break;
    }

    return EXIT_SUCCESS;
}

      

Any idea why it is claiming an empty string is an invalid command?

+3


source to share


1 answer


The problem is that your expression for determining the length of the string is wrong. strlen(init)

will LEN(init)

return a value that is less than is returned because the latter includes a terminating NUL character . This means that you write that NUL is on the pipe (where it doesn't belong) and Tcl interprets it as a weird (but legal!) Command name. Of course, there is no such command (unless you execute proc \u0000 {} {puts BOOM}

), but it still checks if something it knows in its script autoloading mechanism can provide this command. (By adding this little routine to the script you are sending, remember to double the value \

), this message is thrown instead:

i read 'BOOM
'


QED.

If you change write

to send the correct number of bytes, everything works as you expect.

+3


source







All Articles