This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
So I have a header file and 2 .c files within the beginnings of my program. I go to compile and I get the error message (tons of these over and over)
command_parser.c:74:6: error: static declaration of ‘read_args_file’ follows non-static declaration
command_parser.h:9:6: note: previous declaration of ‘read_args_file’ was here
Now I do not use the static keyword ANYWHERE in my program...so why would GCC go and think that I've declared a static function???
Below is the relevant code for read_args_file's declaration in the .h and .c files:
void read_args_file(char* file_name, char* out_file_name, int (*command_read)(char* command, FILE* out));
void read_args_file(char* file_name, char* out_file_name, int (*command_read)(char* command, FILE* out)) {
.....
}
EDIT:
The entire .h file is:
#ifndef COMMAND_PARSER_H_
#define COMMAND_PARSER_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* line 8 follows: */
void switch_parsing(int argc, char* argv[], int (*command_read)(char* command, FILE* out), char* (*pr int_usage)()) {
void read_args_file(char* file_name, char* out_file_name, int (*command_read)(char* command, FILE* ou t));
void read_args_input(int (*command_read)(char* command, FILE* out));
#endif
The command_parser.c file until the function definition is:
void switch_parsing(int argc, char* argv[], int (*command_read)(char* command, FILE* out), char* (*print_usage)()) {
char* arg;
char* return_string;
char* wrong_string = "Please enter either -i, -h, or -f as a switch. Use -h for help.\n";
char* invalid_f_args = "You entered an invalid number of arguments for the -f switch! Only two are permitted, <commands_file> and <output_file>.\n";
int str_len = 0;
char cur;
if (argc > 1) {
arg = argv[1];
}
else {
arg = "\0";
}
str_len = strlen(arg);
if (str_len == 2) {
if (arg[0] == '-') {
cur = arg[1];
if (cur == 'i') {
read_args_input(command_read);
return_string = "";
}
else if (cur == 'f') {
if (argc == 4) {
read_args_file(argv[2], argv[3], (*command_read));
return_string = "";
}
else {
return_string = invalid_f_args;
}
}
else if (cur == 'h') {
return_string = print_usage();
}
else {
return_string = "The switch ";
return_string = strcat(return_string, &cur);
return_string = strcat(return_string, " is an invalid switch.\n");
}
}
}
else if (str_len == 1) {
return_string = wrong_string;
}
else if (str_len > 2) {
return_string = wrong_string;
}
else if (str_len == 0) {
return_string = print_usage();
}
else {
return_string = wrong_string;
}
}
/**
* Reads arguments from a passed in file name, and writes the output from the commands
* in the file to the out_file_name. Arguments are run through command_read function
* passed in to be executed.
*/
/* line 74 follows: */
void read_args_file(char* file_name, char* out_file_name, int (*command_read)(char* command, FILE* out)) {
There is a brace open in the .h file line 8: void switch_parsing(int argc, ..... ){
The lines that follow are treated by the compile as one big function body, and the final error will be found after the compiler fails to find a matching '}'. Many lines (and files) later. The OP got lucky: the compiler first found another (semantic) error.
Related
When using the auto completion with the Readline library in C, the prompt is reprinted when typing the tab key twice:
(prompt) view NAME_OF_F (user presses tab twice)
NAME_OF_FILE1 NAME_OF_FILE2 (suggestions by Readline)
(prompt) view NAME_OF_F
I'd like to suppress the reprinting of the prompt on the 3rd line by keeping the first line printed with the suggestions below it like such:
(prompt) view NAME_OF_F (user presses tab twice)
NAME_OF_FILE1 NAME_OF_FILE2 (suggestions by Readline)
I'd like the cursor back at the end of the first line that has the prompt.
Compiled with gcc -Wall -O0 -ggdb -fno-builtin rline.c -o rline -lreadline -ltermcap.
Here's a code sample:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
int execute_line(char *line);
void initialize_readline();
static char **fileman_completion(char *text, int start, int end);
static char *command_generator(char *text, int state);
char *command[] = { "view", "quit", (char *)NULL };
int done; /* When non-zero, this global means the user is done using this program. */
int main(int argc, char **argv)
{
char *line;
initialize_readline(); /* Bind our completer. */
for ( ; done == 0; ) {
line = readline("> ");
if (!line)
break;
if (*line)
execute_line(line);
free(line);
}
return 0;
}
/* String to pass to system(). This is for the VIEW command. */
static char syscom[1024];
int execute_line(char *line)
{
int i = 0;
char *word;
/* Isolate the command word. */
while (line[i] && whitespace(line[i]))
i++;
word = line + i;
while (line[i] && !whitespace(line[i])) i++;
if (line[i]) line[i++] = '\0';
if (strcmp(word, "quit") == 0) {
done = 1;
return 0;
} else if (strcmp(word, "view")) {
fprintf(stderr, "%s: Choose only \"view FILE\" or \"quit\" as your command.\n", word);
return -1;
}
/* Get argument to command, if any. */
while (whitespace(line[i])) i++;
word = line + i;
if(!word || !*word) {
fprintf(stderr, "view: Argument required.\n");
return -1;
}
sprintf(syscom, "more %s", word);
return system(syscom);
}
void initialize_readline()
{
rl_readline_name = "rline";
rl_attempted_completion_function = (rl_completion_func_t *)fileman_completion;
}
static char **fileman_completion(char *text, int start, int end)
{
if (start == 0)
return rl_completion_matches(text, (rl_compentry_func_t *)*command_generator);
return NULL;
}
static char *command_generator(char *text, int state)
{
static int list_index, len;
char *name;
if (!state) {
list_index = 0;
len = strlen(text);
}
while ((name = command[list_index++]))
if (strncmp(name, text, len) == 0)
return strdup(name);
return NULL;
}
The program only accepts the commands view FILE_NAME to view the contents of a file and quit to exit the program.
The example is a shortened version of a sample program found here.
I don't think that readline has anything like that built in, but it does provide a lot of customisation possibilities if you want to try to write the logic yourself.
You could try writing a custom rl_completion_display_matches_hook to display the completion list. But it's not entirely clear to me how you would restore the cursor position afterwards. I don't think readline has a public interface for either finding or resetting the cursor position. (And, of course, it's possible that the completion list was so big that the original command scrolled off the screen.)
As an alternative, I was able use the hook to print the completion list over top of the current line and then redisplay the prompt after the completion list (although I cheated by assuming that the current input is always just one line). That's not quite what you asked for, but it may be useful for demonstration purposes. I used the following custom match printer:
static void display_matches(char** matches, int len, int max) {
putp(carriage_return);
putp(clr_eol);
putp(cursor_up);
rl_display_match_list(matches, len, max);
rl_forced_update_display();
}
I also added the following to the initialisation function:
rl_completion_display_matches_hook = display_matches;
setupterm(NULL, 1, (int*)0);
Thanks #rici for the inspiration. I got it working with his function with some modifications.
In order for this to work properly you need to download the readline library. In the rlprivate.h file from readline, I removed the lines char **lines;, and the line #include "realdine.h" from display.c. Then in your own .c you must have an #include </PATH/TO/display.c>. In that display.c, an #include points to the modified rlprivate.h. All of this so that I can have access to _rl_move_vert(1).
static void display_matches(char** matches, int len, int max)
{
int saved_point = rl_point;
char *saved_line = rl_copy_text(0, rl_end);
rl_save_prompt();
rl_replace_line("", 0); // Clear the previous text
putp(cursor_up);
_rl_move_vert(1);
rl_display_match_list(matches, len, max);
putp(cursor_up);
rl_restore_prompt();
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
putp(cursor_down);
free(saved_line);
}
I was working on my game and decided to use eclipse as my compiler. I had to compile it for both platforms: x86 and x64. The trouble started there. There are many dependency files in the system path.
And every time I had to change them in order to change the platform. So, I've created a line to set up my configurations faster and without affect the path itself.
This is the line to add into the path that I've created:
%DRIVE%\mingw\mingw%PLATFORM%\bin;%DRIVE%\Dropbox\Machine\Windows\C\Place\bin\x%PLATFORM%;%DRIVE%\Dropbox\Machine\Windows\C\PLUGIN\x%PLATFORM%\bin;
As you guys can see there are two variables there: %DRIVE% and %PLATFORM%.
I wish to change them with a file that I try to create in c.
Here is the code
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
char *strremove(char *str, const char *sub) {
char *p, *q, *r;
if ((q = r = strstr(str, sub)) != NULL) {
size_t len = strlen(sub);
while ((r = strstr(p = r + len, sub)) != NULL) {
while (p < r)
*q++ = *p++;
}
while ((*q++ = *p++) != '\0')
continue;
}
return str;
}
#ifndef HAVE_SETENV
int setenv(const char * variable,const char * value) {
if(!variable || !value)return(0);
int len = strlen(variable)+1+strlen(value)+1;
char * EnvString = calloc(len,sizeof(char));
sprintf(EnvString, "%s=%s", variable, value);
if (!_putenv(EnvString)) {
return (1);
}
if(EnvString)free(EnvString);
return (0);
}
#endif
void change_platform(int argc,char ** argv) {
char * variable = "PLATFORM",* value = "86";
if(argc > 1){
value = argv[1];
}
if (setenv(variable, value)) {
printf("\n environmental variable successfully written");
printf("\n value of the environmental variable written is %s",
getenv(variable));
} else {
printf("\n error in writing the environmental variable");
}
}
int main(int argc, char ** argv) {
change_platform(argc,argv);
getch();
return 0;
}
My code shows the right result inside the program, but when I go and check the system environment itself, nothing changes. Am I doing something wrong.
Detail: I thought it was because of mingw which isn't native from Windows, then I've created I file in Visual c++ too, but it did not work either.
Please remember it affects only the environment of the current process
getenv, _wgetenv
int main( void )
{
char *libvar;
// Get the value of the LIB environment variable.
libvar = getenv( "LIB" ); // C4996
// Note: getenv is deprecated; consider using getenv_s instead
if( libvar != NULL )
printf( "Original LIB variable is: %s\n", libvar );
// Attempt to change path. Note that this only affects the environment
// variable of the current process. The command processor's
// environment is not changed.
_putenv( "LIB=c:\\mylib;c:\\yourlib" ); // C4996
// Note: _putenv is deprecated; consider using putenv_s instead
// Get new value.
libvar = getenv( "LIB" ); // C4996
if( libvar != NULL )
printf( "New LIB variable is: %s\n", libvar );
}
I am trying to find the file(say marks.txt) in the particular path passed as argument to a function. Is it possible to give the filename and path as arguments to a function which checks if the file exists and prints out the path?
The below function only takes path as argument.
int fileexists(const char *path){
File *ptr = fopen(path, "r");
if (fptr == NULL)
return 0;
fclose(fptr);
return 1;
}
The required function prototype :
int fileexists(const char *path, const char *filename)
There are two parts to this question, and the right answers to them depend on what you're trying to do.
Concatenate a directory name and a file name to form a full path name.
Determine whether a file (referred to by a full path name) exists or not.
Concatenating a directory name and a file name is straightforward. Your friendsstrcpy and strcat will do most of the work. There are a few minor details to be careful of: (a) You'll need a big enough buffer for the full pathname, and you'll need to decide whether to use a fixed-size array (perhaps of size MAX_PATH), or a malloc'ed buffer; (b) you might need to insert an explicit '/' character (and it usually doesn't hurt to stick one in even if the directory string already ends in one); (c) under Windows you might want to use '\\' instead of '/'.
And then determining whether a file named by a full pathname exists is already well answered over at What's the best way to check if a file exists in C?. The big question to ask here is, are you asking whether the file exists in preparation to doing something with the file? If so, you have a serious vulnerability if you check for the file's existence, but then before you do the other thing, something else happens to cause the file to appear or disappear. So rather than checking-and-then-doing, it's usually better to just try doing the other thing, and deal gracefully with any errors.
The function you have checks if the file can be opened, but it will fail for some files that exist but you have no rights to open. I'd use stat instead. To concatenate the path and filename you can use string functions.
The usual Unix C APIs are dismal. It takes lots of effort to do the simplest of things correctly - and even then I'm not sure that I didn't forget some Unix-ism like signal handling or some obscure error cases. I.e. stuff that's rather trivial to get right in modern C++.
I wish someone designed a modern C system API and implemented it for at least Linux, so that our suffering would end...
Usually, string concatenation requires some higher level API to be done while maintaining a modicum of sanity. Thus, the example below uses a strbuilder class to build the string. This makes things vaguely readable and avoids most common mistakes.
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct strbuilder {
unsigned items, item;
size_t length, *lengths;
char *str, *dst;
};
bool strbuilder_pass(struct strbuilder *builder, int *rc);
void strcat_str(struct strbuilder *builder, const char *src);
void strcat_c_ifnone(struct strbuilder *builder, char c);
bool strbuilder_is_freed(const struct strbuilder *builder);
int fileExists(const char *path, const char *filename)
{
const char pathSep = '/';
int rc;
struct strbuilder bld = {0};
while (strbuilder_pass(&bld, &rc))
{
strcat_str(&bld, path);
strcat_c_ifnone(&bld, pathSep);
strcat_str(&bld, filename);
if (!rc)
{
struct stat statbuf;
printf("path = %s\n", bld.str);
rc = stat(bld.str, &statbuf);
}
}
assert(strbuilder_is_freed(&bld));
return rc;
}
int main()
{
int rc = fileExists("/", "dev");
assert(rc == 0);
return 0;
}
The string building is controlled by a strbuilder_pass function, which advances the string builder's state through five passes of operation:
Determine the number of items whose width has to be stored (avoids the need to call strlen twice).
Prepare the length storage vector. Determine the length of the buffer needed.
Prepare the output string buffer. Concatenate the elements into the buffer.
Use the output string buffer.
Free the output string buffer.
This API is not particularly special, but fits this use case. Some other ad-hoc approach would work too, but this is IMHO a bit more elegant.
void strbuilder_free(struct strbuilder *builder)
{
free(builder->lengths);
free(builder->str);
memset(builder, 0, sizeof(*builder));
}
bool strbuilder_pass(struct strbuilder *builder, int *rc)
{
if (!builder->length) {// start of pass 1
builder->length = 1; /*term*/
*rc = EAGAIN;
return true;
}
else if (!builder->lengths) // end of pass 1
{
builder->lengths = malloc(sizeof(*builder->lengths) * builder->items);
if (builder->lengths)
return true;
*rc = ENOMEM;
}
else if (!builder->str) // end of pass 2
{
builder->dst = (builder->str = malloc(builder->length));
builder->item = 0;
builder->length = 0;
if (builder->dst) {
*builder->dst = '\0';
return true;
}
*rc = ENOMEM;
}
else if (builder->dst) // end of pass 3
{
while (*builder->dst) { // include optional content
builder->dst++; // skip
builder->length++;
}
builder->dst = NULL;
*rc = 0;
return true;
}
else if (!builder->dst) // end of pass 4 (if any)
{}
else {
*rc = EINVAL;
}
strbuilder_free(builder);
return false;
}
void strcat_str(struct strbuilder *builder, const char *src)
{
if (!src)
return;
if (!builder->lengths) // pass 1
builder->items ++;
else if (!builder->str) // pass 2
{
size_t len = strlen(src);
builder->lengths[builder->item++] = len;
builder->length += len;
}
else if (builder->dst) // pass 3
{
size_t len = builder->lengths[builder->item++];
if (*builder->dst && (!len || *builder->dst != *src))
{
builder->dst++;
builder->length++;
}
memcpy(builder->dst, src, len);
builder->dst += len;
builder->length += len;
*builder->dst = '\0';
}
}
void strcat_c_ifnone(struct strbuilder *builder, char c)
{
if (!builder->lengths) {} // pass 1
else if (!builder->str) // pass 2
{
if (c) builder->length ++;
}
else if (builder->dst) // pass 3
{
if (!builder->length || builder->dst[-1] != c)
*(builder->dst) = c;
}
}
bool strbuilder_is_freed(const struct strbuilder *builder)
{
return !builder || (!builder->lengths && !builder->str);
}
You probably want something like this (no error checking for brevity):
...
#include <string.h> // for str* functions
#include <unistd.h> // for access
#include <stdlib.h> // for malloc
...
int fileexists(const char *path, const char *filename)
{
char *name= malloc(strlen(path) + strlen(filename) + 1);
strcpy(name, path);
strcat(name, filename);
int retval = access(name, F_OK) == 0;
free(name);
return retval;
}
Call like this:
if (fileexists("/some/path/", "somefilename.txt")) ...
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Edit: I am really sorry if I have wasted time of your guys, I was running out of time when posting this problem. Here comes the code that I have done my best to minimize it
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum Error {
ERROR_UNRESOLVED_NAME = 1,
ERROR_CANNOT_OPEN_FILE,
ERROR_NO_ARGV,
ERROR_UNRECOGNIZED_SYMBOL,
ERROR_UNCOMPLETED_SENTENCE,
ERROR_RECURSIVE_SELF
};
struct _Piece;
typedef struct _Piece *(*PieceFunc)(struct _Piece *, void *);
struct _Piece {
PieceFunc function;
void *backpack;
};
typedef struct _Piece Piece;
Piece *piece_create(PieceFunc func, void *pack) {
Piece *piece = malloc(sizeof(Piece));
piece->function = func;
piece->backpack = pack;
return piece;
}
typedef struct _Record {
char *name;
int name_len;
Piece *piece;
struct _Record *previous;
} Record;
Record *record_register(Record *pre, char *name, int name_len, Piece *piece) {
Record *record = malloc(sizeof(Record));
record->name = name;
record->name_len = name_len;
record->piece = piece;
record->previous = pre;
return record;
}
typedef struct {
char *file_name;
char *source;
int length;
int current;
int line;
int column;
} Source;
Source *source_create(char *s, int len, char *file_name) {
Source *source = malloc(sizeof(Source));
source->source = s;
source->file_name = file_name;
source->length = len;
source->current = 0;
source->line = source->column = 1;
return source;
}
Piece *apply(Piece *caller, Piece *callee) {
return caller->function(callee, caller->backpack);
}
// Part 3, internals
Piece *internal_self(Piece *callee, void *backpack) {
if (callee->function == internal_self) {
fprintf(stderr,
"recursive `self` calling between two pieces\n"
"piece 1 backpack: %p\n"
"piece 2: %p backpack: %p",
backpack, callee, callee->backpack);
exit(ERROR_RECURSIVE_SELF);
}
return apply(callee, piece_create(internal_self, backpack));
}
Piece *internal_put(Piece *callee, void *backpack) {
int *p_char = callee->backpack;
putchar(*p_char);
return piece_create(internal_self, NULL);
}
Source *main_create_source(char *file_name) {
FILE *source_file = fopen(file_name, "r");
if (!source_file) {
fprintf(stderr, "cannot open file \"%s\"\n", file_name);
exit(ERROR_CANNOT_OPEN_FILE);
}
char *source = NULL;
int length = 0;
while (true) {
char *line = NULL;
int line_len = 0;
line_len = (int)getline(&line, (size_t *)&line_len, source_file);
if (line_len < 0) {
break;
}
if (source == NULL) {
source = line;
} else {
source = realloc(source, sizeof(char) * (length + line_len + 1));
strcat(source, line);
// free(line);
}
length += line_len;
}
fclose(source_file);
return source_create(source, length, file_name);
}
#define MAIN_REGISTER_INTERNAL(record, name, func) \
record = record_register(record, name, sizeof(name) - 1, \
piece_create(func, NULL)); \
printf("%p %p\n", record, record->previous);
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "please specify source file by command line argument\n");
exit(ERROR_NO_ARGV);
}
Record *r = NULL;
MAIN_REGISTER_INTERNAL(r, "put", internal_put);
printf("main %p\n", r);
Source *s = main_create_source(argv[1]);
printf("main %p\n", r);
}
At first, the program crashed with a segmentation fault, I located the bad access code line, which have been deleted in this code demo. I figure out the original bug is that variable r in main would unexpected change after an unrelated calling to main_create_source, which would be demonstrated like this (save this code file as foo.c)
$ cc -O0 -g foo.c
$ ./a.out futaba_test.ftb
0x7fc0024025b0 0x0
main 0x7fc0024025b0
main 0x7fc0024025b0
$ cc -O3 -g foo.c
$ ./a.out futaba_test.ftb
0x7fe861c025b0 0x0
main 0x7fe861c025b0
main 0x7fe800000000
The behavior varied when changing optimization level. It has nothing todo with EOF since I have removed it, and in my opinion the memory for strcat's destination is rich enough. Thanks for any help.
By the way if there is any requirement to point out the purpose of this snippet. This is an interpreter for a minimal language I am working on. It is able to evaluate small source code snippet at the time and this is the first time I have tried to build it with -O3. The bug will only disappear without any level optimization.
(The following is the original post and is able to be ignored.)
I have this code file. When compiling with cc -O0 futaba.c, and running it with ./a.out futaba_test.ftb, the result will be
0x7fba60c025b0 0x0
0x7fba60c025e0 0x7fba60c025b0
0x7fba60c02610 0x7fba60c025e0
0x7fba60c02640 0x7fba60c02610
0x7fba60c02670 0x7fba60c02640
0x7fba60c026b0 0x7fba60c02670
0x7fba60c026d0 0x7fba60c026b0
0x7fba60c02700 0x7fba60c026d0
0x7fba60c02730 0x7fba60c02700
main 0x7fba60c02730
main 0x7fba60c02730
A%
(Zsh add the postfix %) everything is going well. But when compiling with -O3 rather than -O0, than result will be
0x7f8f274025b0 0x0
0x7f8f274025e0 0x7f8f274025b0
0x7f8f27402610 0x7f8f274025e0
0x7f8f27402640 0x7f8f27402610
0x7f8f27402670 0x7f8f27402640
0x7f8f274026b0 0x7f8f27402670
0x7f8f274026d0 0x7f8f274026b0
0x7f8f27402700 0x7f8f274026d0
0x7f8f27402730 0x7f8f27402700
main 0x7f8f27402730
main 0x7f8f00000000
[1] 27811 segmentation fault ./a.out futaba_test.ftb
The last two main line print different address, and the second one is not valid, which cause the stack overflow bug later in record_resolve function.
What is the problem?
That's a lot of code, but here's at least a flag:
char source_fetch(Source *s) {
return s->current == s->length ? EOF : s->source[s->current];
}
This forces EOF into a char, which is a very bad idea. That's why all standard C functions that can return EOF (like getchar() return int.
No idea what an optimizing compiler can make out of that, but once you factor in code that waits for EOF using that ... it's smelly.
Note: this is perhaps bad form as an answer; but it's pointing out a concrete problem with the code.
Also none of the heap allocations seems to have code looking for NULL being returned; that's a bit scary too.
I would like to test whether GNUPlot is installed on the system on which my program is running.
For that, I figured I'll test for the existence of the gnuplot executable in the user's install locations through stat() call.
However, I don't know how to read the $PATH environment variable in C so I can test for the existence of the file in those locations.
Use the getenv() function.
char *paths = getenv("PATH");
To loop through the parts of the column-separated list of paths, use 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);
Use getenv() to inspect the value of a particular environment variable.
To read the PATH environment variable, use getenv("PATH").
However, if you just want to run gnuplot if it's available, and perform some fallback action if it's not, then you should just try to run it (e.g. with fork and execvp or posix_spawnp) and handle the failure case.
Let which do the work for you
if (system("which gnuplot"))
/* not installed or not in path or not executable or some other error */
If you need the full path for some reason, run which with popen.
Or run gnuplot with some flag which makes it return immediately with 0 */
if (system("gnuplot --version"))
/* not installed ... */
I had a similar need and resolved it by copying libc execvp code source. I did in the most cross platform I could think of(I have no guatanty and tested just on linux). If it's not such a matter to you and you care about performances, you should use acess or _acess. Note that there is no error check whatsoever and it will just return NULL or a founded openable file in path.
The accepted answer is sometime not acceptable, when you are willing to run the same small binary over and over, redoing the path search every time by calling execvp can be non negligable overhead.
So here is the code and associated tests, you will be mainely interested in the search_in_path_openable_file function.
.h file:
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 file:
#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);
}
tests (As you see I did not test a lot this function, there may exist some problem, use at your risk):
#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" );
}
To build on one of the previous answers, you can use getenv to get the contents of PATH and then iterate over its components. Instead of using strchr you can use strsep:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
bool exists(const char fname[])
{
return access(fname, F_OK | X_OK) != -1;
}
bool find_in_path(const char name[], char *fullpath, size_t sz) {
char *paths = strdup(getenv("PATH"));
char *tmp = paths; // to use in free
const char *item;
bool found = false;
while ((item = strsep(&paths, ":")) != NULL) {
snprintf(fullpath, sz, "%s/%s", item, name);
if (exists(fullpath)) {
found = true;
break;
}
}
free(tmp);
return found;
}
int main() {
char fullpath[512];
bool found = find_in_path("uname", fullpath, sizeof(fullpath));
if (found) {
printf("found: %s\n", fullpath);
}
return 0;
}
Using C++17 to get a vector of path elements.
% a.out ls
/bin/ls
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
using namespace std;
vector<string> get_paths (string str)
{
vector<string> result;
while(!str.empty())
{
if (auto pos { str.find_first_of (':') }; pos == string::npos)
{
result.push_back(str);
break;
}
else
{
result.emplace_back(str.substr(0, pos));
str.erase(0, pos + 1);
}
}
return move(result);
}
bool exist(const string& fname, int perm=F_OK) { return access(fname.c_str(), perm) == 0; }
int main (int argc, char *argv[])
{
auto result { get_paths(getenv("PATH")) };
for (auto pp : result)
{
string npath { pp };
if (*npath.rbegin() != '/')
npath += '/';
npath += argv[1];
if (exist(npath))
cout << npath << endl;
}
return 0;
}