Trying to recursively loop the folder contents - c

I'm trying to create a function which recursively returns the contents of a folder. While testing it, I encounter some problems. With some folders it works, With others it gives me an EXC_BAD_ACCESS, and sometimes it just stops.
I have been trying to debug it with GDB for a long time, but I just can't find a solution to my problem. The function is not quit short an goes as follows.
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
/* THIS IS AN EXTRACT FROM A LARGER FILE
THERE MAY BE TO MUCH INCLUDE STATEMENTS
*/
struct Directory {
DIR *handle;
const char *filename;
};
typedef struct Directory Directory;
int DirectoryCreate(const char *n, Directory *d) {
DIR *dh;
char *str;
dh = opendir(n);
if(dh == NULL) {
return -1;
}
d->handle = dh;
str = malloc(strlen(n) + 1);
if(str == NULL) {
errno = ENOMEM;
closedir(d->handle);
return -1;
}
strcpy(str, n);
d->filename = (const char *)str;
return 0;
}
void DirectoryFree(Directory *s) {
if(s->handle) {
closedir(s->handle);
}
if(s->filename) {
free((void *)s->filename);
}
}
void FreeDirectoryArray(Directory *array, size_t size) {
register size_t i;
for(i = 0; i < size; i++) {
DirectoryFree(&(array[i]));
}
free(array);
}
Directory *ReadFolders = NULL;
size_t ReadFoldersSize = 0;
const char *ReadFolderFilename = NULL;
const char *ReadNextRecursiveItemInFolder(const char *folder) {
struct dirent *entry;
struct stat fileStatus;
int status;
mode_t mode;
const char *newFilename;
char *fullName;
char *ptr;
size_t strLen;
if(folder == NULL && ReadFolders == NULL) {
errno = 0;
return NULL;
}
if(folder != NULL) {
/* free the previous directory list */
FreeDirectoryArray(ReadFolders, ReadFoldersSize);
ReadFolders = NULL;
ReadFoldersSize = 0;
/* open the new directory */
ReadFolders = (Directory *)realloc(ReadFolders, sizeof(Directory));
ReadFoldersSize++;
status = DirectoryCreate(folder, ReadFolders);
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize-1);
ReadFolders = NULL;
return NULL;
}
}
entry = readdir(ReadFolders[ReadFoldersSize - 1].handle);
/* If NULL, go to previous folder */
if(entry == NULL) {
DirectoryFree(&(ReadFolders[ReadFoldersSize - 1]));
--ReadFoldersSize;
/* if it's empty, we've reached the end */
if(ReadFoldersSize == 0) {
free(ReadFolders);
ReadFolders = NULL;
errno = 0;
return NULL;
}
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
}
/* Make sure the entry name is not . or .. */
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
}
/* we've got an entry, now construct the full path */
strLen =
strlen(ReadFolders[ReadFoldersSize - 1].filename) +
1 +
strlen(entry->d_name);
fullName = malloc(strLen + 1);
ptr = fullName;
strcpy(ptr, ReadFolders[ReadFoldersSize - 1].filename);
ptr += strlen(ReadFolders[ReadFoldersSize - 1].filename);
strcpy(ptr, "/");
ptr++;
strcpy(ptr, entry->d_name);
newFilename = fullName;
/* no recurse on symbolic links */
status = lstat(newFilename, &fileStatus);
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize);
ReadFolders = NULL;
ReadFoldersSize = 0;
return NULL;
}
mode = fileStatus.st_mode;
/* if not readable for file or not searchable for folder, get next */
/* if folder and not link, recursively continue */
/* else return the new name */
if((((mode & S_IFDIR) == S_IFDIR) && (mode & S_IXUSR) != S_IXUSR) ||
(mode & S_IRUSR) != S_IRUSR) {
free((void *)newFilename);
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
} else if((mode & S_IFDIR) && (mode & S_IFLNK) != S_IFLNK) {
ReadFolders = realloc(ReadFolders, ReadFoldersSize + 1);
ReadFoldersSize++;
errno = 0;
status = DirectoryCreate(newFilename, &(ReadFolders[ReadFoldersSize - 1]));
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize - 1);
ReadFolders = NULL;
ReadFoldersSize = 0;
return NULL;
}
if(newFilename != ReadFolderFilename) {
free((void *)ReadFolderFilename);
ReadFolderFilename = newFilename;
}
} else {
if(newFilename != ReadFolderFilename) {
free((void *)ReadFolderFilename);
ReadFolderFilename = newFilename;
}
errno = 0;
}
return ReadFolderFilename;
}
int main() {
const char *filename = "/Users/";
const char *entry;
while(1) {
entry = ReadNextRecursiveItemInFolder(filename);
filename = NULL;
if(entry == NULL) {
if(errno == 0) {
printf("End reached\n");
} else {
printf("Error: %s\n", strerror(errno));
}
break;
}
printf("Entry: %s\n", entry);
}
return 0;
}
I'll give a brief explanation how the code works. To start looping a directory, you have to give the full directory path to the function. All subsequent calls have to pass NULL to get the next item in line, unless they want to process another directory.
The code counts every file and folder in a folder, recursively. It does not follow symbolic links, and it only counts readable files and executable directories. To keep track of its 'flow', the function uses 3 global variables:
ReadFolders: an array of Directory structures used to keep track of different levels of folders. The last one at the back.
ReadFoldersSize: the amount of Directory structures in ReadFolders.
ReadFolderFilename: the string that contains the last item processed.
I hope I can find some help around here,
ief2.

