unable to clear string during/after a while loop in c - c

I have a code that scans all the files in a directory for targeted words, and prints them out into a new file. The problem right now is after the while loop reads a file and stores a variable into the string (ex. customer), if the next file being read does not have the targeted word, it still displays the result stored in the string from the previous file. My goal is to make it display "N/A" if the current file does not have the target word.
I have tried a few ways to clear the string at the end or beginning of the while loop, but none of them work most of them just gives me a coredump error. Running out of ideas, any help would be much appreciated!
Code (shortened for easier reading):
int main(int argc, char** argv)
{
char directory[100];
char buff[100];
char delims[] = " :=";
char* result = NULL;
char* customer;
char* device;
char* buffer;
int i = 0;
DIR* FD;
struct dirent* in_file;
int c = 0;
printf("Enter directory:");
scanf("%s",directory);
FILE* ft = fopen("workorderlist.csv", "w"); /* Open file to write to*/
if (ft == NULL)
{
puts("Cannot open target file");
exit(1);
}
fprintf (ft, "Work Order,Customer,Device,Test_Prog,Software,DUT_board_id,Corl box\n");
/* Open Directory */
if (NULL == (FD = opendir(directory)))
{
puts("Cannot open directory");
return 1;
}
while ((in_file = readdir(FD)))
{
if (!strcmp (in_file->d_name, "."))
{
continue;
}
if (!strcmp (in_file->d_name, ".."))
{
continue;
}
/* Open files to read from */
buffer = (char*)malloc(100);
sprintf(buffer, "%s/%s", directory, in_file->d_name);
size_t len = strlen(buffer);
if (len >= 4 && memcmp(buffer + len - 4, ".wor", 4) == 0) /* checks if file ends with .wor */
{
FILE* fs = fopen(buffer, "r"); /* open file to read */
if (fs == NULL)
{
puts("Cannot open source file");
return 1;
}
/* Scanning each file for targeted words: */
while (fgets(buff, 100, fs) != NULL)
{
result = strtok( buff, delims );
while (result != NULL)
{
if ((strcmp(result, "Customer") == 0))
{
result = strtok(NULL,delims);
customer = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(customer, result);
for (i = 0; i < strlen(customer) + 1; i++)
{
if (customer[i] == '\n')
{
break;
}
}
customer[i] = ' ';
}
if (strcmp(result, "device") == 0)
{
result = strtok(NULL, delims);
device = (char*)malloc((strlen(result) + 1) * sizeof(char));
strcpy(device, result);
for (i = 0; i < strlen(device) + 1; i++)
{
if(device[i] == '\n')
{
break;
}
}
device[i] = ' ';
}
result = strtok(NULL,delims);
}
}
if (customer == '\0')
{
customer = "N/A";
}
if (device == '\0')
{
device = "N/A";
}
fprintf(ft, "%s,%s,%s,%s,%s,%s,%s\n",
in_file->d_name, customer, device, testprog,
software, dutboardid, corlbox);
printf(in_file->d_name);
printf("\n");
fclose (fs) ;
c++;
}
}
printf("Total Workorders Found: %d (Info saved to workorderlist.csv)\n", c);
fclose(ft);
return 0;
}

First at all, customer/device are strings. You should not be doing == for it comparison. You can, for example, compare the first char of the string: device[0] == '\0';
You should do string initialization before the loop starts.
You can achieve this by using strcpy with a known value or any other string manipulation function. The value that you use to initialize the string before the loop is the one you gonna test with strcmp or similar later.
Is like with ints or any other C data type, but you need manipulation functions instead.
By the way, haven't you posted your read file loop in a question here too?
Hope this helps.

Related

Having trouble reading from text file into a struct array

