C- uncorrect method needs to be corrected - c

I have a problem which may look like if I copied my homework, but it's not my homework. It was part of a Test at University and I want to solve it (as well as others) at home so that I am prepared for the next Test.
My goal here is that I understand so that I can solve similar questions on my own. I am familiar with high-level languages but C is one of my weaknesses, this is why I have problems here.
The Question
Given the following method:
int safe_read(int fd, char *buff, size_t len){
do {
errno = 0;
pos += read(fd, buff + pos, len);
if (-1 == len) {
if (ENTER != errno) {
return 0;
}
}
} while(ENTER == errno);
return pos;
}
and the following call:
pos = safe_read(STDIN_FILENO, msg, 225);
Analyse the code and answer the following questions:
1) Does safe-read return the number of bytes read correctly in all cases?
2) If not, how can this be fixed?
Well. For now, I already understood the following:
1)
No it does not, as for the following reasons:
-the caller may set fd to something invalid.
-pos is not properly initialized.
-The variable ENTER is also not initialized.
-if(-1==len) does not make sense as len is a parameter set by the caller, thus will alsways be true or false at one call.
-it is also not safe as it is possible to go beyond the buffer's maximum size (by setting len to a value >= sizeof(buff))
-it does not return the number of characters read in all cases, as f.e. when I read len characters the first time, And the second time it fails. I will then return zero even though len characters have already been read.
2) Here is my fix.
initialize variables
/*
For a better understanding, I write what I understood about what this method is supposed to do:
-reads characters into char* buff.
-returns the number of characters read as int
-fd is a file descriptor of the file to read
-len is the number of bytes to be read
*/
int safe_read(int fd, char *buff, size_t len){
int ENTER=0;
int pos=0;
do {
errno = 0;
pos += read(fd, buff + pos, len);
if (len < 0) {
if (ENTER != errno) {
return 0;
}
}
} while(ENTER == errno);
return pos;
}
Did I understand you correctly? Is my fix correct?
Thank you!
Special thanks to Paul Ogilvie for the help before my edit!

Your code contains many errors and other members wonder if this is homework. But I'll attempt to help you. First your code:
int safe_read(int fd, char *buff, size_t len)
{
int pos= 0;
do{
errno=0;
pos+=read(fd, buff+pos, len);
if(-1==len){
if(ENTER!=errno){
return 0;
}
}
}while(ENTER==errno);
return pos;
}
Variable pos was not defined and even it it was defined global, you probably would have to initialize it to zero.
Then your funny variable ENTER, which is neither defined and, more importantly, is never set in your code. So it won't change value. What is your intention with this variable?
Then if(-1==len). len is a parameter that doesn't change so either it was -1 or it never will be. Clearly you want to check for an error on read, but this is not the way.
Then whether this is safe: no, it isn't. Assuming that len is the size of buff, then you repeatedly append len characters to buff, so at the second read it will go beyond the buffer's size.
And lastly whether this function will always return the correct number of characters read: no it doesn't. Suppose you read len characters the first time, and the second time it fails. You then return 0 but len characters had already been read.

Related

I have a set of values within a text file with delimiters how do I modify a value using C Programming

