C OS X Read Directory Contents into String Array - c

I am trying to get the contents of a directory. Ideally, I would like to store them in a string array. Is there a way to do this in c other than opening the directory, iterating through its contents, and populating an array as it goes?
I am working on a system running OS X 10.9

You can obtain an allocated directory listing with the POSIX scandir function, which takes a path and optional filtering and sorting callbacks, and returns an array of dirent structures. OS X also provides an equivalent function which takes blocks rather than callbacks for sorting and filtering.
int scandir(const char *dirname, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
Just retrieving an unsorted list of entries is very straightforward:
int num_entries;
struct dirent **entries = NULL;
num_entries = scandir("/", &entries, NULL, NULL);
for(int i = 0; i < num_entries; i++)
puts(entries[i]->d_name);
//entries is ours to free
for(int i = 0; i < num_entries; i++)
free(entries[i]);
free(entries);
POSIX also provides a pre-made sorting function to use with scandir for alphabetical ordering. To use it, just pass alphasort as the last argument.
Be careful of scandir returning an error (-1). The above code is structured in such a way that an explicit check isn't necessary, but that may not be possible in more elaborate uses.

You might want to run using system libc call and fopen.
Here is the sample code, take care of all the array lengths, there is NO validation done here.
#include
#include
#include
int
main(int argc, char* argv[])
{
char cmd[254] = "ls ";
char arr[1024];
char line[254];
FILE *fp;
if(argc < 2) return -1;
if(argv[1]) strcat(cmd, argv[1]);
strcat(cmd, " > /tmp/out");
system(cmd);
fp = fopen("/tmp/out", "r");
if(!fp){
perror("");
return fprintf(stderr, "could not open /tmp/out!\n");
}
while(fgets(line, 254, fp) != NULL) {
strcat(arr, line);
}
printf("%s\n", arr);
return 0;
}

Related

Segmentation fault when attempting to get length of file name

I have narrowed a segmentation fault down to the specific line of code causing it. Here is a straightforward example showing the issue I am having.
int main()
{
char** files;
int sum;
int i;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
sum = file_sum();
char* files[sum];
i = 0;
while ((dir = readdir(d)) != NULL)
{
files[i] = dir->d_name;
i++;
}
closedir(d);
}
else
{
return -1;
}
int len = strlen(files[0]); /*segmentation fault here*/
return 0;
}
Essentially what the program is doing is taking the names of all of the files from the current directory and putting them into an array. I am then going to try to get the sizes of all of the file names, but I have been getting a segmentation fault. My theory is that perhaps the file names are not null-terminated? If this is true, I am unsure of a workaround for that, but any assistance is appreciated.
Thanks
EDIT: Sorry I had an error here. The segmentation fault only occurs when I try to store the strlen return value, as I have now changed the code to above
Inside of your if block, you're defining a variable named files. This masks the variable of the same name at the top of the function. It is not specifying the size of the array at the higher scope, which seems to be your intent. So when you exit the if block, the inner files goes out of scope, and the outer files is left uninitialized. You then dereference the uninitialized pointer, causing the core dump.
What you want to do is dynamically allocate the necessary memory inside of the if block for the memory you need.
Also, the directory name retrieved into dir->d_name probably gets overwritten on each call to readdir, so you need to allocate space for that as well.
EDIT:
You also don't need a separate function to get the file count. You can allocate the array with a default size and realloc to expand as needed:
int main()
{
char** files;
int sum;
int i;
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
int size = 10;
sum = 0;
files = malloc(size * sizeof(char *)); // allocate the array
if (files == NULL) {
perror("malloc failed");
exit(1);
}
while ((dir = readdir(d)) != NULL)
{
if (sum >= size) {
// if the array is full, realloc twice the size
char **tmp;
size *= 2;
tmp = realloc(size * sizeof(char *));
if (tmp == NULL) {
perror("realloc failed");
exit(1);
}
files = tmp;
}
files[sum] = strdup(dir->d_name); // allocate and copy each string
sum++;
}
closedir(d);
}
else
{
return -1;
}
strlen(files[0]);
// free the individual strings
for (i=0; i<sum; i++) {
free(files[i]);
}
// free the array
free(files);
return 0;
}
Using "char* files[sum]" is only safe under 2 conditons:
you not trying to access the array after the end of it's declaration range: you shouln't try to access the "files" array after the end of the "if" where it's declared
that char* target need to be safely reserved. It's reasonnable to assume that the target is valid right after dir->d_name but after having closed the d DIR it's pretty risky
Here's a simple fix if you know how many files there can be in your directory : copying the dirent struct to dirList is the simplest but it's quite a big struct. You can also copy only a pointer to the dirent using "dirListPtr" but you need to be sure that your dirent struct is still valid. That's why i have put "closedir" a bit farther down.
If you're not sure about the number of files, dynamic allocation is the way to go (see dbush answer). If you want to use the data in another function, allocation is required too.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
int main()
{
int sum;
int i;
DIR *d;
struct dirent* dir;
// you must be sure to have less than 255 files
struct dirent dirList[255];
struct dirent* dirListPtr[255];
d = opendir(".");
if (d)
{
i = 0;
while ((dir = readdir(d)) != NULL)
{
// copy the dirent information to our local variable which will live until the end of the main
dirList[i] = *dir;
// or keep only a pointer to the dirent struct: the system will surely not discard the dirent at least as long as d is open
dirListPtr[i] = dir;
i++;
}
}
else
{
return -1;
}
printf("size of filename: %lu size of dirent %lu\n", strlen(dirList[0].d_name), sizeof(dirList[0]));
printf("size of filename: %lu\n", strlen(dirListPtr[0]->d_name));
closedir(d);
return 0;
}
size of filename: 2 size of dirent 280
size of filename: 2

