我正在使用 ncurses 创建一个 tui 来在目录中移动。我让程序使用系统调用在 vi 中打开可编辑文件
def_prog_mode();
endwin();
sprintf(command, "%s %s", di->Settings.editor, di->Directory.File[di->Variables.item_selection].Path);
system(command);
reset_prog_mode();
但是当我退出 vi 并返回到程序 vi 时,报告错误
E138: Can't write viminfo file /home/user/.di/.viminfo!
Press ENTER or type command to continue
.di 是我的程序的配置文件,我不知道为什么它试图将自己包含在 .viminfo 文件的路径中。
有人知道如何解决这个问题吗?我的所有变量都以 di_ 为前缀,所以我不知道为什么会发生这种情况。非常感谢大家的帮助!
- 该错误发生在 vi 中,而不是我的程序中。
- sprintf 中没有溢出,因为每个字符的末尾都有一个空终止字符。
- 编辑器首选项是 vi 的默认设置,但用户可以通过配置文件进行操作。
- /home/user/.di 不是一个文件夹,它是一个文件,并且只在获取设置文件内容时调用,并且在文件的初始创建和填充之后所有 fopen 实例都会立即关闭。
- 不使用 ncurses 创建编辑器的目的是为了让用户可以选择使用自己的编辑器(服务器上有许多可用的编辑器)
- 似乎没有系统调用失败。这似乎仍然是 vi 的错误。
请注意:这只是一个烦人的问题。vi 中的故障不会导致我的程序故障。
在我最近重写整个程序时,我已将此功能实现到我的程序中。它允许用户在主目录中的配置文件“.di”中更改设置。自从实现此功能以来,退出 vi(或 vim)时,vi(或 vim)会显示错误,然后成功返回到程序。
#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);
}
答案1
我的 getenv 手册页说:
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.
你在这里违反了这条规则:
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");
因此,您正在修改不应该修改的内存,特别是您正在修改 HOME 环境变量的值(此外,您可能正在终止其他环境变量,或者以其他方式破坏内存中 HOME 之后的任何内容)。
vim
继承的新值$HOME
并尝试将其用作您的主目录,包括将其viminfo
内容存放在那里。
您需要将 getenv("HOME") 复制到您自己的缓冲区中,并确保缓冲区有足够的空间容纳副本以及您想要附加到其中的内容。一种方式是使用asprintf
:
asprintf(&di_settings_file_path , "%s/%s",
getenv("HOME"), di_default_settings_file);
/* ... use it ... */
free(di_settings_file_path);
答案2
首先,您应该使用调试器进行编译gcc -Wall -g
并学习使用它gdb
。检查它command
是否具有您想要的值。(如果某些目录或文件名中有空格,我不确定它是否能按您想要的方式工作)。
使用sprintf
已弃用且危险(可能导致缓冲区溢出)。使用snprintf(3)或(GNU libc 特定)打印函数。
那么,我猜你对的调用返回了一个非 0 代码。你永远不应该忽略库函数 system
的结果。参见system
这个答案更多细节。
至少你应该
int editfailedcode = system(command);
并显示当非零时editfailedcode
(实际处理它) 。command
editfailedcode
最后,惯例是使用EDITOR
环境变量。阅读环境(7)请参阅手册页。
您确定该/home/user/.di
目录存在且可写吗?您可能需要使用统计(2)系统调用来检查。
此外,既然您ncurses
已经在使用,您可以考虑将其用于您自己的内部编辑器......
顺便说一句,你可以使用strace(1)(也许strace -f
)找出哪个系统调用失败以及原因......