How to use fgetpwent()? - c

I am trying to get a list of all the users in the system (linux, fedora).
and i've heard that the function:fgetpwent is the one that i need to that mission.
the sad part is that i didnt find any documentation or example of how to use this function.
if someone would give me an example, that would be great, thanks in advance.

No idea why I ever could have used it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crypt.h>
#include <pwd.h>
#include <sys/types.h>
char *testentry = "testread";
static void read_etc_passwd (void) {
struct passwd *pwd_entry = NULL;
int found = 0;
setpwent(); // go to the top of /etc/passwd
while (!found && (pwd_entry = getpwent())){
if (0 == strcmp (testentry, pwd_entry->pw_name)){
found = 1;
}
}
if (found) {
printf ("name = %s\nhome = %s\n", pwd_entry->pw_name,
pwd_entry->pw_dir);
} else {
puts("could not find the entry you were looking for, or"
"some error occurred");
}
}
void change_etc_passwd (void){
struct passwd *pwd = NULL;
FILE *pwd_fd = NULL;
FILE *pwd_new = NULL;
int result = 0;
pwd_fd = fopen ("/etc/passwd", "r");
pwd_new = fopen ("/tmp/passwd.neu", "a");
// assuming everthing went fine (bad idea)
while (pwd = fgetpwent (pwd_fd)){
if (0 == strcmp (pwd->pw_name, testentry)){
pwd->pw_passwd = crypt ("new_pwd", "aa");
}
result = putpwent(pwd, pwd_new);
if (result < 0){
fprintf (stderr, "Failed to write entry, giving up\n");
exit (EXIT_FAILURE);
}
}
}
int main (void) {
/* handling of /etc/passwd */
read_etc_passwd ();
change_etc_passwd();
return 0;
}
Add error handling and it may even work without breaking ;-)

Related

getting virtual memory address(vma) of linker symbols

I'm playing around the bfd library (<bfd.h>), and I was able to implement my own version of objdump -h on binary files by printing out sections, their vmas, size, etc. Now, I'm having trouble implementing nm. I'm able to use the bfd library to obtain all the different symbols of a binary executable file, but how can I get each symbol's (main, etc) vma using asection/asymbol struct data? Here's the code I have that prints out every symbol name:
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <bfd.h>
int main(int argc, char *argv[])
{
bfd *ibfd = NULL;
if (!argv[1])
{
printf("Please supply a second argument\n");
return -1;
}
else
{
// initialize bfd so we can use it
bfd_init();
// open the supplied argument file
const char *str = argv[1];
ibfd = bfd_openr(str, "elf64-x86-64");
// if issue opening
if (!ibfd)
{
bfd_perror("open failure\n");
return -1;
}
// if file isnt elf binary file
if (!bfd_check_format(ibfd, bfd_object))
{
printf("not an object file\n");
return -1;
}
int spaceNeeded = bfd_get_symtab_upper_bound(ibfd);
if (spaceNeeded < 0)
{
return -1;
}
else if (spaceNeeded == 0)
{
return 1;
}
asymbol **symTable = malloc(spaceNeeded);
long numSyms = bfd_canonicalize_symtab(ibfd, symTable);
if (numSyms < 0)
return -1;
for (int i = 0, count = 0; i < numSyms; i++)
{
printf("%s\n", symTable[i]->name);
}
bfd_close(ibfd);
}
// success code
return 1;
}
nm uses the function bfd_symbol_info to fetch the virtual memory addresses of the symbols. You can read the source code for that function to get an idea as to the implementation.
void
bfd_symbol_info (symbol, ret)
asymbol *symbol;
symbol_info *ret;
{
ret->type = bfd_decode_symclass (symbol);
if (bfd_is_undefined_symclass (ret->type))
ret->value = 0;
else
ret->value = symbol->value + symbol->section->vma;
ret->name = symbol->name;
}

Why does free() keep triggering a breakpoint