Passing pointer of pointers to function, returning both an int and an address (by parameter)

I've got a function which, as is, works correctly. However the rest of the program has a limitation in that I've preset the size of the array (the space to be allocated). Obviously, this is problematic should an event arise in which I need extra space for that array. So I want to add dynamic allocation of memory into my program.
But I'm having an issue with the whole pointer to a pointer concept, and I've utterly failed to find an online explanation that makes sense to me...
I think I'll want to use malloc(iRead + 1) to get an array of the right size, but I'm not sure what that should be assigned to... *array? **array? I'm not at all sure.
And I'm also not clear on my while loops. &array[iRead] will no longer work, and I'm not sure how to get a hold of the elements in the array when there's a pointer to a pointer involved.
Can anyone point (heh pointer pun) me in the right direction?
I can think of the following approaches.
First approach
Make two passes through the file.
In the first pass, read the numbers and discard them but keep counting the number of items.
Allocate memory once for all the items.
Rewind the file and make a second pass through it. In the second pass, read and store the numbers.
int getNumberOfItems(FILE* fp, int hexi)
{
int numItems = 0;
int number;
char const* format = (hexi == 0) ? "%X" : "%d";
while (fscanf(fp, format, &number) > 0) {
++numItems;
return numItems;
}
void read(int *array, FILE* fp, int numItems, int hexi)
{
int i = 0;
char const* format = (hexi == 0) ? "%X" : "%d";
for ( i = 0; i < numItems; ++i )
fscanf(fp, format, &array[i]);
}
int main(int argc, char** argv)
{
int hexi = 0;
FILE* fp = fopen(argv[1], "r");
// if ( fp == NULL )
// Add error checking code
// Get the number of items in the file.
int numItems = getNumberOfItems(fp, hexi);
// Allocate memory for the items.
int* array = malloc(sizeof(int)*numItems);
// Rewind the file before reading the data
frewind(fp);
// Read the data.
read(array, fp, numItems, hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
Second approach.
Keep reading numbers from the file.
Every time you read a number, use realloc to allocate space the additional item.
Store the in the reallocated memory.
int read(int **array, char* fpin, int hexi)
{
int number;
int iRead = 0;
// Local variable for ease of use.
int* arr = NULL;
char const* format = (hexi == 0) ? "%X" : "%d";
FILE *fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
while (fscanf(fp, format, &number) > 0) {
arr = realloc(arr, sizeof(int)*(iRead+1));
arr[iRead] = number;
iRead += 1;
}
fclose(fp);
// Return the array in the output argument.
*array = arr;
return iRead;
}
int main(int argc, char** argv)
{
int hexi = 0;
int* array = NULL;
// Read the data.
int numItems = read(&array, argv[1], hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
int read(int **array, char* fpin, int hexi) {
int iRead = 0;
int i, *ary;
char *para;
FILE *fp;
fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
para = (hexi == 0) ? "%*X" : "%*d";
while (fscanf(fp, para)!= EOF)
++iRead;
ary = *array = malloc(iRead*sizeof(int));
if(ary == NULL){
printf("malloc error!\n");
exit(-2);
}
rewind(fp);
para = (hexi == 0) ? "%X" : "%d";
for(i = 0; i < iRead; ++i)
fscanf(fp, para, &ary[i]);
fclose(fp);
return iRead;
}
I'd suggest something like this:
int read(int **array_pp, char* fpin, int hexi) {
...
int *array = malloc (sizeof (int) * n);
for (int i=0; i < n; i++)
fscanf(fp, "%X",&array[i]);
...
*array_pp = array;
return n;
}
Notes:
1) You must use "**" if you want to return a pointer in a function argument
2) If you prefer, however, you can declare two pointer variables (array_pp and array) to simplify your code.
I think you wouldn't call it an array. Arrays are of fixed size and lie on the stack. What you need (as you already said), is dynamically allocated memory on the heap.
maybe that's why you didn't find much :)
here are some tutorials:
http://en.wikibooks.org/wiki/C_Programming/Arrays (and following pages)
http://www.eskimo.com/~scs/cclass/int/sx8.html
you got the function declaration correctly:
int read(int **array, char* fpin, int hexi)
What you need to do:
find out how much memory you need, eg. how many elements
allocate it with *array = malloc(numElements * sizeof(int)) (read "at the address pointed by array allocate memory for numElements ints")
now you can (*array)[idx] = some int (read "at the address pointed by array, take the element with index idx and assign some int to it")
call it with int* destination; int size = read(&destination, "asdf", hexi)
hope it helps..

read file of 10 lines of integer, put them in a array, and output the array

In this program, i want to let user to input 2 arguments, the number
of integer,and the file name.
the file has 10 lines of integer value.
read the file, and put it to inArray[];
and then output it as the end;
notes: For the complete program, i want to make a program that
will scan a file consists of random integer,and then sort
them in ascend order, and print out the first 10 percent
of the sorted integer.
Error: For now, i want to test if it can read the file and put values
into the inArray properly, but its keep getting errors.
warning: initialization makes integer from pointer without a cast
findTotal.c:43:6: warning: passing argument 1 of ‘fopen’
makes pointer from integer without a cast
/usr/include/stdio.h:271:14: note: expected ‘const
char * __restrict__’ but argument is of type ‘char’
Please help me with this, thank you
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int numOfInt;
char fileName="";
sscanf(argv[1],"%d",&numOfInt);
sscanf(argv[2],"%c",&fileName);
int i, rc;
/* the origninal list , initialise as 0 index*/
int inArray[numOfInt];
/* the number of output int */
int outNumInt = numOfInt * 0.1;
/* the output array of int */
int outArray[outNumInt];
FILE *inFile;
inFile = fopen(fileName,"r");
/* check if the file is empty */
if(inFile==NULL){
printf("can not open the file");
}
for (i = 0; (rc = getc(inFile)) != EOF && i < numOfInt; inArray[i++] = rc)
{
}//for
fclose(inFile);
for(i = 0; i < numOfInt;i++){
printf("%x\n",inArray[i]);
}
}//main
I think you could be using scanf better here. You use it to read in two pieces of information that should have been passed as arguments to the program, and then refreain from using it for what it would actually be good for, which is reading the file in question. Here is my take at this:
#include <stdlib.h>
#include <stdio.h>
int cmp(const void *a, const void *b) { return *(int*)b - *(int*)a; }
int main(int argc, char *argv[])
{
char * ifile = argv[1];
int n = atoi(argv[2]), m = n/10, i;
int nums[n];
FILE * f = fopen(ifile, "r");
for(i = 0; i < n; i++) fscanf(f, "%d", &nums[i]);
qsort(nums, n, sizeof(int), cmp);
for(i = 0; i < m; i++) printf("%d\n",nums[i]);
return 0;
}
If this file is prog.c and the corresponding executable is prog, and your file with numbers is called nums.txt, and contains 100 integers, you would call this as
prog nums.txt 100
The advantage of taking in parameters this way is that it makes repeating the command later easier (all the information needed to repeat it will be in the command history of the shell), and that it is the standard way of passing parameters to a program. It also frees up standard input for other uses.
You have indeed a problem with the filename's management. char is for characters; if you want to handle a file name, you have to use a string. In C, we can use an array of char, terminated by a nul-character. Here, because argv[2] holds directly the name, you can simply use a pointer.
char *fileName = argv[2];
And then:
fopen(fileName, "r");
Since you don't modify the argv pointer, you can also send directly argv[2] as argument.
One of the problems I see in your code is:
char fileName="";
sscanf(argv[2],"%c",&fileName)
A string literal is a constant string, which means you shouldn't attempt to modify it, you should either use a static (or dynamic) char array for that string and use the %s format specifier, or just point fileName to argv[2]
char *fileName;
fileName = argv[2];

Concatenate path and variable in C

This is my code. I need create 10 files with this format:
0.txt
1.txt
...
But I can't do it, the result is different. Can anyone help?
#include <stdlib.h>
#include <string.h>
char* direccion(char *dirD,int number){
char buffer[100];
char *n;
char *q;
char* formato=".txt";
sprintf(buffer, "%i", number);
n= strcat(dirD,buffer);
q= strcat(n,formato);
return q;
}
int main(){
int u;
int number= 0;
int t= 0;
char* q = "e:/c9/";
for(t=0; t< 10 ; t++){
char* dir = direccion(q,number);
if(fopen(dir,"w")){
fopen(dir,"w");
u=1;
}
else{
u=0;
printf("Error\n");
}
number++;
}
return 0;
}
Thanks !
Problem No 1. is:
char *q = "e:/c9/";
for(t = 0; t < 10; t++)
{
char *dir = direccion(q, number);
}
The variable q is a string literal. However, in the function direccion(), you pass it as the first argument to strcat(), which tries to modify it — BOOM, a segfault. Solution:
char buf[1000];
strcpy(buf, "e:/c9/");
char *dir = direccion(buf, number);
Problem #2 is as #Charlie Martin pointed out is that you return a statically automatically allocated local string, which is invalid after the direccion() function returns. Maybe
return strdup(buffer);
in this case; don't forget to free it after use.
Edit: seems you don't even ask about this. You can create a file using the open() system call (function):
#include <fcntl.h>
#include <stdio.h>
int fd = open("file.txt", O_RDWR | O_CREAT, 0644);
close(fd);
Of course substitute the actual file mode you want for 0644 (but don't make the file executable unless it contains a program to be executed).
Edit 2: I didn't even catch this one... So, in the for loop, you want to reset the base filename over and over:
for (yadda yadda)
{
strcpy(buf, "e:/c9/");
char *dir = direccion(buf, i);
}
etc.
You're allocating the buffer in automatic inside your function, which means it's allocated on the stack. When you return, that memory is no longer valid. You want to allocate the buffer in the caller and pass a pointer to it, or allocate it as static.
Windows has a series of path handling functions that you can use to do things like add an extension to a path.
For example:
PathAddExtension(buffer,".txt");
For more information see http://msdn.microsoft.com/en-us/library/windows/desktop/bb773559(v=vs.85).aspx
Also, it's a good idea to work with buffers of _MAX_PATH size when dealing with paths.

create array of pointers to files

How would I go about making an array of file pointers in C?
I would like to create an array of file pointers to the arguments of main... like a1.txt, a2.txt, etc... So I would run ./prog arg1.txt arg2.txt arg3.txtto have the program use these files.
Then the argument for main is char **argv
From argv, I would like to create the array of files/file pointers. This is what I have so far.
FILE *inputFiles[argc - 1];
int i;
for (i = 1; i < argc; i++)
inputFiles[i] = fopen(argv[i], "r");
The code is fine, but remember to compile in C99.
If you don't use C99, you need to create the array on heap, like:
FILE** inputFiles = malloc(sizeof(FILE*) * (argc-1));
// operations...
free(inputFiles);
#include <stdio.h>`
int main(int argc, char **argv)
{
FILE *inputFiles[argc - 1];
int i;
for (i = 1; i < argc; i++)
{
printf("%s\n",argv[i]);
inputFiles[i] = fopen(argv[i], "r");
printf("%p\n",inputFiles[i]);
}
return 0;
}
It prints different pointers for each file pointer along with the names. Allowing OS to close files properly :)

Resources