C Unions with Files and buffer overflow? - c

My program loads/saves double value into a text file using union.
I think I am having a buffer overflow, when I use fflush(stdin) it works, but I can't. The problem maybe somewhere else though. The code jumps without letting me enter a file to load from ( 2.000000 is autoinserted ).
Ps. Yes I know that tab[i] would be better than *(tab+i) but spare me, I just have to do it like this.
output :
Enter double value:8794.061758
Enter path to file you wish to save:valleykingstopwantreachspellshipcontinue
File saved
Enter a path to file you wish to load: 2.000000 ----- This value is inserted automatically ( I didnt write it)
Process finished with exit code 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union double_to_char_t
{
double d;
char bytes[8];
};
int save_double(const union double_to_char_t *dtc, const char *filename)
{
if(dtc == NULL || filename == NULL) return 1;
FILE * f=fopen(filename,"w");
if(f == NULL) return 2;
int result = fwrite(dtc, sizeof(dtc), 1, f);
if( result != 1)
{
fclose(f);
return 3;
}
fclose(f);
return 0;
}
int load_double(union double_to_char_t *dtc, const char *filename)
{
if( dtc == NULL || filename == NULL) return 1;
FILE * f = fopen(filename,"r");
if( f == NULL ) return 2;
int result = fread(dtc, sizeof(dtc), 1, f);
if( result != 1)
{
fclose(f);
return 3;
}
fclose(f);
return 0;
}
int main()
{
union double_to_char_t unia;
char temp1[40] = {0};
char temp2[40] = {0};
char *input1 = temp1;
char *input2 = temp2;
printf("Enter double value: ");
if(scanf(" %lf",&(unia.d))!=1)
{
printf("Incorrect input");
exit(1);
}
printf("Enter path to file you wish to save:");
fgets(input1, 40, stdin);
int wynik = save_double(&unia,input1);
if(wynik > 0)
{
printf("Couldn't create file\n");
exit(5);
}
else
{
printf("File saved\n");
}
printf("Enter a path to file you wish to load: ");
fgets(input2, 40, stdin);
// scanf(" %39[^\n]", input2);
int score = load_double(&unia,input2);
if(score > 0 && score < 3)
{
printf("Couldn't open file\n");
exit(4);
}
if(score == 3)
{
printf("File corrupted\n");
exit(6);
}
printf("%f",unia.d);
return 0;
}

