简体   繁体   中英

Open a file for edit with $EDITOR variable from a C program

I'm writing a C program and I want to open a file with the $EDITOR variable to make changes to it.

Assuming that I already checked if the $EDITOR is not set and that the part missing is to open the file for edit, is execl() the best option or should I use a different function?

I'm working under Arch linux.

Provided you are reasonably sure that you are in a single-threaded program, you have standard input and standard output (and probably standard error too) going to a terminal, and you aren't going to be upset by the signal handling imposed on you by system() , you can use system() to execute the command via a shell.

If you don't want to trust system() , then one of the exec*() family of functions (plus fork() ) will do what you need. You still need to be reasonably sure about the standard I/O channels — some editors do not react well to being given random files or piped input. You get to choose what signal handling to use — and how it is installed. You can get to deal with any thread-safety issues. It is a moderate amount of work.

You probably want to think carefully about whether you give the user the 'real' file to edit or a copy of it. Your code should probably recognize whether the editor exited successfully (and should probably ignore the output file if it did not exit successfully). You may also want to check whether the new version of the file is sensibly sized (not zero bytes, for example — but maybe that doesn't matter; it depends on the context). If the file being edited is a precious configuration file, you worry about this; if it is some previous commands to be re-executed (a history mechanism), you worry less about some of these details.


This is the history 'edit' command in a program of mine. It allows a user to specify a range of commands to be copied into a file, which is then edited, and the result (which may be empty) is then executed. It is code verbatim. Most of the function calls are to program-specific functions, but most of the names should be interpretable (I think). The ctxt_*() family of functions handle 'context', the current settings for the program. It works with more environment variables than yours needs to. The sql_file() function executes the commands from the input file in the current context — this code creates a new context in which to run the commands.

/* Edit history command(s) */
static void do_edit(char *s)
{
    FILE           *fp;
    long            c1;
    long            c2;
    char            tmpfname[BUFSIZ];
    char            sys[BUFSIZ];
    const char     *editor;

    if (ctxt_gethistory() != OP_ON)
    {
        cmd_warning(E_HISTORYOFF, "");
        return;
    }

    s = skipblanks(s);
    c1 = c2 = 0;
    if (sscanf(s, "%ld%ld", &c1, &c2) != 2)
        c2 = c1;

    if ((fp = fopen_namedtmpfile(tmpfname, sizeof(tmpfname))) == 0)
    {
        cmd_warning(E_FAILCREATETMPFILE, "");
        return;
    }

    hist_output(fp, c1, c2, H_COMMAND);
    fclose(fp);

    if ((editor = getenv("DBEDIT")) == NIL(char *) &&
        (editor = getenv("VISUAL")) == NIL(char *) &&
        (editor = getenv("EDITOR")) == NIL(char *))
        editor = DEF_EDITOR;
    esnprintf(sys, sizeof(sys), "%s %s", editor, tmpfname);
    system(sys);

    fp = fopen(tmpfname, "r");
    unlink(tmpfname);
    if (fp == 0)
    {
        cmd_warning(E_FAILREOPENTMPFILE, tmpfname);
    }
    else
    {
        /* Copy file to history log */
        if ((c1 = hist_input(fp)) > 0)
            cmd_set_promptnum(c1);
        fseek(fp, 0L, SEEK_SET);
        ctxt_newcontext();
        ctxt_newinput(fp, "<<temp>>");
        ctxt_sethistory(op_off);
        sql_file();
        ctxt_endcontext();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM