I'm implementing the ls -R linux command in C and I'm having trouble with the recursion step.
So far I have
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
void lsr(char *directory){
DIR *dir;
dir = opendir(directory);
int size = 0;
struct dirent *d;
char *que[100];
char buf[100];
while((d = readdir(dir)) != NULL){
struct stat statbuff;
if(strcmp(d->d_name,".")== 0 || strcmp(d->d_name,"..")==0){
continue;
}
sprintf(buf, "%s/%s", directory, d->d_name);
stat(buf,&statbuff);
printf(" %s",d->d_name);
printf("\n");
if(S_ISDIR(statbuff.st_mode)){
printf("%s Is a directory\n",buf);
que[size] = buf;
size++;
}
}
int i;
for(i = 0; i < size; i++){
printf("dir: %s\n",que[i]);
}
printf("Are in the queue\n");
//for(i = 0; i < size; i++){
// lsr(que[i]);
//}
closedir(dir);
}
int main(int argc, char *argv[]){
char directory[200];
if(argc ==1){
strcpy(directory,".");
}
else{
strcpy(directory,argv[1]);
}
lsr(directory);
return 0;
}
What im going for is that the function finishes printing all the files in the directory before it recurses into the first directory it found. The only thing i could think of is to store them into a array and then make the call that way.
If anyone could point out why it is that when i loop through the array, its content is wrong or maybe even better, A more practical solution as to how to recurse into directories after the file's names have been printed I would greatly appreciate it!
Here you store the address pointed by buf in que[size].
que[size] = buf;
In the next loop iteration,stat(buf,&statbuff); modifies the content pointed by buf and que[size]
So every slot of you array points to this same address and everytime you modifies this content, you modifies the full array.
I suggest to use strcpy() or to reallocate buf before the stat() call.
Related
Why can't I read what is in a directory, it keeps on giving me segmentation faults?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
int count(char* loc)
{
int num = 0;
char c;
FILE *file = fopen(loc, "r");
while( (c = fgetc(file)) != EOF) {
if(c == '\n')
num++;
}
int r = fclose(file);
return num;
}
int main()
{
int lines = 0;
int files = 0;
char* names[files];
int i = 0;
DIR* d = opendir("./visual"); //You can change this bit
struct dirent *file;
while((file = readdir(d)) != NULL){
i++;
names[i] = file->d_name;
files++;
printf("%s\n", names[i]);
}
closedir(d);
printf("__________\n");
for(int i = 0;i < files;i++){
printf("i = %d\n", i);
lines = lines + count(names[i]);
}
printf("you have written %d lines of code", lines);
}
Here you define an array of size 0.
int files = 0;
char* names[files];
Here (while i and files are both 0) you access the first of those zero (note the conflict here?) elements in the array.
names[i] = file->d_name;
Then you increase files.
files++;
This does however not change the size of the array, and even if it would it would be too late.
Going on, I will quote WhozCraigs helpful comment (with permission):
Even fixing that, you're still in for an awakening. names[i] = file->d_name will store off a pointer to memory that is neither guaranteed, nor even likely, to be static for the lifetime of the enumeration.
It can/will be reused as you enumerate each file entry. And even if it didn't, all that memory is guaranteed to be off-limits once closedir is fired.
If you want to retain the file names, you need to make copies of them; not just save off pointers.
End of quote.
I currently have functioning code, that when I attempted to make a function out of the conversion of a file to array, I get a segmentation fault. I know that the objects inside of fileToArray are correct (as far as the myData objects are concerned) because when inside of the function, the myData.length, and myData.array all return correctly. However, after the pointer is referenced in the main, I get a seg fault. I'm new to c, but all of this is working without that specific pointer to the struct.
So, if I call this program with an argument with a file with multiple rows of text, the set fault happens.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int length;
char** array;
} FileStruct;
void fileToArray(FileStruct* fileDataPtr, int argc, char *argv[]){
int fd, i, n, count;
struct stat statbuf;
char *buf, *inbuf, *str, *saveptr;
char **array;
if ((fd = open(argv[1], O_RDONLY)) == -1) {
printf("Error opening file %s\n", argv[1]);
exit (-1);
}
if (lstat(argv[1], &statbuf) < 0) {
printf("Unable to lstat file %s\n", argv[1]);
exit (-1);
}
off_t filesize = statbuf.st_size;
buf = malloc(sizeof(char)*filesize);
array = malloc(sizeof(char *)*filesize);
count = 0;
if ((n = read(fd, buf, filesize)) > 0){
inbuf = buf;
for (i = 1; ; inbuf = NULL, i++) {
str = strtok_r(inbuf, "\n", &saveptr);
if (str == NULL)
break;
array[count] = malloc(sizeof(char)*(strlen(str)+1));
strcpy(array[count++], str);
}
} else {
printf("Error reading input file\n");
exit (-1);
}
close(fd);
// I know array works because it prints correctly here.
for (i = 0; i < count; i++) {
printf("%s\n", array[i]);
free(array[i]);
}
fileDataPtr->length = count;
fileDataPtr->array = array;
free(array);
free(buf);
}
int main(int argc, char *argv[]) {
int i;
FileStruct myData;
FileStruct* fileDataPtr = &myData;
fileToArray(fileDataPtr, argc, argv);
printf("length: %i", myData.length);
// I know this doesn't work because anything related to myData causes Seg fault.
// for (i = 0; i < 1; i++) {
// printf("%s\n", myData.array[i]);
// free(myData.array[i]);
// }
return 0;
}
Near the end of fileToArray, you assign array to fileDataPtr->array, then on the next line you free array. This will leave fileDataPtr->array pointing to freed memory (a dangling pointer). When you dereference it later, you're into Undefined Behavior and anything can happen.
Since the assignment transfers ownership of the allocated memory to fileDataPtr, you do not need to free array before returning from fileToArray.
Remove the free(array); line.
I'm attempting to copy a C-string, which is read in from a file to an element of a struct array, but it is not copying. When I attempt to print, the word is not there. I'm kind of new to C. Below is my code. Many thanks for your help.
typedef struct Tree{
int numTimes; //number of occurrences
char* word; //the word buffer
}Node;
#include "proj2.h"
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]){
FILE* readIn; //read file pointer
FILE* writeOut; //write file pointer
char buffer[18]; //allocate buffer ***please do not fuzz
int length = 0;
int count = 0;
Node* array = (Node*) malloc(sizeof(Node));
/*if(argc < 3){ //if the number of command line arguments is < 3, return EXIT_FAILURE
return EXIT_FAILURE;
}*/
argv[1] = "/Users/magnificentbastard/Documents/workspaceCPP/proj2/Password.txt"; //****testing
argv[2] = "outFile.txt"; //****testing
readIn = fopen(argv[1], "r"); //opens the selected argument file for reading
writeOut = fopen(argv[2], "w"); //opens the selected argument file for writing
if(readIn == NULL){ //if there
printf("ERROR: fopen fail.\n");
return EXIT_FAILURE; //exits if the file opens
}
while(fscanf(readIn, "%18s", buffer) == 1){ //loop to read in the words to the buffer
count++; //counts the words coming in
modWord(buffer); //modifies the words coming in
array = (Node*)realloc(array, sizeof(Node));
for(int i = 0; i < count; i++){ //****not copying over...HELP
strcpy(array[i].word, buffer);
}
}
//Node array[count];
fprintf(stderr, "%d ", count); //***for testing purposes only
int elements = sizeof(array)/sizeof(array[0]); //***testing assigns num elements
fprintf(stderr, "%d ", elements); //***testing prints num elements
fclose(readIn); //closes the in-file
fclose(writeOut); //closes the out-file
return EXIT_SUCCESS;
}
array[count] doesn't allocate the memory. I believe what you're trying to implement here is single-linked list of strings.
What you're trying to do can be achieved, but you'd need to allocate memory for array by using malloc/free combo. What's more, what you're trying to achieve should by done by either making Node.word an array of fixed size OR a pointer and allocating the memory on Node-by-Node basis.
Length of an array cannot be retrieved by use of sizeof operator as sizeof is evaluated in compile and it'll always return a size of a pointer on your platform.
I'm having trouble trying to figure out how to concatenate the file path with the strings. The user input the strings in the command prompt like this:
StringLabMain.exe Mary had a little lamb 1234
It supposed to print out something like this:
Concatenated arguments: d:\Documents and Settings\labadmin\My Documents\Visual Studio 2012\Projects\test\debug\StringLabMain.exeMaryhadalittlelamb1234
but my code prints out this:
Concatenated arguments: StringLabMain.exeMaryhadalittlelamb1234
Here is my code (I don't understand how the concatenate works to include the file path with the strings):
int main(int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i++)
{
printf("%s", argv[i]);
}
return 0;
}
I hope I explained this clearly.
First, if your only purpose is to print the directory and the concatenated args, so you just have to print the current directory before the main loop. This may be done using getcwd().
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
printf("%s", getcwd(0,0));
for (i = 0; i < argc; i++)
{
printf("%s", argv[i]);
}
return 0;
}
But for more general purposes I really recommend you to use stracat() which concatenates string. So you have to declare a "string" (using char *) with the current working directory, and then concatenate the args. This will be done like this way:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char* toPrint;
int i;
toPrint = getcwd(0,0);
for (i = 0; i < argc; i++)
strcat (toPrint, argv[i]);
printf("%s\n",toPrint);
return 0;
}
I hope that know it's clear.
The following code demonstrates how to use strcat() to build up a string of all argv[] elements:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize = 1;
char *output = NULL;
/* Allocate a buffer large enough to hold the string termination character. */
output=malloc(outputSize);
if(!output)
{
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
*output = '\0';
/* Iterate argv[] elements. */
for(i = 0; i < argc; i++)
{
char *tmp;
/* Increase the size of the output buffer to hold this argv[] element. */
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
/* Concatinate this argv[] element to the output string. */
strcat(output, argv[i]);
}
/* Print the result. */
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}
On Linux, you can also include the path of the current working directory, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize;
char *output = NULL;
output=getcwd(NULL,0);
if(!output)
{
fprintf(stderr, "getcwd() failed.\n");
goto CLEANUP;
}
outputSize = strlen(output) + 1;
for(i = 0; i < argc; i++)
{
char *tmp;
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
strcat(output, argv[i]);
}
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}
The above example is Linux specific due to a Linux extension to 'getcwd()'. The Linux getcwd man page states:
As an extension to the POSIX.1-2001 standard, Linux (libc4, libc5, glibc) getcwd() allocates the buffer dynamically using malloc(3) if buf is NULL. In this case, the allocated buffer has the length size unless size is zero, when buf is allocated as big as necessary. The caller should free(3) the returned buffer.
Apparently, _getcwd() works the same way on MS Windows. MSDN states about _getcwd():
The _getcwd function gets the full path of the current working directory for the default drive and stores it at buffer. The integer argument maxlen specifies the maximum length for the path. An error occurs if the length of the path (including the terminating null character) exceeds maxlen. The buffer argument can be NULL; a buffer of at least size maxlen (more only if necessary) is automatically allocated, using malloc, to store the path. This buffer can later be freed by calling free and passing it the _getcwd return value (a pointer to the allocated buffer).
So, perhaps the following (untested) code would be suitable for a MS Windows environment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <direct.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize;
char *output = NULL;
output=_getcwd(NULL,0);
if(!output)
{
fprintf(stderr, "_getcwd() failed.\n");
goto CLEANUP;
}
outputSize = strlen(output) + 1;
for(i = 0; i < argc; i++)
{
char *tmp;
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
strcat(output, argv[i]);
}
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}
I have to create a function that reads a file called grwords.txt containing around 540000 words which are written in Greek letters.
I have to convert these words to uppercase and fill an array called char **words.
This is what I have so far.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include <ctype.h>
void fp();
int main(int argc, char *argv[]) {
SetConsoleOutputCP(1253);
fp();
return 0;
}
void fp(){
char **words;
words = malloc(546490 * sizeof(int *));
for (i = 0; i < 546490; i++)
words[i] = malloc(24 * sizeof(int));
FILE *file;
char *word;
size_t cnt;
file = fopen("grwords.txt", "rt");
if (file == NULL){
printf("File cannot be opened.\n");
exit(1);
}
cnt = 0;
while (1==fscanf(file, "%24s",word)){
if (cnt == 546490)
break;
strcpy(words[cnt++], word);
}
fclose(file);
}
I'm still trying to figure out pointers. I know that & makes a pointer from a value and * a value from a pointer. Updated the program and it successfully fills the array with the words from the file! I still have no idea how to convert Greek lowercase to uppercase.
Handling Greek words can be dependent on your platform.
First of all, you need to understand how file handling works. Here is what I wrote:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define bufSize 1024 // max lenght of word
// we are going to receive the .txt from cmd line
int main(int argc, char *argv[])
{
FILE *fp;
// Assume file has max 10 words
const size_t N = 10;
// Allocate a 2D array of N rows
// and bufSize columns.
// You can think of it like an array
// of N strings, where every string
// has, at most, bufSize length.
char buf[N][bufSize];
// make sure we got the .txt
if (argc != 2)
{
fprintf(stderr,
"Usage: %s <soure-file>\n", argv[0]);
return 1;
}
// open the file
if ((fp = fopen(argv[1], "r")) == NULL)
{ /* Open source file. */
perror("fopen source-file");
return 1;
}
// we will use that for toupper()
char c;
// counters
int i = 0, j;
while (fscanf(fp, "%1024s", buf[i]) == 1)
{ /* While we don't reach the end of source. */
/* Read characters from source file to fill buffer. */
// print what we read
printf("%s\n", buf[i]);
j = 0;
// while we are on a letter of word placed
// in buf[i]
while (buf[i][j])
{
// make the letter capital and print it
c = buf[i][j];
putchar (toupper(c));
j++;
}
i++;
printf("\ndone with this word\n");
}
// close the file
fclose(fp);
return 0;
}
For this test.txt file:
Georgios
Samaras
Γιώργος
Σαμαράς
the code would run as:
./exe test.txt
Georgios
GEORGIOS
done with this word
Samaras
SAMARAS
done with this word
Γιώργος
Γιώργος
done with this word
Σαμαράς
Σαμαράς
done with this word
As you can see, I could read the Greek words, but failed to convert them in upper case ones.
Once you got how file handling goes, you need to use wide characters to read a file with Greek words.
So, by just modifying the above code, we get:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <wchar.h>
#include <wctype.h>
#include <locale.h>
#define bufSize 1024
int main(int argc, char *argv[])
{
setlocale(LC_CTYPE, "en_GB.UTF-8");
FILE *fp;
const size_t N = 15;
wchar_t buf[N][bufSize];
if (argc != 2)
{
fprintf(stderr,
"Usage: %s <soure-file>\n", argv[0]);
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
perror("fopen source-file");
return 1;
}
wchar_t c;
int i = 0, j;
while (fwscanf(fp, L"%ls", buf[i]) == 1)
{
wprintf( L"%ls\n\n", buf[i]);
j = 0;
while (buf[i][j])
{
c = buf[i][j];
putwchar (towupper(c));
j++;
}
i++;
wprintf(L"\ndone with this word\n");
}
fclose(fp);
return 0;
}
And now the output is this:
Georgios
GEORGIOS
done with this word
Samaras
SAMARAS
done with this word
Γιώργος
ΓΙΏΡΓΟΣ
done with this word
Σαμαράς
ΣΑΜΑΡΆΣ
done with this word
I see that you may want to create a function which reads the words. If you need a simple example of functions in C, you can visit my pseudo-site here.
As for the 2D array I mentioned above, this picture might help:
where N is the number of rows (equal to 4) and M is the number of columns (equal to 5). In the code above, N is N and M is bufSize. I explain more here, were you can also found code for dynamic allocation of a 2D array.
I know see that you are on Windows. I tested the code in Ubuntu.
For Windows you might want to take a good look at this question.
So, after you read all the above and understand them, you can see what you asked for with dynamic memory management.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <wctype.h>
#include <locale.h>
#define bufSize 1024
wchar_t **get(int N, int M);
void free2Darray(wchar_t** p, int N);
int main(int argc, char *argv[])
{
setlocale(LC_CTYPE, "en_GB.UTF-8");
FILE *fp;
const size_t N = 15;
wchar_t** buf = get(N, bufSize);
if (argc != 2)
{
fprintf(stderr,
"Usage: %s <soure-file>\n", argv[0]);
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
perror("fopen source-file");
return 1;
}
wchar_t c;
int i = 0, j;
while (fwscanf(fp, L"%ls", buf[i]) == 1)
{
wprintf( L"%ls\n", buf[i]);
j = 0;
while (buf[i][j])
{
c = buf[i][j];
putwchar (towupper(c));
j++;
}
i++;
wprintf(L"\ndone with this word\n");
}
fclose(fp);
// NEVER FORGET, FREE THE DYNAMIC MEMORY
free2Darray(buf, N);
return 0;
}
// We return the pointer
wchar_t **get(int N, int M) /* Allocate the array */
{
/* Check if allocation succeeded. (check for NULL pointer) */
int i;
wchar_t **table;
table = malloc(N*sizeof(wchar_t *));
for(i = 0 ; i < N ; i++)
table[i] = malloc( M*sizeof(wchar_t) );
return table;
}
void free2Darray(wchar_t** p, int N)
{
int i;
for(i = 0 ; i < N ; i++)
free(p[i]);
free(p);
}
Note that this code is expected to work on Linux (tested on Ubuntu 12.04), not on Windows (tested on Win 7).