I would really love some debugging help in this one. I've been working on this since the morning and its 4am. (I'm suppose to deliver this in 7 hours [11am])
Everything in main.c works but when I create some child processes to run compute.c's compiled file with execl it doesnt do it and sends an error of "Bad Address".
I've attached 3 pastebin links with main.c and compute.c and a txt file containing the tables I mention below.
The program is suppose to read 2 tables with integers from a file called pinakes.txt and then by using POSIX's shared memory API to place those tables in shared memory and create processes to calculate a 'row * column' sum from them and place that sum in another table.
sum += A[row][i] * B[i][column] = C[row][column]
Everything until the line below from main.c should work properly (I debugged it numerous times).
ppid = getpid();
main.c http://pastebin.com/iMCefaLZ
compute.c http://pastebin.com/Ejp214Up
pinakes.txt http://pastebin.com/h8yKXFvv
compile and then run
./main pinakes.txt
main.c
188 lines of code
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <errno.h>
int pinA_X = 0, pinA_Y = 0, pinB_X=0, pinB_Y=0;
int pinA[10][10], pinB[10][10], pinC[10][10];
main(int argc, char *argv[]){
int pid, ppid;
FILE *stream;
// general variables
int i, c, j, rc, converted, lines = 0;
//flags
int flagV=0, flagW=0, flagX=0, flagY=0, flagZ=0;
//shared memory
int dumpedArray[101];
int size = sizeof(dumpedArray);
int sid1 = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
int sid2 = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
int sid3 = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
int* shared_A = (int*) shmat(sid1, NULL, 0);
int* shared_B = (int*) shmat(sid2, NULL, 0);
int* shared_C = (int*) shmat(sid3, NULL, 0);
if(argc!=2){
printf("wrong number of arguments\n");
return -1;
}else{
stream = fopen(argv[1] , "r");
while((c = getc(stream))!= EOF){
if(flagZ == 0){
if(flagX == 1){pinA_X = c - 48;flagX = 0;}
if(c == 88){flagX = 1;}
if(flagY == 1){pinA_Y = c - 48;flagY = 0;}
if(c == 89){flagY = 1;}
if(c == 90){flagZ = 1;}
}else if(flagZ == 1){
if(flagX == 1){pinB_X = c - 48;flagX = 0;}
if(c == 88){flagX = 1;}
if(flagY == 1){pinB_Y = c - 48;flagY = 0;}
if(c == 89){flagY = 1;}
}
}
fclose(stream);
printf("pinA[%d][%d] * pinB[%d][%d] = C[%d][%d]\n\n", pinA_X, pinA_Y, pinB_X, pinB_Y, pinA_X, pinB_Y);
// get A
stream = fopen(argv[1] , "r");
i=0;j=0;
while((c = getc(stream))!= EOF){
if(i <= pinA_X && j <= pinA_Y){
if(flagW == 0){
if(c == 87){
flagW = 1;
}
}else{
if(c > 47 && c < 58){
pinA[i][j] = c - 48;
j++;
}
if(c == 13){
j=0;
i++;
}
}
}
}
fclose(stream);
// get B
stream = fopen(argv[1] , "r");
i=0;j=0;
while((c = getc(stream))!= EOF){
if(i <= pinB_X && j <= pinB_Y){
if(flagV == 0){
if(c == 86){
flagV = 1;
}
}else{
if(c > 47 && c < 58){
pinB[i][j] = c - 48;
j++;
}
if(c == 13){
j=0;
i++;
}
}
}
}
fclose(stream);
// print A
printf("A={\n");
for(j=0; j<pinA_X;j++){
for(i=0;i<pinA_Y;i++){
printf(" %d", pinA[j][i]);
}
printf("\n");
}
printf("}\n\n");
// print B
printf("B={\n");
for(j=0; j<pinB_X;j++){
for(i=0;i<pinB_Y;i++){
printf(" %d", pinB[j][i]);
}
printf("\n");
}
printf("}\n");
// Save pinA to shared Memory
converted = 0;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
converted = (i * 10) + j;
shared_A[converted] = pinA[i][j];
}
}
// Save pinA to shared Memory
converted = 0;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
converted = (i * 10) + j;
shared_B[converted] = pinB[i][j];
}
}
// Push size of arrays in shared memory
shared_A[100] = pinA_X;
shared_A[101] = pinA_Y;
shared_B[100] = pinB_X;
shared_B[101] = pinB_Y;
ppid = getpid();
for(i=0; i<pinA_X; i++){
for(j=0; j<pinB_Y; j++){
if(ppid == getpid()){
pid = fork();
if(pid==0){
if(execl("./compute", "compute", i, j, sid1, sid2, sid3, NULL) == -1){
printf("error exec\n");
printf("Error opening file: %s\n", strerror(errno));
};
}else if(pid<0){
printf("\nDen egine h fork!\n");
}else{
wait(0);
}
}
}
}
//print C
converted = 0;
printf("C={\n");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
converted = (i * 10) + j;
pinC[i][j] = shared_C[converted];
printf(" %d", pinC[i][j]);
}
printf("\n");
}
printf("}\n");
}
}
Neither compute.c nor pintakes.txt is directly relevant to answering this question.
The bad address problem arises because you run:
for(i=0; i<pinA_X; i++){
for(j=0; j<pinB_Y; j++){
if(ppid == getpid()){
pid = fork();
if(pid==0){
if(execl("./compute", "compute", i, j, sid1, sid2, sid3, NULL) == -1){
The arguments to execl() must be strings; i and j are manifestly not strings (and sid1, sid2 and sid3 are the identifiers for three chunks of shared memory).
Convert those values to strings and try again.
Your program creates the shared memory with IPC_PRIVATE, so your code in compute.c (which is executed via execl() is going to be hard to make work. You may get away with transferring the shared memory IDs like that; I'm not sure.
I think I'd be using a single shared memory segment.
It also looked like your reading code is going to read the same data into the two arrays - but I may have been misreading it.
Finally, your PasteBin examples expire in 23 hours. That limits the usefulness of your question. You should really transfer the data into the question - with, I suggest, no tabs and tabstops set at 4 rather than 8. (Or use more functions to prevent such deep indentation.)
You are passing ints to execl, those should all be 0-terminated strings. Also, the final NULL must be cast to Char*.
Related
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <libio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <ctype.h>
int main(int argc, char** argv) {
int shmID;
char* shmptr, *array, *filearray;
FILE* infile = fopen(argv[1], "r");
if (!infile) {
printf("No file exists\n");
}
int length = 0;
char ch;
ch = getc(infile);
while ((ch = getc(infile)) != EOF) {
filearray[length] = ch;
length++;
}
length++;
fclose(infile);
shmID = shmget(IPC_PRIVATE, length, IPC_CREAT | 0666);
if (shmID < 0) {
printf("There is an error while creating memory \n");
}
int pid = fork();
if (pid > 0) {
shmptr = shmat(shmID, NULL, 0);
if (shmptr == (char * ) - 1) {
printf("There is an error while attaching memory \n");
}
int j = 0;
while ( * array != '!') {
if (array[j] >= 'A' && array[j] <= 'Z') {
array[j] = tolower(array[j]);
} else if (array[j] >= 'a' && array[j] <= 'z') {
array[j] = toupper(array[j]);
} else if (array[j] >= '0' && array[j] <= '9') {
j--;
}
j++;
}
* shmptr = '#';
shmdt(shmptr);
shmctl(shmID, IPC_RMID, NULL);
} else if (pid == 0) {
shmptr = shmat(shmID, NULL, 0);
if (shmptr == (char * ) - 1) {
printf("There is an error while attaching memory \n");
}
array = shmptr;
int x;
for (x = 0; x <= length; x++) {
* array = filearray[x];
array++;
}
* array = '!';
while ( * shmptr != '#') {
sleep(1);
}
int k = 0;
//here we restore the values back into file
while (*array != '!') {
printf("%c", array[k]);
k++;
}
shmdt(shmptr);
} else if (pid < 0) {
printf("Error\n");
}
return 0;
}
This is the code. What I intend to do is take the data from the file and input it into an array. We do this so that we have a temporary data point to store into. I then apply the appropriate checks to see if there is an error while creating the shared memory or the fork command.
After this we enter the child where we intend to
Attach memory
Check error for 1.
Get the array(tried with another char array poiinter but still ran into problems) pointed at shmptr which should initially hold NULL at first but with a length of L(number of characters in file)
Copy the values from the file array into the array(acts as a moving head similar to in a link list) and then attach as a final block a ! to tell the parent the array is over.
Use a # as a char to be added to array so we can know waiting period is over.
In he parent:
Attach memory
Get array
Upper,lower case and check if value is an integer(remove integer by j-- goes back 1 location, then j++ moves back to same location)
attach a # at the end.
As child was rerun it saw this # and should print the array
Hope I was clear. Thank you for the help.
Several problems with your code:
You check if the file open worked, but then you proceed still with the NULL pointer in such a case.
It should be:
FILE *infile = fopen(argv[1], "r");
if(!infile)
{
printf("No file exists\n");
return 1;
}
Pointers are not initialized but you already access them, which invokes undefined behavior.
You need to initialize these pointers before you access them.
char *shmptr;
char *array;
char *filearray;
You can determine the filesize like this:
fseek(infile, 0L, SEEK_END);
length = ftell(infile);
fseek(infile, 0L, SEEK_SET);
Now you can allocate the memory for the file and read it in one go, instead of that loop:
filearray = malloc(length);
if (!filearray)
{
// error
return 1;
}
if(fread(filearray, length, 1, infile) != 1)
{
// error
return 1;
}
As said above you access uninitialized pointers you do this also for array but fro your example it is not clear where this comes from, so you have to allocate memory for it, and initialize it, before you read it.
Program should read list of filenames, open these files and put their handles in the array of structure, then read strings and print consecutive lines of strings to smallest files by using handles contained in array of structures.
My program puts data from all lines to only one file which is initially the smallest which is false because it should the one which is smallest with every time it prints data into the file. This is my program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
struct file_t
{
FILE* f;
int size;
}t[5];
void close_file(struct file_t* f) {
if (f == NULL || f->f == NULL) {
}
else {
fclose(f->f);
}
}
int open_file(struct file_t* f, const char* filename) {
if (f == NULL || filename == NULL) {
return 1;
}
FILE* fp;
fp = fopen(filename, "ab");
if (fp == NULL) {
return 2;
}
long int res = ftell(fp);
fclose(fp);
f->size = res;
f->f = fopen(filename, "ab+");
if (fp == NULL) {
return 2;
}
return 0;
}
struct file_t* find_min(const struct file_t* files, int size) {
if (files == NULL || size <= 0) {
return NULL;
}
int x = (files + 0)->size, i = 0, index = 0;
for (i = 0; i < size; i++) {
if ((files + i)->size <= x) {
x = (files + i)->size;
index = i;
}
}
return (struct file_t*)(files + index);
}
int main() {
puts("Input files' names:");
char tab[100];
int num = 0;
while(1==1){
if(fgets(tab, 100, stdin)==NULL||*tab=='\n'){
if (num == 0) {
printf("Couldn't open file");
return 4;
}
break;
}
int index=strlen(tab);
*(tab+index-1)='\x0';
if (strlen(tab) > 30) {
*(tab + 30) = '\x0';
}
if (open_file((t + num), tab) > 0) {
}
else {
num++;
}
}
if (num == 0) {
printf("Couldn't open file");
return 4;
}
char str[1000];
printf("Input text:");
*str = '\x0';
while (fgets(str, 1000, stdin)==NULL||*str!='\n') {
int index=strlen(str);
*(str+index-1)='\x0';
struct file_t* p = find_min(t, num);
fwrite(str, sizeof(char), strlen(str), p->f);
}
for (int i = 0; i < num; i++) {
close_file(t + i);
}
printf("File saved");
return 0;
}
There are some critical bugs that you need to resolve.
fseek(stdin, 0, SEEK_END) -- fseek normally only work on a disk file, or something reasonably similar. Please refer to this link Using fseek with a file pointer that points to stdin
As a matter of fact even fflush() won't work. fflush is something that is designed for flushing output streams, and its behavior with input streams is implementation-dependent. Please refer to this link for more details stdinflush
scanf("%[^\n]s", tab)
If you are using this in a loop or multiple times, only the first read will succeed. The reason being, the \n character is left out from the previous input, and as said earlier fflush() might not be successful in removing that \n. The further calls to scanf() will simply return without reading anything.
'\0x' If you are intending to use this as string terminator then this is not it. It is a multi-character constant with an integer value 120. Below is a vague test run
Code
#include <stdio.h>
int main()
{
if ('\0' == '\0x' )
printf("both are same\n");
printf("%d",'\0x');
}
Compilation Warnings
test.c: In function ‘main’:
test.c:5:14: warning: multi-character character constant [-Wmultichar]
5 | if ('\0' == '\0x' )
| ^~~~~
test.c:8:14: warning: multi-character character constant [-Wmultichar]
8 | printf("%d",'\0x');
| ^~~~~
Output
120
fseek(fp, 0, SEEK_END); ftell(fp); -- This should not be used to determine the file sizes. The behavior of the fseek() with SEEK_END is undetermined in the case of binary files. Please refer to this link Do not use fseek() and ftell() to compute the size of a regular file
Some Logic Errors
1) You should compute the file size every time in find_min() as it gets changed whenever you write data to the file.
2) fwrite()won't actually dump the data to file immediately. you need to call fflush().
After resolving the above issues, this is the modified code.
Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <sys/stat.h>
struct file_t
{
FILE* f;
int size;
}t[5];
void close_file(struct file_t* f) {
if (f == NULL || f->f == NULL) {
}
else {
fclose(f->f);
}
}
int open_file(struct file_t* f, const char* filename) {
if (f == NULL || filename == NULL) {
return 1;
}
f->f = fopen(filename, "a");
if (f->f == NULL)
return 2;
struct stat statbuf;
fstat(fileno(f->f), &statbuf);
f->size = statbuf.st_size;
return 0;
}
struct file_t* find_min(const struct file_t* files, int size) {
if (files == NULL || size <= 0) {
return NULL;
}
struct stat statbuf;
fstat(fileno(files->f), &statbuf);
int x = statbuf.st_size, i = 0, index = 0;
for (i = 0; i < size; i++) {
fstat(fileno((files+i)->f), &statbuf);
if (statbuf.st_size < x) {
x = statbuf.st_size;
index = i;
}
}
return (struct file_t*)(files + index);
}
int main() {
puts("Input files' names:");
char tab[100];
int num = 0;
while(1){
int c;
while (1) {
c = getc(stdin);
if (c == EOF || c == ' ')
goto user_input;
if(c != '\n')
break;
}
tab[0] = c;
if (scanf("%[^\n]s", tab+1) == EOF)
break;
if (*tab == '\0') {
if (num == 0) {
printf("Couldn't open file");
return 4;
}
break;
}
if (strlen(tab) > 30) {
*(tab + 30) = '\0';
}
if (open_file((t + num), tab) > 0) {
}
else {
num++;
}
*tab = '\0';
}
user_input:
if (num == 0) {
printf("Couldn't open file");
return 4;
}
fflush(stdin);
char str[1000];
printf("Input text:\n");
*str = '\0';
while(1) {
int c;
while(1) {
c = getc(stdin);
if (c == EOF)
goto main_exit;
if (c != '\n')
break;
}
str[0] = c;
if (scanf("%[^\n]s", str+1) == EOF)
break;
struct file_t* p = find_min(t, num);
fwrite(str, sizeof(char), strlen(str), p->f);
fflush(p->f);
}
main_exit:
for (int i = 0; i < num; i++) {
close_file(t + i);
}
printf("File saved");
return 0;
}
Terminal Session
$ ./a.out
Input files' names:
test file1.txt
test file2.txt
' '(NOTE: Space character inputted before pressing enter.)
Input text:
this is
stackoverflow
File saved
test file1.txt
this is
test file2.txt
stackoverflow
Note for breaking from the first loop (Files input). You need to enter space and then press enter (You can tweak around this).
Where are you updating the file_t->size when you write into a file?
You are calling this:
fwrite(str, sizeof(char), strlen(str), p->f);
But after that you should do p->size += strlen(str) to update its size, otherwise all file sizes are set to initial values, and hence all strings get written to a single file.
As for getting garbage data, try printing the string you are reading from scanf in the while loop.
You are using scanf to read characters until '\n', but you are not reading the '\n' itself. You need a fseek(stdin, 0, SEEK_END); in that loop as well.
Finally, why are you using syntax like this:
(files + i)->size
When you can call it more cleanly like this:
files[i].size
You code is really hard to read because of this.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I guess the real goal is to successfully mimic strace's output in regards to the read() syscall argument reading that it magically outputs.
And to make this as clear as possible:
It means that it displays them in a way that WILL NOT be interpreted by the console. For example, if a file contains \0004 or \0104 then it will DISPLAY \0004 and \0104 as LITERAL STRINGS (as if you wrote \\0004 or \\0104 instead) so it will NOT be interpreted by the console.
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char * argv[]) {
#include <stdio.h>
int shift(int times) {
#ifdef SHIFT_VERBOSE
int shift_mode_verbose = 1;
#else
int shift_mode_verbose = 0;
#endif
if (shift_mode_verbose == 1) {
printf("The following arguments were passed (before) to main(%i):\n", argc);
for(int i=1; i<argc; i++) {
printf("arg%i: %s\n", i, argv[i]);
}
printf("\n");
}
if (shift_mode_verbose == 1) {printf("shifting %i times\n", times);}
for(int t=1; t<times+1; ++t) { // we set this to 1 and respectively increase by 1, to avoid shifting argv[0] as it is holds the program name
for(int i=1; i<argc; ++i) { // we set this to 1 to avoid shifting argv[0] as it is holds the program name
if (shift_mode_verbose == 1) {printf("shift %i: arg%i: %s >", t, i, argv[i]);}
argv[i] = argv[i+1];
if (shift_mode_verbose == 1) {printf(" %s\n", argv[i]);}
}
--argc;
}
if (shift_mode_verbose == 1) {
printf("The following arguments were passed (after) to main(%i):\n", argc);
for(int i=1; i<argc; i++) {
printf("arg%i: %s\n", i, argv[i]);
}
printf("\n");
}
return 0;
}
int fshift(int times) { // a potentially faster version of shift()
times = argc < times ? argc : times;
argc = (argc) - times;
(argv) += times;
return 0;
}
if (argc < 3) {
printf("Usage: %s lines (-1 = all lines), files\n", argv[0]);
return 1;
}
int LINES_TO_READ = atoi(argv[1]);
shift(1);
for (ssize_t i = 1; i < argc; i++) {
const char *filename = argv[i];
printf("printing \"%s\"\n\n", filename);
int fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("cannot open \"%s\", returned %i\n", filename, fd);
return -1;
}
char unsigned ch;
size_t lines = 1;
// Read the file byte by byte
while (read(fd, &ch, 1) == 1) {
if (ch == '\n') {
printf("\\n");
} else if (ch == '\0') {
printf("\\0");
} else if (ch == '\r') {
printf("\\r");
} else if (ch == '\t') {
printf("\\t");
} else if(isprint(ch)) {
printf("%c", ch);
} else {
printf("\\%03o", ch);
}
// FILE *file = fopen(filename, "rb");
// char unsigned ch;
// size_t lines = 1, bytes=1;
// // Read the file byte by byte
// while (fread(&ch, 1, 1, file) == 1) {
// if (ch == '\n') {
// printf("\\n");
// } else if (ch == '\0') {
// printf("\\0");
// } else if (ch == '\r') {
// printf("\\r");
// } else if (ch == '\t') {
// printf("\\t");
// } else if(isprint(ch)) {
// printf("%c", ch);
// } else {
// printf("\\%03o", ch);
// }
if (ch == '\n') {
// Stop if we read 10 lines already
if (lines == LINES_TO_READ) {
break;
}
lines++;
}
}
if (close(fd) < 0) {
printf("cannot close \"%s\"\n", filename);
return -1;
}
printf("\n");
}
return 0;
}
Below is an example of what it should look like (apart from the ./catraw's output which is incorrect). (ELF header and instructions are acquired from nasm, strace, and cat.)
elf_header='\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\2\0\3\0\1\0\0\0T\200\4\0104\0\0\0\0\0\0\0\0\0\0\0004\0 \0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\200\4\10\0\200\4\10T\0\0\0T\0\0\0\5\0\0\0\0\20\0\0'
instructions='\263*f1\300f#\315\200'
printf "$elf_header$instructions" > return_42
chmod +x return_42
./return_42 # test run to see if it actually works
echo $? # echo return code of ./return_42
strace -s 4096 cat ./return_42
gcc catraw.c --static -o catraw
strace ./catraw -1 ./return_42
./catraw -1 ./return_42
[PROGRAM ] \177ELF\001\001\001\0\0\0\0\0\0\0\0\0\002\0\003\0\001\0\0\0T\200\004\0104\0\0\0\0\0\0\0\0\0\0\04\0 \0\001\0\0\0\0\0\0\0\001\0\0\0\0\0\0\0\0\200\004\010\0\200\004\010T\0\0\0T\0\0\0\005\0\0\0\0\020\0\0\263*f1\300f#\315\200
This is a bit unclear (not sure about all the assembly), but something like this:
#include <ctype.h>
// Format the character 'c' (which should be cast from char) into a
// string. Will use backslash-escaped form for non-printables.
void tostring(char *buf, size_t buf_max, unsigned int c)
{
if(isprint(c))
{
snprintf(buf, buf_max, "%c", c);
}
else
{
snprintf(buf, buf_max, "\\%3o", c);
}
}
This builds "\177" when called with c = 0177.
And yes, obviously the call to snprintf() in the first case can be factored out for printables.
So this is my second time adapting my code to fscanf to get what I want. I threw some comments next to the output. The main issue I am having is that the one null character or space is getting added into the array. I have tried to check for the null char and the space in the string variable and it does not catch it. I am a little stuck and would like to know why my code is letting that one null character through?
Part where it is slipping up "Pardon, O King," output:King -- 1; -- 1
so here it parses king a word and then ," goes through the strip function and becomes \0, then my check later down the road allows it through??
Input: a short story containing apostrophes and commas (the lion's rock. First, the lion woke up)
//Output: Every unique word that shows up with how many times it shows up.
//Lion -- 1
//s - 12
//lion -- 8
//tree -- 2
//-- 1 //this is the line that prints a null char?
//cub -- //3 it is not a space! I even check if it is \0 before entering
//it into the array. Any ideas (this is my 2nd time)?
//trying to rewrite my code around a fscanf function.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
//Remove non-alpha numeric characters
void strip_word(char* string)
{
char* string_two = calloc(80, sizeof(char));
int i;
int c = 0;
for(i = 0; i < strlen(string); i++)
{
if(isalnum(string[i]))
{
string_two[c] = string[i];
++c;
}
}
string_two[i] = '\0';
strcpy(string, string_two);
free(string_two);
}
//Parse through file
void file_parse(FILE* text_file, char*** word_array, int** count_array, int* total_count, int* unique_count)
{
int mem_Size = 8;
int is_unique = 1;
char** words = calloc(mem_Size, sizeof(char *)); //Dynamically allocate array of size 8 of char*
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int* counts = calloc(mem_Size, sizeof(int)); //Dynamically allocate array of size 8 of int
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char* string;
while('A')
{
is_unique = 1;
fscanf(text_file, " ,");
fscanf(text_file, " '");
while(fscanf(text_file, "%m[^,' \n]", &string) == 1) //%m length modifier
{
is_unique = 1;
strip_word(string);
if(string == '\0') continue; //if the string is empty move to next iteration
else
{
int i = 0;
++(*total_count);
for(i = 0; i < (*unique_count); i++)
{
if(strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if(is_unique)
{
++(*unique_count);
if((*unique_count) >= mem_Size)
{
mem_Size = mem_Size*2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if(words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count)-1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count)-1], string);
counts[(*unique_count) - 1] = 1;
}
}
free(string);
}
if(feof(text_file)) break;
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char* argv[])
{
if(argc < 2 || argc > 3) //Checks if too little or too many args
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE * text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
}
int total_count = 0;
int unique_count = 0;
char** word_array;
int* count_array;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
int i;
if(argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for(i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for(i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for(i = 0; i < unique_count; i++)
{
free(word_array[i]);
}
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
I'm not sure quite what's going wrong with your code. I'm working on macOS Sierra 10.12.3 with GCC 6.3.0, and the local fscanf() does not support the m modifier. Consequently, I modified the code to use a fixed size string of 80 bytes. When I do that (and only that), your program runs without obvious problem (certainly on the input "the lion's rock. First, the lion woke up").
I also think that the while ('A') loop (which should be written conventionally while (1) if it is used at all) is undesirable. I wrote a function read_word() which gets the next 'word', including skipping blanks, commas and quotes, and use that to control the loop. I left your memory allocation in file_parse() unchanged. I did get rid of the memory allocation in strip_word() (eventually — it worked OK as written too).
That left me with:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
static void strip_word(char *string)
{
char string_two[80];
int i;
int c = 0;
int len = strlen(string);
for (i = 0; i < len; i++)
{
if (isalnum(string[i]))
string_two[c++] = string[i];
}
string_two[c] = '\0';
strcpy(string, string_two);
}
static int read_word(FILE *fp, char *string)
{
if (fscanf(fp, " ,") == EOF ||
fscanf(fp, " '") == EOF ||
fscanf(fp, "%79[^,' \n]", string) != 1)
return EOF;
return 0;
}
static void file_parse(FILE *text_file, char ***word_array, int **count_array, int *total_count, int *unique_count)
{
int mem_Size = 8;
char **words = calloc(mem_Size, sizeof(char *));
if (words == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
int *counts = calloc(mem_Size, sizeof(int));
if (counts == NULL)
{
fprintf(stderr, "ERROR: calloc() failed!");
}
printf("Allocated initial parallel arrays of size 8.\n");
fflush(stdout);
char string[80];
while (read_word(text_file, string) != EOF)
{
int is_unique = 1;
printf("Got [%s]\n", string);
strip_word(string);
if (string[0] == '\0')
continue;
else
{
int i = 0;
++(*total_count);
for (i = 0; i < (*unique_count); i++)
{
if (strcmp(string, words[i]) == 0)
{
counts[i]++;
is_unique = 0;
break;
}
}
if (is_unique)
{
++(*unique_count);
if ((*unique_count) >= mem_Size)
{
mem_Size = mem_Size * 2;
words = realloc(words, mem_Size * sizeof(char *));
counts = realloc(counts, mem_Size * sizeof(int));
if (words == NULL || counts == NULL)
{
fprintf(stderr, "ERROR: realloc() failed!");
exit(EXIT_FAILURE);
}
printf("Re-allocated parallel arrays to be size %d.\n", mem_Size);
fflush(stdout);
}
words[(*unique_count) - 1] = calloc(strlen(string) + 1, sizeof(char));
strcpy(words[(*unique_count) - 1], string);
counts[(*unique_count) - 1] = 1;
}
}
}
printf("All done (successfully read %d words; %d unique words).\n", *total_count, *unique_count);
fflush(stdout);
*word_array = words;
*count_array = counts;
}
int main(int argc, char *argv[])
{
if (argc < 2 || argc > 3)
{
fprintf(stderr, "ERROR: Invalid Arguements\n");
return EXIT_FAILURE;
}
FILE *text_file = fopen(argv[1], "r");
if (text_file == NULL)
{
fprintf(stderr, "ERROR: Can't open file");
return EXIT_FAILURE;
}
int total_count = 0;
int unique_count = 0;
char **word_array = 0;
int *count_array = 0;
file_parse(text_file, &word_array, &count_array, &total_count, &unique_count);
fclose(text_file);
if (argv[2] == NULL)
{
printf("All words (and corresponding counts) are:\n");
fflush(stdout);
for (int i = 0; i < unique_count; i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
else
{
printf("First %d words (and corresponding counts) are:\n", atoi(argv[2]));
fflush(stdout);
for (int i = 0; i < atoi(argv[2]); i++)
{
printf("%s -- %d\n", word_array[i], count_array[i]);
fflush(stdout);
}
}
for (int i = 0; i < unique_count; i++)
free(word_array[i]);
free(word_array);
free(count_array);
return EXIT_SUCCESS;
}
When run on the data file:
the lion's rock. First, the lion woke up
the output was:
Allocated initial parallel arrays of size 8.
Got [the]
Got [lion]
Got [s]
Got [rock.]
Got [First]
Got [the]
Got [lion]
Got [woke]
Got [up]
All done (successfully read 9 words; 7 unique words).
All words (and corresponding counts) are:
the -- 2
lion -- 2
s -- 1
rock -- 1
First -- 1
woke -- 1
up -- 1
When the code was run on your text, including double quotes, like this:
$ echo '"Pardon, O King,"' | cw37 /dev/stdin
Allocated initial parallel arrays of size 8.
Got ["Pardon]
Got [O]
Got [King]
Got ["]
All done (successfully read 3 words; 3 unique words).
All words (and corresponding counts) are:
Pardon -- 1
O -- 1
King -- 1
$
It took a little finnagling of the code. If there isn't an alphabetic character, your code still counts it (because of subtle problems in strip_word()). That would need to be handled by checking strip_word() more carefully; you test if (string == '\0') which checks (belatedly) whether memory was allocated where you need if (string[0] == '\0') to test whether the string is empty.
Note that the code in read_word() would be confused into reporting EOF if there were two commas in a row, or an apostrophe followed by a comma (though it handles a comma followed by an apostrophe OK). Fixing that is fiddlier; you'd probably be better off using a loop with getc() to read a string of characters. You could even use that loop to strip non-alphabetic characters without needing a separate strip_word() function.
I am assuming you've not yet covered structures yet. If you had covered structures, you'd use an array of a structure such as struct Word { char *word; int count; }; and allocate the memory once, rather than needing two parallel arrays.
I'm trying to get my output to be
Starting Professor 1
Starting Professor 2
Starting Professor 3
...
but I never get "Starting Professor 1" when num_professors = 2. I thought making an array of ids would save the whole passing in of the address of id, but apparently not. There's about 70 other things I have to do for this project and having a roadblock on this simple thing (that probably takes a few seconds to fix) is quite frustrating to say the least. Thanks is greatly appreciated
void * professorFunc(void *p){
sem_wait(&workAssignment);
if(buffer == 0){
buffer++;
Professor *professor = (Professor*)p;
fprintf(stdout,"Starting Professor %d\n", *professor->id);
}
buffer = 0;
sem_post(&workAssignment);
pthread_exit(0);
}
int main(int argc, char ** argv){
//Semaphore intialization
buffer = 0;
if(sem_init(&workAssignment, 0, 1)){
printf("Could not initialize semaphore.\n");
exit(1);
}
//Creating threads
pthread_t professor[num_professors];
Professor *p;
int ids[num_professors];
int i;
p = malloc (sizeof (*p) * num_professors);
for(i = 0; i < num_professors; ++i){
ids[i] = i + 1;
p->id = &ids[i];
//printf("Id: %d\n", *p->id);
if(pthread_create(&professor[i], NULL, professorFunc, p) != 0){
perror("pthread_create");
exit(1);
}
//printf("yo I'm here after function now\n");
}
for(i = 0; i < num_professors; ++i){
if(pthread_join(professor[i], NULL) != 0){
perror("pthread_join");
exit(1);
}
}
free(p);
}
This line:
if(pthread_create(&professor[i], NULL, professorFunc, p) != 0){
should be:
if(pthread_create(&professor[i], NULL, professorFunc, &p[i]) != 0){