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().
Related
This is a really long question due to the code snippets and the detailed explanations. TL;DR, are there issues with the macros shown below, is this a reasonable solution, and if not then what is the most reasonable way to solve the issues presented below?
I am currently writing a C library which deals with POSIX threads, and must be able to handle thread cancellation cleanly. In particular, the library functions may be called from threads that were set to be cancellable (either PTHREAD_CANCEL_DEFFERED or PTHREAD_CANCEL_ASYNCHRONOUS canceltype) by the user.
Currently the library functions that interface with the user all begin with a call to pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate), and at each return point, I make sure that a call to pthread_setcancelstate(oldstate, &dummy) is made to restore whatever cancellation settings the thread had previously.
This basically prevents the thread from being cancelled while in the library code, thus ensuring that the global state remains consistent and resources were properly managed before returning.
This method unfortunately has a few drawbacks:
One must be sure to restore the cancelstate at every return point. This makes it somewhat hard to manage if the function has nontrivial control flow with multiple return points. Forgetting to do so may lead to threads that don't get cancelled even after return from the library.
We only really need to prevent cancellations at points where resources are being allocated or global state is inconsistent. A library function may in turn call other internal library functions that are cancel-safe, and ideally cancellations could occur at such points.
Here is a sample illustration of the issues:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
int mylib_function(size_t len)
{
char *buffer;
int oldstate, oldstate2;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
buffer = malloc(len);
if (buffer == NULL) {
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
do_some_long_computation(buffer, len);
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
write(fd, buffer, len); /* Normally also do error-check */
close(fd);
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return 0;
}
Here it is not so bad because there are only 3 return points. One could possibly even restructure the control flow in such a way as to force all paths to reach a single return point, perhaps with the goto cleanup pattern. But the second issue is still left unresolved. And imagine having to do that for many library functions.
The second issue may be resolved by wrapping each resource allocation with calls to pthread_setcancelstate that will only disable cancellations during resource allocation. While cancellations are disabled, we also push a cleanup handler (with pthread_cleanup_push). One could also move all resource allocations together (opening the file before doing the long computation).
While solving the second issue, it is still somewhat hard to maintain because each resource allocation needs to be wrapped under these pthread_setcancelstate and pthread_cleanup_[push|pop] calls. Also it might not always be possible to put all resource allocations together, for instance if they depend on the results of the computation. Moreover, the control flow needs to be changed because one cannot return between a pthread_cleanup_push and pthread_cleanup_pop pair (which would be the case if malloc returns NULL for example).
In order to solve both issues, I came up with another possible method that involves dirty hacks with macros. The idea is to simulate something like a critical section block in other languages, to insert a block of code in a "cancel-safe" scope.
This is what the library code would look like (compile with -c -Wall -Wextra -pedantic):
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include "cancelsafe.h"
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
static void free_wrapper(void *arg)
{
free(*(void **)arg);
}
static void close_wrapper(void *arg)
{
close(*(int *)arg);
}
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
CANCELSAFE_INIT();
CANCELSAFE_PUSH(free_wrapper, buffer) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
CANCELSAFE_BREAK(buffer);
}
}
do_some_long_computation(buffer, len);
CANCELSAFE_PUSH(close_wrapper, fd) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
CANCELSAFE_BREAK(fd);
}
}
write(fd, buffer, len);
CANCELSAFE_POP(fd, 1); /* close fd */
CANCELSAFE_POP(buffer, 1); /* free buffer */
CANCELSAFE_END();
return rc;
}
This resolves both issues to some extent. The cancelstate settings and cleanup push/pop calls are implicit in the macros, so the programmer only has to specify the sections of code that need to be cancel-safe and what cleanup handlers to push. The rest is done behind the scenes, and the compiler will make sure each CANCELSAFE_PUSH is paired with a CANCELSAFE_POP.
The implementation of the macros is as follows:
#define CANCELSAFE_INIT() \
do {\
int CANCELSAFE_global_stop = 0
#define CANCELSAFE_PUSH(cleanup, ident) \
do {\
int CANCELSAFE_oldstate_##ident, CANCELSAFE_oldstate2_##ident;\
int CANCELSAFE_stop_##ident;\
\
if (CANCELSAFE_global_stop)\
break;\
\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_push(cleanup, &ident);\
for (CANCELSAFE_stop_##ident = 0; CANCELSAFE_stop_##ident == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_##ident = 1, pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident))
#define CANCELSAFE_BREAK(ident) \
do {\
CANCELSAFE_global_stop = 1;\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
goto CANCELSAFE_POP_LABEL_##ident;\
} while (0)
#define CANCELSAFE_POP(ident, execute) \
CANCELSAFE_POP_LABEL_##ident:\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_pop(execute);\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
} while (0)
#define CANCELSAFE_END() \
} while (0)
This combines several macro tricks that I have encountered before.
The do { } while (0) pattern is used to have a multiline function-like macro (with semicolon required).
The CANCELSAFE_PUSH and CANCELSAFE_POP macros are forced to come in pairs by the use of the same trick as the pthread_cleanup_push and pthread_cleanup_pop using unmatched { and } braces respectively (here it is unmatched do { and } while (0) instead).
The usage of the for loops is somewhat inspired by this question. The idea is that we want to call the pthread_setcancelstate function after the macro body to restore cancellations after the CANCELSAFE_PUSH block. I use a stop flag that is set to 1 at the second loop iteration.
The ident is the name of the variable that will be released (this needs to be a valid identifier). The cleanup_wrappers will be given its address, which will always be valid in a cleanup handler scope according to this answer. This is done because the value of the variable is not yet initialized at the point of cleanup push (and also doesn't work if the variable is not of pointer type).
The ident is also used to avoid name collisions in the temporary variables and labels by appending it as a suffix with the ## concatenation macro, giving them unique names.
The CANCELSAFE_BREAK macro is used to jump out of the cancelsafe block and right into the corresponding CANCELSAFE_POP_LABEL. This is inspired by the goto cleanup pattern, as mentioned here. It also sets the global stop flag.
The global stop is used to avoid cases were there might be two PUSH/POP pairs in the same scope level. This seems like an unlikely situation, but if this happens then the content of the macros is basically skipped when the global stop flag is set to 1. The CANCELSAFE_INIT and CANCELSAFE_END macros aren't crucial, they just avoid the need to declare the global stop flag ourselves. These could be skipped if the programmer always does all the pushes and then all the pops consecutively.
After expanding the macros, we obtain the following code for the mylib_function:
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
do {
int CANCELSAFE_global_stop = 0;
do {
int CANCELSAFE_oldstate_buffer, CANCELSAFE_oldstate2_buffer;
int CANCELSAFE_stop_buffer;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_push(free_wrapper, &buffer);
for (CANCELSAFE_stop_buffer = 0; CANCELSAFE_stop_buffer == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_buffer = 1, pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer)) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
goto CANCELSAFE_POP_LABEL_buffer;
} while (0);
}
}
do_some_long_computation(buffer, len);
do {
int CANCELSAFE_oldstate_fd, CANCELSAFE_oldstate2_fd;
int CANCELSAFE_stop_fd;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_push(close_wrapper, &fd);
for (CANCELSAFE_stop_fd = 0; CANCELSAFE_stop_fd == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_fd = 1, pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSTATE_oldstate2_fd)) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
goto CANCELSAFE_POP_LABEL_fd;
} while (0);
}
}
write(fd, buffer, len);
CANCELSAFE_POP_LABEL_fd:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
} while (0);
CANCELSAFE_POP_LABEL_buffer:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
} while (0);
} while (0);
return rc;
}
Now, this set of macros is horrendous to look at and it is somewhat tricky to understand how they work exactly. On the other hand, this is a one-time task, and once written, they can be left and the rest of the project can benefit from their nice benefits.
I would like to know if there are any issues with the macros that I may have overlooked, and whether there could be a better way to implement similar functionality. Also, which of the solutions proposed do you think would be the most reasonable? Are there other ideas that could work better to resolve these issues (or perhaps, are they really non-issues)?
Unless you use asynchronous cancellation (which is always very problematic), you do not have to disable cancellation around malloc and free (and many other POSIX functions). Synchronous cancellation only happens at cancellation points, and these functions are not.
You are abusing the POSIX cancellation handling facilities to implement a scope exit hook. In general, if you find yourself doing things like this in C, you should seriously consider using C++ instead. This will give you a much more polished version of the feature, with ample documentation, and programmers will already have experience with it.
Ok, here is the deal...i remember creating a program past week that required me to open a file in binary mode and write data to it. At first, i tried using the fopen function, checking if the result is ok, then try to write data. I remember that at first try, the fwrite operation wasn't working. Then, after moving declaration of variables from a place to another, i was finally able to make the fwrite to insert data to the file.
Now, i need to create another similar program to do some other stuff, so i wanted to use the same allocation code (actually, i wanted to create a specific function to do the same), and here is what i was able to come up with:
#include <stdio.h>
int openfile(FILE *main, char *name, int option);
int main(void)
{
FILE *main;
int header_init;
int result;
switch (openfile(main,"main_file.bin",1)) {
case 1:
header_init = -1;
//fseek(main,0,SEEK_SET); --> useless
fwrite(&header_init,sizeof(int),1,main);
printf("ftell = %d\n",ftell(main)); break;
case 2:
fread(&header_init,sizeof(int),1,main);
printf("%d\n",header_init); break;
default:
printf("Error trying to open file\n");
}
printf("header_init is %d\n",header_init);
fclose(main); exit(0);
}
int openfile(FILE *main, char *name, int option)
{
int result_alloc;
int F_OK;
if (result_alloc = access (name, F_OK) != 0) {
printf("File not found, allocating a new one\n");
if ((main= fopen(name,"w+b")) != NULL) return 1;
}
else {
printf("File exist, allocating as r+b\n");
if ((main= fopen(name,"r+b")) != NULL) return 2;
}
printf("Failure trying to open");
return 0;
}
For some unfortunate reason, the fwrite operation is not writing -1 to the allocated file. My intention with this program is so that it will always check for existence of that specific file: if there is one in place, simply open it with r+b to allow update functions without overwriting the actual file contents. Otherwise, allocate a new one with a header value of -1 (i will use this file as a record file with chained list structure).
Seriously, i cannot understand why this is not working. The idea is the same of my previous program. The only thing that changed is that i created a function, because this is going to happen me later (because of the 3rd parameter that will allow me to reduce my code and make it more "readable" - at least this is the intention!). I HAVE to admit that i have some attention to details problem, but i am working hard to get better at it, i am probably missing something stupid in this code, but after hours looking at it i really wanted to ask here for some help. Thanks
Edit: I am running it under z/Linux. What i am trying to understand is, why the code above doesn't write -1 to the file, but the one below writes ok?
#include <stdio.h>
int main(void)
{
FILE *main;
int result_alloc;
int header_init;
int F_OK;
if (result_alloc = access ("test.bin", F_OK) != 0) {
printf("File not found, allocating a new one\n");
if ((main = fopen("test.bin","w+b")) == NULL) {
printf("Failure trying to open file");
return 1;
}
else {
header_init = -1;
printf("current pos is: w+b %d\n",ftell(main));
fwrite(&header_init,sizeof(int),1,main);
printf("current pos is: write header_init %d\n",ftell(main));
}
}
else {
if ((main = fopen("test.bin","r+b")) == NULL) {
printf("Failure trying to open file");
return 2;
}
else {
printf("current pos is: r+b %d\n",ftell(main));
fread(&header_init,sizeof(int),1,main);
printf("current pos is: read header_init %d\n",ftell(main));
}
}
}
The main issue that the assignment to the main variable inside of the openfile function is not seen by the calling function. Because C is pass by value, you're only changing the value of the function parameter, not the value of the variable that was passed in. So when openfile returns, the main variable inside of the main function is unchanged.
What you need to do is pass the address of that variable to the function, then within the function you dereference the local variable (which is a pointer) and assign a value to the dereferenced variable.
Also, it's not a good idea to have a variable with the same name as a function as it hides the function in that scope and can cause confusion.
So you would define your function as follows:
int openfile(FILE **fp, char *name, int option);
You would then call it like this (changing the name of the main variable to fp):
FILE *fp;
...
openfile(&fp,"main_file.bin",1)
Then inside of openfile, you dereference fp to change the value in the calling function:
*fp = fopen(name,"w+b")
The reason why the second code sample is working is that you're assigning directly to a local variable and then using that same variable later on in the function.
Also, you're "lucky" that the second piece of code is working because you did this:
int F_OK;
F_OK is already defined in unistd.h, where access() is defined. So by doing this you're redeclaring it and not giving it a value, causing undefined behavior. Get rid of this definition, and #include <unistd.h>, and the call to access() is guaranteed to work as expected.
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.
Dear respected programmers. Please could you help me (again) on how to put the following code into functions for my program.
I have read on-line and understand how functions work but when I do it myself it all goes pear shaped/wrong(I am such a noob).
Please could you help with how to for example to write the code below into functions.(like opening the input file).
My initial code looks like:
main (int argc, char **argv)
{
int bytes_read, bytes_written;
struct stat inode;
int input_fd, output_fd;
char buffer[64];
int eof = 0;
int i;
/* Check the command line arguments */
if (argc != 3)
{
printf("syntax is: %s \n", <fromfile> <tofile>\n", argv[0]);
exit (1);
}
/* Check the input file exists and is a file */
if ((stat(argv[1], &inode) == -1) || (!S_ISREG(inode.st_mode)))
{
printf("%s is not a file\n", argv[1]);
exit(2);
}
/* Check that the output file doesnt exist */
if (stat(argv[2], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[2]);
exit(2);
}
/* Open the input file for reading */
input_fd = open(argv[1], O_RDONLY, 0);
if (input_fd == -1)
{
printf("%s cannot be opened\n", argv[1]);
exit(3);
}
output_fd = open(argv[2], O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if (output_fd == -1)
{
printf("%s cannot be opened\n", argv[2]);
exit(3);
}
/* Begin processing the input file here */
while (!eof)
{
bytes_read = read(input_fd, buffer, sizeof(buffer));
if (bytes_read == -1)
{
printf("%s cannot be read\n", argv[1]);
exit(4);
}
if (bytes_read > > 0)
{
bytes_written = write(output_fd, buffer, bytes_read);
if (bytes_written == -1)
{
printf("There was an error writing to the file %s\n",argv[2]);
exit(4);
}
if (bytes_written != bytes_read)
{
printf("Devistating failure! Bytes have either magically appeared and been written or dissapeard and been skipped. Data is inconsistant!\n");
exit(101);
}
}
else
{
eof = 1;
}
}
close(input_fd);
close(output_fd);
}
My attempt at opening an output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Any help on how I would now reference to this in my program is appreciated thank you.
I tried:
ouputfile (but I cant figure out what goes here and why either).
Maybe the most useful function for you is:
#include <stdio.h>
#include <stdarg.h>
extern void error_exit(int rc, const char *format, ...); /* In a header */
void error_exit(int rc, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(rc);
}
You can then write:
if (stat(argv[2], &inode) != -1)
error_exit(2, "Warning: The file %s exists. Not going to overwrite\n",
argv[2]);
Which has the merit of brevity.
You write functions to do sub-tasks. Deciding where to break up your code into functions is tricky - as much art as science. Your code is not so big that it is completely awful to leave it as it is - one function (though the error handling can be simplified as above).
If you want to practice writing functions, consider splitting it up:
open_input_file()
open_output_file()
checked_read()
checked_write()
checked_close()
These functions would allow your main code to be written as:
int main(int argc, char **argv)
{
int bytes_read;
int input_fd, output_fd;
char buffer[64];
if (argc != 3)
error_exit(1, "Usage: %s <fromfile> <tofile>\n", argv[0]);
input_fd = open_input_file(argv[1]);
output_fd = open_output_file(argv[2]);
while ((bytes_read = checked_read(input_fd, buffer, sizeof(buffer)) > 0)
check_write(output_fd, buffer, bytes_read);
checked_close(input_fd);
checked_close(output_fd);
return 0;
}
Because you've tucked the error handling out of sight, it is now much easier to see the structure of the program. If you don't have enough functions yet, you can bury the loop into a function void file_copy(int fd_in, int fd_out). That removes more clutter from main() and leaves you with very simple code.
Given an initial attempt at a function to open the output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Critique:
You have to define the variables used by the function in the function (you will want to avoid global variables as much as possible, and there is no call for any global variable in this code).
You have to define the return type. You are opening a file - how is the file descriptor going to be returned to the calling code? So, the return type should be int.
You pass only the information needed to the function - a simple form of 'information hiding'. In this case, you only need to pass the name of the file; the information about file modes and the like is implicit in the name of the function.
In general, you have to decide how to handle errors. Unless you have directives otherwise from your homework setter, it is reasonable to exit on error with an appropriate message. If you return an error indicator, then the calling code has to test for it, and decide what to do about the error.
Errors and warnings should be written to stderr, not to stdout. The main program output (if any) goes to stdout.
Your code is confused about whether argv[i] or argv[argc-1] is the name of the output file. In a sense, this criticism is irrelevant once you pass just the filename to the function. However, consistency is a major virtue in programming, and using the same expression to identify the same thing is usually a good idea.
Consistency of layout is also important. Don't use both if( and if ( in your programs; use the canonical if ( notation as used by the language's founding fathers, K&R.
Similarly, be consistent with no spaces before commas, a space after a comma, and be consistent with spaces around operators such as '|'. Consistency makes your code easier to read, and you'll be reading your code a lot more often than you write it (at least, once you've finished your course, you will do more reading than writing).
You cannot have return -1; inside a function that returns no value.
When you a splitting up code into functions, you need to copy/move the paragraphs of code that you are extracting, leaving behind a call to the new function. You also need to copy the relevant local variables from the calling function into the new function - possibly eliminating the variables in the calling function if they are no longer used there. You do compile with most warnings enabled, don't you? You want to know about unused variables etc.
When you create the new function, one of the most important parts is working out what the correct signature of the function is. Does it return a value? If so, which value, and what is its type? If not, how does it handle errors? In this case, you probably want the function to bail out (terminate the program) if it runs into an error. In bigger systems, you might need to consistently return an error indicator (0 implies success, negative implies failure, different negatives indicating different errors). When you work with function that return an error indicator, it is almost always crucial that you check the error indicators in the calling code. For big programs, big swathes of the code can be all about error handling. Similarly, you need to work out which values are passed into the function.
I'm omitting advice about things such as 'be const correct' as overkill for your stage in learning to program in C.
you seem to actually understand how to make a function. making a function really isnt that hard. first, you need to kind of understand that a function has a type. in otherwords, argc has type int and argv has type char *, your function (currently) has type void. void means it has no value, which means when you return, you return nothing.
however, if you look at your code, you do return -1. it looks like you want to return an interger. so you should change the top from void outputfile(...) to int outputfile(...).
next, your function must return. it wont compile if there is a circumstance where it won't return (besides infinite loops). so at the very bottom, if no errors happen, it will reach the end. since you're no longer using "void" as the return type, you must return something before the end of the function. so i suggest putting a return 1; to show that everything went great
There's several things.
The function return type isn't what you want. You either want to return a file descriptor or an error code. IIRC, the file descriptor is a nonnegative int, so you can use a return type of int rather than void. You also need to return something on either path, either -1 or file_desc_out.
You probably don't want to pass in the command-line arguments as a whole, but rather something like argv[argc - 1]. In that case, the argument should be something like char * filename rather than the argc/argv it has now. (Note that the argv[i] you've got in the last printf is almost certainly wrong.)
This means it would be called something like
int file_desc_out = outputFile(argv[argc - 1]);
You need to have all variables declared in the function, specifically inode and file_desc_out.
Finally, put an extra level of indentation on the code inside the { and } of the function itself.
I have a C program which will print the prime numbers into a .txt file.
I want program to ask me the execution time by minutes. Trying to solve with the piece of code.
#include <stdio.h>
#include <time.h>
int main()
{
execute();
return(0);
}
int execute(int minute)
{
time_t now;
time(&now);
struct tm *tmp = localtime(&now);
printf("How long you want to execute the program by minute? ");
scanf("%d",&minute);
printf("%d %02d %02d\n",tmp->tm_hour, tmp->tm_min+minute, tmp->tm_sec);
return(minute);
}
and here also is the code that I want to run.
#include <stdio.h>
int isprime(int x);
int main(void)
{
int AX_REGISTER, BX_REGISTER;
FILE *number, *primes;
int forever = 1;
while (forever)
{
if ((number = fopen("lastnumber.txt", "r")) == NULL)
{
fprintf(stderr, "Cannot read LASTNUMBER.txt file.\n");
return 1;
}
fscanf(number,"%d",&AX_REGISTER);
fclose(number);
BX_REGISTER=AX_REGISTER;
AX_REGISTER=AX_REGISTER+1;
if ((number = fopen("lastnumber.txt", "w")) == NULL)
{
fprintf(stderr, "Cannot write LASTNUMBER.txt file.\n");
return 1;
}
fprintf(number,"%d\n",AX_REGISTER);
fclose(number);
if (isprime(BX_REGISTER))
{
if ((primes = fopen("primes.txt", "a")) == NULL)
{
fprintf(stderr, "Cannot open PRIMES.txt file.\n");
return 1;
}
fprintf(primes,"%d\n",BX_REGISTER);
fclose(primes);
}
}
return 0;
}
int isprime(int x)
{
int i;
for (i=2;i<x;i++)
{
if (x%i==0)
return 0;
}
return 1;
}
Since I am a newbie in C programming language I can not solve it by myself. Can someone help me?
Some platforms have an alarm() function that you can use to send a signal to your application after a specified number of seconds.
If you're not on Windows, man alarm should give you some information about this function. If you're on Windows, a different approach may be needed.
On another note, you should consider using variable names that mean something to the code you're writing. To a casual reader, the name BX_REGISTER means nothing. Perhaps consider changing its name to number_to_test (and AX_REGISTER to next_number or something).
Since you are running in a while loop, simply get the current time outside the while loop, add the minutes, and also get the time at the end of the while loop. Something like:
time_t now;
time_t end_time;
now = time();
end_time = now + (minutes * 60);
while(now < end_time)
{
...Do processing here...
now = time();
}
One drawback of this method is that if isprime() takes a very very long time to run then you may overshoot the end time by quite a lot. One way to solve that is to pass the end_time value to isprime() and check it on the loop in that function too. Or just press Ctrl-C in the terminal where you are running the program :)
By all means write to the files in case the program crashes - you won't want to lose hours of work, but I suggest you only read the value from lastnumber.txt before the while loop and after that just hold it in memory. It will speed up your programs.
Also, unless running on Windows which has horrible file locking semantics, it may be worth holding the write file handles open the whole time to save cycles constantly opening and closing the files.