I am writing a program that should search through a directory that the user supplies in order to find all files in said directory that were accessed, modified, or changed within a day of a given time. I am having two definite problem and perhaps another one.
The first problem is that I can only get the program to do shallow searches, it won't look through any subdirectories. I am sure it has to do with what I concatenate to the directory buffer ( right now it is .). The second problem is that it is not searching every file, though it does look at most of them - I think this goes back to problem one though. The third "problem" is that when I check the access time of each file, it seems as though they are all the same (though I don't have this problem with changed/modified time). I am running on Ubuntu through VM, if that might be affecting the access times.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <glob.h>
/* Function that checks if specified file was accessed, modified, or
changed within a day of the specified time. */
void checkFile(long long time, char * fileName) {
struct stat *s;
time_t accessTime;
time_t modTime;
time_t changeTime;
s = malloc(sizeof(struct stat));
if(stat(fileName,s) == 0) {
accessTime = s->st_atime;
modTime = s->st_mtime;
changeTime = s->st_ctime;
if((time - accessTime) <= 86400 && (time - accessTime) >= -86400)
printf("%s\n",fileName);
else if((time - modTime) <= 86400 && (time - modTime) >= -86400)
printf("%s\n",fileName);
else if((time - changeTime) <= 86400 && (time - changeTime) >= -86400)
printf("%s\n",fileName);
}
free(s);
}
void searchDirectory(long long time, glob_t globbuf) {
if(globbuf.gl_pathc == 0)
printf("there were no matching files");
else {
int i;
for(i = 0; i < globbuf.gl_pathc; i++)
checkFile(time,globbuf.gl_pathv[i]);
}
}
int main(int argc, char** argv) {
long long time = atol(argv[1]);
char * buf = argv[2];
strcat(buf,"*.*");
glob_t globbuf;
glob(buf, 0, NULL, &globbuf);
searchDirectory(time,globbuf);
globfree(&globbuf);
return 0;
}
Thanks for your time!
You should not
cat(buf, "*.*");
...since 'buf' is a pointer to a string provided by the os - you don't know if that buffer is large enough to hold the extra text you are adding. You could allocate a large buffer, copy the contents of argv[2] into it and then append "*.*", but to be really safe you should determine the length of argv[2] and ensure your buffer is large enough.
You can use the st_mode member of the struct stat structure to determine if the file is a directory (check if it equals S_IFDIR). If it is, you could make it the current directory and as jonsca suggested, call your searchDirectory function again. But when using recursion you usually want to ensure you have a limit on the depth of recursion, or you can overflow the stack. This is a kind of 'depth first search'. The solution I prefer is to do a 'breadth first search' using a queue: basically push your first glob onto the start of a list, then repeatedly take the first item off that list and search it, adding new directories to the end of the list as you go, until the list is empty.
When evaluating programs like this, teachers love to award extra points for those that don't blow their stack too easily :)
P.S. I'm guessing the access time issue is a VM/filesystem/etc incompatibility and not your fault.
Related
I using g++ on linux with eclipse. I am making a code that get file's time and output file's month,hour,etc...
While debugging, the value of time1 changed unexpectedly but I have no idea about this issue.
What is problem of this code?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
struct stat stat1, stat2;
struct tm *time1, *time2;
void filestat1(void);
void filestat2(void);
void filetime1(void);
void filetime2(void);
void datecmp(void);
void timecmp(void);
int main(void)
{
filestat1();
filestat2();
filetime1();
filetime2();
datecmp();
timecmp();
}
void filestat1(void)
{
// check if there is no text1
int check = 0;
check = stat("text1", &stat1);
if(check != 0)
{
printf("Error : there is no text1\n");
}
return;
}
void filestat2(void)
{
// check if there is no text2
int check = 0;
check = stat("text2", &stat2);
if(check != 0)
{
printf("Error : there is no text2\n");
}
return;
}
void filetime1(void)
{
time1 = localtime(&stat1.st_mtime); //!!! this change unexpectedly
return;
}
void filetime2(void)
{
time2 = localtime(&stat2.st_mtime);
return;
}
void datecmp(void)
{
printf("date compare\n");
// compare tm_mon
if(time1->tm_mon > time2->tm_mon)
printf("time1 is early \n");
else if(time1->tm_mon < time2->tm_mon)
printf("time2 is early \n");
else{
// compare tm_mday
if(time1->tm_mday > time2->tm_mday)
printf("time1 is early \n");
else if(time1->tm_mday < time2->tm_mday)
printf("time2 is early \n");
// same date
else
printf("same time \n");
}
printf("\n");
}
void timecmp(void)
{
printf(time1->tm_hour);
printf(time2->tm_hour);
printf("time compare\n");
// compare hour
if(time1->tm_hour > time2->tm_hour)
printf("time1 is early \n");
else if(time1->tm_hour < time2->tm_hour)
printf("time2 is early \n");
else{
// compare minutes
if(time1->tm_min > time2->tm_min)
printf("time1 is early \n");
else if(time1->tm_min < time2->tm_min)
printf("time2 is early \n");
// same time
else
printf("same time \n");
}
}
localtime returns a pointer to a static structure. You need to copy the result before calling localtime again.
I would declare time1 and time2 as structures instead of pointers to store the values.
struct tm time1, time2;
void filetime1(void)
{
struct tm *tmp = localtime(&stat1.st_mtime);
if (tmp == NULL) {
//... handle error
}
time1 = *tmp;
}
Similarly for filetime2.
If you are writing multi-threaded code, it is safer to use the reentrant variant of the function, localtime_r. In that case, you pass in the pointer to the structure for the result.
void filetime1(void)
{
struct tm *tmp = localtime_r(&stat1.st_mtime, &time1);
if (tmp == NULL) {
//... handle error
} else {
assert(tmp == &time1);
}
}
You are using global variables, completely unnecessarily, making your life harder than it has to be. It is very hard for us humans to track where global variables are modified, especially when you have several global variables with very similar names.
So, instead of trying to unravel all that, let's rewrite it using function parameters, without any global variables at all.
First, we tell the C library we want POSIX.1-2008 features, and include the headers that expose the functionality we need:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
Next, let's define a function that takes a file name as a parameter, and pointers to where the function can store the last access and last modification timestamps. If the function is successful, it'll return 0; otherwise, it'll return -1 with errno set to indicate the error.
int filetime(const char *path,
time_t *accessed, long *accessed_nsec,
time_t *modified, long *modified_nsec)
{
struct stat info;
/* Path must not be NULL or empty. */
if (!path || !path[0]) {
errno = EINVAL;
return -1;
}
/* Get file statistics. */
if (stat(path, &info) == -1)
return -1; /* errno was set by stat() */
/* Save timestamps. */
if (accessed)
*accessed = info.st_atim.tv_sec;
if (accessed_nsec)
*accessed_nsec = info.st_atim.tv_nsec;
if (modified)
*modified = info.st_mtim.tv_sec;
if (modified_nsec)
*modified_nsec = info.st_mtim.tv_nsec;
/* Success. */
return 0;
}
Let's continue by writing a simple main(), that takes one or more file names as command-line parameters, and describes them. I like to start the main by checking the number of command line arguments, and if specified, the first argument. If none, or the first one is -h or --help, I like to print the usage of the utility. This way, I can keep my example programs in their own directories, and to find one I'm looking for, I can just execute each one without parameters, to see what each of them does. It's much faster than reading the sources!
int main(int argc, char *argv[])
{
int arg;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s FILENAME ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program will print the last access and\n");
fprintf(stderr, "last modification timestamps for each of\n");
fprintf(stderr, "the specified files.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
Because there is at least one, but possibly more than one file name parameter, we handle each of them in a loop:
for (arg = 1; arg < argc; arg++) {
time_t accessed, modified;
long accessed_ns, modified_ns;
struct tm accessed_localtime, modified_localtime;
Within the loop, we first call our filetime() function. Note how we have declared the variables we want filled above, and how we call the function: &accessed yields a pointer to accessed.
if (filetime(argv[arg], &accessed, &accessed_ns,
&modified, &modified_ns)) {
/* Nonzero return value, so an error occurred! */
fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
In C, function parameters are passed by value, not by reference. This means that if a function parameter is say int foo, any changes to foo within the function are visible within the function only; the changes are not visible to the caller. When we pass a pointer to a variable, say int *foo, then changes to foo are still visible only within the function, but *foo refers to the value pointed at by the pointer; and changes to that are visible to the caller.
In short, when we want a function to be able to modify the value of a variable, we use a pointer. That's just how C works.
Now that we have the times in Unix Epoch time (time_t), we want to split them into local time fields:
if (!localtime_r(&accessed, &accessed_localtime) ||
!localtime_r(&modified, &modified_localtime)) {
fprintf(stderr, "%s: Cannot compute timestamps in local time: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
}
Note that I again used a POSIX.1-2008 function, localtime_r(). In tutorials, you often see the older localtime() used instead, but that one may use a global variable internally (it can always return a pointer to the same structure, reusing it for every call); localtime_r() is better.
See how the second parameter to localtime_r is also a pointer (to a struct tm)? Again, this is just how you do functions that change some values in a way that is visible to the caller.
Also, it is rare for localtime_r() (or localtime()) to fail, so many simply ignore checking it for errors. There is no excuse for that, as it's just a couple of lines more code, and if an error does occur at some point, the user will be immensely more satisfied with a clear error code, rather than just seeing the program crash due to segmentation fault.
All that is left, is to print out the information gathered. I like to use a variant the ISO 8601 international standard for the time format; in particular, it sorts in proper time order even if sorted alphabetically. (My variant is that I like to use a space, and not a T, between the date and the time.)
printf("%s:\n", argv[arg]); /* The file name or path */
printf(" Modified: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
modified_localtime.tm_year + 1900,
modified_localtime.tm_mon + 1,
modified_localtime.tm_mday,
modified_localtime.tm_hour,
modified_localtime.tm_min,
modified_localtime.tm_sec,
modified_ns / 1000000L);
printf(" Accessed: %04d-%02d-%02d %02d:%02d:%02d.%03d\n",
accessed_localtime.tm_year + 1900,
accessed_localtime.tm_mon + 1,
accessed_localtime.tm_mday,
accessed_localtime.tm_hour,
accessed_localtime.tm_min,
accessed_localtime.tm_sec,
accessed_ns / 1000000L);
/* Make sure everything written to stdout
is actually written to standard output right now. */
fflush(stdout);
}
return EXIT_SUCCESS;
}
The fflush(stdout) tells the C library to ensure all preceding writes to stdout are actually written to the standard output. (By default, stdout is buffered, and stderr is unbuffered.) Normally, the C library will flush the output at every newline, but having the explicit flush there also reminds us human programmers that we want everything thus far printed, to actually appear on the programs standard output at that point. (This way, if one of the files is on some slow filesystem, say old USB stick or a network share, the information on previous files gets shown before the program accesses the slow file. Essentially, the "stall" will occur at the expected place for the human users.)
It is probably a good idea to mention relatime and other related mount options at this point. In simple terms, it means that to avoid the number of writes to storage media due to read accesses, the access time is not always updated. So, if you don't see it changing even after you read a file (using e.g. cat FILENAME >/dev/null), it just means your system has mount options enabled that reduce the access time updates to speed up your filesystem access and reduce the number of writes to it. It is a good option; I use it.
Finally, most Linux filesystems do not have a created timestamp at all. The st_ctime (and st_ctim.tv_sec and st_ctim.tv_nsec) fields refer to last status change timestamp. It tracks changes to the owner, group, permissions, and the number of hard links.
When you examine the above code, especially the if clauses, it is useful to remember that in C, the logical OR operation, ||, is short-circuiting: the left side is evaluated first, but if it fails, the right side is not evaluated at all. So, if you have e.g. int x = 1, y = 0; and you do (x == 0 || ++y), y will not be incremented at all. I utilize this when examining argv[1] in the very first if clause in main().
Why do I need to hit enter twice to get the current directory to change? I notice that if I change the location of the printf statement in the loop, the behavior changes. I don't understand why that is.
Working code for you below. It compiles on a Ubuntu system. It works fairly minimally. Most of it is usage from man-pages.
#define _GNU_SOURCE // http://man7.org/linux/man-pages/man3/getline.3.html
#include <stdio.h>
#include <unistd.h> // http://man7.org/linux/man-pages/man3/getcwd.3.html
int main() {
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, stdin)) > 0) {
char * buf = NULL;
size_t size = 1000;
char * s = getcwd(buf, size);
printf("%s# ", s);
*(line+linelen-1) = '\0';
chdir(&line[3]);
}
return 0;
}
I don't get the output to the program, which I show below (first, I need to hit enter once to get the prompt -- that's okay and intended for this example).
/path/to/dir# cd ..
/path/to/dir#
/path/to#
Your loop does the following logic:
Wait for user input
Print the current directory
Change the current directory
So each time you enter user input, it will show the old directory, change directory (with no visible output) and then wait for new input. This is indeed what your sample output shows.
To get your desired behaviour, move the getcwd and display down to below the chdir call.
Note that if the person subsequently enters a string shorter than 3 (e.g. they press Enter again, I did while testing and so did you) you go on to call chdir(&line[3]); anyway, causing undesired results (possibly UB) since this is past the end of the string. On my system it repeated the chdir(".."). To fix this , you should probably check linelen >= 3 (and also that it did actually start with cd), and not call chdir if not.
For a class, I've been given the task of writing radix sort in parallel using pthreads, openmp, and MPI. My language of choice in this case is C -- I don't know C++ too well.
Anyways, the way I'm going about reading a text file is causing a segmentation fault at around 500MB file size. The files are line separated 32 bit numbers:
12351
1235234
12
53421
1234
I know C, but I don't know it well; I use things I know, and in this case the things I know are terribly inefficient. My code for reading the text file is as follows:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
int main(int argc, char **argv){
if(argc != 4) {
printf("rs_pthreads requires three arguments to run\n");
return -1;
}
char *fileName=argv[1];
uint32_t radixBits=atoi(argv[2]);
uint32_t numThreads=atoi(argv[3]);
if(radixBits > 32){
printf("radixBitx cannot be greater than 32\n");
return -1;
}
FILE *fileForReading = fopen( fileName, "r" );
if(fileForReading == NULL){
perror("Failed to open the file\n");
return -1;
}
char* charBuff = malloc(1024);
if(charBuff == NULL){
perror("Error with malloc for charBuff");
return -1;
}
uint32_t numNumbers = 0;
while(fgetc(fileForReading) != EOF){
numNumbers++;
fgets(charBuff, 1024, fileForReading);
}
uint32_t numbersToSort[numNumbers];
rewind(fileForReading);
int location;
for(location = 0; location < numNumbers; location++){
fgets(charBuff, 1024, fileForReading);
numbersToSort[location] = atoi(charBuff);
}
At a file of 50 million numbers (~500MB), I'm getting a segmentation fault at rewind of all places. My knowledge of how file streams work is almost non-existent. My guess is it's trying to malloc without enough memory or something, but I don't know.
So, I've got a two parter here: How is rewind segmentation faulting? Am I just doing a poor job before rewind and not checking some system call I should be?
And, what is a more efficient way to read in an arbitrary amount of numbers from a text file?
Any help is appreciated.
I think the most likely cause here is (ironically enough) a stack overflow. Your numbersToSort array is allocated on the stack, and the stack has a fixed size (varies by compiler and operating system, but 1 MB is a typical number). You should dynamically allocate numbersToSort on the heap (which has much more available space) using malloc():
uint32_t *numbersToSort = malloc(sizeof(uint32_t) * numNumbers);
Don't forget to deallocate it later:
free(numbersToSort);
I would also point out that your first-pass loop, which is intended to count the number of lines, will fail if there are any blank lines. This is because on a blank line, the first character is '\n', and fgetc() will consume it; the next call to fgets() will then be reading the following line, and you'll have skipped the blank one in your count.
The problem is in this line
uint32_t numbersToSort[numNumbers];
You are attempting to allocate a huge array in stack, your stack size is in few KBytes (Moreover older C standards don't allow this). So you can try this
uint32_t *numbersToSort; /* Declare it with other declarations */
/* Remove uint32_t numbersToSort[numNumbers]; */
/* Add the code below */
numbersToSort = malloc(sizeof(uint32_t) * numNumbers);
if (!numbersToSort) {
/* No memory; do cleanup and bail out */
return 1;
}
I'm working on a class project in which I must write a command line shell with the following requirements:
The shell must able to read buffered input
Buffer should be 64 characters
Error conditions should be handled
Exceeded buffer size
Interruptions (when a signal arrives) – see the man page for read()
Invalid input (unparsable characters, blank lines, etc)
Any other error that may be encountered.
Shell must have a history of at least 20 items, and the history must not be of a static size. When the history buffer is full, the oldest item is removed and the newest item added.
Programs should be able to run in the foreground or background. (using &)
Ctrl-D will exit the shell
Ctrl-C will print the complete history
The Command ‘history’ will also print the complete history. Newest items will be at the bottom of the list.
All other signals will be trapped and displayed to the user in the shell
Program will use the read() command to read in input, unless the arrow keys are supported
I have opted to implement arrow keys for history cycling, so I'm using ncurses for input, rather than read(). I think I'm doing all right using strtok() to parse input, and fork() and execvp() to run the processes, but I'm not doing all right implementing ncurses correctly. All I've gotten it to do so far is init a new screen, display the prompt, then segfault upon any key press. Not good.
I reckon the problem must be in my design. I'm not wrapping my head around ncurses too well. What sort of data structures should I be using for this project? How should I handle the ncurses setup, teardown, and everything in between? What's the deal with windows and screens, and should I have a single globally accessible window/screen that I work with? Also, I've been trying to use a char* for the input buffer, and a char** for the command history, but I have no experience in C, so despite reading up on malloc, calloc, and realloc, I'm not sure of the best way to store commands in the buffer and the history. Any tips on managing these char arrays?
tl;dr: How do I use ncurses correctly to make a command line shell, and how do I handle the command memory management with C?
I realize this is a pretty hefty question. :(
edit: I have already seen http://www.gnu.org/software/libc/manual/html_node/Implementing-a-Shell.html and http://www.linuxinfor.com/english/NCURSES-Programming/ but the ncurses documentation has actually too much overhead. I just want to use its ability to recognize arrow keys.
Here's some sample code which:
Performs dynamic memory allocation.
Reads from the console in non-blocking mode.
Uses VT100 codes to print a frame buffer to the console.
It compiles on Linux using GCC without warnings or errors. It's far from bug free, but it should give you some ideas of what's possible. Compile and run it, pressing [up] and [down] will print messages, typing characters and hitting [enter] will "execute" the command.
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s
{
int x;
int y;
char *data;
};
static int draw_frame(struct frame_s *frame)
{
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for (row = 0, data = frame->data; row < frame->y; row++, data += frame->x)
{
/* 0 for normal, 1 for bold, 7 for reverse. */
attrib = 0;
/* The VT100 commands to move the cursor, set the attribute, and the
* actual frame line. */
fprintf(stdout, "\033[%d;%dH\033[0m\033[%dm%.*s", row + 1, 0, attrib, frame->x, data);
fflush(stdout);
}
return (0);
}
int main(void)
{
const struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[128];
unsigned int count = 0;
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
/* Set up a little frame. */
frame.x = 80;
frame.y = 5;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL)
{
fprintf(stderr, "No memory\n");
exit (1);
}
memset(frame.data, ' ', frame.x * frame.y);
/* Get the terminal state. */
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
/* Turn off "cooked" mode (line buffering) and set minimum characters
* to zero (i.e. non-blocking). */
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
/* Set the terminal attributes. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
/* Un-mask all signals while in ppoll() so any signal will cause
* ppoll() to return prematurely. */
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
/* Loop forever waiting for key presses. Update the output on every key
* press and every 1.0s (when ppoll() times out). */
do
{
fds[0].revents = 0;
ret = ppoll(fds, sizeof(fds) / sizeof(struct pollfd), &timeout, &sigmask);
if (fds[0].revents & POLLIN)
{
ret = read(STDIN_FILENO, &line[count], sizeof(line) - count);
if (ret > 0)
{
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0)
{
snprintf(frame.data, frame.x, "up");
count = 0;
}
else if (strcmp(&line[count], "\033[B") == 0)
{
snprintf(frame.data, frame.x, "down");
count = 0;
}
else if (line[count] == 127) // backspace
{
if (count != 0) { count -= ret;}
}
else if (line[count] == '\n')
{
snprintf(frame.data, frame.x, "entered: %s", line);
count = 0;
}
else
{
count += ret;
}
}
}
/* Print the current time to the output buffer. */
current_time = time(NULL);
tp = localtime(¤t_time);
strftime(&frame.data[1 * frame.x], frame.x, "%Y/%m/%d %H:%M:%S", tp);
/* Print the command line. */
line[count] = '\0';
snprintf(&frame.data[(frame.y - 1) * frame.x], frame.x, "$ %s", line);
draw_frame(&frame);
}
while (1);
/* Restore terminal and free resources. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
return (0);
}
If your input buffer is defined to be 64 characters, then I would recommend using a char array instead of a char*. Something like char input_buffer[65]; should serve your purposes (add an extra character for the trailing '\0').
As far as command history goes, you can use a two-dimensional array for that. Something like char command_history[20][65]; should let you store 20 old commands of 64 characters each.
Allocating these buffers statically should make things a bit easier for you, as you won't have to worry about malloc and friends.
It's hard to give you too much specific advice without seeing your code. I have a feeling that you are making the same type of mistakes that are typical to people first learning C. Can you post the part of your code that is giving you problems so that we can learn more about what you are doing?
Update after posted provided code:
One problem I'm seeing is that the function takeInput doesn't have a return statement. When you use input = takeInput(); inside your main function, the value of input isn't being set to what you think it is. It's probably not a valid pointer, which is causing your line that says input[j] to segfault.
Your usage of cmdHistory also needs revisiting. You allocate it with cmdHistory = (char**)calloc(21,sizeof(int));, which gives you enough space to store 21 integers. In the function printHistory, you pass elements of cmdHistory to printw as if they were strings (they're only integers). This is most definitely not doing what you want it to do. Instead, your allocation logic for cmdHistory needs to look more like your de-allocation logic (except backwards). Allocate an array of char**, then iterate through the array, assigning each pointer to a newly-allocated buffer. Just like you have one free statement for each element in the array plus a free for the array as a whole, you should have one malloc for each element plus one malloc for the array as a whole.
Even if you can't use a statically-allocated stack, try writing your program using one anyway. This will let you work the kinks out of your key detection logic, etc without having to worry about the dynamic memory part of the program. Once the rest of it is working, go back in and swap out the static memory for dynamic memory allocation. That way, you're only having to debug a little bit at a time.
Have you looked at the Readline library? It's ideal for use in your project.
http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html
I am currently trying to count the number of bytes consumed by files in a certain directory. It recursively goes through all the folders on the current directory and counts the bytes of the files.
When I recursively call the function rec_bytes, I print off "Go in"... but when it returns the value... It segfaults.
I labeled the problematic line in the code below.
I think the problem has to do with open/closing directories.
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
int rec_Bytes(char path[])
{
int bytesSum = 0;
printf("PathX %s\n", path);
DIR *mydir = opendir(path); // Look in current directory
struct dirent *entry = NULL;
while((entry = readdir(mydir))) /* If we get EOF, the expression is 0 and
* the loop stops. */
{
if (!isDir(entry->d_name)) // Check to see if the entry is a directory of a file
{
char tempPath[] = "";
strcat(tempPath, path);
strcat(tempPath,"/");
strcat(tempPath,entry->d_name);
int tempSum = fileSize(tempPath); // Get file size
bytesSum += tempSum; // Add to sum
printf("%s\t%d\n", entry->d_name, tempSum);
}
else // The current entry is a directory
{
if ((strcmp((entry->d_name),"..") != 0) && (strcmp((entry->d_name),".")) != 0)
{
printf("Directory%s\n", entry->d_name);
char tempPath[] = "";
strcat(tempPath, path);
strcat(tempPath,"/");
strcat(tempPath,entry->d_name);
printf("Go in\n");
int tempSum = rec_Bytes(tempPath); <<<<<< Get segmentation fault here.
printf("Come Out%d\n", tempSum);
bytesSum += tempSum;
printf("%s\t%d\n", entry->d_name, tempSum);
}
}
}
closedir(mydir);
printf("XXXX\t%s\t%d\n", path, bytesSum);
return bytesSum;
}
// Thanks to : http://cboard.cprogramming.com/cplusplus-programming/117431-how-tell-if-file-directory.html
int isDir(const char* target)
{
struct stat statbuf;
stat(target, &statbuf);
return S_ISDIR(statbuf.st_mode);
}
Your problem is with lines like this...
char tempPath[] = "";
This will allocate a buffer with one byte, that byte being a null character. There is no room in that buffer for any longer string.
Basically, C does not have dynamically resizable strings. It has null terminated strings that live within fixed-size arrays of characters. This creates an issue when you won't know the length of the string until you've finished building it, of course.
Try something like...
char tempPath[5000] = "";
as a quick fix. Also, look up strncat - it's less likely to segfault. There's a printf variant as well, but I use too much C++ these days.
EDIT
Actually, the segfault is probably due to those strcats and printfs corrupting the stack. The segfault is probably when a function tries to return. The basic issue is the too-small string buffers thing, though.
Whoops!
The real quick fix is...
char tempPath[5000];
tempPath [0] = 0;
Otherwise, it won't always get initialised to an empty string when you expect it to.