You used %39[^\n] to read the filename, then typed a 40 character filename. The first 39 characters were put into input1, and the last character was left in the input stream.
When you then tried to get the filename for input2, it read that last character. That's why it didn't wait for you to type anything -- there was still input available after the first filename was read.
So you saved to valleykingstopwantreachspellshipcontinu and loaded from e.
You need to ensure that your input buffer is longer than any potential input. If you use fgets() instead of scanf(), you can test whether the input ends with a newline. If not, you can report that the input was too long, read characters until the newline, then ask for input again. (The non-standard fflush(stdin) discards everything up to the next newline, which is why adding that fixed the problem.)
if (!fgets(input1, 40, stdin)) {
printf("input error\n");
exit(1);
}
if (input1[strlen(input1)-1] != '\n') { // check for trailing newline
printf("filename too long\n);
exit(3);
}
input1[strlen(input1)-1] = '\0'; // remove it before using as filename
You could also use the getline() function. This isn't standard C, but it's POSIX and widely implemented. It allocates the input buffer dynamically so any length can be accomodated.

Related

How to use for loop to count number

I would like to write a program that can continuously + 1 to ID once user registered a user. I use for loop to design it, but I don't know how to break the loop after registered a user. Please help me.
Expected Data in idtest.txt:
SID001:USER1
SID002:USER2
SID003:USER3
.
.
and so on
Actual Data in idtest.txt:
SID001:USER1
SID002:USER1
SID003:USER1
.
until SID00999:USER1
My Code:
int main()
{
FILE* f;
char data[1024], user[50];
int num = 0, c, buf;
printf("Enter User: ");
scanf("%s", user);
f = fopen("idtest.txt", "a+");
while (fgets(data, sizeof data, f));
for (buf = 1; buf <= 999; buf++)
{
c = fgetc(f);
if (c == EOF)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 1)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 9)
{
fprintf(f, "SID0%d:%s\n", num, user);
}
else if (num >= 99)
{
fprintf(f, "SID%d:%s\n", num, user);
}
break;
}
return 0;
}
There are multiple problems in your code:
you do not test for errors
as coded, num is always 0: the program cannot produce the file contents you posted.
it makes no sense to try an read the next byte with fgetc(f) since the previous loop reached the end of the file already. This call returns EOF, which explains why you always write 2 leading zeroes.
you cannot mix reading and writing operations without an intervening seek operation, it has undefined behavior. Your code manages to write to the file, but it might behave differently with a different compiler / library / operating system...
you can simplify the output code using %03d to format numbers with leading zeroes.
naming an int variable buf is very confusing.
If you intend for the program to register one user at a time, you should read the file and count the number of lines, then write a single entry at the end.
Here is a modified version:
#include <stdio.h>
int main() {
char user[50];
char data[1024];
FILE *f;
int num;
printf("Enter User: ");
if (scanf("%49s", user) != 1) {
fprintf(stderr, "invalid or missing input\n");
return 1;
}
f = fopen("idtest.txt", "a+");
if (f == NULL) {
fprintf(stderr, "cannot open file idtest.txt\n");
return 1;
}
/* count the number of users */
num = 1;
rewind(f);
while (fgets(data, sizeof data, f)) {
num++;
}
/* switch to writing mode */
// No need to seek because the above loop hit the end of file.
// which is a special case where the next call can be a write operation
//fseek(f, 0L, SEEK_CUR);
fprintf(f, "SID%03d:%s\n", num, user);
fclose(f);
return 0;
}

C: How to read a text file and grab certain values after a certain point?

So basically the text file would look like this
Starting Cash: 1500
Turn Limit (-1 for no turn limit): 10
Number of Players Left To End Game: 1
Property Set Multiplier: 2
Number of Houses Before Hotels: 4
Must Build Houses Evenly: Yes
Put Money In Free Parking: No
Auction Properties: No
Salary Multiplier For Landing On Go: 1
All I need from the file is basically anything after ":"
I'm just confused how to only read anything after a ":"?
This is what I have right now. I just can't seem to think of a way to only scan for the numbers/yesorno.
void readRules(char*file_name)
{
Rules r;
FILE *file = NULL;
file = fopen(file_name, "r");
if (file == NULL) {
printf("Could not open %s\n", file_name);
return;
}
char c=fgetc(file);
fscanf(file, "%c", &c);
while (!feof(file))
{
fscanf(file, "%c", &c);
if(c==':')
{
r.startCash=c;
}
}
printf("There are %c word(s).\n", r.startCash);
fclose(file);
}
Thank you.
This program will read integers following a colon in each line of the file given. I imagine this is appropriate? You also have some strings after colons. If you want to read those, you can try scanning for a string "%s" and testing if the function returns nonzero (for at least one format pattern matched).
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 1000
void readRules (const char *filename) {
FILE *fp;
char *lp, line[MAXLINE];
int n;
// Return early if file cannot be opened.
if ((fp = fopen(filename, "r")) == NULL) {
fprintf(stderr, "Error: Couldn't open \"%s\"!\n", filename);
return;
}
// Use fgets to read consecutive lines. Returns NULL on error or EOF.
while (fgets(line, MAXLINE, fp) != NULL) {
// Read until newline is hit or buffer size exceeded.
for (lp = line; *lp != '\n' && (lp - line) < MAXLINE; lp++) {
// If encounter colon and sccanf reads at least 1 integer..
if (*lp == ':' && sscanf(lp + 1, "%d", &n) == 1) {
fprintf(stdout, "%d\n", n);
break;
}
}
}
// Clean up.
fclose(fp);
}
int main (int argc, const char *argv[]) {
readRules("test.txt");
return 0;
}
When run with your example input, it produces:
1500
10
1
2
4
1