I recently started at university with C programming (beginner course), and now we are doing our final examination which is about a patients' database.
I'm required to read data from a text file to a struct array (size 10000). The file contains 2 string arrays (personal identification string (10 numbers seperated by a '-') and name string), 1 int array containing photo references and 1 integer containing the amount of photo references per patient. I have tried fscanf but the program just hangs whenever i try to read, when i use fgets, it reads the whole line and stores the integers from the photo reference array into my name array (middle one). I am wondering how I should go about doing this, I've spent days trying to figure out a solution but nothing seems to work. This is what my text file looks like:
123456-1234 Name Name [1, 2, 3, 4]
234567-2345 Name2 Name2 [1, 2]
345678-3456 Name3 Name3 []
And this is my write_to_file function which writes to the file when the program exits:
void write_to_file(Patient reg[], int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","w");
if(*pNr_of_patients>0){
int i,j;
for(i=0;i<*pNr_of_patients;i++){
fprintf(fp,"%s\t%s\t[",reg[i].pers_nr,reg[i].name);
for(j=0;j<reg[i].nr_of_ref-1;j++){
fprintf(fp,"%d, ",reg[i].photo_ref[j]);
}
if(reg[i].photo_ref[j]==0){
fprintf(fp,"]");
}else{
fprintf(fp,"%d]",reg[i].photo_ref[j]);
}
fprintf(fp,"\n");
}
fclose(fp);
}
}
This is my read_from_file function, it's missing code for reading the int array values at the end:
Edit: I added a for loop to remove the characters starting at "[" from the name string, now i just need to know how to read the array values at the end into the struct's photo reference array.
void read_from_file(Patient reg[],int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","r");
if(fp!=NULL){
reg[*pNr_of_patients].nr_of_ref=0;
int i=0, pos;
while(fgets(reg[*pNr_of_patients].pers_nr,13,fp)!=NULL){
reg[*pNr_of_patients].pers_nr[strlen(reg[*pNr_of_patients].pers_nr)-1]='\0';
fgets(reg[*pNr_of_patients].name,31,fp);
reg[*pNr_of_patients].name[strlen(reg[*pNr_of_patients].name)-1]='\0';
for(pos=0;pos<30;pos++){
if(reg[*pNr_of_patients].name[pos]=='['){
reg[*pNr_of_patients].name[pos]='\0';
}
}
(*pNr_of_patients)++;
}
fclose(fp);
}else{
printf("File does not exist\n");
}
}
This is what my Patient struct looks like:
struct patient{
char pers_nr[12], name[30];
int photo_ref[10], nr_of_ref;
};
typedef struct patient Patient;
Calling read_from_file in main:
int main(void){
Patient patient_register[10000];
int nr_of_patients=0;
read_from_file(patient_register,&nr_of_patients);
database_management(patient_register,&nr_of_patients); //this is where I fill all the data into the array before writing to the file at the end
write_to_file(patient_register,&nr_of_patients);
return 0;
}
I think that scanning input is one of the hardest in C. That's why libraries like cs50 exists, to ease up reading input for new C users. Anyway, I constructed my solution, but I redesigned your function.
The first solution reads a single Patient from a line. It does not use sscanf the only standard call that set's errno is to strtol, which is used to convert up numbers.
The second function uses sscanf and some crazy format string construction to stay safe of buffer overflow.
It all brings down at to how the input stream is constructed and how much you trust it.
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
struct patient{
char pers_nr[12];
char name[30];
int photo_ref[10];
size_t nr_of_ref;
};
typedef struct patient Patient;
int patient_read_from_line_1(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
// check the first 12 characters ----------
// first 6 chars must be numbers
for (int i = 0; i < 6; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a single '-'
if (line[6] != '-') {
return -__LINE__;
}
// followed by 4 numbers
for (int i = 7; i < 7 + 4; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a space
if (line[7 + 4] != ' ') {
return -__LINE__;
}
// read up first field ---------------------
// cool first field checks out
memcpy(p->pers_nr, line, 11);
p->pers_nr[11] = '\0';
line += 12;
// let's omit spaces
while (line[0] == ' ') {
line++;
}
// read up second field --------------------------
// now we should read a two strings separated by a space
// so we should read up until a second space
if (!isalpha(*line)) {
return -__LINE__;
}
const char *pnt_first_space = strchr(line, ' ');
if (pnt_first_space == NULL) {
return -__LINE__;
}
const char *pnt_another_space = strchr(pnt_first_space + 1, ' ');
if (pnt_another_space == NULL) {
return -__LINE__;
}
const size_t name_to_read_length = pnt_another_space - line;
if (name_to_read_length > sizeof(p->name)) {
return -__LINE__;
}
memcpy(p->name, line, name_to_read_length);
p->name[name_to_read_length] = '\0';
// buh two fields done, now the array
line += name_to_read_length;
// let's omit the spaces
while (line[0] == ' ') {
line++;
}
// read up array -----------------------------------
// array
if (line[0] != '[') {
return -__LINE__;
}
line++;
for (size_t numscnt = 0;; ++numscnt) {
if (numscnt >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
char *pnt;
errno = 0;
long num = strtol(line, &pnt, 10);
if (errno) {
return -__LINE__;
}
if (!(INT_MIN < num && num < INT_MAX)) {
return -__LINE__;
}
p->photo_ref[numscnt] = num;
line = pnt;
// omit spaces
while (*line == ' ') line++;
// now we should get a comma
if (line[0] != ',') {
// if don't get a comma, we need to get a ]
if (line[0] == ']') {
// cool
++line;
// but remember to save the count
p->nr_of_ref = numscnt + 1;
// cool
break;
}
return -__LINE__;
}
++line;
// omit spaces
while (*line == ' ') line++;
// start again
}
// this needs to be end of line or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
// ok, ok, ok, let's use sscanf
int patient_read_from_line_2(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
int ret;
int pos;
// read up first fiedl and half of the second ------------------
ret = sscanf(line, "%12s %30[^ ] %n", p->pers_nr, p->name, &pos);
if (ret != 2) {
return -__LINE__;
}
line += pos;
// read up another half of the second field -------------------
const size_t cur_name_len = strlen(p->name);
p->name[cur_name_len] = ' ';
char tmp[20];
ret = snprintf(tmp, 20, "%%%d[^ ] [%%n", (int)(sizeof(p->name) - cur_name_len - 1));
if (ret < 0) {
return -__LINE__;
}
ret = sscanf(line, tmp, &p->name[cur_name_len + 1], &pos);
if (ret != 1) {
return -__LINE__;
}
line += pos;
// read up array *sigh* -------------------------------------------
for (p->nr_of_ref = 0;; ++p->nr_of_ref) {
if (p->nr_of_ref >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
ret = sscanf(line, " %d%1s%n", &p->photo_ref[p->nr_of_ref], tmp, &pos);
if (ret == 0) {
// hm...
if (line[0] == ']') {
// ach all ok, empty numbers list;
line++;
p->nr_of_ref++;
break;
}
return -__LINE__;
}
if (ret != 2) {
return -__LINE__;
}
line += pos;
if (tmp[0] != ',') {
if (tmp[0] == ']') {
// whoa! success
p->nr_of_ref++;
// cool
break;
}
return -__LINE__;
}
}
// so what's left? - EOF or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
long patient_read_from_file(FILE *fp, Patient patients[], size_t patients_len)
{
size_t patients_cnt = 0;
char line[256];
// for each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
const int ret = patient_read_from_line_2(line, &patients[patients_cnt]);
if (ret < 0) {
// hanle reading error
return ret;
}
patients_cnt++;
if (patients_cnt > patients_len) {
// no more memory in patients left
return -__LINE__;
}
}
return patients_cnt;
}
void patient_fprintln(FILE *f, const Patient *p)
{
fprintf(f, "%s %s [", p->pers_nr, p->name);
for (size_t i = 0; i < p->nr_of_ref; ++i) {
fprintf(f, "%d", p->photo_ref[i]);
if (i + 1 != p->nr_of_ref) {
fprintf(f, ",");
}
}
fprintf(f, "]\n");
}
int main()
{
FILE *fp;
fp = stdin; // fopen("file.txt","r");
if (fp == NULL) {
return -__LINE__;
}
Patient patients[3];
const long patients_cnt = patient_read_from_file(fp, patients, sizeof(patients)/sizeof(*patients));
if (patients_cnt < 0) {
fprintf(stderr, "patient_read_from_file error %ld\n", patients_cnt);
return patients_cnt;
}
fclose(fp);
printf("Readed %d patients:\n", patients_cnt);
for (size_t i = 0; i < patients_cnt; ++i) {
patient_fprintln(stdout, &patients[i]);
}
return 0;
}
Live version available at onlinedbg.
This can be simplified for 100%. This has bugs for 100%. It is just to show what methods (strtol, memcpy, sscanf, isdigit, isalpha) are sometimes used by people to read from input. Also I specify length modifier to scanf (sscanf(..., "%12s") to handle overflows (hopefully). Try to always check return values from scanf and other standard functions (maybe checking snprintf return value is a little too much, but hey, let's be consistent). Be vary, that on some platforms the %n scanf modifier happens not to work. Also this can be build up to use dynamic allocation using malloc, realloc and free, both on line reading (basically it is equal to writing custom version of GNU getline), reading strings from input, reading int's array from input and dynamic allocations of patients.
This was meant as a comment but got too long, so I type it here.
read_from_file() appears overly complex. You might consider revisiting fscanf, reading the photo references as a whole string and then parsing into integers which you can assign to the photo_ref array. (While the code below might compile, I haven't verified that it works. It's just an idea of how one might proceed.)
void read_from_file (Patient reg[], int *pNr_of_patients)
{
FILE *fp;
fp = fopen ("file.txt", "r");
if (fp != NULL)
{
int n;
int i = 0; // position in photo_ref
char refs[30];
*pNr_of_patients = 0;
while (EOF !=
(n =
fscanf (fp, "%s %[^[]%[^]]]", reg[*pNr_of_patients].pers_nr,
reg[*pNr_of_patients].name, refs)))
{
// btw, reg[*pNr_of_patients].name may contain terminating blanks. right trim it. that's easy enough.
if (n > 2)
{ /* found photo refs.Now split the string into integers */
char *s = refs + 1; //skip '['
char *p;
while (*s && i<10){ // scan for the integers, 10 of them
while (*s && *s == ' ')
s++; // skip blanks
p = s; // mark start of number
while (*p && *p != ',')
p++;
if (*p == ',')
*p = 0;
reg[*pNr_of_patients].photo_ref[i++] = atoi (s); //tip: use strtol(3), verify that `i' isnt larger than size of the array
s = p + 1; // skip ','. Must Do: verify that `s' hasnt yet moved past the end of `ref'!!
}
}
(*pNr_of_patients)++;
}
fclose (fp);
}
else
{
printf ("File does not exist\n");
}
}
There are some good answers already, but most of them try to use a single method to parse all elements of the line. I would read whole lines into a buffer first, then use sscanf() to parse the patient number and name, but use strtok() to split the array into its individual components:
void read_from_file(Patient reg[], int *pNr_of_patients) {
FILE *fp = fopen("file.txt", "r");
if (!fp) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
*pNr_of_patients = 0;
return;
}
char line[1024];
int i = 0;
while (fgets(line, sizeof line, fp)) {
int offset = 0;
int refs = 0;
sscanf(line, "%11s %29[^[] [%n", &reg[i].pers_nr, &reg[i].name, &offset);
for (char *tok = strtok(line + offset, ","); tok && refs < 10; tok = strtok(NULL, ",")) {
if (*tok != ']')
reg[i].photo_ref[refs++] = atoi(tok);
}
reg[i].nr_of_ref = refs;
i++;
}
*pNr_of_patients = i;
}
Divide and Conquer
Break this down into steps. Make a function that populates 1 Patient.
The below is untested code. Consider it a starting point. The deign goal is to make a function that reads 1 line into 1 Patient.
Read in 1 entire line
// return 1: success, 0: failure EOF:end-of-file
int read_once_from_file(FILE *stream, Patient *pat_ptr) {
Patient pat = { 0 };
char buffer[100 + 30*13];
if (fgets(buffer, sizeof buffer, stream) == NULL) {
return EOF;
}
Parse the first part. Use "%n" which records the parsing offset. Use width limits on string input.
int n = 0;
if (sscanf(buffer, " %11[^\t] %29[^\t] [ %n", pat.pers_nr, pat.name) != 2) {
return 0; // improper formatted input
}
char *p = buffer + n;
Now look for ']' and photo_ref
if (*p != ']') {
for (pat.nr_of_ref=0; ; pat.nr_of_ref++) {
if (sscanf(p, "%d %n", &pat.photo_ref[i], &n) != 1) {
return 0; // improper formatted input
}
p += n;
if (*p == ']') {
pat.nr_of_ref++;
break;
}
if (*p != ',' || pat.nr_of_ref + 1 == 10) {
return 0; // improper formatted input
}
p++;
}
}
Save result
*pat_ptr = pat;
return 1;
}
Call read_once_from_file() as needed
void read_from_file(Patient reg[],int *pNr_of_patients){
*pNr_of_patients = 0;
FILE *fp = fopen("file.txt","r");
if(fp){
for (int i = 0; i<10000; i++) {
int count = read_once_from_file(fp, &reg[i]);
if (count == EOF) {
break;
}
if (count != 1) {
// error
fprintf(stderr, "Input error\n");
break;
}
}
*pNr_of_patients = i;
fclose(fp);
}
}

How to edit .csv files in C

I'm new at programming, and I need help in my C project. I have to search for a city, confirm it exists in the first file (city.csv), and take its id from there. Then I have to match that id with the corresponding one in the second file (meteo.csv), and then edit its weather information, that is in that second file. However, I don't know how I can take the city id from the first file, and then how to edit the second file after obtaining all the new weather informations. Here is the code:
void addInfo() {
FILE * fp;
char id_city[100];
char city[100];
char humidity[100];
char temp_max[100];
char temp_min[100];
char pressure[100];
char date[100];
printf("Name of the city: ");
scanf("%s", city);
// I think it's here that I have to write the code for take the city's id from the first file
if (id_city != NULL) {
printf("Maximun temperature: ");
scanf("%s", temp_max);
printf("Minimun temperature: ");
scanf("%s", temp_min);
printf("Humidity: ");
scanf("%s", humidity);
printf("Pressure: ");
scanf("%s", pressure);
printf("Date, in the format YYYY-MM-DD: ");
scanf("%s", date);
fp = fopen ("meteo.csv", "a");
fprintf(fp, "%s, %s, %s, %s, %s \n", temp_max, temp_min, humidity, pressure, date); //I think there's something wrong here too...
fclose(fp);
printf("Information edited successfully");
}
The file city.csv has 152 lines and 4 columns:
(id_city,city,county,district)
such as
(56,Lisbon,Lisbon,Lisbon)
The file meteo.csv has 152 lines and 7 columns:
(id_meteo_city,id_city,temp_max,temp_min,humidity,pressure,date)
such as
(56,56,14,5,62,1025,2018-02-12)
The first thing I would do is encapsulate the data in a struct, that makes it
easier to map a line of a CSV file into an object representing a line.
If both files city.csv and meteo.csv have different columns, I'd create a
different struct for each file. If both files have the same columns, you could
use the struct. I assume that both files are different and that city has the
format meteo_id,city_id,name.
typedef struct city_t {
int meteo_id;
int city_id;
char name[100]; // no city should have
// longer than 100 chars
} city_t;
typedef struct meteo_t {
int meteo_id;
int city_id;
int tempt_max;
int tempt_mix;
double humidity;
double preassure;
char date[11];
} meteo_t;
Let's assume that both files are well formatted, otherwise you would have to
write code that checks for errors and handles them, that would be the next step
in the exercise, so I'm going to write only the basic version with basic error
recognition.
#include <stdio.h>
#include <string.h>
#include <errno.h>
// takes 2 params, the filename and a pointer
// to size_t where the number of cities is stored
city_t *read_cities(const char *filename, size_t *len)
{
if(filename == NULL || len == NULL)
return NULL;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
return NULL;
}
city_t *arr = NULL, *tmp;
*len = 0;
// assuming that no line will be longer than 1023 chars long
char line[1024];
while(fgets(line, sizeof line, fp))
{
tmp = realloc(arr, (*len + 1) * sizeof *arr);
if(tmp == NULL)
{
fprintf(stderr, "could not parse the whole file %s\n", filename);
// returning all parsed cities so far
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
arr = tmp;
// %99[^\n] is to read up to 99 characters until the end of the line
if(sscanf(line, "%d,%d,%99[^\n]", &(arr[*len].meteo_id),
&(arr[*len].city_id), arr[*len].name) != 3)
{
fprintf(stderr, "Invalid line format (skipping line):\n%s\n", line);
// skip this line, and decrement *len
(*len)--;
continue;
}
// incrementing only when parsing of line was OK
(*len)++;
}
fclose(fp);
// file is empty or
// all lines have wrong format
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
void print_cities(city_t *cities, size_t len, FILE *fp)
{
if(cities == NULL || fp == NULL)
return;
for(size_t i = 0; i < len; ++i)
fprintf(fp, "%d,%d,%s\n", cities[i].meteo_id, cities[i].citiy_id,
cities[i].name);
}
Now I've written the read and write functions for the file citiy.csv assuming the
format meteo_id;city_id;name. The print_cities allows you to print the CSV
content on the screen (passing stdout as the last argument) or to a file
(passing a FILE object as the last argument).
You can use these functions as templates for reading and writing meteo.csv, the
idea is the same.
You can use these function as follows:
int main(void)
{
size_t cities_len;
city_t *cities = read_cities("city.csv", &cities_len);
// error
if(cities == NULL)
return 1;
do_something_with_cities(cities, cities_len);
// update csv
FILE *fp = fopen("city.csv", "w");
if(fp == NULL)
{
fprintf(stderr, "Could not open city.csv for reading: %s\n",
strerror(errno));
free(cities);
return 1;
}
print_cities(cities, cities_len, fp);
fclose(fp);
free(cities);
return 0;
}
Now for your exercise: write a similar function that parses meteo.csv (using
my function as a template shouldn't be that difficult) and parse both files. Now
that you've got them in memory, it's easy to manipulate the data (insert,
update, delete). Then write the files like I did in the example and that's it.
One last hint: how to search for a city:
// returns the index in the array or -1 on error or when not found
int search_for_city_by_name(city_t *cities, size_t len, const char *name)
{
if(cities == NULL || name == NULL)
return -1;
for(size_t i = 0; i < len; ++i)
if(strcmp(name, cities[i].name) == 0)
return i;
// not found
return -1;
}
Now I have given you almost all parts of the assignment, all you have to do is
stick them together and write the same functions for the meteo.csv file.
To edit one field:
void _ERR(char a) {
if (a == "f") printf("\n\tError File !!\n\n");
if (a == "m") printf("\n\tError Memory !!\n\n");
exit(1); }
char* stmm(const char* src) {
char* dst = malloc(strlen(src) + 1);
if (dst == NULL) return NULL;
strcpy(dst, src);
return dst; }
const char* getfield(char* line, int num) {
const char* tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return NULL; }
void edit_file(char* FName, char* NewValue, int row, int col) {
int i, r = 0, c;
char line[1024];
FILE* fr, * fw;
fr = fopen(FName, "r");
fw = fopen(FName, "r+");
if (fr == NULL|| fw == NULL) _ERR("f");
while (fgets(line, 1024, fr))
{
char* tmp = stmm(line);
if (tmp == NULL) _ERR("m");
for (i = 0, c = 1; i < strlen(tmp); i++) {
if (tmp[i] == 44) c++;
}
for (i = 0; i < c; i++) {
if (r == row && i+1 == col) {
fprintf(fw,"%s", NewValue);
} else {
free(tmp);
tmp = stmm(line);
if (tmp == NULL) _ERR("m");
fprintf(fw,"%s", getfield(tmp, i + 1));
}
(i < c - 1) ? fprintf(fw,",") : fprintf(fw,"\n");
}
free(tmp);
r++;
}
fclose(fr);
fclose(fw); }
edit_file(".\FileName.csv","NewValue",Row,Column);

Problems with Implementing Mutex in C

I am implementing a program to read filenames with room names, connections, and room types. Ex:
ROOM NAME: chicago
CONNECTION 1: sarasota
CONNECTION 2: columbus
CONNECTION 3: miami
CONNECTION 4: boston
ROOM TYPE: END_ROOM
The program is designed to show the user the room they are starting in, ask for input from the user, check to see if the input is the end room or another connection. If it is another connection, the prompt will display again. If the user reaches the end room, the game ends. However, I am required to implement a mutex that, if the user enters "time", a file is created, the time is written to it, and it is displayed on the screen. After that, prompt is displayed again for the user. My code works fine when the mutex implementation is stripped out. Here is what I am seeing when the mutex is in the code. I appear to reach the time function and the program seems to recognize incorrect rooms but when a "correct" room is entered the cursor just returns and does nothing. Any clue on why I am getting this behavior only on mutex implementation?
The program is below, do you see anything that would cause this issue?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
//Read in the room data
//Perform the stat() function on the rooms directory in the same directory
//and open the file with the most recent st_mtime component of the returned stat struct
#define NUM_USED_ROOMS 7
char usedRooms[NUM_USED_ROOMS][256];
char roomFilePath[75];
char timeFilePath[75];
char* connections[NUM_USED_ROOMS];
int end = 0;
char input[20];
int numberOfSteps = -1;
char *steps[75];
int file_descriptor;
char timeText[100];
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
void * getTime() {
pthread_mutex_lock(&myMutex);
printf("You asked for the time!\n");
pthread_mutex_unlock(&myMutex);
return NULL;
}
//Check if the room number passed is the end
void isEnd(int roomNumber, char *dirName){
//Counting the number of steps for the end of the program
numberOfSteps++;
steps[numberOfSteps - 1] = usedRooms[roomNumber];
//Getting the name of the proper file
sprintf(roomFilePath, "%s/%s", dirName, usedRooms[roomNumber]);
char substring[20];
int numLine = 1;
FILE * filePointer;
filePointer = fopen(roomFilePath, "r");
int lines = 0;
char buffer[256];
while(fgets(buffer, sizeof(buffer), filePointer) != NULL){
lines = lines + 1;
}
fclose(filePointer);
//Opening the file to read to see if it is the end. If it is, assign end = 1.
filePointer = fopen(roomFilePath, "r");
while(fgets(buffer, sizeof(buffer), filePointer) != NULL) {
if (numLine == lines)
{
strcpy(substring, buffer+11);
}
numLine = numLine + 1;
}
if(strstr(substring, "END" ) != NULL) {
end = 1;
}
}
//Get the user input
void getInput() {
fflush(stdin);
scanf("%s", input);
fflush(stdin);
fflush(stdout);
}
void readFile(char *dirName){
DIR *dir;
struct dirent *ent;
int i = 0;
if ((dir = opendir (dirName)) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
if (strncmp(ent->d_name,".",sizeof(ent->d_name)) == 0 ||
strncmp(ent->d_name,"..",sizeof(ent->d_name)) == 0 )
{
} else {
strcpy(usedRooms[i],ent->d_name);
i++;
}
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
}
}
void playGame(int roomNumber, char * dirName){
int i;
printf("usedRooms is %s", usedRooms[roomNumber]);
pthread_mutex_lock(&myMutex);
pthread_t secondThread;
pthread_create(&secondThread, NULL, getTime,NULL);
//Check if the user guessed the end room
if(end == 1) {
fflush(stdout);
return;
}
else{
isEnd(roomNumber, dirName);
if (end == 1)
{
fflush(stdout);
return;
}
}
int move = 1;
while(move == 1) {
//Open the file of the path of the room passed in
sprintf(roomFilePath, "%s/%s", dirName, usedRooms[roomNumber]);
FILE * filePointer;
filePointer = fopen(roomFilePath, "r");
int fileLines = 0;
char line[256];
//Count the lines in the file so I know how to traverse it
while(fgets(line, sizeof line, filePointer) != NULL) {
fileLines = fileLines + 1;
}
fclose(filePointer);
filePointer = fopen(roomFilePath, "r");
int currentLine = 0;
//Create the array for the rooms that can be navigated to
char gameRooms[6][30];
while(fgets(line, sizeof line, filePointer) != NULL) {
char *pos;
if((pos = strchr(line, '\n')) != NULL)
{
*pos = '\0';
}
//Print out the current room
if (currentLine == 0)
{
char substring[20];
strcpy(substring, line+11);
printf("CURRENT LOCATION: %s\n", substring);
}
//Print the first connection from this room
else if (currentLine == 1){
printf("POSSIBLE CONNECTIONS: ");
fflush(stdout);
char substring[20];
strcpy(substring, line+14);
printf("%s", substring);
fflush(stdout);
strcpy(gameRooms[currentLine - 1], substring);
}
//Print the rest of the connections, comma separated
else if (currentLine > 1 && currentLine < fileLines - 1) {
printf(",");
fflush(stdout);
char substring[20];
strcpy(substring, line+14);
printf("%s", substring);
fflush(stdout);
strcpy(gameRooms[currentLine - 1], substring);
//gameRooms[currentLine - 1] = substring;
}
else {
printf(".");
fflush(stdout);
}
currentLine = currentLine + 1;
}
fclose(filePointer);
printf("\nWHERE TO?>");
//Get the user input
getInput();
if(strcmp("time", input) == 0){
pthread_mutex_unlock(&myMutex);
pthread_join(secondThread, NULL);
pthread_mutex_lock(&myMutex);
pthread_create(&secondThread, NULL, getTime, NULL);
}
//Loop through the file to see if the input matches a room name in the array
for(i = 0; i < fileLines - 2; i++) {
if (strcmp(gameRooms[i], input) == 0)
{
int j;
for(j = 0; j < NUM_USED_ROOMS; j++) {
//If there is a match, play the game starting at the room entered
if(strcmp(usedRooms[j], input) == 0) {
printf("THE STRINGS MATCH usedRooms is %s "
"and input is %s\n",usedRooms[j],input);
playGame(j,dirName);
}
}
move = 0;
}
}
//If the user's input didn't match the list of rooms
if (move == 1) {
printf("\nHUH? I DON'T UNDERSTAND THAT ROOM. TRY AGAIN. \n\n");
fflush(stdout);
fflush(stdin);
}
}
}
int main() {
int newestDirTime = -1; // Modified timestamp of newest subdir examined
char targetDirPrefix[32] = "walterer.rooms."; // Prefix we're looking for
char newestDirName[256]; // Holds the name of the newest dir that contains prefix
memset(newestDirName, '\0', sizeof(newestDirName));
DIR* dirToCheck; // Holds the directory we're starting in
struct dirent *fileInDir; // Holds the current subdir of the starting dir
struct stat dirAttributes; // Holds information we've gained about subdir
dirToCheck = opendir("."); // Open up the directory this program was run in
if (dirToCheck > 0) // Make sure the current directory could be opened
{
while ((fileInDir = readdir(dirToCheck)) != NULL) // Check each entry in dir
{
if (strstr(fileInDir->d_name, targetDirPrefix) != NULL) // If entry has prefix
{
//printf("Found the prefex: %s\n", fileInDir->d_name);
stat(fileInDir->d_name, &dirAttributes); // Get attributes of the entry
if ((int)dirAttributes.st_mtime > newestDirTime) // If this time is bigger
{
newestDirTime = (int)dirAttributes.st_mtime;
memset(newestDirName, '\0', sizeof(newestDirName));
strcpy(newestDirName, fileInDir->d_name);
}
}
}
}
closedir(dirToCheck);
//Read the file at the specified directory
readFile(newestDirName);
int start;
int i;
for (i = 0; i < NUM_USED_ROOMS; i++)
{
memset(roomFilePath, '\0', sizeof(roomFilePath));
sprintf(roomFilePath,"%s/%s", newestDirName, usedRooms[i]);
char output[256];
memset(output, '\0', sizeof(output));
char* token;
char* connectRoom;
FILE *filePointer;
filePointer = fopen(roomFilePath,"r");
//Find the starting room and pass that into the playGame function
if (filePointer == NULL)
{
printf("Unable to open file!\n");
} else {
while(!feof(filePointer)) {
fgets(output, 256, filePointer);
token = strtok(output, "\n");
if(strstr(token, "START") != NULL){
start = i;
}
}
fclose(filePointer);
}
}
//Play the game with the starting room at the directory name
playGame(start, newestDirName);
printf("YOU HAVE FOUND THE END ROOM. CONGRATULATIONS!\n");
printf("YOU TOOK %d STEPS. YOUR PATH TO VICTORY WAS: \n", numberOfSteps);
for(i = 0; i < numberOfSteps; i++){
printf("%s\n", steps[i]);
}
return 0;
}
As a rule, you should lock a mutex for the shortest time possible. Violation of this rule is a severe anti-pattern called "asking for trouble" which is usually punished with deadlocks. Remove that anti-pattern from your code.

C program directory scanner only working for one directory

my code here suppose to scan a directory for all the .wor files, and extract info from each file to a new .csv file.
However, it only works for one directory ( the directory i am writing this c program in, which has 2 .wor files and a few text files). When I try to enter a different directory with just 3 .wor files in it, it gives me a the error message i assigned it to display when the file==NULL; from this line:
if (fs == NULL)
{
puts ("Cannot open source file");
return 1;
}
if i remove this if statement all together, it displays a "bus error(Core dump)" error.
I am not sure what is going on, is there something i am not doing here?
Here is my complete code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char **argv) {
char directory[100];
char buff[100];
char delims[] = " :=";
char *result = NULL;
char *customer;
char *device;
char *testprog;
char *software;
char *dutboardid;
char *corlbox;
int i=0;
DIR * FD;
struct dirent* in_file;
int c=0;
/*printf ("Enter directory:");
scanf ("%s",directory);*/
FILE * ft = fopen ( "workorderlist.csv", "w" ) ; /* Open file to write to*/
if ( ft == NULL )
{
puts ( "Cannot open target file" ) ;
exit( 1 ) ;
}
fprintf (ft, "Work Order,Customer,Device,Test_Prog,Software,DUT_board_id,Corl box\n");
/* Open Directory*/
if (NULL == (FD = opendir ("/home/iselabs/dwang/pinscale/workorder/practice")))
{
puts ("Cannot open directory");
return 1;
}
while ((in_file = readdir(FD)))
{
if (!strcmp (in_file->d_name, "."))
continue;
if (!strcmp (in_file->d_name, ".."))
continue;
/* Open files to read from */
size_t len = strlen(in_file->d_name);
if (len >= 4 && memcmp(in_file->d_name + len - 4, ".wor", 4) == 0) /* checks if file ends with .wor */
{
FILE * fs = fopen(in_file->d_name, "r"); /* open file to read */
if (fs == NULL)
{
puts ("Cannot open source file");
return 1;
}
/* Scanning each file for targeted words: */
while( fgets(buff, 100,fs) != NULL )
{
result = strtok( buff, delims );
while(result != NULL){
if((strcmp(result,"Customer")==0)){
result = strtok(NULL,delims);
customer = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(customer, result);
for(i=0;i<strlen(customer)+1;i++){ if(customer[i] == '\n') break; }
customer[i] = ' ';
}
if((strcmp(result,"name")==0)){
result = strtok(NULL,delims);
customer = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(customer, result);
for(i=0;i<strlen(customer)+1;i++){ if(customer[i] == '\n') break; }
customer[i] = ' ';
}
if(strcmp(result,"device")==0){
result = strtok(NULL,delims);
device = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(device, result);
for(i=0;i<strlen(device)+1;i++){ if(device[i] == '\n') break; }
device[i] = ' ';
}
if(strcmp(result,"test_prog")==0){
result = strtok(NULL,delims);
testprog = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(testprog, result);
for(i=0;i<strlen(testprog)+1;i++){ if(testprog[i] == '\n') break; }
testprog[i] = ' ';
}
if(strcmp(result,"Rev")==0 || strcmp(result,"use")==0){
result = strtok(NULL,delims);
software = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(software, result);
for(i=0;i<strlen(software)+1;i++){ if(software[i] == '\n') break; }
software[i] = ' ';
}
if(strcmp(result,"rev")==0){
result = strtok(NULL,delims);
software = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(software, result);
for(i=0;i<strlen(software)+1;i++){ if(software[i] == '\n') break; }
software[i] = ' ';
}
if(strcmp(result,"DUT_board_id")==0){
result = strtok(NULL,delims);
dutboardid = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(dutboardid, result);
for(i=0;i<strlen(dutboardid)+1;i++){ if(dutboardid[i] == '\n') break; }
dutboardid[i] = ' ';
}
else if (strcmp(result,"DUT_board_id")==1){
corlbox = "N/A";
}
if(strcmp(result,"box")==0){
result = strtok(NULL,delims);
corlbox = (char*)malloc((strlen(result)+1)*sizeof(char));
strcpy(corlbox, result);
for(i=0;i<strlen(corlbox)+1;i++){ if(corlbox[i] == '\n') break; }
corlbox[i] = ' ';
}
else if (strcmp(result,"box")==1){
corlbox = "N/A";
}
result = strtok(NULL,delims);
}
}
fprintf (ft, "%s,%s,%s,%s,%s,%s,%s\n", in_file->d_name, customer, device, testprog, software, dutboardid, corlbox);
printf(in_file->d_name);
printf("\n");
fclose (fs) ;
c++;
}
}
printf("Total Workorders Found: %d \n", c);
fclose ( ft ) ;
return 0;
}
You need to combine the directory name with the filename that you're trying to open, or possibly call chdir() before trying to open the file.
You could malloc a buffer based on the combined length of the directory name, plus filename, plus an extra character for the path separator (/) and one more for the null terminator.
Then use sprintf( buffer, "%s/%s", directory, filename); to get the full path to the file, and then try to open that.
Add a printf to your error string, with the name of the file you're trying to open, to help with debugging.

C: Too many files open

This code opens a directory, and for every file in the directory it loops through every line of data inside the file, and then parses it to do some calculations and outputs the resulting data into a new file.
The problem is that I can only output a maximum of around 1021 files. I'm closing all of the fopens after outputting all the data, so I'm not sure what I'm doing wrong.
Shouldn't fclose() be closing the open files therefore this not happening?
int main(int argc, char *argv[])
{
//sample data values
double lat;
double lon;
double convergence;
double pt_scale;
int zone = 54;
double major_axis = 6378137.0000;
double flattening = (1/298.2572);
double zoneWidth = 6;
double centMeridian = -177;
double falseEast = FALSE_EASTING;
double falseNorth = FALSE_NORTHING;
double scale = SCALE_FACTOR;
int max_size = 128;
int current_size = max_size;
char *pathStr = malloc(max_size);
char *outPathStr = malloc(max_size);
char coData[100]; //max length of line;
long firstTerm, secondTerm; //terms we will split the line into, lat, lon, elevation.
int counter = 0; //pos counter
int d = EOF; //end of file ASCII
char strIn[200];
char* elevation;
char strOut[200];
char dirOut[200]; //sprintf must use a actual defined buffer otherwise there will be a buffer overflow.
char* cchr;
int j;
_setmaxstdio(2048);
printf("Please enter the path of the files: \n");
getUserInput(pathStr, current_size, max_size);
printf("Please enter the output path of the files: \n");
getUserInput(outPathStr, current_size, max_size);
//loop through each file in the directory. Open the file, convert, then close it.
//we will use dirent.h as it is cross platform so we wont have to worry about sharing issues
DIR *dir; //new directory
struct dirent *ent;
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));}
int k;
if(dir != NULL)
{
while((ent = readdir(dir)) != NULL) //loop through each file in the directory.
{
//open the file and loop through each line converting it then outputing it into a new file
if((!strcmp(ent->d_name,"..") || !strcmp(ent->d_name,".")) == 1)
{
//dont want these directories
continue;
}
else
{
sprintf(strIn,"%s%s",pathStr,ent->d_name); //get the file n
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
{ printf("Error: %d (%s)\n", errno, strerror(errno));
getchar();
break; }
sprintf(dirOut,"%s%d%s",outPathStr,counter,".geo");
printf("%s \n",dirOut);
FILE *fp2 = fopen(dirOut, "w"); //for outputting file
if(fopen(dirOut, "w") == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));
getchar();
break; }
while(fgets(coData, 100, fp) != NULL)//loop through line by line, allocate into 2 doubles and a string, pass the two coordinates and convert
{
//extract terms from coData
char * pch; //pointer to array pos
char * pend;
pch = strtok(coData," ");
j = 0;
while(j <= 2) //We only want to split the first three parameters.
{
//convert char array to double for co-oridinate conversion
if(j == 0)
{
firstTerm = atof(pch); //latitude;
j++;
continue;
}
if(j == 1)
{
pch = strtok(NULL, " ");
secondTerm = atof(pch); //longitude
j++;
continue;
}
if(j == 2)
{
pch = strtok(NULL," ");
elevation = pch; //elevation doesnt need to be converted because it isnt used in the coordinate conversion.
break;
}
}
grid2spheroid(&lat,&lon,&convergence,&pt_scale,firstTerm,secondTerm,zone,0, major_axis,flattening,zoneWidth,centMeridian,falseEast,falseNorth,scale);
sprintf(strOut,"%f %f %s",lat,lon,elevation);
//printf("%d %d", lat, lon);
fputs(strOut,fp2);
} //end of while
fclose(fp2);
fclose(fp);
counter++;
}
}
closedir(dir);
}
free(pathStr); //finished using the path string so we can finish the
free(outPathStr);
getchar();
return 0;
}
void getUserInput(char *pathStr, int current_size, int max_size)
{
unsigned int i = 0;
if(pathStr != NULL)
{
int c = EOF;
//get the user input and reallocate the memory size if the input it too large.
while((c = getchar()) != '\n' && c != EOF) //WHILE NOT END OF FILE OR NEW LINE (USER PRESSED ENTER)
{
pathStr[i++] = (char)c;
if(i == current_size)
{
current_size = i+max_size;
pathStr = realloc(pathStr, current_size);
}
}
}
}
You aren't closing all the files ;-)
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
Same applies to your output.
I think you meant something more like:
FILE *fp = fopen(strIn, "r");
if(fp == NULL) //for inputting file
{
// error handling.
No, no! You're opening every file twice (and only closing once)!
/* Bad! */
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
{ printf("Error: %d (%s)\n", errno, strerror(errno));}
int k;
/* Correct */
dir = opendir(pathStr); //allocate it a path
if(!dir) {
printf("Error: %d (%s)\n", errno, strerror(errno));
return;
}
You're also doing the same thing with fopen(). In both places :)
Just check the pointer; don't call "fopen()" a second time; don't call "opendir()" a second time!
Also: please don't put code on the same line as your opening brace. OK?
dir = opendir(pathStr); //allcate it a path
if(opendir(pathStr) == NULL)
(...)
FILE *fp2 = fopen(dirOut, "w"); //for outputting file
if(fopen(dirOut, "w") == NULL)
(...)
FILE *fp = fopen(strIn, "r");
if(fopen(strIn, "r") == NULL) //for inputting file
Here you open the file twice but only store the pointer once. Change these to:
FILE *fp = fopen(strIn, "r");
if(fp == NULL) //for inputting file
and the other one in the same way.

Resources