Creating a grep command from scratch - c

Hello I've been mixing and matching code around the internet in an attempt to make a command that recursively searches through a directory. I'm rather stuck right now. I only have two files in my directory at the moment (no subdirectories) and my print output keeps showing that I have more files than I actually have.
Here is my print output:
entered
entered
entered
enteredIF
entered
enteredIF
entered
enteredIF
entered
enteredIF
What I think it should do is print enteredIF just twice. Interestingly, there are eleven characters in my two files, and there are eleven print statements. Perhaps this is somehow related to my issue.
Could anyone please tell me how to just get the two print outputs (one for each file)?
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <dirent.h>
/* A process is a single process. */
typedef struct process
{
struct process *next; /* next process in pipeline */
char **argv; /* for exec */
pid_t pid; /* process ID */
char completed; /* true if process has completed */
char stopped; /* true if process has stopped */
int status; /* reported status value */
} process;
/* A job is a pipeline of processes. */
typedef struct job
{
struct job *next; /* next active job */
char *command; /* command line, used for messages */
process *first_process; /* list of processes in this job */
pid_t pgid; /* process group ID */
char notified; /* true if user told about stopped job */
struct termios tmodes; /* saved terminal modes */
int stdin, stdout, stderr; /* standard i/o channels */
} job;
/* The active jobs are linked into a list. This is its head. */
job *first_job = NULL;
int main(int argc, char** argv) {
// char cwd[1024]; // buffer
// char* sdirectory = getcwd(cwd, sizeof(cwd));
// printf("dir name: %s\n", sdirectory);
int file_count = 0;
DIR* dirp;
struct dirent* entry;
dirp = opendir(".");
while ((entry = readdir(dirp)) != NULL) {
printf("entered\n");
if (entry-> d_type == DT_REG) {
printf("enteredIF\n");
file_count++;
}
}
closedir(dirp);
printf("file count: %d\n", file_count);
}

Change your entered printf to: printf("saw filename '%s'\n", entry->d_name); and then you can see what filenames it thinks it is reading. Perhaps you have more dot files than just . and ... – JohnH

Related

How to print path of a file properly?

Trying to make a pwd for the c shell. This is what I found on a website and wanted to learn more about it.
I have use debugging printf statements all the way through the program already and it returns the "." instead of the actual dir name all the way through. What am I missing? Why would this be happening?
#include <dirent.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
struct stat stat_buf;
struct dirent *file_info;
ino_t itself_ino; /* holds current folder inode */
ino_t parent_ino; /* holds parent folder inode */
char Current[PATH_MAX]; /* folder name */
char Path[PATH_MAX]; /* holds the full path */
char Slash[PATH_MAX]; /* add / before the folder name */
DIR *dir;
while (1)
{
dir = opendir(".");
if(dir == NULL) {
fprintf(stderr, "cannot get current directory.\n");
exit(-1);
}
/* read the information about the current folder */
file_info = readdir(dir);
lstat(file_info->d_name, &stat_buf);
itself_ino = stat_buf.st_ino;
closedir(dir);
chdir(".."); /* go to parent directory */
dir = opendir(".");
file_info = readdir(dir);
lstat(file_info->d_name, &stat_buf);
parent_ino = stat_buf.st_ino;
if(itself_ino == parent_ino) {
/*closedir(dir);*/
break;
} else {
strcpy(Slash, "/");
strcpy(Current, file_info->d_name);
strcat(Slash, Current); /* add "/" as the first */
strcat(Slash, Path); /* charcter of the directory */
/* check the length of the pathname */
if(strlen(Slash) >= PATH_MAX) {
fprintf(stderr, "Error! Path too long!\n");
exit(0);
}
/* save the full pathname */
strcpy(Path, Slash);
}
closedir(dir);
}
/* print the full path of the current working directory */
printf("%s\n", Path);
return 0;
}
It's just realpath:
if (realpath(".", &Path) == NULL) {
// handle error
}
However maybe you aim at getcwd or get_current_dir_name.
printf("%s\n", get_current_dir_name());

How does ls sort filenames?