The text file that I have contains the below as the data (Starting with the K and D and P respectively as the ID)
K1234:Green_Book:A_green_book:10:
K3346:Red_Book:A_red_book:7:
D3333:Grey_Book:A_grey_book:15:
D1111:Black_Book:A_black_book:1:
P0000:White_Book:A_white_book:6:
what I would like to do is to modify the Data in the line that starts with D3333 and change the value from 15 to 17 in the text file. I really do not know how to do that as I am new to C programming and this has been troubling me for days now. I have tied searching all over the net but my searches were of no avail. If anyone can please help me with the code that can do that or something similar, I would really appreciate it. Thanks.
This is what i have done so far:
void show(){
FILE * fl;
long fl_size;
char * buffer;
size_t res;
fl = fopen("inventItems.txt", "r+");
if (fl == NULL) {
fprintf(stderr, "File error\n");
_getch();
exit(1);
}
fseek(fl, 0, SEEK_END);
fl_size = ftell(fl);
rewind(fl);
buffer = (char*)malloc(sizeof(char)*fl_size);
if (buffer == NULL) {
fputs("Memory error", stderr);
_getch();
exit(2);
}
res = fread(buffer, 1, fl_size, fl);
if (res != fl_size) {
fputs("Reading error", stderr);
_getch();
exit(3);
}
char * strtok_res;
strtok_res = strtok(buffer, ":");
while (strtok_res != NULL)
{
printf("%s\n", strtok_res);//this prints the values from the file to a new line when i test it
//however i DO NOT KNOW how to modify and save it back onto the text file
strtok_res = strtok(NULL, ":");
_getch();
}
_getch();
fclose(fl);
free(buffer);
}
Ok, looks like you are doing this along the right lines. A couple of comments:
Repitition and Readability
You are repeating yourself a lot; variations on
if(whateverPointer == NULL) {
fprintf("My error message", stderr);
_getch();
exit(2);
}
are used on three separate occasions; so should be moved into a separate function that takes a char* (string) message to pass into fprintf(), so that you can just do
if(whateverPointer == NULL) {
errorThenDeath("My message", 2);
}
This might seem irrelavent to the question, but as well as being a good habit to get into, it will make your code easier to read. The easier your code is to read, the easier it is for other people to help you when you are stuck: always remember this.
On a similar veign, you have indicated that you want to use a specific identifier to change a specific line. With this in mind, you should be breaking your problem up to identify this specific problem, so the code that performs that particular task should be contained in a function that might have a signature like this.
int changeLineValue(char** buffer, size_t size, char* identifier, int newValue)
{
...
}
Note the double pointer (char**) this is a pointer to the original char*. When inside this function, you can get at the original pointer by dereferencing it (*) so
*buffer = "hello";
Would change the buffer to the string 'hello'.
The Problem
I'm not sure tokenising the buffer is the right approach. strtok actually writes in \0 characters at the end of each token, which will make it a pain when you try to write this back to a file. Another thing to note; because you don't know how many digits your new number might have, you may have to resize the buffer to compensate. The function signature assumes this is done, and returns a number representing how many characters were added or removed (if any).
My general approach is to iterate through each character looking for newlines. Each time you've found one, check to see if it is the correct line, if it is, get the value, determine whether you need to resize the buffer and do so if necessary. Once that has been done, change the value.
int changeLineValue(char** buffer, size_t size, char* identifier, int newValue)
for(int i = 0; i < size; i++) {
if(buffer[i] == '\n' && isCorrectLine(*buffer, size, i)) {
int oldVal = getCurrentValue(*buffer, size, i);
int resize = getDigitsDifference(oldVal, value);
if(resize != 0) {
resizeBuffer(buffer, resize);
}
modifyValueInPosition(buffer, fl_size, i, value);
return resize;
}
}
}
Note; because of the way that the C language works, isCorrectLine(...) will only be called if buffer[i] == \n evaluates to true. This is called short circuit evaluation.
Obviously the code above calls a couple of functions that have not yet been created, so part of your task will be to implement them. Note that the second parameter which has been passed fl_size is defined as size_t not long even though fl_size is a long. You should change this variable to size_t.
Below I have provided the function signatures. Since you are trying to learn C, I am not going to implement them for you.
This function will be called each time a newwline is encountered, with 'index' set to the position of that newline. It should return a 0 if the identifier is not found at this position or a 1 if it HAS beenfound at this position. Do not change anything in the buffer at this point
int isCorrectLine(char* buffer, char* identifier, size_t size, int index) {
}
This function should iterate along the line and return the number before the next newline character ('\n')
int getCurrentValue(char* buffer, size_t fl_size, i) {
}
This number should return the difference between how many digits each number has. E.g. 1 = 1 digit, 324 = 3 digits so 3 - 1 = 2
int digitsDifference(int old, int new) {
}
This function takes a double pointer (char**) and reallocates the memory with a larger or smaller buffer to take into account the different number of digits if required.
void resizeBuffer(char** buffer, int resize) {
*buffer = // This is how you change the original pointer. Line is still incomplete
}
Now the buffer is the correct size, you can go ahead and change the value in the buffer. Again, this function is passed in the position of the newline before the correct line, so you need to iterate along the line, change the values in position. If you find yourself overwriting a newline character (because the new number is longer), you might need to move all of the characters after this value along
int modifyValueInPosition(char* buffer, size_t fl_size, int index) {
}
Writing to file
Once you have changed the original buffer, writing it back to file is fairly easy. Your main function should look something like this now
int main() {
// ...code that gets buffer from file. Note, make sure you close the file handle
// afterwards it isn't good practise to leave a file handle open
fl_size += changeLineValue(buffer, fl_size, identifier, newValue);
// ...Reopen file handle as you did before ...
fwrite(buffer, sizeof(char), fl_size, fileHandle);
// ...Close file handle...
}

