プログラムを実行しているシステムに GNUPlot がインストールされているかどうかをテストしたいと思います。
そのために、stat() 呼び出しを使用して、ユーザーのインストール場所に gnuplot 実行可能ファイルが存在するかどうかをテストすることにしました。
ただし、C で $PATH 環境変数を読み取る方法がわからないため、それらの場所にファイルが存在するかどうかをテストできます。
プログラムを実行しているシステムに GNUPlot がインストールされているかどうかをテストしたいと思います。
そのために、stat() 呼び出しを使用して、ユーザーのインストール場所に gnuplot 実行可能ファイルが存在するかどうかをテストすることにしました。
ただし、C で $PATH 環境変数を読み取る方法がわからないため、それらの場所にファイルが存在するかどうかをテストできます。
機能を使用してくださいgetenv()
。
char *paths = getenv("PATH");
列で区切られたパスのリストの一部をループするには、次を使用しますstrchr()
。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *dup = strdup(getenv("PATH"));
char *s = dup;
char *p = NULL;
do {
p = strchr(s, ':');
if (p != NULL) {
p[0] = 0;
}
printf("Path in $PATH: %s\n", s);
s = p + 1;
} while (p != NULL);
free(dup);
getenv()
特定の環境変数の値を検査するために使用します。
PATH
環境変数を読み取るには、 を使用しますgetenv("PATH")
。
gnuplot
ただし、利用可能な場合に実行し、そうでない場合にフォールバック アクションを実行したい場合は、 ( andまたは などを使用して)実行して、失敗のケースを処理する必要があります。fork
execvp
posix_spawnp
あなたのために仕事をさせてください
if (system("which gnuplot"))
/* not installed or not in path or not executable or some other error */
なんらかの理由でフル パスが必要な場合は、popen で which を実行します。
または、フラグを付けて gnuplot を実行すると、すぐに 0 で返されます */
if (system("gnuplot --version"))
/* not installed ... */
同様のニーズがあり、libc execvp コード ソースをコピーして解決しました。私は考えられる最もクロスプラットフォームで行いました(保証はなく、Linuxでのみテストしました)。それが問題ではなく、パフォーマンスを気にする場合は、acess または _acess を使用する必要があります。エラーチェックはまったく行われず、NULL またはパスにあるオープン可能なファイルが返されるだけであることに注意してください。
同じ小さなバイナリを何度も実行したい場合、execvp を呼び出して毎回パス検索をやり直すと、無視できないオーバーヘッドになる可能性があります。
ここにコードと関連するテストがあります。主に機能に関心があるでしょうsearch_in_path_openable_file
。
.h ファイル:
bool is_openable_file(char* path);
/*Return true if path is a readable file. You can call perror if return false to check what happened*/
char* search_in_path_openable_file(char* file_name);
/*Search into PATH env variable a file_name and return the full path of the first that is openable, NULL if not in path*/
char* search_executable(char* file_name);
/*Search file, if not openable and not absolute path(contain /), look for opennable file in the path. If nothing is openable, return NULL. If something is openable, return it as it is (not guaratented to have a full path, but garatanted to be openable)*/
.c ファイル:
#include "file_info.h"
#include <stdio.h>
#include <string.h> //strcpy
/*I wanted to do a really cross platform way. access or _acess may be better*/
bool is_openable_file(char *path) {
FILE *fp = fopen(path, "r");
if (fp) {
// exists
fclose(fp);
return true;
}
return false;
}
bool is_openable_file_until(char *path_begin, size_t until) {
char old = path_begin[until];
path_begin[until] = 0;
bool res = is_openable_file(path_begin);
path_begin[until] = old;
return res;
}
/*You may thinks that libc would have done this function and use it to implement execp function family, but you would be wrong. They just hardcoded the search in every execp function. Unbelievable.
*
* So this function is a modification of their execvp function.
*
* */
char* search_in_path_openable_file(char* file){
char *path = getenv("PATH");
if (path == NULL)
return NULL;
size_t pathlen = strlen(path);
size_t len = strlen(file) + 1;
int total_max_size=pathlen + len;
char* buf=malloc(sizeof(char)*total_max_size);
if (*file == '\0') {
return NULL;
}
char *name, *p;
/* Copy the file name at the top. */
name = memcpy(buf + pathlen + 1, file, len);
/* And add the slash. */
*--name = '/';
p = path;
do {
char *startp;
path = p;
//Let's avoid this GNU extension.
//p = strchrnul (path, ':');
p = strchr(path, ':');
if (!p)
p = strchr(path, '\0');
if (p == path)
/* Two adjacent colons, or a colon at the beginning or the end
of `PATH' means to search the current directory. */
startp = name + 1;
else
startp = memcpy(name - (p - path), path, p - path);
/* Try to execute this name. If it works, execv will not return. */
if (is_openable_file(startp))
return startp;
} while (*p++ != '\0');
/* We tried every element and none of them worked. */
return NULL;
}
char* search_executable(char* file_name){
if (is_openable_file(file_name)){//See realpath manual bug. Watch out
return file_name;
}
if (strchr (file_name, '/') != NULL) //Don't search when it contains a slash.
return NULL;
return search_in_path_openable_file(file_name);
}
テスト (ご覧のとおり、私はこの関数をあまりテストしていません。問題がある可能性があります。ご自身の責任で使用してください):
#include "file_info.h"
#include "munit.h"
#include <stdbool.h>
#include <unistd.h>
static void generate_search_executable(char* test_str, char* expected){
char* res= search_executable(test_str);
if (res==NULL)
munit_assert_ptr(expected,==,NULL );
else
munit_assert_string_equal(expected,res);
}
static void generate_openable(char* test_str, bool expected){
bool res= is_openable_file(test_str);
munit_assert_true(expected==res);
}
static void generate_path_search(char* test_str, char* expected_res){
char* res= search_in_path_openable_file(test_str);
if (res==NULL)
munit_assert_ptr(expected_res,==,NULL );
else
munit_assert_string_equal(expected_res,res);
}
//TODO do for other platform, better test would also set path to a custom folder that we control
#define EXISTING_FILE_NOT_IN_PATH "/usr/include/stdlib.h"
#define EXISTING_FILE_IN_PATH "ls"
#define EXISTING_FILE_IN_PATH_FULL "/bin/ls"
#define NOT_EXISTING_FILE "/usrarfzsvdvwxv/ixvxwvnxcvcelgude/ssdvtdbool.h"
int main() {
generate_openable(EXISTING_FILE_IN_PATH, false);
generate_openable(EXISTING_FILE_NOT_IN_PATH, true);
generate_openable(NOT_EXISTING_FILE, false);
generate_path_search(EXISTING_FILE_IN_PATH, EXISTING_FILE_IN_PATH_FULL);
generate_path_search(NOT_EXISTING_FILE, NULL);
generate_path_search(EXISTING_FILE_NOT_IN_PATH, NULL);
generate_search_executable(EXISTING_FILE_IN_PATH, EXISTING_FILE_IN_PATH_FULL);
generate_search_executable(NOT_EXISTING_FILE, NULL);
generate_search_executable(EXISTING_FILE_NOT_IN_PATH, EXISTING_FILE_NOT_IN_PATH);
generate_search_executable("", NULL );
//test current folder existence(maybe it just depend on path containing .,I am not sure, in that case we should remove thoses tests
generate_search_executable("file_info_test", "file_info_test" );
}