Can someone please tell me why my free() function keeps triggering a breakpoint? I then get a Microsoft Visual C++ Runtime Library error: Debug Assertion failed! Program: ... .exe, File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp ... Expression: _CrtlsValidHeapPointer(block).
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "IPHLPAPI.lib")
int main()
{
IP_ADAPTER_INFO *pAdapterInfo = malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
return 1;
}
ULONG size = sizeof(*pAdapterInfo);
ULONG * pOutBufLen = &size;
if(GetAdaptersInfo(pAdapterInfo, pOutBufLen) == ERROR_BUFFER_OVERFLOW) //When this error code is returned, the pOutBufLen parameter points to the required buffer size.
{
pAdapterInfo = realloc(pAdapterInfo, *pOutBufLen);
}
else
{
return 1;
}
if(GetAdaptersInfo(pAdapterInfo, pOutBufLen) == NO_ERROR)
{
while (pAdapterInfo->Next != NULL)
{
printf("%s\n", pAdapterInfo->Description);
pAdapterInfo = pAdapterInfo->Next;
}
}
free(pAdapterInfo);
getchar();
return 0;
}
SOLUTION: Ok, thanks for everyone for your responses. I fixed my problem with another pointer that I've called pAdapter:
int main()
{
IP_ADAPTER_INFO *pAdapterInfo = malloc(sizeof(IP_ADAPTER_INFO));
IP_ADAPTER_INFO *pAdapter = NULL; //for memory freeing purposes only
...
if(GetAdaptersInfo(pAdapterInfo, pOutBufLen) == NO_ERROR)
{
pAdapter = pAdapterInfo;
while (pAdapter->Next != NULL)
{
printf("%s\n", pAdapter->Description);
pAdapter = pAdapter->Next;
}
}
free(pAdapterInfo);

working with directories in POSIX with C

I will go ahead and say this is a homework assignment for an intro to Linux class. I would not be posting it without extensive attempts on my own, and seeing as I am a distance student this semester, I cannot make it to campus for tutoring. I need some help finding out what the issue is.
Essentially the assignment asks us to make a program that serves the same basic function as the pwd command in POSIX, to show the absolute path for the current directory. We are to use three functions along with main. We are not to use the getcwd command as well. I'll list them and their purpose
inum_to_filename: Accepts three arguments (inode number to translate, a pointer to a buffer where the name is written, and the size of the buffer). Returns nothing. It is to:
Open the current directory,
Read the first directory entry,
If the inode of the current directory matches the one passed in, copy name to buffer and return.
Otherwise read the next directory entry and repeat the previous step.
filename_to_inum: Accepts one argument (a char * representing the filename). It returns the corresponding inode number. It is to:
Read the information from the files inode into a structure in memory.
If there is any problem, display the appropriate error.
Return the inode number from the structure.
display_path: Accepts one argument (inode from the current working directory). It returns nothing. It is to:
Create an array of characters to use as a buffer for the name of the directory.
Get the inode for the parent directory using filename_to_inode.
If the parent inode is equal to the current inode, we have reached root and can return.
Otherwise, change to the parent directory and use inum_to_filename to find the name for the inode that was passed into the function. Use the buffer from step 1 to store it.
Recursively call display_path to display the absolute path.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
//print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", pathBuffer);
return;
} else {
//print for test
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
As of right now it is displaying "/./MyName" with MyName being my personal named directory on the server. It is the directory I am running the program from. When using pwd I return "/home/MyName". I'm not really sure what my next step to getting the absolute path correct is.
The code is mostly set up to print one name at a time in the correct order, so the primary problem is the use of strcat() rather than strcpy(). Also, detecting when you're in the root directory at the start is important; if you don't, you can end up with /. or something similar (depending on exactly how you coordinate the printing) when the current directory is the root directory.
This version of your code has:
Squished the loop in inum_to_filename(), but also added error reporting. Remember, a process can be run in a directory which it does not have permission to get to (it requires a setuid program, usually — although permissions could be changed after the program is launched). In that case, it may fail to open .. (or .).
Lost variable count; it wasn't serving a useful purpose. Using the assign-and-test idiom allows the code to contain a single call to readdir().
Use strcpy() instead of strcat().
Use type ino_t to store inode numbers. Use size_t for sizes.
Reduce number of intermediate variables in filename_to_inum().
Note that the code in the if (ino_src == ino_prnt) statement body is for the root directory; in the absence of the testing print, it would do nothing.
Note that the printing in the else part is a major part of the operations, not just test printing.
Error check chdir("..");
Detect root in main().
Observe that this code is not directly suitable for rewriting into a function because it changes the process's current directory to / when it succeeds.
Revised code:
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static void inum_to_filename(ino_t inode_arg, char *pathBuffer, size_t size_arg)
{
assert(size_arg > 0);
DIR *dir_ptr = opendir(".");
if (dir_ptr == 0)
{
fprintf(stderr, "Failed to open directory '.' (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
struct dirent *dirent_ptr;
while ((dirent_ptr = readdir(dir_ptr)) != 0)
{
if (inode_arg == dirent_ptr->d_ino)
{
if (strlen(dirent_ptr->d_name) >= size_arg)
{
fprintf(stderr, "File name %s too long (%zu vs %zu max)\n",
dirent_ptr->d_name, strlen(dirent_ptr->d_name), size_arg);
exit(EXIT_FAILURE);
}
strcpy(pathBuffer, dirent_ptr->d_name);
break;
}
}
closedir(dir_ptr);
}
static ino_t filename_to_inum(char *src)
{
struct stat info;
if (stat(src, &info) != 0)
{
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
}
return info.st_ino;
}
static void display_path(ino_t ino_src)
{
size_t bufSize = 4096;
char pathBuffer[bufSize];
ino_t ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt)
{
// print for test
inum_to_filename(ino_src, pathBuffer, bufSize);
printf("%s", "(root): /\n");
}
else
{
// print for real
if (chdir("..") != 0)
{
fprintf(stderr, "Failed to chdir to .. (%d: %s)\n",
errno, strerror(errno));
}
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("/%s", pathBuffer);
}
}
int main(void)
{
ino_t c_ino = filename_to_inum(".");
ino_t r_ino = filename_to_inum("/");
if (r_ino == c_ino)
putchar('/');
else
display_path(c_ino);
printf("\n");
}
There are undoubtedly other ways to fix this.
Caveat: this is giving me some grief when working in /Volumes/CRUZER/Sub-Directory which is a memory stick. It fails to find the inode (1, which is surprising) when scanning /Volumes, and I've not worked out why. One of my programs — a getpwd implementation — is working fine; another is having a different problem. I expect I'll get to the bottom of it all. Testing on Mac OS X 10.10.5 with GCC 5.1.0.
this is really nice assignment :).
I read and tried your code, and it is almost correct. There were two small issues which were causing the incorrect behaviour.
First issue
When display_path reaches the root folder you don't need to call inum_to_filename and print the name of the folder because you have already printed the first folder of the path in the previous iteration. This prevents your code from showing a "./" in the beginning of the path.
That is, the if condition becomes:
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
Second Issue:
You're not initializing propertly the buffer where you save the name of the directory. This causes random values to be displayed. To solve this issue you can just set the initial value of the buffer to zero by using memset.
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
...
}
closedir(dir_ptr);
}
Full code working :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
DIR *dir_ptr = opendir(".");
struct dirent *dirent_ptr = readdir(dir_ptr);
int counter = 0;
memset(pathBuffer, 0, size_arg);
while (counter != 1) {
if (inode_arg == dirent_ptr->d_ino) {
strcat(pathBuffer, "/");
strcat(pathBuffer, dirent_ptr->d_name);
counter = counter + 1;
return;
} else {
dirent_ptr = readdir(dir_ptr);
}
}
closedir(dir_ptr);
}
int filename_to_inum (char *src) {
int res = 0;
struct stat info;
int result = stat(src, &info);
if (result != 0) {
fprintf(stderr, "Cannot stat ");
perror(src);
exit(EXIT_FAILURE);
} else {
res = info.st_ino;
}
return res;
}
/*
- Create an array of characters to use as a buffer for the name of the directory.
- Get the inode for the parent directory using filename_to_inode.
- If the parent inode is equal to the current inode, we have reached root and can return.
- Otherwise, change to the parent directory and use inum_to_filename to find the name for
the inode that was passed into the function. Use the buffer from step 1 to store it.
- Recursively call display_path to display the absolute path.
*/
void display_path (int ino_src) {
int bufSize = 4096;
char pathBuffer[bufSize];
int ino_prnt = filename_to_inum("..");
if (ino_src == ino_prnt) {
return;
} else {
chdir("..");
inum_to_filename(ino_src, pathBuffer, bufSize);
display_path(ino_prnt);
printf("%s", pathBuffer);
}
}
int main (int argc, char *argv[]) {
int c_ino = filename_to_inum(".");
display_path(c_ino);
printf("\n");
}
Output :
ubuntu#ubuntu-VirtualBox:~/dev$ vi pwd.c
ubuntu#ubuntu-VirtualBox:~/dev$ gcc pwd.c
ubuntu#ubuntu-VirtualBox:~/dev$ ./a.out
/home/ubuntu/dev
ubuntu#ubuntu-VirtualBox:~/dev$ pwd
/home/ubuntu/dev
ubuntu#ubuntu-VirtualBox:~/dev$