I'm trying to write a function that mimics the output of the ls command in Unix. I was originally trying to perform this using scandir and alphasort, and this did indeed print the files in the directory, and it did sort them, but for some reason, this sorted list does not seem to match the same "sorted list" of filenames that ls gives.
For example, if I have a directory that contains file.c, FILE.c, and ls.c.
ls displays them in the order: file.c FILE.c ls.c
But when I sort it using alphasort/scandir, it sorts them as: FILE.c file.c ls.c
How does ls sort the files in the directory such that it gives such a differently ordered result?
To emulate default ls -1 behaviour, make your program locale-aware by calling
setlocale(LC_ALL, "");
near the beginning of your main(), and use
count = scandir(dir, &array, my_filter, alphasort);
where my_filter() is a function that returns 0 for names that begin with a dot ., and 1 for all others. alphasort() is a POSIX function that uses the locale collation order, same order as strcoll().
The basic implementation is something along the lines of
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
static void my_print(const char *name, const struct stat *info)
{
/* TODO: Better output; use info too, for 'ls -l' -style output? */
printf("%s\n", name);
}
static int my_filter(const struct dirent *ent)
{
/* Skip entries that begin with '.' */
if (ent->d_name[0] == '.')
return 0;
/* Include all others */
return 1;
}
static int my_ls(const char *dir)
{
struct dirent **list = NULL;
struct stat info;
DIR *dirhandle;
int size, i, fd;
size = scandir(dir, &list, my_filter, alphasort);
if (size == -1) {
const int cause = errno;
/* Is dir not a directory, but a single entry perhaps? */
if (cause == ENOTDIR && lstat(dir, &info) == 0) {
my_print(dir, &info);
return 0;
}
/* Print out the original error and fail. */
fprintf(stderr, "%s: %s.\n", dir, strerror(cause));
return -1;
}
/* We need the directory handle for fstatat(). */
dirhandle = opendir(dir);
if (!dirhandle) {
/* Print a warning, but continue. */
fprintf(stderr, "%s: %s\n", dir, strerror(errno));
fd = AT_FDCWD;
} else {
fd = dirfd(dirhandle);
}
for (i = 0; i < size; i++) {
struct dirent *ent = list[i];
/* Try to get information on ent. If fails, clear the structure. */
if (fstatat(fd, ent->d_name, &info, AT_SYMLINK_NOFOLLOW) == -1) {
/* Print a warning about it. */
fprintf(stderr, "%s: %s.\n", ent->d_name, strerror(errno));
memset(&info, 0, sizeof info);
}
/* Describe 'ent'. */
my_print(ent->d_name, &info);
}
/* Release the directory handle. */
if (dirhandle)
closedir(dirhandle);
/* Discard list. */
for (i = 0; i < size; i++)
free(list[i]);
free(list);
return 0;
}
int main(int argc, char *argv[])
{
int arg;
setlocale(LC_ALL, "");
if (argc > 1) {
for (arg = 1; arg < argc; arg++) {
if (my_ls(argv[arg])) {
return EXIT_FAILURE;
}
}
} else {
if (my_ls(".")) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
Note that I deliberately made this more complex than strictly needed for your purposes, because I did not want you to just copy and paste the code. It will be easier for you to compile, run, and investigate this program, then port the needed changes -- possibly just the one setlocale("", LC_ALL); line! -- to your own program, than try and explain to your teacher/lecturer/TA why the code looks like it was copied verbatim from somewhere else.
The above code works even for files specified on the command line (the cause == ENOTDIR part). It also uses a single function, my_print(const char *name, const struct stat *info) to print each directory entry; and to do that, it does call stat for each entry.
Instead of constructing a path to the directory entry and calling lstat(), my_ls() opens a directory handle, and uses fstatat(descriptor, name, struct stat *, AT_SYMLINK_NOFOLLOW) to gather the information in basically the same manner as lstat() would, but name being a relative path starting at the directory specified by descriptor (dirfd(handle), if handle is an open DIR *).
It is true that calling one of the stat functions for each directory entry is "slow" (especially if you do /bin/ls -1 style output). However, the output of ls is intended for human consumption; and very often piped through more or less to let the human view it at leisure. This is why I would personally do not think the "extra" stat() call (even when not really needed) is a problem here. Most human users I know of tend to use ls -l or (my favourite) ls -laF --color=auto anyway. (auto meaning ANSI colors are used only if standard output is a terminal; i.e. when isatty(fileno(stdout)) == 1.)
In other words, now that you have the ls -1 order, I would suggest you modify the output to be similar to ls -l (dash ell, not dash one). You only need to modify my_print() for that.
In alphanumeric (dictionary) order.
That changes with language, of course. Try:
$ LANG=C ls -1
FILE.c
file.c
ls.c
And:
$ LANG=en_US.utf8 ls -1
file.c
FILE.c
ls.c
That is related to the "collating order". Not a simple issue by any measure.

Applying fork() and pipe() (or fifo()) on counting words code

I've completed writing of counting words code finally. It counts total number of words in files. (i.e. txt). Now, I want to use multiple fork() to access and read every file. I studied in the last week. Besides, I use global variable to hold number of counted words. As far as I know, If I apply fork(), used global variables are assigned as 0. To avoid it, I tried to use mmap() and similar functions this is okey. But, I also want to use pipe() also (fifo() if it is possible) to communicate (hold values of numbers).
I use nftw() function to go in folders and files. My logic is on the below picture. How can use fork() and pipe() (fifo()) on this code ? fork() is really complicated for me because of my inexperience. I'm new using of pipe() and fork(). According to my idea logic of the code is that if I can use fork() and pipe(), there will be fork() every file(i.e. txt) and access them by using fork. If there is another folder and there are files, again creates fork() from one of created forks , then access file. I try to explain also drawing below. Thank you. I want to learn using of them.
int countInEveryFolder(const char *dir)
is used because I don't know how to count files until the next folder in nftw() function. Number of files is necessary because it is number of fork.
Every folder should be parent of files. The files are included by the folder.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <ftw.h>
#include <ctype.h>
#include <sys/mman.h>
#include <locale.h>
#include <errno.h>
#define MAX_PATH_LEN 2048
unsigned long total_words = 0UL;
unsigned long total_dirs = 0UL;
unsigned long total_files = 0UL;
// Just proves counting number of file in a folder
int countInEveryFolder(const char *dir) {
struct stat stDirInfo;
struct dirent * stFiles;
DIR * stDirIn;
char szFullName[MAX_PATH_LEN];
char szDirectory[MAX_PATH_LEN];
struct stat stFileInfo;
int numOfFile = 0;
strncpy( szDirectory, dir, MAX_PATH_LEN - 1 );
if (lstat( szDirectory, &stDirInfo) < 0)
{
perror (szDirectory);
return 0;
}
if (!S_ISDIR(stDirInfo.st_mode))
return 0;
if ((stDirIn = opendir( szDirectory)) == NULL)
{
perror( szDirectory );
return 0;
}
while (( stFiles = readdir(stDirIn)) != NULL)
{
if (!strcmp(stFiles->d_name, ".") || !strcmp(stFiles->d_name, ".."))
continue;
sprintf(szFullName, "%s/%s", szDirectory, stFiles -> d_name );
if (lstat(szFullName, &stFileInfo) < 0)
perror ( szFullName );
/* is the file a directory? */
if (S_ISREG(stFileInfo.st_mode))
{
printf( "Filename: %s\n", szFullName );
numOfFile++;
}
} // end while
closedir(stDirIn);
return numOfFile;
}
// Count words in files.
unsigned long count_words_in_file(const char *const filename)
{
unsigned long count = 0UL;
int errnum = 0;
int c;
FILE *in;
in = fopen(filename, "rt");
if (in == NULL) {
errnum = errno;
fprintf(stderr, "%s: %s.\n", filename, strerror(errnum));
errno = errnum;
return 0UL;
}
/* Skip leading whitespace. */
do {
c = getc(in);
} while (isspace(c));
/* Token loop. */
while (c != EOF) {
/* This token is a word, if it starts with a letter. */
if (isalpha(c))
count++;
/* Skip the rest of this token. */
while (!isspace(c) && c != EOF)
c = getc(in);
/* Skip the trailing whitespace. */
while (isspace(c))
c = getc(in);
}
/* Paranoid checking for I/O errors. */
if (!feof(in) || ferror(in)) {
fclose(in);
fprintf(stderr, "Warning: %s: %s.\n", filename, strerror(EIO));
errnum = EIO;
} else
if (fclose(in)) {
fprintf(stderr, "Warning: %s: %s.\n", filename, strerror(EIO));
errnum = EIO;
}
errno = errnum;
return count;
}
// Recursively go in folders
int nftw_callback(const char *filepath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
// Directory
if (typeflag == FTW_DP || typeflag == FTW_D)
{
total_dirs++;
printf("%*s%s\n", ftwbuf->level * 4, "", filepath);
//countInEveryFolder(filepath);
}
// Folder
else if (typeflag == FTW_F)
{
total_files++;
total_words += count_words_in_file(filepath);
printf("%*s%s\n", ftwbuf->level * 4, "", filepath);
}
return 0;
}
/* Error message */
void err_sys(const char *msg)
{
perror(msg);
fflush(stdout);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
total_files = total_dirs = total_words = 0UL;
if (nftw(argv[1], nftw_callback, 15, FTW_PHYS) == 0) {
/* Success! */
printf("%s: %lu files, %lu directories, %lu words total.\n",
argv[1], total_files, total_dirs, total_words);
} else {
/* Failed... */
err_sys("ntfw");
}
putchar('\n');
//printf( "\nTotal words = %d\n\n", *wordCount);
//printf( "\nTotal folders = %d\n\n", *folderCount);
//printf( "\nTotal childs = %d\n\n", *childCount); //fork()
return 0;
}
To start I would write the program with two phases. A single-process phase in which all the file-paths are queued up (into a linked-list or dequeue), and a multi-process phase in which the worker processes receive work via their pipe() and send counts back to the main process via their pipe(). The main process would use select() to multiplex the input from its children.
Once you understand how to use select() with pipe()s, then work on having the filepath discovery be concurrent.
This design would be much easier to implement in Go, node.js, or greenlet with Python, but learning how to do it in C gives you a level of understanding for the underlying operations that you don't get with newer languages.

Segmentation fault (core dumped)

I'm writing a program in c that basically copies files, but I'm getting this error: Segmentation fault (core dumped). From what I'm reading I think it's because I'm trying to access memory that hasn't been allocated yet. I'm a newbie when it comes to c and I suck at pointers, so I was wondering if you guys could tell me which pointer is causing this and how to fix it if possible. Btw, this program is supposed to be a daemon, but I haven't put anything inside the infinite while loop at the bottom.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(int c, char *argv[]) {
char *source, *destination;
char *list1[30], *list2[30], *listDif[30];
unsigned char buffer[4096];
int i=0, x=0, sizeSource=0, sizeDest=0, sizeDif=0;
int outft, inft,fileread;
int sleeper;
struct dirent *ent, *ent1;
//Check number of arguments
if(c<3)
{
printf("Daemon wrongly called\n");
printf("How to use: <daemon name> <orginDirectory> <destinationDirectory> \n");
printf("or : <daemon name> <orginDirectory> <destinationDirectory> <sleeperTime(seconds)>");
return 0;
}
//Checks if sleeper time is given or will be the default 5minutes
/*if(c=4)
{
char *p;
errno = 0;
long conv = strtol(argv[3], &p, 10);
if(errno != 0 || *p != '\0')
{
printf("Number given for sleeper incorrect, it has to be an integer value.\n");
return(0);
} else
{
sleeper = conv;
}
} else
{
sleeper = 300;
}*/
//Get path of directories from arguments
source = argv[1];
destination = argv[2];
//Check if directories exist
DIR* dirSource = opendir(source);
if (!dirSource)
{
printf("Source directory incorrect\n");
return 0;
}
DIR* dirDest = opendir(destination);
if (!dirDest)
{
printf("Destination directory incorrect\n");
return 0;
}
/* save all the files and directories within directory */
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = strdup(ent->d_name);
sizeSource++;
if(sizeSource>=30){break;}
}
closedir(dirSource);
while((ent1 = readdir (dirDest)) != NULL) {
list2[sizeDest] = strdup(ent1->d_name);
sizeDest++;
if(sizeDest>=30){break;}
}
closedir(dirDest);
/* Verify the diferences between the directories and save them */
int z;
int dif = 0; //0 - False | 1 - True
printf("Diferenças:\n");
for(i=0;i<sizeSource;i++){
dif = 0;
for(z=0;z<sizeDest;z++){
if(strcmp(list1[i],list2[z])==0){ //If there is no match, it saves the name of the file to listDif[]
dif = 1;
break;
}
}
if(dif==0) {
printf("%s\n",list1[i]);
listDif[sizeDif] = list1[i];
sizeDif++;
}
}
/* This code will copy the files */
z=0;
while(z!=sizeDif){
// output file opened or created
char *pathSource, *pathDest;
strcpy(pathSource, source);
strcat(pathSource, "/");
strcat(pathSource, listDif[z]);
strcpy(pathDest, destination);
strcat(pathDest, "/");
strcat(pathDest, listDif[z]);
// output file opened or created
if((outft = open(pathDest, O_CREAT | O_APPEND | O_RDWR))==-1){
perror("open");
}
// lets open the input file
inft = open(pathSource, O_RDONLY);
if(inft >0){ // there are things to read from the input
fileread = read(inft, buffer, sizeof(buffer));
printf("%s\n", buffer);
write(outft, buffer, fileread);
close(inft);
}
close(outft);
}
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
//sleep(5); /* wait 5 seconds */
}
exit(EXIT_SUCCESS);
}
The result of ls is:
ubuntu#ubuntu:~/Desktop$ ls
Concatenar_Strings.c core D2 daemon.c examples.desktop
Concatenar_Strings.c~ D1 daemon daemon.c~ ubiquity.desktop
D1 and D2 are folders, and in D1 are three text documents that I want to copy into D2.
One other question, is this a delayed error or an immediate one? Because I doubt this message would appear on a code line that with two integers.
Thanks in advance guys.
This loop is wrong:
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = ent->d_name;
Probably, ent points to the same memory block every time, and the readdir function updates it. So when you save that pointer, you end up with your list containing invalid pointers (probably end up all pointing to the same string). Further, the string may be deallocated once you got to the end of the directory.
If you want to use the result of readdir after closing the directory or after calling readdir again you will need to take a copy of the data. In this case you can use strdup and it is usually good style to free the string at the end of the operation.
This may or may not have been the cause of your segfault. Another thing to check is that you should break out of your loops if sizeSource or sizeDest hits 30.
In the strcmp loop, you should really set dif = 0 at the start of the i loop, instead of in an else block.
Update: (more code shown by OP)
char *pathSource, *pathDest;
strcpy(pathSource, source);
You are copying to a wild pointer, which is a likely cause of segfaults. strcpy does not allocate any memory, it expects that you have already allocated enough.
One possible fix would be:
char pathSource[strlen(source) + 1 + strlen(listDif[z]) + 1];
sprintf(pathSource, "%s/%s", source, listDif[z]);
Alternatively (without using VLA):
char pathSource[MAX_PATH]; // where MAX_PATH is some large number
snprintf(pathSource, MAX_PATH, "%s/%s", source, listDif[z]);
Do the same thing for pathDest.
NB. Consider moving the closedir lines up to after the readdir loops; generally speaking you should open and close a resource as close as possible to the times you start and finish using them respectively; this makes your code easier to maintain.