The realloc size is wrong : it not "n" but "n*size".
So the line 153 should be :
ReadFolders = realloc(ReadFolders, (ReadFoldersSize + 1)*sizeof(Directory));

Related

how to get partition number and block device name seperating from a path in c program?

I have path name like this str[20]="/dev/sda1" and needs to get partion number i.e '1' and device name "sda1" seperatly stored in variables.
I tried something like this to get string but I want both partion number and string seperatly.
sscanf(str,"%[^0123456789]", temp);
but temp will give me string only not number?
In this case you can reuse the str buffer, but I will not use sscanf, try with strcspn, it works even if the path doesn't have a number:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "/dev/sda1";
char num[sizeof str] = "";
size_t pos = strcspn(str, "0123456789");
strcpy(num, str + pos);
str[pos] = '\0';
puts(str);
puts(num);
return 0;
}
Output:
/dev/sda
1
A better version scanning from the last slash:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "/dev1/sda1";
char num[sizeof str] = "";
// Searches for the last occurrence of '/'
char *ptr = strrchr(str, '/');
if (ptr != NULL)
{
size_t pos = strcspn(ptr, "0123456789");
strcpy(num, ptr + pos);
ptr[pos] = '\0';
}
puts(str);
puts(num);
return 0;
}
Output:
/dev1/sda
1
EDIT:
any specific reason ,why can't we use sscanf here
You can, but notice that an extra buffer is needed:
#include <stdio.h>
int main(void)
{
char str[] = "/dev/sda1";
char dev[sizeof str] = "";
char num[sizeof str] = "";
sscanf(str, "%[^0-9]%s", dev, num);
puts(dev);
puts(num);
return 0;
}
I would recommend:
Use strrchr(path, '/') to find the last slash in the path, and if non-NULL, use the rest of the string (starting at the character following the last slash) as the device name. If there is no slash in the path, then it consists of the device name only.
Use sscanf() to extract the components of the device name, with a terminating %c (taking a pointer to a char) to detect if the name or path contains extra characters. The return value is the number of successful conversions, and we require that terminating conversion to fail. (If you use %n, it is not counted as a conversion.)
Support at least /dev/hdXP (ATA/ATAPI devices), /dev/sdXP (SCSI and SATA devices), nvmeCnDpP (NVMe devices), and /dev/mmcblkDpP (eMMC devices) partition types.
Here is a working example program. Supply one or more partition names or paths, and it describes each name.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
enum {
BLOCKDEV_NONE = -1,
BLOCKDEV_HD, /* device hd<letter>, partition hd<letter><number> */
BLOCKDEV_SD, /* device sd<letter>, partition sd<letter><number> */
BLOCKDEV_NVME, /* device nvme<number>n<number>, partition nvme<number>n<number>p<number> */
BLOCKDEV_MMC, /* device mmcblk<number>, partition mmcblk<number>p<number> */
};
struct partition_info {
char *blockdev_path; /* Full path to the block device */
char *blockdev_name; /* Block device name, basename(blockdev_path) */
char *partition_path; /* Full path to the partition device */
char *partition_name; /* Partition device name, basename(partition_path) */
int type; /* One of the BLOCKDEV_ constants */
int controller; /* Controller number for NVMe, 0 for others */
int device; /* Device number; 0..25 for letters a..z */
int partition; /* Partition number */
};
const char *blockdev_type_desc(const int type)
{
switch (type) {
case BLOCKDEV_NONE: return "None";
case BLOCKDEV_HD: return "ATA block device";
case BLOCKDEV_SD: return "SCSI or SATA block device";
case BLOCKDEV_NVME: return "NVMe block device";
case BLOCKDEV_MMC: return "eMMC block device";
default: return "(Unknown block device)";
}
}
void free_partition_info(struct partition_info *info)
{
/* Since free(NULL) is safe, make free_partition_info(NULL) safe also. */
if (!info)
return;
info->type = BLOCKDEV_NONE;
info->controller = -1;
info->device = -1;
info->partition = -1;
free(info->blockdev_path);
info->blockdev_path = NULL;
info->blockdev_name = NULL;
free(info->partition_path);
info->partition_path = NULL;
info->partition_name = NULL;
}
static inline char *name_part(char *path)
{
if (!path)
return NULL;
char *last_slash = strrchr(path, '/');
if (last_slash)
return last_slash + 1;
else
return path;
}
int set_partition_info(struct partition_info *info, const char *path)
{
int ctrlnum, devnum, partnum, n;
char letter[2], dummy;
/* A pointer to an uninitialized partition_info structure is required. */
if (!info) {
errno = EINVAL;
return -1;
}
info->type = BLOCKDEV_NONE;
info->controller = -1;
info->device = -1;
info->partition = -1;
info->blockdev_path = NULL;
info->blockdev_name = NULL;
info->partition_path = NULL;
info->partition_name = NULL;
/* Partition path cannot be NULL or empty. */
if (!path || !*path) {
errno = ENOENT;
return -1;
}
/* Partition name part follows the last slash, or is the full string. */
const char *last_slash = strrchr(path, '/');
const char *name = (last_slash) ? last_slash + 1 : path;
/* Prefixlen is the length of the string up to and including the last slash. */
const size_t prefixlen = (last_slash) ? (size_t)(last_slash - path + 1) : 0;
/* NVMe partition? */
if (sscanf(name, "nvme%dn%dp%d%c", &ctrlnum, &devnum, &partnum, &dummy) == 3) {
info->type = BLOCKDEV_NVME;
info->controller = ctrlnum;
info->device = devnum;
info->partition = partnum;
info->blockdev_path = strdup(path);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
*(strrchr(info->blockdev_path, 'p')) = '\0';
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* MMC partition? */
if (sscanf(name, "mmcblk%dp%d%c", &devnum, &partnum, &dummy) == 2) {
info->type = BLOCKDEV_MMC;
info->controller = 0;
info->device = devnum;
info->partition = partnum;
info->blockdev_path = strdup(path);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
*(strrchr(info->blockdev_path, 'p')) = '\0';
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* HD partition? */
if (sscanf(name, "hd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
info->type = BLOCKDEV_HD;
info->controller = 0;
info->device = letter[0] - 'a';
info->partition = partnum;
info->blockdev_path = strndup(path, prefixlen + 3);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* SD partition? */
if (sscanf(name, "sd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
info->type = BLOCKDEV_SD;
info->controller = 0;
info->device = letter[0] - 'a';
info->partition = partnum;
info->blockdev_path = strndup(path, prefixlen + 3);
info->partition_path = strdup(path);
if (!info->blockdev_path || !info->partition_path) {
free(info->partition_path);
free(info->blockdev_path);
info->blockdev_path = NULL;
info->partition_path = NULL;
errno = ENOMEM;
return -1;
}
info->blockdev_name = name_part(info->blockdev_path);
info->partition_name = name_part(info->partition_path);
return 0;
}
/* Unknown device; return "no such device" error. */
errno = ENODEV;
return -1;
}
int main(int argc, char *argv[])
{
for (int arg = 1; arg < argc; arg++) {
struct partition_info p;
if (set_partition_info(&p, argv[arg])) {
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
printf("Specified path: \"%s\" (%p)\n", argv[arg], argv[arg]);
printf(" Type: %s\n", blockdev_type_desc(p.type));
if (p.type == BLOCKDEV_NVME)
printf(" Controller: %d\n", p.controller);
if (p.type == BLOCKDEV_HD || p.type == BLOCKDEV_SD)
printf(" Device: '%c' (%d)\n", p.device + 'a', p.device);
else
printf(" Device: %d\n", p.device);
printf(" Partition: %d\n", p.partition);
printf(" Block device path: \"%s\" (%p)\n", p.blockdev_path, p.blockdev_path);
printf(" Block device name: \"%s\" (%p)\n", p.blockdev_name, p.blockdev_name);
printf("Partition device path: \"%s\" (%p)\n", p.partition_path, p.partition_path);
printf("Partition device name: \"%s\" (%p)\n", p.partition_name, p.partition_name);
printf("\n");
fflush(stdout);
free_partition_info(&p);
}
return EXIT_SUCCESS;
}
Note that the struct partition_info contains dynamically allocated copies. The name parts are pointers to the corresponding path member, just skipping any directory components in the path.
For illustration, the example program shows the string pointer values (target address in hexadecimal) in parentheses after each string output in double quotes. This is to show how the various strings relate (or not) to each other.

Find all files containing search term except sub elements

I'm trying to make a C program that will display all the files and folders containing a given search term. The search term is given as an argument when executing the program. A folder / file is displayed to standard output if its name contains the search term (case insensitive).
The difficulty though is that I do not want to output files and subfolders that are contained in a folder that contains the search term. Here's an example:
Let's assume my search term is docker, this is the current output:
"/Users/me/.docker"
"/Users/me/.docker/contexts"
"/Users/me/.docker/contexts/meta"
"/Users/me/.docker/config.json"
"/Users/me/.docker/scan"
"/Users/me/.docker/scan/config.json"
"/Users/me/.docker/application-template"
"/Users/me/.docker/application-template/logs"
"/Users/me/.docker/application-template/logs/com.docker.log"
"/Users/me/.docker/daemon.json"
"/Users/me/.docker/run"
"/Users/me/Library/Application Support/Docker Desktop"
"/Users/me/Library/Application Support/Docker Desktop/blob_storage"
"/Users/me/Library/Application Support/Docker Desktop/blob_storage/6965e70b-e33a-4415-b9a8-e19996fe221d"
But this is the output I'm trying to achieve:
"/Users/me/.docker"
"/Users/me/Library/Application Support/Docker Desktop"
Here's my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
static int display_info(const char *fpath, const char * fname, const char * term) {
int what_len = strlen(term);
int count = 0;
char *where = fpath;
if (what_len){
while ((where = strcasestr(where, term))) {
where += what_len;
count++;
}
if (count == 1) {
printf("\"%s/%s\"\n", fpath, fname);
}
}
return 0;
}
static void listFilesRecursively(char * basePath, const char * searchTerm) {
char path[1000];
struct dirent * dp;
DIR * dir = opendir(basePath);
// Unable to open directory stream
if (!dir)
return;
while ((dp = readdir(dir)) != NULL) {
if (strcmp(dp -> d_name, ".") != 0 && strcmp(dp -> d_name, "..") != 0) {
// printf("%s %hhu %s\n", basePath, dp->d_type, dp -> d_name);
display_info(basePath, dp->d_name, searchTerm);
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp -> d_name);
listFilesRecursively(path, searchTerm);
}
}
closedir(dir);
}
int main(int argc, const char * argv[]) {
char * home = getenv("HOME");
if (argc == 2) {
listFilesRecursively(home, argv[1]);
} else {
printf("Please provide one argument");
}
return 0;
}
Any feedback is greatly appreciated thanks!
I am not sure I understand the logic of display_info()
On listFilesRecursively() you can not reuse path from call to call
main() should the the first function in your code, maybe in a separate file
an alternative
I will add a C example, changing a bit of your listFilesRecursively()...
not using void() so you can return -1 for an error
testing for . and .. at the beginning of the loop and just using continue may lead into code easier to read
path is allocated locally and free() on return
strstr_ign() is a case insensitive version of strstr() for use in the pattern search
code for list_files() after change
int list_files(char* pattern, char* base_path)
{
struct dirent* dp;
DIR* dir = opendir(base_path);
if (!dir) return -1; // Unable to open directory stream
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") == 0) continue;
if (strcmp(dp->d_name, "..") == 0) continue;
if ( strstr_ign((const char*)dp->d_name, pattern) != NULL )
{
display_info(base_path, dp->d_name);
}
else
{
char* path = (char*)malloc(1 + strlen(dp->d_name) + strlen(base_path) + 1);
sprintf(path, "%s/%s", base_path, dp->d_name);
list_files(pattern, path);
free(path); // ok with path
}
}; // while()
closedir(dir);
return 0;
}; // list_files()
code for strstr_ign()
I hate the arguments order for strstr() but kept it here just to have things equal. This way one can use strstr_ign() as a drop-in replacement for strstr() without changing the order of the arguments. I believe needle should come first :) an in the language: search for a needle in a haystack is far more common than search the haystack for a needle but Ken and Dennis had their reasons to write strstr() the way they did it...
//
// strstr() ignoring case
//
char* strstr_ign(const char* haystack, const char* needle)
{
if (needle == NULL) return NULL;
if (haystack == NULL) return NULL;
if (*needle == 0)
{
if (*haystack == 0)
return (char*) haystack;
else
return NULL;
}
int limit = strlen(haystack) - strlen(needle);
for (int x = 0; x <= limit; x += 1)
{ // search for needle at position 'x' of 'haystack'
int here = 1;
for (unsigned y = 0; y < strlen(needle); y += 1)
{
if ( tolower(haystack[x + y]) != tolower(needle[y]) )
{
here = 0; break;
};
};
if ( here == 1) return (char*)(haystack + x);
}
return NULL;
};
a new display_info()
changed to show last access for folders and file size for regular files that match the search pattern (case insensitive). Below is an example of the output for files and folders. Note the '-' and the 'd' as in the ls -l output.
- "./hms.c" [size: 1546]
d "./sub/1/xyzHMSxyz" [last access: Sat Apr 24 12:38:04 2021]
int display_info(const char* base, const char* file)
{
struct stat Stats;
char* path = (char*)malloc(1 + strlen(base) + strlen(file) + 1);
char atime[40];
sprintf(path, "%s/%s", base, file);
if ( stat(path, &Stats) < 0)
{
perror("Inside display_info()");
free(path);
return -1;
}
if ( S_ISDIR(Stats.st_mode) )
{
strftime( atime, sizeof(atime), "%a %b %d %H:%M:%S %Y", localtime(&Stats.st_atime) );
printf("\td \"%s/%s\"\t[last access: %s]\n", base, file, atime );
}
else
{
if ( S_ISREG(Stats.st_mode) )
printf("\t- \"%s/%s\"\t[size: %ld]\n", base, file, Stats.st_size );
else
printf("is somthing else\n");
}
free(path);
return 0;
}
sample output
Search pattern is "hms" (case is ignored)
- "./hms" [size: 16848]
- "./hms-soma.c" [size: 1379]
- "./hms.c" [size: 1546]
d "./sub/1/xyzHMSxyz" [last access: Sat Apr 24 12:38:04 2021]
d "./sub/2/xyzHMS" [last access: Sat Apr 24 12:21:11 2021]
d "./sub/hMs" [last access: Sat Apr 24 12:21:11 2021]
C code for this test
miminally tested :)
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
int display_info(const char*, const char*);
int list_files(char*, char*);
char* strstr_ign(const char*, const char*);
int main(int argc, const char * argv[])
{
char search_term[80];
if (argc >= 2)
{
if ( strlen(argv[1]) > (sizeof(search_term)-1) )
{
printf("Size of substring (%zd) must not be greater than %zd\n",
strlen(argv[1]), sizeof(search_term)-1 );
return -1;
}
for ( int i = 0; i<= strlen(argv[1]); search_term[i] = (char)(tolower(argv[1][i])), i+=1 );
printf("Search pattern is \"%s\" (case is ignored) \n", search_term );
list_files(search_term,".");
} else {
printf("Please provide pattern to search for.\n");
}
return 0;
}; // main()
int display_info(const char* base, const char* file)
{
struct stat Stats;
char* path = (char*)malloc(1 + strlen(base) + strlen(file) + 1);
char atime[40];
sprintf(path, "%s/%s", base, file);
if ( stat(path, &Stats) < 0)
{
perror("Inside display_info()");
free(path);
return -1;
}
if ( S_ISDIR(Stats.st_mode) )
{
strftime( atime, sizeof(atime), "%a %b %d %H:%M:%S %Y", localtime(&Stats.st_atime) );
printf("\td \"%s/%s\"\t[last access: %s]\n", base, file, atime );
}
else
{
if ( S_ISREG(Stats.st_mode) )
printf("\t- \"%s/%s\"\t[size: %ld]\n", base, file, Stats.st_size );
else
printf("is somthing else\n");
}
free(path);
return 0;
}
int list_files(char* pattern, char* base_path)
{
struct dirent* dp;
DIR* dir = opendir(base_path);
if (!dir) return -1; // Unable to open directory stream
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") == 0) continue;
if (strcmp(dp->d_name, "..") == 0) continue;
if ( strstr_ign((const char*)dp->d_name, pattern) != NULL )
{
display_info(base_path, dp->d_name);
}
else
{
char* path = (char*)malloc(1 + strlen(dp->d_name) + strlen(base_path) + 1);
sprintf(path, "%s/%s", base_path, dp->d_name);
list_files(pattern, path);
free(path); // ok with path
}
}; // while()
closedir(dir);
return 0;
}; // list_files()
//
// strstr() ignoring case
//
char* strstr_ign(const char* haystack, const char* needle)
{
if (needle == NULL) return NULL;
if (haystack == NULL) return NULL;
if (*needle == 0)
{
if (*haystack == 0)
return (char*) haystack;
else
return NULL;
}
int limit = strlen(haystack) - strlen(needle);
for (int x = 0; x <= limit; x += 1)
{ // search for needle at position 'x' of 'haystack'
int here = 1;
for (unsigned y = 0; y < strlen(needle); y += 1)
{
if ( tolower(haystack[x + y]) != tolower(needle[y]) )
{
here = 0; break;
};
};
if ( here == 1) return (char*)(haystack + x);
}
return NULL;
};
Thanks to #KamilCuk I was able to solve my issue. Here's my final listFilesRecursively function:
static void listFilesRecursively(char * basePath, const char * searchTerm) {
char path[1000];
struct dirent * dp;
DIR * dir = opendir(basePath);
// Unable to open directory stream
if (!dir)
return;
while ((dp = readdir(dir)) != NULL) {
if (strcmp(dp -> d_name, ".") != 0 && strcmp(dp -> d_name, "..") != 0) {
if (strcasestr(dp->d_name, searchTerm)) {
printf("%s/%s\n",basePath,dp->d_name);
listFilesRecursively(path, searchTerm);
} else {
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp -> d_name);
listFilesRecursively(path, searchTerm);
}
}
}
closedir(dir);
}