fscanf in a loop saving to the same array causes spaces to fill array

I have a file formatted like:
01,Name1
02,Name2
03,Name3
04,Name4
05,Name5
I am tying it make it so that a user can type in a number like 01, or 02 and the corresponding name will be returned. This works fine when I type 01. However when I type any number after it doesn't work and it seems like when I replace the loop with:
fscanf(fp,"%20[^,],%s[^\n]",ln,name);
printf("1=%c 2=%c 3=%c 4=%c\n",ln[0],ln[1],search[0],search[1]);
fscanf(fp,"%20[^,],%s[^\n]",ln,name);
printf("1=%c 2=%c 3=%c 4=%c\n",ln[0],ln[1],search[0],search[1]);
The 2nd time around the ln[0] gets filled with a lot of spaces or a "\n". I can't tell.
Any help will be appreciated.
Here is my code:
#include <stdio.h>
int main()
{
puts("Enter seach number:");
char search[2];
scanf("%c%c",&search[0],&search[1]);
FILE *fp;
fp = fopen("/Users/user1/Desktop/text.txt","r");
if(fp == NULL){
puts("File dose not exits");
return (1);
}
else{
puts("File found");
}
char ln[3];
char name[20];
fopen("fp","r");
puts("File opened");
int searching = 1;
while(searching == 1)
{
fscanf(fp,"%20[^,],%s[^\n]",ln,name);
if(ln[0]==search[0]&&ln[1]==search[1])
{
printf("%s",name);
searching = 0;
}
else if(ln[0] == '\n')
{
puts("Could not find number");
searching = 0;
}
}
return 0;
}
Wrong use of fscanf() format
// fscanf(fp,"%20[^,],%s[^\n]",ln,name);
fscanf(fp,"%20[^,],%s%*[^\n]",ln,name);
// or
fscanf(fp,"%20[^,],%s ",ln,name);
// or
fscanf(fp," %20[^,],%s",ln,name);
Better to check results and add width to %s
char ln[20+1];
char name[30+1];
if (2 == fscanf(fp,"%20[^,],%30s%*[^\n]",ln,name)) Success();
Best to use fgets()
char buf[100];
if (fgets(buf, sizeof buf, fp) == NULL) {
if (2 == sscanf(buf,"%20[^,],%s",ln,name)) Success();
}

Program works with string literals but not with string arrays