Debug recursive thread call in C

I have been trying to debug my code whenever I had free-time for the past day and a half and I don't know what is wrong with my code. When I add the close() function to a recursive call, the program gives me an invalid pointer. But when I remove the close() function call the program runs fine, except it does not do what it is supposed to do, which is:
add up all the file sizes in a user
input directory
open sub-directories, if any, and add
up all the files inside the
sub-directory
Instead, it adds up all the file sizes in the input directory and is able to open the last sub-directory and add the files within that directory to the total file size count.
I am trying to do this with threads. The main() function creates one main thread from the user input directory and runs opendirectory() off the bat.
/*
* Iterates through given directory
*/
void *opendirectory(void *t)
{
pthread_mutex_lock(&dirlock);
DIR *dpntr;
struct dirent *dentry;
char new_directory[512], dir = t;
printf("OPENING DIRECTORY ... %s\n", t);
/* Checks if given directory can be opened */
if((dpntr = opendir(t)) == NULL) {
printf("DIRECTORY FAILED ...%s\n",t);
perror("ERROR -- COULD NOT OPEN DIR");
pthread_exit(NULL);
}
printf("DIRECTORY OPENED: %s\n", t);
/* Read each file in current directory */
while ((dentry = readdir(dpntr)) != NULL ) {
/* Ignore special directories */
if(strcmp(dentry -> d_name, ".") == 0 || strcmp(dentry -> d_name, "..") == 0) {
continue;
} else {
compilelist( t, dentry->d_name );
}
}
pthread_mutex_unlock(&dirlock);
/* Checks if directory can be closed */
if(closedir(dpntr) < 0)
printf("ERROR CLOSING %s.\n", t);
}
This is the function that will determine if a new thread should be created and is supposed to run recursively.
/*
* Determines if current file is a directory
* Creates a new thread if true
*/
void compilelist (const char* dirname, const char *filename)
{
pthread_mutex_lock(&filelock);
struct stat statdata;
char *filepathname, *dpntr;
/* Allocate memory for filepathname */
if((filepathname = (char *) malloc(sizeof(char) * strlen(dirname))) == NULL)
{
printf("CANNOT ALLOCATE MEMORY FOR FILE PATH NAME.");
pthread_exit(NULL);
}
/* Concats directory name with file name */
if(dirname[strlen(dirname) -1] == '/')
{
pthread_mutex_lock(&pathlock);
sprintf(filepathname, "%s%s", dirname, filename);
pthread_mutex_unlock(&pathlock);
}else
{
pthread_mutex_lock(&pathlock);
sprintf(filepathname, "%s/%s", dirname, filename);
pthread_mutex_unlock(&pathlock);
}
lstat(filepathname, &statdata);
/* Calls print_statdata() if current item is a file */
if(!(S_ISDIR(statdata.st_mode)))
{
printf("FILE: %s\n", filepathname);
if(!stat( filepathname, &statdata))
{
print_statdata( filename, &statdata );
}
else {
fprintf (stderr, "GETTING STAT FOR %s", filepathname);
perror( "ERROR IN STATDATA WHILE GETTING STAT");
}
}
/* Recursive call to opendirectory() */
else {
pthread_mutex_lock(&dircountlock);
dirCount++;
pthread_mutex_unlock(&dircountlock);
dpntr = filepathname;
free(filepathname);
printf("SUB-DIRECTORY THREAD: %s\nTHREAD ID NUMBER: %d\n", dpntr, dirCount);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[dirCount-1], &attr, opendirectory, (void *)dpntr);
}
pthread_mutex_unlock(&filelock);
}
Here is the main()
/*
* Main function prompts user for a directory
*/
int main(int argc, char *argv[])
{
int i;
char *dPtr;
// pthread_attr_t attr;
printf("ENTER A DIRECTORY:\n\t");
scanf("%s", directory);
dPtr = directory;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&filelock, NULL);
pthread_mutex_init(&dirlock, NULL);
pthread_mutex_init(&dircountlock, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, opendirectory, (void *)dPtr);
/* Wait for all threads to complete */
for (i = 0; i < dirCount; i++) {
pthread_join(threads[i], NULL);
}
printf("TOTAL DIRECTORY SIZE: %d\n", dirSize);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&filelock);
pthread_mutex_destroy(&dirlock);
pthread_mutex_destroy(&dircountlock);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit (NULL);
}
And the global variables ...
pthread_mutex_t mutex;
pthread_mutex_t dirlock;
pthread_mutex_t filelock;
pthread_mutex_t dircountlock;
pthread_mutex_t threadlock;
pthread_cond_t count_threshold_cv;
pthread_attr_t attr;
pthread_t threads[128]; // handles up to 128 threads (i.e. 128 directories, change accordingly)
char directory[512];
int dirSize = 0;
int dirCount = 1; // user's input directory
I feel that the pthread_create() called at the bottom of the compilelist() function is not working properly. The threads[] refers to a global array of threads that has a default size of 20, assuming that there will be no more than 20 total directories. dirCount starts off at 1 because of the user's input directory and increases as new directories are encountered.
Your code:
dpntr = opendir(t)
...
if(closedir(t) < 0)
should be:
if(closedir(dpntr) < 0)
Here I found 2 problems of your code:
As wrang-wrang metioned, closedir(t) leads segfault.
"char filepathname[512];" of compilelist() is a local memory buffer, but you pass it to your thread (opendirectory) and use it continuously. You should use copying or dynamic-allocation instead.
Effo Upd#2009nov17:
After fixing above 2 points, it works fine on my FC9 x86_64 so far. Btw: threads number 20 is really not enough.
First problem:
whenever I had free-time for the past day and a half
Don't do that, your brain isn't built for it. Allocate a time, tell your workmates/wife-and-kids that, if they bother you during this time, there will be gunshots and police involvement :-)
Other problems: no idea (hence the community wiki).

Resources