I'm having trouble reading from a socket. The code I'm using is below, sometimes it works just fine, but at other times, it just prints some unreadable characters, or some random readable ones... is there a better way?
char* answer = (char*) malloc(1024);
int answerLength = 0;
char prevChar = 0;
char newChar = 0;
while (answerLength < 1024 && read(sock, &newChar, 1) > 0 ) {
if (newChar == '\n' && prevChar == '\r') {
break;
}
printf("%d\n", answerLength);
answer[ answerLength ] = newChar;
answerLength++;
prevChar = newChar;
}
printf("%s\n", answer);
Strings in C must be null-terminated, which means they must have a symbol \0 as the last character.
Since you don't guarantee that it will happen anywhere in your code, answer may be padded with memory garbage next to the data you read.
To make sure it won't happen, use:
answer[answerLength] = '\0';
printf("%s\n", answer);
Also, you could just read() the whole thing straight to answer, you don't need that pointless loop:
int len;
while (len = read(sock, &answer[answerLength], 1024 - answerLength))
answerLength += len;
answer[answerLength] = '\0';
printf("%s\n", answer);
The data you read isn't terminated with a '\0' character, so you can't treat is as a string.
Your char array is not guaranteed to be null terminated. This means that the printf may print more than just what is in your array since it looks for a null termination to stop outputting characters.
You also don't initialise the allocated memory before using it, which is bad practice since the memory can contain random garbage.
To get the code to work better and hopefully fix your problem, you should do the following:
char* answer = malloc(1024 + 1); /* add extra byte for null terminator */
/* other variables the same */
memset( answer, '\0', 1024 + 1 ); /* initialise memory before use */
while (answerLength < 1024 && read(sock, &newChar, 1) > 0 ) {
/* loop is the same */
}
printf("%s\n", answer);
There is also an argument in printf which will tell it to print a certain number of characters. Like so:
printf( "%.*s\n", answerLength, answer );
Related
I've attempted to write a C program to read a string and display it back to the user. I've tested it with a lot of input and it seems to work properly. The thing is that I'm not sure whether or not the c != EOF condition is necessary inside the while expression, and since by definition, the size of a char is 1 byte, maybe I can remove the sizeof(char) expressions inside the malloc and realloc statements, but I'm not sure about this.
Here's the program, also, I manually added a null terminating character to the string:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *str = malloc(sizeof(char));
if (!str)
return 1;
char c;
char *reallocStr;
size_t len = 0;
size_t buf = 1;
printf("Enter some text: ");
while ((c = getchar()) != '\n' && c != EOF) {
if (len == buf) {
buf *= 2;
reallocStr = realloc(str, buf * sizeof(char));
if (!reallocStr)
return 1;
str = reallocStr;
}
str[len++] = c;
}
str[len] = '\0';
printf("You entered: %s\n", str);
free(str);
return 0;
}
As mentioned in the comments, you have a buffer overflow in your code, so you would need to fix that at the very least. To answer your specific questions, sizeof(char) is guaranteed to be 1 (dictated by the c99 spec), so you don't need to multiply by sizeof(char). It's good practice to check for EOF as if your input is coming from an alternate source that has no newline, you don't die (so if someone for example did printf %s hello | yourprogram from a bash prompt, you wouldn't die).
Problems include
Buffer overflow
#HardcoreHenry
Incorrect type
getchar() reruns an int with the values [0..UCHAR_MAX] and the negative: EOF. These 257 different values lose distinctiveness when saved as a char. Possible outcomes: infinite loop or premature loop end. Instead:
// char c;
int c;
Advanced: Arbitrary length
For very long lines buf *= 2; overflows when buf is SIZE_MAX/2 + 1. An alterative to growing in steps of 1, 2, 4, 8, 16,..., consider 1, 3, 7, 15, .... That way code can handle strings up to SIZE_MAX.
Advanced: Reading '\0'
Although uncommon, possible to read in a null character. Then printf("You entered: %s\n", str); will only print to that null character and not to the end of input.
To print all, take advantage that code knows the length.
printf("You entered: ");
fwrite(str, len, 1, stdout);
printf("\n");
To be clear, text input here is not reading of strings, but of reading of lines. That input is saved and converted to a string by appending a null character. Reading a '\0' complicates things, but something robust code handles.
I have to create a C function that returns a line read from a file descriptor. I have to define a macro READ_SIZE (that can be editable). This READ_SIZE indicates the number of characters to read at each call of read(). The number can only be positive.
I also have to use one or several static variables to save the characters that were read but not sent back to the calling function. One .C file (5 functions max, 25 lines max per function) and one .h file only.
My function Get_Next_Line shall return its return without the '\n'. If there is nothing more to read on the file descriptor, or if an error occur while reading, the function returns NULL.
Here is the prototype of the function:
char *get_next_line(const int fd)
FUNCTIONS ALLOWED: malloc, free, read, write (to use with my_putchar, my_putstr, etc).
Here is what I have, but it doesn't work. It does an infinite loop I am trying to know why.
char *my_strcat(char *str1, char *str2)
{
int i;
int j;
int s;
char *strfinal;
i = 0;
j = 0;
s = 0;
if ((strfinal = malloc(sizeof(char) * (my_strlen(str1) + my_strlen(str2)
+ 1))) == NULL)
return (NULL);
while (str1[i] != '\0')
{
strfinal[j] = str1[i];
i++;
j++;
}
while (str2[s] != '\0')
{
strfinal[j] = str2[s];
s++;
j++;
}
free(str1);
strfinal[j] = '\0';
return (strfinal);
}
char *get_next_line(const int fd)
{
int n;
int i;
char *str_to_return;
static char buff[READ_SIZE] = {'\0'};
n = 1;
i = 0;
str_to_return = NULL;
while (n)
{
if (i == 0 && buff == '\0')
{
if ((read(fd, buff, READ_SIZE)) <= 0)
return(str_to_return);
if (i == READ_SIZE - 1 || buff[i] == '\n')
{
n = 0;
str_to_return = my_strcat(buff, str_to_return);
i = -1;
}
}
i++;
}
printf("%s\n", str_to_return);
return (str_to_return);
}
in this code:
while (str1[i] != '\0')
{
strfinal[j] = str1[i];
i++;
j++;
}
what guarantee do you have that there will be the null character \0 somewhere in str1[] ???
same goes for the str2 while loop.
If no null character is encountered, then there will be an infinite loop there.
verify the functions you are using to populate characters into memory under str1[] and str2[] include the null character. Since you are only using the read() function prior then that answer is no.
The problem with your two while loops for str1[] and str2[] is that you are relying on the null character to already be there in memory. And that then begs the question, who put that data there in memory and were they given a requirement to terminate the character data with a null character?
you therefore need to somehow place a control over any loop you write so as not get caught in an infinite loop condition; in this case maybe use a counter and after so many advances of the i to access str1[i] then stop, because you have yet to see a null character.
for example, the fgets() function will read so many characters from a FILE stream into an array, and always terminate it with the null character.
if (i == 0 && buff == '\0')
is always false because your definition of buff is
static char buff[READ_SIZE] = {'\0'};
You are attempting to test if buff is empty when i is 0. However as a char pointer, buff is an address and is never 0. You mean to make the if
if (i == 0 && buff[0] == '\0')
in order to check if the first character is the Null character.
However, once i is incremented, then it always fails even if you test against
if (i == 0 && buff[i] == '\0')
in order to find the NULL character within the buffer. Since you enter the while with i = 0 and are checking if buff is empty, you do not need the while.
If you want to just fill the buffer and keep reading until it is full, you need a different type of test. You also need a way of checking if you need to exit the while loop if the if fails (put in an else to determine what to do).
You also do not need to check each character in buff against '\0' because your code has always insured that it ends with one (even for initialization). Thus, strlen(buff) would be valid.
Another point is that when you call mystrcat() you have already verified that buffer is empty.
Also, since the second string in the call is what you read in, then the mystrcat() will not always have a '\0' at the end of str2 (though you are guaranteeing that buff (str1) will). You should call it with the number of characters in str2 to use.
please, could you help me, how to read lines from stdin via fgets? Problem is, that sometimes in tmp_string are stored part of data from two lines before etc... I'm using this code to load line:
int loadLine(Line *line) {
char tmp_string[MAX_LOAD];
int return_val;
return_val = 0;
line->length = MAX_LOAD;
line->index = 0;
line->data = (char*) malloc(sizeof (char) * line->length);
while (fgets(tmp_string, MAX_LOAD - 1, stdin) != NULL) {
strncat(line->data, tmp_string, MAX_LOAD);
line->index += strlen(tmp_string);
if (tmp_string[strlen(tmp_string) - 1] == '\n') { /* if I'm at the end of line... */
line->data[strlen(line->data) - 1] = '\0';
return_val = 1;
break;
}
if ((line->index + MAX_LOAD) > line->length) {
resizeLine(line);
}
}
if(feof(stdin))
{
return_val = 0;
}
return return_val;
}
And here is usage:
Line line;
if(loadLine(&line) == 0){ ... }
free(line.data);
line->data has not been initialised (no terminator), so strncat() can fail.
line->data = malloc(sizeof (char) * line->length);
line->data[0] = 0;
Answering the question you posed, your usage of fgets() looks reasonable, though you are wasting one byte of your tmp_string variable. The fgets() function already knows to reserve one byte for a trailing NUL, so if you are reading into an array of length MAX_LOAD then you would normally use
fgets(tmp_string, MAX_LOAD, stdin);
This will overwrite the previous contents of the destination buffer (tmp_string) up to the number of bytes read plus one, and it will automatically add a string terminator after the last byte read. If a previously read line was longer then its tail might remain in the buffer, but that shouldn't matter because the standard string functions will ignore anything after the terminator.
The misbehavior you see is very likely caused by your failure to initialize line->data to a zero-length string before passing it to strncat(), as Weather Vane observed first.
Firstly, I've create a simple program in C
unsigned char * text ="Test program";
int _size = strlen(text);
unsigned char * str = malloc(sizeof(text));
memcpy(str, text, _size);
printf("Before(%d): %s\n", _size, str);
for(i=0;i < _size; i++) {
str[i] -= 13; //rot13
}
printf("After: (%d): %s\n", strlen(str), str);
It runs properly. However, when I move this code to Linux kernel, it seems to fail to work
unsigned char * str;
len = min(count, log->size - read->off);
/* Allocate the memory for storing plain text */
str = kmalloc(len, GFP_KERNEL);
if(str == NULL) {
printk(KERN_ERR "logger: failed to allocate buffer\n");
return -ENOMEM;
}
memcpy(str, log->buf + read->off, len);
/* Start: Add a simple rot13 encryption here */
for(i=0;i < strlen(str); i++)
str[i] -= 13; //rot13
/* End: Add a simple rot13 encryption here */
if (copy_to_user(buf, str, len))
return -EFAULT;
if(str != NULL) {
kfree(str);
}
The problem comes from following code
for(i=0;i < strlen(str); i++)
str[i] -= 13; //rot13
Because if it's removed, program runs as original case. Did I miss something here?
The problem: sizeof(text) returns the size of the pointer, and not the length of the string text points to. Also remember that all string have an extra character that terminates the string. This all means that you write to, and read from, beyond the memory you allocated, and that is undefined behavior which means anything could happen.
Also, literal strings are actually constant (const char *).
And lastly, you might want to read about ROT13, as what you're doing is not ROT13 encryption.
You haven't terminated str with a '\0' so you're most likely just running off the end of the buffer and stomping over memory.
Change:
str = kmalloc(len, GFP_KERNEL);
to:
str = kmalloc(len + 1, GFP_KERNEL); // allocate additional char for terminator
and change:
memcpy(str, log->buf + read->off, len);
to:
memcpy(str, log->buf + read->off, len);
str[len] = '\0'; // put terminator at end of string
If you are dealing with strings try using strncpy() instead of memcpy. 'coz that will put a NULL char at the end automatically and you are safe from buffer over runs. But in this case, i'm not very sure what exactly is the problem, unless you give more details about the issue.
And for any kernel programming errors, the key is to add debugs/printks and collect as much as data about what is happening. If thats not helping you debug yourself, that will help others to help you better.
Update edition:
So, I'm trying to get this code to work without using scanf/fgets. Gets chars from the user, puts it into a pointer array using a while loop nested in a for loop.
#define WORDLENGTH 15
#define MAXLINE 1000
int main()
{
char *line[MAXLINE];
int i = 0;
int j;
int n;
char c;
for (n=0; c!=EOF; n){
char *tmp = (char *) malloc(256);
while ((c=getchar())!=' '){
tmp[i]=c; // This is no longer updating for some reason.
i++;
}
line[n++]=tmp; //
i=0;
printf("\n%s\n",line[n]); //Seg fault here
}
for(j = 0; j (lessthan) n; j++){
printf("\n%s\n", line[j]);
free (line[j]);
}
return 0;
So, now I'm getting a seg fault. Not sure why tmp[i] is not updating properly. Still working on it.
I've never learned this much about programming during the entire semester so far. Please keep helping me learn. I'm loving it.
You print line[i] and just before that, you set i to 0. Print line[n] instead.
Also, you forgot the terminating 0 character. And your code will become easier if you make tmp a char array and then strdup before assigning to line[n].
sizeof(WORLDLENGTH), for one, is wrong. malloc takes an integer, and WORLDLENGTH is an integer. sizeof(WORLDLENGTH) will give you the size of an integer, which is 4 if you compile for a 32-bit system, so you're allocating 4 bytes.
Btw - while ((c=getchar())!=' '||c!=EOF) - what's your intent here? A condition like (a!=b || a!=c) will always return true if b!=c because there is no way a can be both b and c.
And, as others pointed out, you're printing out line[i], where i is always 0. You probably meant line[n]. And you don't terminate the tmp string.
And there's no overflow checking, so you'll run into evil bugs if a word is longer than WORDLENGTH.
Others have already told you some specific problems with your code but one thing they seem to have missed is that c should be an int, not a char. Otherwise the comparison to EOF wil not work as expected.
In addition, the segfault you're getting is because of this sequence:
line[n++]=tmp;
printf("\n%s\n",line[n]);
You have already incremented n to the next array element then you try to print it. That second line should be:
printf("\n%s\n",line[n-1]);
If you just want some code that works (with a free "do what you darn well want to" licence), here's a useful snippet from my code library.
I'm not sure why you think fgets is to be avoided, it's actually very handy and very safe. I'm assuming you meant gets which is less handy and totally unsafe. Your code is also prone to buffer overruns as well, since it will happily write beyond the end of your allocated area if it gets a lot of characters that are neither space nor end of file.
By all means, write your own code if you're educating yourself but part of that should be examining production-tested bullet-proof code to see how it can be done. And, if you're not educating yourself, you're doing yourself a disservice by not using freely available code.
The snippet follows:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
printf ("No input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long\n");
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
It's a useful line input function that has the same buffer overflow protection as fgets and can also detect lines entered by the user that are too long. It also throws away the rest of the too-long line so that it doesn't affect the next input operation.
Sample runs with 'hello', CTRLD, and a string that's too big:
pax> ./qq
Enter string> hello
OK [hello]
pax> ./qq
Enter string>
No input
pax> ./qq
Enter string> dfgdfgjdjgdfhggh
Input too long
pax> _
For what it's worth (and don't hand this in as your own work since you'll almost certainly be caught out for plagiarism - any half-decent educator will search for your code on the net as the first thing they do), this is how I'd approach it.
#include <stdio.h>
#include <stdlib.h>
#define WORDLENGTH 15
#define MAXWORDS 1000
int main (void) {
char *line[MAXWORDS];
int numwords = 0; // Use decent variable names.
int chr, i;
// Code to run until end of file.
for (chr = getchar(); chr != EOF;) { // First char.
// This bit gets a word.
char *tmp = malloc(WORDLENGTH + 1); // Allocate space for word/NUL
i = 0;
while ((chr != ' ') && (chr != EOF)) { // Read until space/EOF
if (i < WORDLENGTH) { // If space left in word,
tmp[i++] = chr; // add it
tmp[i] = '\0'; // and null-terminate.
}
chr = getchar(); // Get next character.
}
line[numwords++] = tmp; // Store.
// This bit skips space at end of word.
while ((chr == ' ') && (chr != EOF)) {
chr = getchar();
}
}
// Now we have all our words, print them.
for (i = 0; i < numwords; i++){
printf ("%s\n", line[i]);
free (line[i]);
}
return 0;
}
I suggest you read that and studdy the comments so that you know how it's working. Feel free to ask any questions in the comments section and I'll answer or clarify.
Here's a sample run:
pax$ echo 'hello my name is pax andthisisaverylongword here' | ./testprog
hello
my
name
is
pax
andthisisaveryl
here
Change your printf line - you need to print line[n] rather than line[i].
first your malloc formula is wrong
malloc(sizeof(char)*WORDLENGTH);
you need to allocate the sizeof a char enought times for the lenght of your word (also 15 seems a bit small, your not counting the longest word in the dictionnary or the "iforgettoputspacesinmyphrasestoscrewtheprogrammer" cases lol
don't be shy char is small you can hit 256 or 512 easily ^^
also
printf("\n%s\n",line[i]);
needs to be changed to
int j = 0;
for(j=0;j<i;j++){
printf("\n%s\n",line[j]);
}
your i never changes so you always print the same line