Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm using ncurses to create a tui to move through directories. I have the program opening editable files in vi using a system call

def_prog_mode();
endwin();
sprintf(command, "%s %s", di->Settings.editor, di->Directory.File[di->Variables.item_selection].Path);
system(command);
reset_prog_mode();

but when I exit out of vi to return to the program vi reports the error

E138: Can't write viminfo file /home/user/.di/.viminfo!
Press ENTER or type command to continue

.di is the config file for my program and I have no idea why it is trying to include itself in the path of the .viminfo file.

Does anyone have any ideas how to fix this? All my variables are prefixed with di_ so I have no idea why this is happening. All help is greatly appreciated!

  1. The error occurs within vi and not my program.
  2. There is no overflow in sprintf because everyting has a null terminating character placed on the end.
  3. The editor preference comes as a default of vi but is manipulated by the user through the config file.
  4. /home/user/.di is not a folder it is a file and is only ever called in the acquisition of the setting files contents, and in the initial creation and population of the file and all fopen instances are closed immediatley after.
  5. the purpose of not using ncurses to create an editor is to give the user the option of using their own editor (many available on server being used)
  6. There does not appear to be a syscall failure. This stil appears to be an error on vi's part.


Just to note: This is a merely annoyance issue. The failure in vi does not cause a failure in my program.


This was implemented into my program in my recent rewrite of the entire program. It allows the user to change settings in a config file ".di" in the home directory. Ever since this implementation, on exiting vi (or vim), vi (or vim) state the error and then successfully returns to the program.

#include "di.h"

void settings(DI *di, int mode) {

    check_settings_exist(di);

    switch (mode) {

        case 0 :
            read_settings(di);
            break;

        case 1 :
            write_settings(di);
            break;

    }

}

void check_settings_exist(DI *di) {

    if (!di->Settings.file_location) {

        char *di_settings_file_path = (char *) getenv("HOME");
        strcat(di_settings_file_path, "/");
        strcat(di_settings_file_path, di_default_settings_file);
        strcat(di_settings_file_path, "\0");

        di->Settings.file_location = (char *) calloc(sizeof(di_settings_file_path) + 12, sizeof(char));
        strcpy(di->Settings.file_location, di_settings_file_path);

    }

    if(access(di->Settings.file_location, F_OK)) {

        int counter;
        FILE * di_settings_file = fopen(di->Settings.file_location, "w+");

        for (counter = 0; di_settings_file_default[counter][0]; counter++) {

            fprintf(di_settings_file, "%s%s\n", di_settings_file_default[counter][0], di_settings_file_default[counter][1]);

        }

        fclose(di_settings_file);

    }

}

void read_settings(DI *di) {

    FILE * di_settings_file = fopen(di->Settings.file_location, "r");

    di->Settings.editor = calloc(16, sizeof(char));
    di->Settings.deletion_mode = calloc(16, sizeof(char));

    fscanf(di_settings_file, "%*[^:]:%[^\n]\n", di->Settings.editor);
    fscanf(di_settings_file, "%*[^:]:%[^\n]\n", di->Settings.deletion_mode);

    fclose(di_settings_file);

}

void write_settings(DI *di) {

    FILE * di_settings_file = fopen(di->Settings.file_location, "w+");

    //

    fclose(di_settings_file);

}
share|improve this question

migrated from stackoverflow.com Jun 24 '13 at 3:51

This question came from our site for professional and enthusiast programmers.

    
I don't understand what is your command buffer before running system(command) –  Basile Starynkevitch Jun 22 '13 at 17:15
    
And google for can't write viminfo file gives a lot of relevant answers. –  Basile Starynkevitch Jun 22 '13 at 17:23
    
The command buffer for an example file would be "vi (absolute path to file)" –  James C Jun 22 '13 at 17:39
    
Usually (absolute path to file) is not a file name. If it happens to be your filename, it cannot work (because you need to escape such spaces, and the parenthesis, for the sh run by system(command) ....) –  Basile Starynkevitch Jun 22 '13 at 17:41
    
substitute (absolute path to file) with /home/user/file.c, I just put that there to show that whenever the call is made it is in the format of "(editor) (absolute path to file)" vi is merely the default. The error does not lie within this part of the program as it has only occured since I rewrote the entire program and implemented a setting file. I also check system for an error and none are returned. –  James C Jun 22 '13 at 18:08

2 Answers 2

up vote 1 down vote accepted

My getenv man page says:

As typically implemented, getenv() returns a pointer to a string within
the  environment  list.   The  caller must take care not to modify this
string, since that would change the environment of the process.

You broke that rule here:

char *di_settings_file_path = (char *) getenv("HOME");
strcat(di_settings_file_path, "/");
strcat(di_settings_file_path, di_default_settings_file);
strcat(di_settings_file_path, "\0");

So you're modifying memory that you're not supposed to modify, in particular you are modifying the value of the HOME environment variable (in addition you're probably killing some other environment variable, or otherwise corrupting whatever comes after HOME in memory).

vim inherits the new value of $HOME and tries to use it as your home directory, including depositing its viminfo stuff there.

You need to copy getenv("HOME") into your own buffer, and make sure that buffer has enough room for the copy plus what you want to append to it. One way to do that is with asprintf:

asprintf(&di_settings_file_path , "%s/%s",
    getenv("HOME"), di_default_settings_file);
/* ... use it ... */
free(di_settings_file_path);
share|improve this answer
    
Thank you so much! That worked perfectly :) –  James C Jun 23 '13 at 2:50

First, you should compile with gcc -Wall -g and learn to use the gdb debugger. Check that command has the value you want it to have. (I am not sure it would work as you want if some directory or file name has spaces in it).

Using sprintf is deprecated and dangerous (possible buffer overflow). Use snprintf(3) or (GNU libc specific) asprintf.

Then, I guess that your call to system returns a non 0 code.You should never ignore the result of system library function. See this answer for more details.

At the very least, you should have

int editfailedcode = system(command);

and show the editfailedcode (actually handle it) with the command when editfailedcode is non zero.

And at last, the convention is to use the EDITOR environment variable. Read environ(7) man page.

Are you sure that /home/user/.di exists and is a writable directory? You may want to use the stat(2) syscall to check that.

Also, since you are using ncurses already, you may consider to use it for your own internal editor....

BTW, you could use strace(1) (perhaps as strace -f) to find out which syscall is failing and why...

share|improve this answer
    
1. The error occurs within vi and not my program. 2. There is no overflow in sprintf because everyting has a null terminating character placed on the end. 3. The editor preference comes as a default of vi but is manipulated by the user through the config file. –  James C Jun 22 '13 at 16:48
    
4. /home/user/.di is not a folder it is a file and is only ever called in the acquisition of the setting files contents, and in the initial creation and population of the file and all fopen instances are closed immediatley after. 5. the purpose of not using ncurses to create an editor is to give the user the option of using their own editor (many available on server being used) 6. There does not appear to be a syscall failure. This stil appears to be an error on vi's part. –  James C Jun 22 '13 at 16:48
    
You could have an overflow in sprintf so you should use snprintf; you should check -thru the debugger or by logging- what exact command you are running, and what is the return code given by system(command); indeed vi could fail and you should handle such failures.... –  Basile Starynkevitch Jun 22 '13 at 17:03

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.