Dynamically allocate user inputted string

I am trying to write a function that does the following things:
Start an input loop, printing '> ' each iteration.
Take whatever the user enters (unknown length) and read it into a character array, dynamically allocating the size of the array if necessary. The user-entered line will end at a newline character.
Add a null byte, '\0', to the end of the character array.
Loop terminates when the user enters a blank line: '\n'
This is what I've currently written:
void input_loop(){
char *str = NULL;
printf("> ");
while(printf("> ") && scanf("%a[^\n]%*c",&input) == 1){
/*Add null byte to the end of str*/
/*Do stuff to input, including traversing until the null byte is reached*/
free(str);
str = NULL;
}
free(str);
str = NULL;
}
Now, I'm not too sure how to go about adding the null byte to the end of the string. I was thinking something like this:
last_index = strlen(str);
str[last_index] = '\0';
But I'm not too sure if that would work though. I can't test if it would work because I'm encountering this error when I try to compile my code:
warning: ISO C does not support the 'a' scanf flag [-Wformat=]
So what can I do to make my code work?
EDIT: changing scanf("%a[^\n]%*c",&input) == 1 to scanf("%as[^\n]%*c",&input) == 1 gives me the same error.
First of all, scanf format strings do not use regular expressions, so I don't think something close to what you want will work. As for the error you get, according to my trusty manual, the %a conversion flag is for floating point numbers, but it only works on C99 (and your compiler is probably configured for C90)
But then you have a bigger problem. scanf expects that you pass it a previously allocated empty buffer for it to fill in with the read input. It does not malloc the sctring for you so your attempts at initializing str to NULL and the corresponding frees will not work with scanf.
The simplest thing you can do is to give up on n arbritrary length strings. Create a large buffer and forbid inputs that are longer than that.
You can then use the fgets function to populate your buffer. To check if it managed to read the full line, check if your string ends with a "\n".
char str[256+1];
while(true){
printf("> ");
if(!fgets(str, sizeof str, stdin)){
//error or end of file
break;
}
size_t len = strlen(str);
if(len + 1 == sizeof str){
//user typed something too long
exit(1);
}
printf("user typed %s", str);
}
Another alternative is you can use a nonstandard library function. For example, in Linux there is the getline function that reads a full line of input using malloc behind the scenes.
No error checking, don't forget to free the pointer when you're done with it. If you use this code to read enormous lines, you deserve all the pain it will bring you.
#include <stdio.h>
#include <stdlib.h>
char *readInfiniteString() {
int l = 256;
char *buf = malloc(l);
int p = 0;
char ch;
ch = getchar();
while(ch != '\n') {
buf[p++] = ch;
if (p == l) {
l += 256;
buf = realloc(buf, l);
}
ch = getchar();
}
buf[p] = '\0';
return buf;
}
int main(int argc, char *argv[]) {
printf("> ");
char *buf = readInfiniteString();
printf("%s\n", buf);
free(buf);
}
If you are on a POSIX system such as Linux, you should have access to getline. It can be made to behave like fgets, but if you start with a null pointer and a zero length, it will take care of memory allocation for you.
You can use in in a loop like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h> // for strcmp
int main(void)
{
char *line = NULL;
size_t nline = 0;
for (;;) {
ptrdiff_t n;
printf("> ");
// read line, allocating as necessary
n = getline(&line, &nline, stdin);
if (n < 0) break;
// remove trailing newline
if (n && line[n - 1] == '\n') line[n - 1] = '\0';
// do stuff
printf("'%s'\n", line);
if (strcmp("quit", line) == 0) break;
}
free(line);
printf("\nBye\n");
return 0;
}
The passed pointer and the length value must be consistent, so that getline can reallocate memory as required. (That means that you shouldn't change nline or the pointer line in the loop.) If the line fits, the same buffer is used in each pass through the loop, so that you have to free the line string only once, when you're done reading.
Some have mentioned that scanf is probably unsuitable for this purpose. I wouldn't suggest using fgets, either. Though it is slightly more suitable, there are problems that seem difficult to avoid, at least at first. Few C programmers manage to use fgets right the first time without reading the fgets manual in full. The parts most people manage to neglect entirely are:
what happens when the line is too large, and
what happens when EOF or an error is encountered.
The fgets() function shall read bytes from stream into the array pointed to by s, until n-1 bytes are read, or a is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte.
Upon successful completion, fgets() shall return s. If the stream is at end-of-file, the end-of-file indicator for the stream shall be set and fgets() shall return a null pointer. If a read error occurs, the error indicator for the stream shall be set, fgets() shall return a null pointer...
I don't feel I need to stress the importance of checking the return value too much, so I won't mention it again. Suffice to say, if your program doesn't check the return value your program won't know when EOF or an error occurs; your program will probably be caught in an infinite loop.
When no '\n' is present, the remaining bytes of the line are yet to have been read. Thus, fgets will always parse the line at least once, internally. When you introduce extra logic, to check for a '\n', to that, you're parsing the data a second time.
This allows you to realloc the storage and call fgets again if you want to dynamically resize the storage, or discard the remainder of the line (warning the user of the truncation is a good idea), perhaps using something like fscanf(file, "%*[^\n]");.
hugomg mentioned using multiplication in the dynamic resize code to avoid quadratic runtime problems. Along this line, it would be a good idea to avoid parsing the same data over and over each iteration (thus introducing further quadratic runtime problems). This can be achieved by storing the number of bytes you've read (and parsed) somewhere. For example:
char *get_dynamic_line(FILE *f) {
size_t bytes_read = 0;
char *bytes = NULL, *temp;
do {
size_t alloc_size = bytes_read * 2 + 1;
temp = realloc(bytes, alloc_size);
if (temp == NULL) {
free(bytes);
return NULL;
}
bytes = temp;
temp = fgets(bytes + bytes_read, alloc_size - bytes_read, f); /* Parsing data the first time */
bytes_read += strcspn(bytes + bytes_read, "\n"); /* Parsing data the second time */
} while (temp && bytes[bytes_read] != '\n');
bytes[bytes_read] = '\0';
return bytes;
}
Those who do manage to read the manual and come up with something correct (like this) may soon realise the complexity of an fgets solution is at least twice as poor as the same solution using fgetc. We can avoid parsing data the second time by using fgetc, so using fgetc might seem most appropriate. Alas most C programmers also manage to use fgetc incorrectly when neglecting the fgetc manual.
The most important detail is to realise that fgetc returns an int, not a char. It may return typically one of 256 distinct values, between 0 and UCHAR_MAX (inclusive). It may otherwise return EOF, meaning there are typically 257 distinct values that fgetc (or consequently, getchar) may return. Trying to store those values into a char or unsigned char results in loss of information, specifically the error modes. (Of course, this typical value of 257 will change if CHAR_BIT is greater than 8, and consequently UCHAR_MAX is greater than 255)
char *get_dynamic_line(FILE *f) {
size_t bytes_read = 0;
char *bytes = NULL;
do {
if ((bytes_read & (bytes_read + 1)) == 0) {
void *temp = realloc(bytes, bytes_read * 2 + 1);
if (temp == NULL) {
free(bytes);
return NULL;
}
bytes = temp;
}
int c = fgetc(f);
bytes[bytes_read] = c >= 0 && c != '\n'
? c
: '\0';
} while (bytes[bytes_read++]);
return bytes;
}

Read from a .txt file and save it in an array.Trouble with fscanf

I want read from a .txt file which contains english sentences and store them into a character array. Each character by character. I tried but got segmentation fault:11 . I have trouble with fscanf and reading from a file in C.
#include<stdio.h>
#include<math.h>
#include<limits.h>
int main()
{
FILE* fp = fopen("file1.txt","r");
char c , A[INT_MAX];
int x;
while(1)
{
fscanf("fp,%c",&c);
if(c == EOF)
{break;}
A[x] = c;
x++;
}
int i;
for (i=0;i<x;i++)
printf("%c",A[i]);
return 0;
}
Problem 1: Putting the array onto the stack as A[INT_MAX] is bad practice; it allocates an unreasonable amount of space on the stack (and will crash on machines where INT_MAX is large relative to the size of memory). Get the file size, then malloc space for it.
fseek(fp, SEEK_END);
long size = ftell(fp);
rewind(fp);
char *A = malloc((size_t) size); // assumes size_t and long are the same size
if (A == NULL) {
// handle error
}
Problem 2: The fscanf is wrong. If you insist on using fscanf (which is not a good way to read an entire file; see problem 4), you should change:
fscanf("fp,%c",&c);`
should be
int count = fscanf(fp, "%c",&c);
if (count <= 0)
break;
Problem 3: Your x counter is not initialized. If you insist on using fscanf, you'd need to initialize it:
int x = 0;
Problem 4: The fscanf is the wrong way to read the entire file. Assuming you've figured out how large the file is (see problem 1), you should read the file with an fread, like this:
int bytes_read = fread(A, 1, size, fp);
if (bytes_read < size) {
// something went wrong
}
My initial answer, and a good general rule:
You need to check the return value, because your c value can never be EOF, because EOF is an int value that doesn't fit into a char. (You should always check return values, even when it seems like errors shouldn't happen, but I haven't consistently done that in the code above.)
From http://www.cplusplus.com/reference/cstdio/fscanf/ :
Return Value
On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.
If a reading error happens or the end-of-file is reached while reading, the proper indicator is set (feof or ferror). And, if either happens before any data could be successfully read, EOF is returned.
If an encoding error happens interpreting wide characters, the function sets errno to EILSEQ.
Hi you should declear till where the program should read data. You can access all characters even if you read line like a string.
try it out
#include<stdio.h>
#include<string.h>
#define INT_MAX 100
int main()
{
FILE* fp = fopen("file1.txt","r");
char c , A[INT_MAX];
int i;
int x;
j=0
while(fscanf(fp,"%s",A[j])!=EOF)
{
j++;
}
int i;
int q;
for(q=0;q<j;q++)
{
for (i=0;i<strlen(A[q]);i++)
printf("%c ",A[q][i]);
printf("\n");
}
return 0;
}

Reading Data in C From Socket Until End Character

EDIT: It has been proven in the comments that defining the length instead should produce the same results and would not use any significant extra data. If you are looking for a way to send data between machines running your program(s), sending the length is better than reading until a terminating character. BonzaiThePenguin has some very good points you should look at.
But for educational purposes: I never found good example code that does this for standard C sockets that handles situations where the data is not all received in one packet, or multiple separate messages are contained within one packet. Simply calling recv repeatedly will not work in all cases.
This is one of those questions where I've answered it myself below, but I'm not 100% confident in my response.
It isn't 'dangerous to allow the client to specify the size of the message it is sending'. Most of the protocols in the word do that, including HTTP and SSL. It's only dangerous when implementations don't bounds-check messages properly.
The fatal flaw with your suggestion is that it doesn't work for binary data: you have to introduce an escape character so that the terminating character can appear within a message, and then of course you also need to escape the escape. All this adds processing and data copying at both ends.
Here is what I came up with. I cannot guarantee that this is perfect because I am not a professional, so if there are any mistakes, I (and anyone else looking for help) would greatly appreciate it if someone would point them out.
Context: socket is the socket, buffer is the array that stores all network input, line is the array that stores just one message extracted from buffer (which is what the rest of your program uses), length is the length of both inputted arrays, and recvLength is a pointer to an integer stored outside of the function that is meant to be 0 initially and should not be freed or modified by anything else. That is, it should persist across multiple calls to this function on the same socket. This function returns the length of the data outputted in the line array.
size_t recv_line(int socket, char* buffer, char* line, size_t length, size_t* recvLength){ //receives until '\4' (EOT character) or '\0' (null character)
size_t readHead = 0;
size_t lineIndex = 0;
char currentChar = 0;
while (1){
for (; readHead < *recvLength; readHead = readHead + 1){
currentChar = buffer[readHead];
if (currentChar=='\4' || currentChar=='\0'){ //replace with the end character(s) of your choice
if (DEBUG) printf("Received message===\n%s\n===of length %ld\n", line, lineIndex+1);
memcpy(buffer, buffer + readHead + 1, length-(readHead)); //shift the buffer down
*recvLength -= (readHead + 1); //without the +1, I had an "off by 1" error before!
return lineIndex+1; //success
}
if (readHead >= length){
if (DEBUG) printf("Client tried to overflow the input buffer. Disconnecting client.\n");
*recvLength = 0;
return 0;
}
line[lineIndex] = currentChar;
lineIndex++;
}
*recvLength = recv(socket, buffer + readHead, length, 0);
}
printf("Unknown error in recv_line!\n");
return 0;
}
Simple example usage:
int function_listening_to_network_input(int socket){
char netBuffer[2048];
char lineBuffer[2048];
size_t recvLength = 0;
while (1==1){
size_t length = recv_line(socket, netBuffer, lineBuffer, 2048, &recvLength);
// handle it…
}
return 0;
}
Note that this does not always leave line as a null-terminated string. If you want it to, it's easy to modify.

Cannot read binary video files in GNU/Linux

I'm stuck with an apparently harmless piece of code. I'm trying to read a whole flv video file into a uint8_t array, but by no reason only the 10 first bytes are read.
contents = malloc(size + 1);
if (read(fd, contents, size) < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
I've tried with fopen and "rb" also, but seems that Glibc ignores that extra 'b' or something. Any clues?
Thanks in advance.
Edit: Maybe it reads a EOF character?
PS. 'size' is a variable containing the actual file size using stat().
It seems the original code correctly reads the entire content.
The problem seems to be in making use of that binary data - printing it out will truncate at the first null, making it appear that only 10 bytes are present.
You can't use any methods intended for strings or character arrays to output binary data, as they will truncate at the first null byte, making it appear the array is shorter than it really is.
Check out some other questions related to viewing hex data:
how do I print an unsigned char as hex in c++ using ostream?
Converting binary data to printable hex
If you want to append this to a string - in what format? hex? base64? Raw bytes won't work.
Here's the original code I posted. A few minor improvements, plus some better diagnostic code:
int ret, size = 4096; /* Probably needs to be much bigger */
uint8_t *contents;
contents = malloc(size + 1);
if(contents == NULL)
{
log_message(WARNING, __func__, EMSG_MEMORY);
return (NULL);
}
ret = read(fd, contents, size);
if(ret < 0)
{
/* Error reading file */
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
for(i = 0;i < ret;++i)
{
printf("%c", contents[i]);
/* printf("%0.2X", (char) contents[i]); /* Alternatively, print in hex */
}
Now, is ret really 10? Or do you just get 10 bytes when you try to print the output?
The 'read()' function in the C library doesn't necessarily return the whole read in one shot. In fact, if you're reading very much data at all, it usually doesn't give it to you in a single call.
The solution to this is to call read() in a loop, continuing to ask for more data until you've got it all, or until read returns an error, indicated by a negative return value, or end-of-file, indicated by a zero return value.
Something like the following (untested):
contents = malloc(size + 1);
bytesread = 0;
pos = 0;
while (pos < size && (bytesread = read(fd, contents + pos, size - pos)) > 0)
{
pos += bytesread;
}
if (bytesread < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
/* Go on to use 'contents' now, since it's been filled. Should probably
check that pos == size to make sure the file was the size you expected. */
Note that most C programmers would do this a little differently, probably making 'pos' a pointer which gets moved along, rather than offsetting from 'contents' each time through the loop. But I thought this approach might be clearer.
On success, read() returns the number of bytes read (which may be less than what you asked for, at which point you should ask for the rest.) On EOF it will return 0 and on error it will return -1. There are some errors for which you might want to consider re-issuing the read (eg. EINTR which happens when you get a signal during a read.)

Resources