I have a hashtable ADT which has two functions, insert and lookup. I put in to the insert function a hash table, hash table size, ID #, and book title and that inserts it into the hash table. This works fine when I pass it a string literal, i.e. insert(...,"Hello, world!"...); It doesn't work when I read in strings from a file, store them in an array, and try and use my insert and lookup functions.
I have all of my code here but the most important files are main.c and hash.c. Hash.c has the newHash(), hash(), insert(), and lookup() functions and main.c reads from two files, in this case test1.lib.in and test1.req.in, and from the first file will get the library id and title of a book from each line and then put it in the hash table. From the second file, it gets requests for a book title and should print the ids in its linked list.
List of links to files https://docs.google.com/document/d/1tFNs-eVkfnCfjwAHcAUdHtUl1KVv_WcnW2IS0SRFvcM/edit?usp=sharing
Example of code that works.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list.h"
#include "hash.h"
int main(){
ListHndl* temp = newHash(10);
insert(442440, "cvyaqbznxel", 10,temp);
lookup(temp,"cvyaqbznxel", 10);
return 0;
}
Code that doesn't work
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "list.h"
#include "hash.h"
int main(int argc, char * argv[]) {
if (argc != 3) {
printf("Incorrect arguments, please specify 2 files to be read\n");
return EXIT_FAILURE;
}
FILE *file = fopen( argv[1], "r");
FILE *secondFile = fopen(argv[2], "r");
if (file == 0 || secondFile == 0) {
printf("Could not open a file\n");
return EXIT_FAILURE;
}
int numDataLines2;
int numDataLines;
int hashTableSize;
//First line of first file gives number of lines in file and
//size of hash table to be made
if(fscanf(file, "%d%d", &numDataLines, &hashTableSize) < 2) {
printf("Unable to parse first line of first file\n");
return EXIT_FAILURE;
}
ListHndl* theHash = newHash(hashTableSize);
int libraryID;
char *tempString = calloc(numDataLines,41*sizeof(char));
char lineHolder[129];
//discard the new line which always shows up
fgets(lineHolder, 128, file);
for(int i = 0; i < numDataLines; i++) {
//Gets the whole line to be scanned with sscanf
fgets(lineHolder, 128, file);
//If the line consists of just a newline char, continue
if(strcmp(lineHolder, "\n") == 0 ) {
continue;
}
//Scans the line retrieved from fgets and placed in lineHolder
if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID,&tempString[i]) == 0){
printf("Unable to parse line %d of first file\n",i+2);
return EXIT_FAILURE;
}
insert(libraryID, &tempString[i], hashTableSize, theHash);
}
char String[41];
fgets(String, 40, secondFile);
numDataLines2 = atoi(String);
char *storeSecondFileStuff = calloc(numDataLines2,41*sizeof(char));
for(int i = 0; i< numDataLines2; i++) {
fgets(lineHolder, 128, secondFile);
if(strcmp(lineHolder, "\n") == 0) {
continue;
}
if(sscanf(lineHolder, "%40[^\n]",&storeSecondFileStuff[i]) == 0) {
printf("Unable to parse line %d of second file\n",i+2);
return EXIT_FAILURE;
}
lookup(theHash, &storeSecondFileStuff[i], hashTableSize);
}
printf("\n");
fclose(file);
fclose(secondFile);
return 0;
}
Thanks!
I think you have multiple problems. To start with, you might not be scanning your input line correctly. Change your line
if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID,&tempString[i]) == 0)
to
if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID, tempString) < 0)
that way, you will trap the situation where the sscanf function did not successfully convert both arguments - for example, if there is no comma in the input line. Note that sscanf returns the number of successful conversions; success would return a value of 2, so testing for <2 is the right way to go.
Note also that I changed &tempString[i] to tempString. The former points to some place along tempString - which only has 41 characters allocated to it. Yet you always allow up to 40 characters (plus '\0' to be written to it - so you will write past the end of the string. Since this is only a temporary variable, there is no sense in doing this. Just scan the input into the temp variable, then do whatever you need to do with it.
This means that your insert also changes, from
insert(libraryID, &tempString[i], hashTableSize, theHash);
to
insert(libraryID, tempString, hashTableSize, theHash);
Again, you need to do the same thing lower down in your code.
Here is an attempt at making the code work for you - see if this hits the spot. Note that all I really did was change the type of tempString and storeSecondFileStuff, and modified the way they were used in various function calls accordingly. I did not attempt to compile / run because of the complexity of the other files involved - but this should help a bit:
int main(int argc, char * argv[]) {
if (argc != 3) {
printf("Incorrect arguments, please specify 2 files to be read\n");
return EXIT_FAILURE;
}
FILE *file = fopen( argv[1], "r");
FILE *secondFile = fopen(argv[2], "r");
if (file == 0 || secondFile == 0) {
printf("Could not open a file\n");
return EXIT_FAILURE;
}
int numDataLines2;
int numDataLines;
int hashTableSize;
//First line of first file gives number of lines in file and
//size of hash table to be made
if(fscanf(file, "%d%d", &numDataLines, &hashTableSize) < 2) {
printf("Unable to parse first line of first file\n");
return EXIT_FAILURE;
}
ListHndl* theHash = newHash(hashTableSize);
int libraryID;
char **tempString = calloc(numDataLines,sizeof(char*)); // <<< ARRAY of pointers
char lineHolder[129];
//discard the new line which always shows up
fgets(lineHolder, 128, file);
for(int i = 0; i < numDataLines; i++) {
//Gets the whole line to be scanned with sscanf
fgets(lineHolder, 128, file);
tempString[i] = calloc(1, 41 * sizeof(char)); // <<< space for this string
//If the line consists of just a newline char, continue
if(strcmp(lineHolder, "\n") == 0 ) {
continue;
}
//Scans the line retrieved from fgets and placed in lineHolder
if(sscanf(lineHolder, "%d, %40[^\n]", &libraryID, tempString[i]) < 0){ // <<< changed
printf("Unable to parse line %d of first file\n",i+2);
return EXIT_FAILURE;
}
insert(libraryID, tempString[i], hashTableSize, theHash); // <<< changed
}
char String[41];
fgets(String, 40, secondFile);
numDataLines2 = atoi(String);
char **storeSecondFileStuff = calloc(numDataLines2, sizeof(char*)); // changed: again char **
for(int i = 0; i< numDataLines2; i++) {
fgets(lineHolder, 128, secondFile);
storeSecondFileStuff[i] = calloc(1, 41 * sizeof(char));
if(strcmp(lineHolder, "\n") == 0) {
continue;
}
if(sscanf(lineHolder, "%40[^\n]",storeSecondFileStuff[i]) == 0) {
printf("Unable to parse line %d of second file\n",i+2);
return EXIT_FAILURE;
}
lookup(theHash, storeSecondFileStuff[i], hashTableSize); // <<<< changed
}
printf("\n");
fclose(file);
fclose(secondFile);
return 0;
}

C programming Reading a specific line of a text file

So i've been given an exercise to work on: Have the user input a number and the program will display the line of text associated with that line for example
Password
abcdefg
Star_wars
jedi
Weapon
Planet
long
nail
car
fast
cover
machine
My_little
Alone
Love
Ghast
Input 3: Output: Star_wars
Now i have been given a program to solve this, however it uses the function getline() , which doesn't complie on DEV C++.
#include <stdio.h>
int main(void)
{
int end = 1, bytes = 512, loop = 0, line = 0;
char *str = NULL;
FILE *fd = fopen("Student passwords.txt", "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
printf("Enter the line number to read : ");
scanf("%d", &line);
do {
getline(&str, &bytes, fd);
loop++;
if (loop == line)
end = 0;
}while(end);
printf("\nLine-%d: %s\n", line, str);
fclose(fd);
}
All i need is to know how to do this, in a simple program without the use of getline()
Thanks
Edit: I also don't want to download software to make this work
use fgets instead of getline.
#include <stdio.h>
int main(void){
int end, loop, line;
char str[512];
FILE *fd = fopen("data.txt", "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
printf("Enter the line number to read : ");
scanf("%d", &line);
for(end = loop = 0;loop<line;++loop){
if(0==fgets(str, sizeof(str), fd)){//include '\n'
end = 1;//can't input (EOF)
break;
}
}
if(!end)
printf("\nLine-%d: %s\n", line, str);
fclose(fd);
return 0;
}
You have wrote:
char *str = NULL;
and you used it without initializing:
getline(&str, &bytes, fd);
first you must initialize it:
char *str=(char*)malloc(SIZEOFSTR);
you can add this part in your program instead of your do-while loop. You will be using fscanf() whose arguments are the file pointer, specifier of data type and the variable you want to store.
printf("Enter the line number to read : ");
scanf("%d", &line);
while(line--) {
fscanf(fd,"%s",str);
}
printf("\nLine-%d:%s\n",line,str);

Resources