There are two errors that show up:
main.c:80: warning: ‘main’ is normally a non-static function
main.c:88: error: expected declaration or statement at end of input
and I cant't seem to find the problem... There number of curly braces is equal... What seems to be the problem?
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include "main-getopt.h"
void print_usage_and_abort( const char *message )
{
if( NULL != message )
fprintf( stderr, "Error: %s\n", message );
fprintf( stderr, "Usage: partitioner -n <nodes> [ -f <basename> ]\n\n" );
exit( -1 );
}
void parsing (int argc, char **argv, struct Params *params)
{
char error_message[256];
params->nodes = 0;
memcpy( params->filename_base, "output", strlen("output") + 1 );
int opt;
size_t len;
int numarg;
while ((opt = getopt(argc, argv, "n:f:")) != -1) {
int i;
for (i = 1; i < argc; i++)
{
if (argv[i][0] == '-')
{
if (i+1 == argc || argv[i+1][0] == '-')
{
sprintf( error_message, "No Filename");
print_usage_and_abort( error_message );
}
if (argv[i][1] == 'n')
{
numarg = atoi( optarg );
if( numarg < 1 || numarg > 2048 )
{
sprintf( error_message, "Number of nodes agrument expects a number between 1 and 2048, actual %s", optarg );
print_usage_and_abort( error_message );
}
}
else if (argv[i][1] == 'f')
len = strlen( optarg );
// limit to buffer capacity
if( len >= MAX_FILENAME_BASE )
{
sprintf( error_message, "Base filename parameter length is expected to be less than %d but is %d", (int)MAX_FILENAME_BASE, (int)len );
print_usage_and_abort( error_message );
}
else if(len<MAX_FILENAME_BASE)
{
memcpy( params->filename_base, optarg, len + 1 );
break;
}
else
{
sprintf( error_message, "Unknown command switch %c", (char)optopt );
print_usage_and_abort( error_message );
break;
}
}
}
if( 0==params->nodes )
{
sprintf( error_message, "Number of nodes switch -n is required" );
print_usage_and_abort( error_message );
}
}
int main(int argc, char *argv[])
{
struct Params params;
parse_arguments( argc, argv, ¶ms );
fprintf( stdout, "Parameters are:\n\tNumber of nodes:\t%d\n\tFilename base:\t%s\n\n", params.nodes, params.filename_base );
return 0;
}
I've edited indentation for you. Do you see now that somewhere in parsing function you've missed one closing bracket?
You didn't closing bracket '}' at the end of the parsing function.
Related
I receive an error when I make the call to execve, I am confused on why when I pass a char array into it, it does not accept my full input and considered it too few arguments. This program is meant to take file names as an input, and then sort them into a directory called SORTED. I know that execve requires 3 arguments, but am I wrong in the calling?
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main( int argc, char **argv ){
//arguments
int i, status, finish = 1;
pid_t *childPID;
char buffer[256];
//checks to make sure there is arguments
if ( argc < 2 ) {
sprintf( buffer, "No arguments were given.\n" );
write( 2, buffer, strlen( buffer ) );
return (-1);
}
//uses mkdir to check if directory is there
//if it is not, makes it with 0777 permissions
//if it exists, it prints out error message saying that
//any other error happens, it uses perror to print out the error
if(mkdir( "./SORTED", S_IRWXU ) == 0) {
sprintf( buffer, "Creating SORTED folder with 0700 Permissions.\n" );
write( 1, buffer, strlen( buffer ) );
}
else if (errno == EEXIST) {
sprintf( buffer, "SORTED folder already exists.\n" );
write( 2, buffer, strlen( buffer ) );
}
else
perror( "mkdir" );
childPID = (pid_t*)malloc(sizeof(pid_t)*(argc));
//for each argument, forks a new child from parent
//calls execve, prints error if there is one
//and then sleeps for 20 seconds before exiting
for ( i = 1; i < argc; i++ ) {
char output[256];
sprintf( output, "SORTED/%s", argv[i] );
char *newARGV[] = { "sort", "-o", output, argv[i], NULL };
if ( childPID[i] = fork() == 0 ) {
execve( "/usr/bin/sort", newARGV);
perror( "execve" );
exit( 0 );
}
}
//Goes through each argument, waits on the child PID
//Prints if the sort was successful, and if not, prints error code
for ( i = 1; i < argc; i++ ) {
waitpid( childPID[i], &status, 0 );
if ( status == 0 )
sprintf( buffer, "%s : success (%d)\n", argv[i], status );
else {
finish = 0;
sprintf( buffer, "%s : fail (%d)\n", argv[i], status );
}
write( 1, buffer, strlen( buffer ) );
}
//Prints the all done if there was no errors
//informs you if there were errors
if ( finish == 1 )
sprintf( buffer, "All Done.\n" );
else
sprintf( buffer, "All Done. Error was encountered.\n" );
write( 1, buffer, strlen( buffer ) );
free(childPID);
return 0;
}
The full error is:
error: too few arguments to function ‘execve’
execve( "/usr/bin/sort", newARGV);
The execve function expects three arguments but you're passing two arguments. Hence the error message.
You seem to be under the impression that passing an array to a function means that each element of the array becomes an argument to the function, but that is not the case.
The function is declared as follows:
int execve(const char *filename, char *const argv[], char *const envp[]);
The first argument is the command to run, the second is an array of arguments, and the third is an array of environment variables. If you're not interested in passing environment variables, use execv instead which doesn't have the third argument.
I am writing some C code to process some data in a file, but I just learned that the file is going to be constantly added to (about 1 time/second, maybe faster). So I'm wondering how do I keep reading from the file as its being added to. Then when I get to the end, wait until the next line is added and then process it. Then wait again and then process, and so on and so on. I have something like:
while(1){
fgets(line, sizeof(line), file);
while(line == NULL){
//wait ? then try to read again?
}
//tokenize line and do my stuff here
}
I thought I could maybe use inotify, but I am getting nowhere with that. Does anyone have any advice?
The most efficient way is using inotify, and the direct way is using the read() system call directly.
using inotify
The following code may give you some help, It works well on Debian 7.0, GCC 4.7:
/*This is the sample program to notify us for the file creation and file deletion takes place in “/tmp/test_inotify” file*/
// Modified from: http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
int main( )
{
int length, i = 0;
int fd;
int wd;
char buffer[EVENT_BUF_LEN];
/*creating the INOTIFY instance*/
fd = inotify_init();
/*checking for error*/
if ( fd < 0 ) {
perror( "inotify_init error" );
}
/* adding the “/tmp/test_inotify” test into watch list. Here,
* the suggestion is to validate the existence of the
* directory before adding into monitoring list.
*/
wd = inotify_add_watch( fd, "/tmp/test_inotify", IN_CREATE | IN_DELETE | IN_ACCESS | IN_MODIFY | IN_OPEN );
/* read to determine the event change happens on “/tmp/test_inotify” file.
* Actually this read blocks until the change event occurs
*/
length = read( fd, buffer, EVENT_BUF_LEN );
/* checking for error */
if ( length < 0 ) {
perror( "read" );
}
/* actually read return the list of change events happens.
* Here, read the change event one by one and process it accordingly.
*/
while ( i < length ) {
struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
if( event->len == 0) {
// For a single file watching, the event->name is empty, and event->len = 0
printf(" Single file watching event happened\n");
} else if ( event->len ) {
if ( event->mask & IN_CREATE ) {
if ( event->mask & IN_ISDIR ) {
printf( "New directory %s created.\n", event->name );
} else {
printf( "New file %s created.\n", event->name );
}
} else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s deleted.\n", event->name );
} else {
printf( "File %s deleted.\n", event->name );
}
} else if( event->mask & IN_ACCESS ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s accessed.\n", event->name );
} else {
printf(" File %s accessed. \n", event->name );
}
} else if( event->mask & IN_MODIFY ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s modified.\n", event->name );
} else {
printf(" File %s modified. \n", event->name );
}
} else if( event->mask & IN_OPEN ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s opened.\n", event->name );
} else {
printf(" File %s opened. \n", event->name );
}
} else {
printf( "Directory or File is accessed by other mode\n");
}
}
i += EVENT_SIZE + event->len;
}
/* removing the “/tmp/test_inotify” directory from the watch list. */
inotify_rm_watch( fd, wd );
/* closing the INOTIFY instance */
close( fd );
}
When runing the above program. You could test it by create a file or directoy named /tmp/test_inotify.
A detailed explanation could be found here
Use read system call
If a file is open, and have read to the end of current file size. the read() system call will return 0. And if some writer wrote N bytes to this file later, and then the read() will just return min(N, buffersize).
So it works correctly for your circumstance. Following is an examples of the code.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef int FD ;
int main() {
FD filed = open("/tmp/test_inotify", O_RDWR );
char buf[128];
if( !filed ) {
printf("Openfile error\n");
exit(-1);
}
int nbytes;
while(1) {
nbytes = read(filed, buf, 16);
printf("read %d bytes from file.\n", nbytes);
if(nbytes > 0) {
split_buffer_by_newline(buf); // split buffer by new line.
}
sleep(1);
}
return 0;
}
Reference
Thanks to Jonathan Leffler's Comment
http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int
main()
{
char ch;
FILE *fp;
long int nbytes_read = 0;
char str [128];
int j = 0;
int first_time = 1;
memset(str, '\0', 128);
fp = fopen("file.txt", "r");
while (1) {
if (first_time != 1) {
fp = fopen("file.txt", "r");
fseek(fp, nbytes_read, SEEK_SET);
sleep(10);
}
if (fp != NULL) {
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') {
str[j++] = ch;
printf("%s", str);
memset(str, '\0', 128);
j = 0;
} else {
str[j++] = ch;
}
nbytes_read++;
}
//printf("%ld\n", nbytes_read);
first_time = 0;
}
fclose(fp);
}
return 0;
}
You can use select() with the fileno(file) as the file-descriptor. select will return either with a timeout (if you set a timeout) or when you can read from the file.
Using select can be a good choice but if you do not wish to use it, you can add a sleep for a small amount of milliseconds before reading value.
Here is my code, for my own shell in C. When compiling I get an error: use of undeclared identifier 'output'. Here are examples of some of the errors when compiling:
error: use of undeclared identifier 'output' char
input[100];output[100];
test3.c:53:15: error: use of undeclared identifier 'output'
strcpy(output,args[i+1]);
^
test3.c:53:15: error: use of undeclared identifier 'output'
test3.c:60:8: warning: implicit declaration of function 'open' is
invalid in C99
[-Wimplicit-function-declaration] j = open(input, O_RDONLY, 0);
^
test3.c:60:20: error: use of undeclared identifier 'O_RDONLY' j =
open(input, O_RDONLY, 0);
^
test3.c:61:29: error: use of undeclared identifier 'O_RDONLY'
if ((j = open(input, O_RDONLY, 0)) < 0) {
test3.c:70:12: warning: implicit declaration of function 'creat' is
invalid in
C99 [-Wimplicit-function-declaration] if ((i= creat(output , 0644)) < 0) {
test3.c:70:18: error: use of undeclared identifier 'output' if ((i=
creat(output , 0644)) < 0) {
Here is my code:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "signal.h"
#include "unistd.h"
void prompt(char*);
void execute( char* );
char** parse( char* );
int main( int ac, char* av[] )
{
char input[255]; // buffer for supporting command
signal( SIGINT, SIG_IGN ); // ignore ctrl-c
while(1)
{
prompt(input);
execute( input );
}
};
void execute( char* str)
{
int fork_result, status, i = 0,j=0,in=0,out=0;
char input[100];output[100];
char** args = parse( str ); // splits the user command into arguments
fork_result = fork(); // attempt to fork
if ( fork_result == -1 ) // failure
{
perror("Failed to fork\n");
exit(1);
}
else if ( fork_result == 0 ) // I'm the child
{
for(i=0;args[i]!='\0';i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if (in)
{
j = open(input, O_RDONLY, 0);
if ((j = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(j, 0);
close(j);
}
if (out)
{
if ((i= creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(i, STDOUT_FILENO);
close(i);
}
execvp( args[0], args );
perror("failed to exec\n");
exit(2);
}
else // I'm the parent
{
// wait here
wait(&status); // wait for child to finish
free( args ); // free dynamic memory
}
}
char** parse( char* str )
{
char** args = malloc( 256 );
int i = 0;
args[i] = strtok( str, " " );
while( args[i] )
{
i++;
args[i] = strtok( NULL, " " );
}
return args;
}
void prompt(char* input)
{
printf("$ "); // print prompt
fgets( input, 255, stdin );
input[strlen(input)-1] = '\0'; // overwrite \n with \0
if ( strcmp( input, "exit" ) == 0 ) // shell command
exit(0);
}
char input[100];output[100];
You want:
char input[100], output[100];
Also add: #include <fcntl.h>
In general, man open (and other functions you use) is your friend -- it tells you what #includes to add.
There are many more potential bugs and arbitrary limitations in your code. Some examples:
void execute( char* str)
{
char input[100], output[100];
...
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]); // possible stack buffer overflow.
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]); // possible stack buffer overflow
char** parse( char* str )
{
char** args = malloc( 256 ); // limit of 256/sizeof(char*) parameters.
// on a 64-bit system, if more than 32 parameters are supplied ...
args[i] = strtok( NULL, " " ); // ... possible heap buffer overflow.
fgets( input, 255, stdin ); // arbitrary limit of 254 characters on command line.
There is no guarantee that the string ends with \n:
input[strlen(input)-1] = '\0'; // overwrite \n with \0
If I was grading this "shell", I'd give it an "F".
There are multiple errors in your code.
1. On line 27 you need to separate the two variable definitions of input and output with a comma instead of a semicolon char input[100], output[100]; or specify the type of output like char input[100]; char output[100]; You have done this already in the line above.
2. The compiler complains about missing definitions of the function open and the identifier O_RDONLY. This can be fixed by adding #include "fcntl.h" to your includes at the top of the file.
After these changes, the code compiles fine for me (with gcc 5.4.0):
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "signal.h"
#include "unistd.h"
#include "fcntl.h"
void prompt(char*);
void execute( char* );
char** parse( char* );
int main( int ac, char* av[] )
{
char input[255]; // buffer for supporting command
signal( SIGINT, SIG_IGN ); // ignore ctrl-c
while(1)
{
prompt(input);
execute( input );
}
};
void execute( char* str)
{
int fork_result, status, i = 0,j=0,in=0,out=0;
char input[100], output[100];
char** args = parse( str ); // splits the user command into arguments
fork_result = fork(); // attempt to fork
if ( fork_result == -1 ) // failure
{
perror("Failed to fork\n");
exit(1);
}
else if ( fork_result == 0 ) // I'm the child
{
for(i=0;args[i]!='\0';i++)
{
if(strcmp(args[i],"<")==0)
{
args[i]=NULL;
strcpy(input,args[i+1]);
in=2;
}
if(strcmp(args[i],">")==0)
{
args[i]=NULL;
strcpy(output,args[i+1]);
out=2;
}
}
if (in)
{
j = open(input, O_RDONLY, 0);
if ((j = open(input, O_RDONLY, 0)) < 0)
{
perror("Couldn't open input file");
exit(0);
}
dup2(j, 0);
close(j);
}
if (out)
{
if ((i= creat(output , 0644)) < 0)
{
perror("Couldn't open the output file");
exit(0);
}
dup2(i, STDOUT_FILENO);
close(i);
}
execvp( args[0], args );
perror("failed to exec\n");
exit(2);
}
else // I'm the parent
{
// wait here
wait(&status); // wait for child to finish
free( args ); // free dynamic memory
}
}
char** parse( char* str )
{
char** args = malloc( 256 );
int i = 0;
args[i] = strtok( str, " " );
while( args[i] )
{
i++;
args[i] = strtok( NULL, " " );
}
return args;
}
void prompt(char* input)
{
printf("$ "); // print prompt
fgets( input, 255, stdin );
input[strlen(input)-1] = '\0'; // overwrite \n with \0
if ( strcmp( input, "exit" ) == 0 ) // shell command
exit(0);
}
Here are two problems in the program
First, is that when I uncomment the pthread_join() in the main function, there will be a seg fault, other wise the program will run...
Second, is that the output file will be missing the first letter of each word that has stored in the global variable words from last read file. So, for example, there are two files:
one has words "abc abc abc abc abc abc abc abc".
the second has words "def def"
if i input 5 for the second argument when calling a.out, the output in the output file will be
abc
abc
abc
abc
abc
bc
bc
bc
def
def
This is also a werid thing I could not figure out why.
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <pthread.h>
#include "hw3.h"
int index_;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct files
{
char *inputfile;
FILE * outputfile;
} files;
void * readFile( void *arg ){
files *info = (files *)arg;
char fileName[80];
strncat(fileName, (info->inputfile), 79);
fileName[80] = '\0';
FILE *outputfd = info->outputfile;
FILE* fd;
fd = fopen(fileName, "r");
if ( fd == NULL) {
fprintf(stderr, "ERROR:<open() failed>\n");
}
printf("TID %d: Opened \"%s\"\n", (unsigned int)pthread_self(), fileName);
fflush(stdout);
int rc;
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
if( isalnum(ch[0]) ){
// char str = ch[0];
strncat(word, ch, 1);
}
else{//it's a word
if( strlen( word ) >= 2 ){
words[index_] = word;
printf("TID %d: Stored \"%s\" in shared buffer at index [%d]\n",(unsigned int)pthread_self(), word, index_ );
if( index_+ 1 == maxwords ){
index_ = 0;
printf("MAIN: Buffer is full; writing %d words to output file\n", maxwords);
for( unsigned int i = 0; i<maxwords; i++ ){
rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
fwrite( "\n", 1, sizeof("\n"), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
//return EXIT_FAILURE;
}
}
}
else{
index_ ++;
}
}
for(int i = 0; i< strlen(word); i++){
word[i] = '\0';
}
}
ch[0] = fgetc(fd);
}
pthread_mutex_unlock(&mutex);
printf("TID %d: Closed \"%s\"; and exiting\n", (unsigned int)pthread_self(), fileName );
fclose(fd);
pthread_exit( NULL );
}
int main( int argc, char * argv[] ){
if(argc != 4){
fprintf(stderr, "ERROR: Invalid arguments\nUSAGE: ./a.out <input-directory> <buffer-size> <output-file>\n");
return EXIT_FAILURE;
}
//dynamically allocated words buffer with argument 2
maxwords = atoi(argv[2]);
words = (char**)calloc(maxwords, sizeof(char*) );
if ( words == NULL)
{
fprintf( stderr, "ERROR:<word calloc() failed\n>" );
return EXIT_FAILURE;
}
printf("MAIN: Dynamically allocated memory to store %d words\n", maxwords);
fflush(stdout);
//open/create output file of the third argument
FILE* outputfd = fopen (argv[3], "w");
if ( outputfd == NULL )
{
perror( "open() failed" );
return EXIT_FAILURE;
}
DIR * dir = opendir( argv[1] );
if(dir == NULL){
perror("ERRPR:<opendir() failed>");
return EXIT_FAILURE;
}
chdir(argv[1]);
printf("MAIN: Opened \"%s\" directory\n", argv[1]);
fflush(stdout);
pthread_t tid[10];
index_ = 0;
int i = 0;//files index
struct dirent * file;
//files allfiles[20];
char fileName[80];
int rc;
//-----------------------------------------------------------------------
// while loop reads all files in the directory
while ( ( file = readdir( dir ) ) != NULL )
{
struct stat buf;
rc = lstat( file->d_name, &buf ); /* e.g., "xyz.txt" */
/* ==> "assignments/xyz.txt" */
if ( rc == -1 ){
fprintf(stderr, "ERRPR:<lstat() failed>\n");
return EXIT_FAILURE;
}
if ( S_ISREG( buf.st_mode ) )
{
// printf( " -- regular file\n" );
// fflush(stdout);
strncpy(fileName, file->d_name, 79);
files info;
info.inputfile = fileName;
info.outputfile = outputfd;
//printf("%d",i);
printf("MAIN: Created child thread for \"%s\"\n",fileName);
rc = pthread_create( &tid[i], NULL, readFile,(void *)&info );
sleep(1);
i++
}
else if ( S_ISDIR( buf.st_mode ) )
{
// printf( " -- directory\n" );
// fflush(stdout);
}
else
{
// printf( " -- other file\n" );
// fflush(stdout);
}
}
closedir(dir);
printf("MAIN: Closed \"%s\" directory\n", argv[1]);
fflush(stdout);
printf("MAIN: Created \"%s\" output file\n",argv[3]);
fflush(stdout);
//-----------------------------------------------------------------------
for( int j = 0; j<i; j++){
printf( "MAIN: Joined child thread: %u\n", (unsigned int)tid[j] );
pthread_join(tid[i], NULL);
}
for( unsigned int i = 0; i<index_; i++ ){
int rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
return EXIT_FAILURE;
}
}
printf( "MAIN: All threads are done; writing %d words to output file\n", index_);
fflush(stdout);
free( words );
fclose( outputfd );
return EXIT_SUCCESS;
}
This here is the whole program, and there is a header file which is just two global variab
char ** words = NULL;
/* global/shared integer specifying the size */
/* of the words array (from argv[2]) */
int maxwords;
Thanks to everyone for the help!
You need separate info objects for each thread. Right now, all of the threads get the same info object, which you change in between creating threads, and therefore, for most of them, by the time they get a chance to look at the name of the file they are supposed to process, it has been changed.
The segmentation fault is being caused by code you have not shown us, so I can't help you with that except to suggest that you apply valgrind.
Here are two more bugs:
char fileName[80];
strncat(fileName, (info->inputfile), 79);
You can only concatenate onto a string, not an unitialized array of characters that may or may not contain a valid string.
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
The fgets function returns an integer that will be EOF on end of file, otherwise it returns the character value. You convert it to a char and then compare the char to EOF. But that makes no sense since EOF is the integer value that represents end of file. Once cast to a character, it is a valid character that could have been read from the file since the file can contain any characters and "end of file" is not a character.
I am writing some C code to process some data in a file, but I just learned that the file is going to be constantly added to (about 1 time/second, maybe faster). So I'm wondering how do I keep reading from the file as its being added to. Then when I get to the end, wait until the next line is added and then process it. Then wait again and then process, and so on and so on. I have something like:
while(1){
fgets(line, sizeof(line), file);
while(line == NULL){
//wait ? then try to read again?
}
//tokenize line and do my stuff here
}
I thought I could maybe use inotify, but I am getting nowhere with that. Does anyone have any advice?
The most efficient way is using inotify, and the direct way is using the read() system call directly.
using inotify
The following code may give you some help, It works well on Debian 7.0, GCC 4.7:
/*This is the sample program to notify us for the file creation and file deletion takes place in “/tmp/test_inotify” file*/
// Modified from: http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
int main( )
{
int length, i = 0;
int fd;
int wd;
char buffer[EVENT_BUF_LEN];
/*creating the INOTIFY instance*/
fd = inotify_init();
/*checking for error*/
if ( fd < 0 ) {
perror( "inotify_init error" );
}
/* adding the “/tmp/test_inotify” test into watch list. Here,
* the suggestion is to validate the existence of the
* directory before adding into monitoring list.
*/
wd = inotify_add_watch( fd, "/tmp/test_inotify", IN_CREATE | IN_DELETE | IN_ACCESS | IN_MODIFY | IN_OPEN );
/* read to determine the event change happens on “/tmp/test_inotify” file.
* Actually this read blocks until the change event occurs
*/
length = read( fd, buffer, EVENT_BUF_LEN );
/* checking for error */
if ( length < 0 ) {
perror( "read" );
}
/* actually read return the list of change events happens.
* Here, read the change event one by one and process it accordingly.
*/
while ( i < length ) {
struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
if( event->len == 0) {
// For a single file watching, the event->name is empty, and event->len = 0
printf(" Single file watching event happened\n");
} else if ( event->len ) {
if ( event->mask & IN_CREATE ) {
if ( event->mask & IN_ISDIR ) {
printf( "New directory %s created.\n", event->name );
} else {
printf( "New file %s created.\n", event->name );
}
} else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s deleted.\n", event->name );
} else {
printf( "File %s deleted.\n", event->name );
}
} else if( event->mask & IN_ACCESS ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s accessed.\n", event->name );
} else {
printf(" File %s accessed. \n", event->name );
}
} else if( event->mask & IN_MODIFY ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s modified.\n", event->name );
} else {
printf(" File %s modified. \n", event->name );
}
} else if( event->mask & IN_OPEN ) {
if ( event->mask & IN_ISDIR ) {
printf( "Directory %s opened.\n", event->name );
} else {
printf(" File %s opened. \n", event->name );
}
} else {
printf( "Directory or File is accessed by other mode\n");
}
}
i += EVENT_SIZE + event->len;
}
/* removing the “/tmp/test_inotify” directory from the watch list. */
inotify_rm_watch( fd, wd );
/* closing the INOTIFY instance */
close( fd );
}
When runing the above program. You could test it by create a file or directoy named /tmp/test_inotify.
A detailed explanation could be found here
Use read system call
If a file is open, and have read to the end of current file size. the read() system call will return 0. And if some writer wrote N bytes to this file later, and then the read() will just return min(N, buffersize).
So it works correctly for your circumstance. Following is an examples of the code.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef int FD ;
int main() {
FD filed = open("/tmp/test_inotify", O_RDWR );
char buf[128];
if( !filed ) {
printf("Openfile error\n");
exit(-1);
}
int nbytes;
while(1) {
nbytes = read(filed, buf, 16);
printf("read %d bytes from file.\n", nbytes);
if(nbytes > 0) {
split_buffer_by_newline(buf); // split buffer by new line.
}
sleep(1);
}
return 0;
}
Reference
Thanks to Jonathan Leffler's Comment
http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int
main()
{
char ch;
FILE *fp;
long int nbytes_read = 0;
char str [128];
int j = 0;
int first_time = 1;
memset(str, '\0', 128);
fp = fopen("file.txt", "r");
while (1) {
if (first_time != 1) {
fp = fopen("file.txt", "r");
fseek(fp, nbytes_read, SEEK_SET);
sleep(10);
}
if (fp != NULL) {
while ((ch = fgetc(fp)) != EOF) {
if (ch == '\n') {
str[j++] = ch;
printf("%s", str);
memset(str, '\0', 128);
j = 0;
} else {
str[j++] = ch;
}
nbytes_read++;
}
//printf("%ld\n", nbytes_read);
first_time = 0;
}
fclose(fp);
}
return 0;
}
You can use select() with the fileno(file) as the file-descriptor. select will return either with a timeout (if you set a timeout) or when you can read from the file.
Using select can be a good choice but if you do not wish to use it, you can add a sleep for a small amount of milliseconds before reading value.