Can't get the reason for segfault

EDIT : Note that It's not that I can't access the memory allocated by storeContents() in main() if you think so. Program crashes during the execution of storeContents()
The program fails here :
strcpy(contents[count], dir->d_name);
printf("Stored %s(out hiddenVisible)\n", dir->d_name); // for testing
It's strcpy() not the printf(), I added it just for the reference.
The debugger(gdb) says :
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f3cd72 in __strcpy_ssse3 () from /usr/lib/libc.so.6
I am making a program that involves the following function "storeContents"(It stores contents' names of a directory in a dynamic array). There are two issues with this function : (1) It says "Stored file_name" twice for the first file and (2) says "Segmentation fault". I can't figure out either of them. Thanks for your efforts!
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#include <limits.h>
static short hiddenVisible = 0;
/* Store directory's contents in **contents */
static char ** storeContents(struct dirent *dir, DIR *dirp, unsigned numOfContents);
/* Count files/directories in a directory */
static unsigned getNumOfContents(struct dirent *dir, DIR *dirp);
int main(int argc, char const *argv[])
{
char **contents;
DIR *dirp;
struct dirent *dir;
unsigned numOfContents;
dirp = opendir("/home/gaurav");
if((dir = readdir(dirp)) == NULL) {
perror("readdir");
exit(1);
}
/* Getting number of files/directories */
numOfContents = getNumOfContents(dir, dirp);
printf("There are %u files.\n", numOfContents);
/* To position again to the first entry */
rewinddir(dirp);
contents = storeContents(dir, dirp, numOfContents);
/* Print contents */
for(unsigned i = 0; i < numOfContents; ++i)
printf("%s\n", contents[i]);
closedir(dirp);
return 0;
}
char **
storeContents(struct dirent *dir, DIR *dirp, unsigned numOfContents) {
char **contents;
unsigned count = 0;
/* Allocating memory for entries */
contents = malloc(numOfContents * sizeof(*contents));
/* Allocating memory for each '*contents' */
for(unsigned i = 0; i < numOfContents; i++)
contents[i] = (char *)malloc(NAME_MAX); /* we know char is 1 byte, so no "sizeof" */
while(count < numOfContents) {
/* Ignore "." and ".." */
if(!(strcmp(dir->d_name, ".")) || !(strcmp(dir->d_name, ".."))) {
if((dir = readdir(dirp)) == NULL) {
perror("readdir");
exit(1);
}
continue;
}
if(hiddenVisible) {
strcpy(contents[count], dir->d_name);
if((dir = readdir(dirp)) == NULL) {
perror("readdir");
exit(1);
}
count++;
} else {
if(dir->d_name[0] == '.')
if((dir = readdir(dirp)) == NULL) {
perror("readdir");
exit(1);
}
else {
strcpy(contents[count], dir->d_name);
if((dir = readdir(dirp)) == NULL) {
perror("readdir");
exit(1);
}
count++;
}
}
}
return contents;
}
unsigned
getNumOfContents(struct dirent *dir, DIR *dirp) {
unsigned count = 0;
while(dir) {
if(hiddenVisible) {
/* Ignore "." and ".." */
if(!(strcmp(dir->d_name, ".")) || !(strcmp(dir->d_name, ".."))) {
if((dir = readdir(dirp)) == NULL) {
perror("readdir a");
exit(1);
}
continue;
}
count++;
if((dir = readdir(dirp)) == NULL) {
perror("readdir b");
exit(1);
}
} else {
if(dir->d_name[0] == '.') {
if((dir = readdir(dirp)) == NULL) {
perror("readdir c");
exit(1);
}
}
else {
count++;
if((dir = readdir(dirp)) == NULL) {
perror("readdir d");
exit(1);
}
}
}
}
return count;
}
contents in the function storeContents is a local copy of contents from main.
Changing it in the function does not change the variable in main.
You should return the array. Change
static void storeContents(struct dirent *dir, DIR *dirp, char **contents, unsigned numOfContents);
to
static char **storeContents(struct dirent *dir, DIR *dirp, unsigned numOfContents);
,return contents; in the function and call it like char **contents = storeContents(...);
Some bugs:
contents is a local parameter to the function, it will not get returned to main(). See Dynamic memory access only works inside function.
contents = (char **)malloc(numOfContents); is wrong, you need to allocate room for numOfContents pointers. Change this to contents = malloc(numOfContents * sizeof(*contents)).
You should check each call to readdir and make sure it doesn't return NULL.

Incorrect code to check if a word can be made of smaller given words (word break)

Incorrect code to check if a word can be made of smaller given words (word break).This is the code I wrote for the above mentioned problem, however an online judge declares it as incorrect, what could be the possible reasons? And how should I modify my code?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Node structure */
typedef struct node {
int letter[26];
struct node* next[26];
int is_word;
} node;
/* Create node */
node* getnode(void) {
node* p = malloc(sizeof(node));
int i;
for (i = 0; i < 1004; i++) {
p->letter[i] = 0;
p->next[i] = NULL;
}
p->is_word = 0;
return p;
}
/* make dictionary */
void fill_dictionary(char word[], node* start) {
int len = strlen(word), i;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[word[i] % 'a'] == 0) {
temp->letter[word[i] % 'a'] = 1;
temp->next[word[i] % 'a'] = getnode();
temp = temp->next[word[i] % 'a'];
} else {
temp = temp->next[word[i] % 'a'];
}
}
temp->is_word = 1;
return;
}
int spell_check(char line[100003], node* start) {
int len = strlen(line), i, flag = 0;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[line[i] % 'a'] == 0) {
return 1;
} else {
temp = temp->next[line[i] % 'a'];
flag = 0;
if (temp->is_word == 1) {
flag = 1;
temp = start;
}
}
}
if (flag == 1) {
return 0;
} else {
return 1;
}
}
int main(void) {
int n, i, ans, m;
scanf("%d %d", &n,&m); // no. of words in dictionary
node* start = getnode();
for (i = 0; i < n; i++) {
char word[11]; // max length of dictionary word
scanf("%s", word);
fill_dictionary(word, start);
}
scanf("%d", &n); // no. of lines to be checked
for (i = 0; i < n; i++) {
char line[100003]; // max length of a line
scanf("%s", line);
ans = spell_check(line, start);
if (ans == 0) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
Here's one way to to it. This compiles and runs. It displays the parsed result. It tries to read the dictionary from a file called "dictionary.text" in the current directory. You can change it to put the dictionary wherever you want. I commented it heavily to help you understand it but it has some subtle C things you may need to really think about and figure out. One bit of advice: Name everything in a program as extremely accurately for what it is/does as possible (but reasonably succinct). That will help immensely when trying to debug or figure out what you did wrong. Careless names really make code confusing and hard to debug.
Good luck!
Example:
$ gcc -o wordsplitter wordsplitter.c
$ wordsplitter xyzhellogoodbyefoodogcatpigcarwhereareyouhorse
xyz "hello" "goodbye" foo "dog" "cat" pigcar "where" "are" "you" horse
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DICTIONARY_FILEPATH "dictionary.txt"
#define MAX_WORD_SIZE 100
/*
* Error codes (usually this is put in a header file and included)
*/
#define SUCCESS 0
#define FILE_NOT_FOUND -1
#define OUT_OF_MEMORY -2
typedef struct word {
struct word *next;
char *word;
} word_t;
word_t *dictionaryListhead = NULL;
typedef struct wordsubcomponent {
struct wordsubcomponent *next;
char *text;
int isDictionaryWord;
} wordsubcomponent_t;
int
loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}
wordsubcomponent_t
*newsubcomponent() {
wordsubcomponent_t *subcomp = NULL;
if ((subcomp = calloc(sizeof(wordsubcomponent_t), 1)) != NULL) {
subcomp->text = strdup(""); // seed with empty string (instead of NULL) so we can append
} else {
fprintf(stderr, "out of memory (fatal). program exiting\n");
exit(-1);
}
return subcomp;
}
/*
* Returns an linked list of word subcomponents for the given word, split up around dictionary words
*/
wordsubcomponent_t *getWordSubcomponents(char *wordToParse, word_t *listhead) {
wordsubcomponent_t *subcomponents, *currSubcomp;
subcomponents = currSubcomp = newsubcomponent();
for (char *cp = wordToParse; cp < wordToParse + strlen(wordToParse);) { // exit when cp gets to end of word to parse.
int matchFlag = 0;
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
if (strncasecmp(cp, wordNode->word, strlen(wordNode->word)) == 0) { // prefix of cur. ptr is dict word.
if (strlen(currSubcomp->text) != 0) // Detected non-dict text in subcomp.
currSubcomp = currSubcomp->next = newsubcomponent(); // leave in list & add new subcomp for dict word.
currSubcomp->text = wordNode->word; // save dict-word in subcomp
currSubcomp->isDictionaryWord = 1;
currSubcomp = currSubcomp->next = newsubcomponent(); // dict-word in list, so get new subcomp
cp += strlen(wordNode->word); // advance cp past extracted dict-word
matchFlag = 1;
break; // break out of inner-loop
}
}
if (!matchFlag) { // No dict-word found at cp
char oneNullTerminatedLetter[2] = { *cp++, '\0' }; // put 1st ltr into NULL-terminated string & adv cp.
strcat(currSubcomp->text, oneNullTerminatedLetter); // append letter-as-string to curr subcomp
}
}
return subcomponents;
}
void
dumpDictionary(word_t *listhead) {
printf("\nList of dictionary words:\n");
printf("----------------\n");
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
printf(" %s\n", wordNode->word);
}
printf("----------------\n\n");
}
int
main(int argc, char **argv)
{
int status;
if ((status = loadDictionaryFromFile(DICTIONARY_FILEPATH, &dictionaryListhead)) < 0) {
switch(status) {
case FILE_NOT_FOUND:
fprintf(stderr, "Error accessing dictionary: %s\n", argv[0]);
break;
case OUT_OF_MEMORY:
fprintf(stderr, "Out of memory");
break;
}
return EXIT_FAILURE;
}
/*
* Load dictionary first so we can show them the list of words if they didn't
* pass in a command line argument with the word to parse.
*/
if (argc < 2) {
fprintf(stderr, "Usage: %s <word_to_parse>\n\n", argv[0]);
dumpDictionary(dictionaryListhead);
return EXIT_FAILURE;
}
wordsubcomponent_t *subcomp = getWordSubcomponents(argv[1], dictionaryListhead);
while(subcomp != NULL && strlen(subcomp->text) > 0) {
if (subcomp->isDictionaryWord)
printf("\"%s\" ", subcomp->text);
else
printf("%s ", subcomp->text);
subcomp = subcomp->next;
}
printf("\n");
return EXIT_SUCCESS;
}
#nerdist colony:
There is a resource leak in loadDictionaryFromFile. This means a file pointer was not closed when returning from this function in case of an error.
Here is a corrected copy of this function
int loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
fclose(dictionaryFile); // <-- Close the file pointer
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}