creating multiple recursive directories in c

I am completing cs50x (the edX (free) version of the Harvard cs50) course and am trying to be a bit tricky/lazy/test myself.
I am trying to use a C program to create all the directories I will need for my psets.
I have looked online and found that <sys/stat.h> includes the mkdir() function and therefore tried creating some nested loops to create all the necessary folders by doing something similar to mkdir {pset1,pset1/{standard,hacker},pset2,pset2{standard... to give me a directory structure like this:
pset1/Standard
pset1/Hacker
pset2/Standard
etc...
I came up with this:
#include <cs50.h>
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, string argv[])
{
for(int i = 1; i <=8; i++)
{
string dir = argv[1];
sprintf(dir,"%s%i", argv[1], i);
mkdir(dir, 0777);
for(int j = 0; j<2; j++)
{
string subDir[] = {"Standard","Hacker"};
sprintf(dir,"%s%i/%s", argv[1], i, subDir[j]);
mkdir(dir, 0777);
}
}
}
However, the program only creates pset1 and completes, there are no subfolders, no pset2 etc.
Yes, you're being lazy since you seem to have very little knowledge of C, yet try to program in it. :)
C is not Python, there is no string interpolation/formatting operator. You have to call a function, specificially snprintf(). Read that manual page.
Also, you can't create a bunch of nested directories with a single call to mkdir(). Read the manual page.
To create nested directories, you're either going to have to build each's absolute path (i.e. each successive time you call mkdir() the path will be longer than the previous time), or actually enter each directory as you create it, and go from there.
To create a full path you can call mkdir() recursivly like this:
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
int mkdirr(const char * path, const mode_t mode, const int fail_on_exist)
{
int result = 0;
char * dir = NULL;
do
{
if (NULL == path)
{
errno = EINVAL;
result = -1;
break;
}
if ((dir = strrchr(path, '/')))
{
*dir = '\0';
result = mkdirr(path, mode, fail_on_exist);
*dir = '/';
if (result)
{
break;
}
}
if (strlen(path))
{
if ((result = mkdir(path, mode)))
{
char s[PATH_MAX];
sprintf(s, "mkdir() failed for '%s'", path);
perror(s);
if ((EEXIST == result) && (0 == fail_on_exist))
{
result = 0;
}
else
{
break;
}
}
}
} while (0);
return result;
}
And then call mkdirr() like this;
int main(void)
{
char p[] = "test/1/2/3";
if (-1 == mkdirr(p, 0777, 0))
{
perror("mkdirr() failed()");
}
return 0;
}

Search for a file in $PATH on Linux in C

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;
}

Resources