having trouble with my cd and history function

My results for cd and history:
sgraham#myshell:/home/class/sgraham/proj1>cd .. (works fine)
sgraham#myshell:/home/class/sgraham>cd .. (not working)
sgraham#myshell:/home/class/sgraham>cd .. (not working)
cd: Too many arguments
sgraham#myshell:/home/class/sgraham>history
sgraham#myshell:/home/class/sgraham> (not printing out history)
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define MAX_COMMAND_SIZE 80
#define MAX_ARGS 9
#define HIS_SIZE 100
typedef struct
{
int argument; // userCom arguments
char *arg[MAX_ARGS + 1]; // userCom arguments array
char *history[HIS_SIZE]; //history array
char *input; // hold input file
char *output; // hold output file
} Command;
int main()
{
Command userCom = {0}; //holds userCom struct
const char *whitespace = " \n\r\t\v\f"; // userCom delimiting chars
char* username = getenv("USER"); //Get user name
char* curDirect = getenv("HOME"); //get cwd
char* token[MAX_ARGS];
char* cwd;
char* buf;
char* cmd;
char buffer[MAX_COMMAND_SIZE + 1]; //hold userCom line
//char history[HIS_SIZE][MAX_ARGS +1]; //2D array for history
int tok = 0;
int new;
int i;
int limit;
int last = 0;
int hist = 0; //initialize history size to 0
long size;
struct stat buff; //holds file information
//gets current working directory and also changes buffer if necessary
size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *)malloc((size_t)size)) != NULL)
cwd = getcwd(buf, (size_t)size);
while(1){
//prints users prompt
printf("\n%s#myshell:%s>", username,cwd);
//gets string from userCom line
fgets(buffer, MAX_COMMAND_SIZE + 1, stdin);
buffer[strlen(buffer)-1] = 0 ;//set null
//set-up history function
userCom.history[last% HIS_SIZE] = cmd;
last++;
//parses tokens and looks for delimiters
token[tok] = strtok(buffer,whitespace);
while(token[tok])
{
++tok;
if(tok == MAX_ARGS)
printf("Reached MAX userCom arguments");
break;
token[tok] = strtok(NULL, whitespace);
}
i =0;
//sort tokens based on special characters
for (;i<tok;++i)
{
if(!strcmp(token[i], "<"))
{
userCom.output = token[++i];
}
else if(!strcmp(token[i], ">"))
{
userCom.input = token[++i];
}
else if (token[i][0] == '$')
{
char* toktok = getenv((const char*)&token[i][1]);
if (!toktok)
{
printf("%s: ERROR: variable.\n", token[i]);
return 0;
}
else
{
userCom.arg[userCom.argument] = toktok;
++(userCom.argument);
}
}
else
{
userCom.arg[userCom.argument] = token[i];
++(userCom.argument);
}
}
tok = 0;
userCom.arg[userCom.argument] = 0;
//handles the "cd" command
if((strcmp(userCom.arg[0],"cd") == 0))
{
if (userCom.argument > 2)
printf("cd: Too many arguments\n");
// change directories if valid target and update cwd
else if (userCom.argument == 1)
{
new = chdir(cwd);
if (new != 0)
printf("%s: No such file or directory\n");
// if no argument is given, new directory should be $HOME
else
{
new = chdir(curDirect);
// get the new current working directory
size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *)malloc((size_t)size)) != NULL)
cwd = getcwd(buf, (size_t)size);
}
}
}//end "cd" function
//handles the echo command
else if(strcmp(userCom.arg[0], "echo") == 0)
{
int p;
for(p=1;p < userCom.argument; ++p)
printf("%s ", userCom.arg[p]);
}//ends echo function
//handles exit
else if(strcmp(userCom.arg[0], "exit") == 0)
{
exit(0);
}
//handles history functions
else if(strcmp(userCom.arg[0], "history") == 0)
{
int j;
for(j = last, limit = 0; userCom.history[j] != NULL && limit != HIS_SIZE ; j = (j -1)% HIS_SIZE, limit++)
printf(" %s ",userCom.history[j]);
}
else {
break;
}
}//end while 1 loop
return 0;
}//End int main
while(token[tok])
{
++tok;
if(tok == MAX_ARGS)
printf("Reached MAX userCom arguments");
break;
token[tok] = strtok(NULL, whitespace);
}
This loop breaks at the first time. Probably the if should be like
if(tok == MAX_ARGS) {
printf("Reached MAX userCom arguments");
break;
}
Also userCom.argument is never set back to zero, which causes the cd .. to work first time.

Resources