This question already has an answer here:
Print last 10 lines of file or stdin with read write and lseek [closed]
(1 answer)
Closed 7 years ago.
I'm working on an assignment where part of it is to read from stdin using the system function read(), and then print the last 10 lines and so far I've got this:
int tailSTD()
{
char *lines = malloc(1);
char buffer[10];
int cmdCount = 0, buffCount, rState;
while((rState = read(STDOUT_FILENO, buffer, 10)) > 0)
{
if(rState < 0)
{
if(errno == EINTR) rState = 0;
else
{
perror("read()");
return 0;
}
}
else if (rState == 0) break;
lines = realloc(lines, strlen(lines) + strlen(buffer));
for(buffCount = 0; buffCount < strlen(buffer); buffCount++)
{
lines[cmdCount] = buffer[buffCount];
cmdCount++;
}
}
printf("do we get this far?");
printSTDLines(lines);
return 0;
}
The problem is that I get a segmentation fault somewhere along the loop and I\m not sure where, this worked with fgets(), and I simply modified it just because it just HAS to be done with read(). It's probably very messy, for which I apologize, but it just has to be done in this manner. I know the problem is here, because it never gets to the last printf before printSTDLines.
Here's printSTDLines if you need it:
void printSTDLines(char *lines)
{
int lineCount = strlen(lines), newLineCount = 0;
while(newLineCount < 10)
{
if(lines[lineCount] == '\n')
{
newLineCount++;
}
lineCount--;
}
int readSize = strlen(lines) - lineCount;
for(lineCount = readSize; lineCount < sizeof(lines); lineCount++)
{
write(STDOUT_FILENO, &lines[lineCount], 1);
}
}
You are using strlen(lines) but you never nul terminate it. The strlen() function expects to find a '\0' byte to tell where the end of the "string" is, you don't add that byte to any of your arrays and that invokes undefined behavior, one possible consequence of that is a segmentation fault.
Also, this
for(lineCount = readSize; lineCount < sizeof(lines); lineCount++)
seems wrong, the sizeof operator doesn't work for an dynamically allocated array, in this case lines is a pointer and that means sizeof is giving you the size of a pointer and by no means the length of the array which is apparently what you want.
To use the length of the array inside printSTDLine() you need to pass it as a parameter.
Related
I am reading a message from the socket using this tcp_read function:
ssize_t tcp_read(int fd, void *buf, size_t count)
{
size_t nread = 0;
while (count > 0) {
int r = read(fd, buf, count);
if (r < 0 && errno == EINTR)
continue;
if (r < 0)
return r;
if (r == 0)
return nread;
buf = (unsigned char *) buf + r;
count -= r;
nread += r;
}
return nread;
}
In the server.c file I am creating a guessing the word game, I am reading the message from the user using this tcp_read function which is close to the real read().
The user will input something like: "L: someword\r\n" and I am reading this in the following way:
char read[strlen(token2)+5];
char *token2 = "someword"
int n = tcp_read(cfd, read, strlen(token2)+5);
read[n] = '\0';
char *s;
size_t len = strlen(read);
s = malloc(len);
strcpy(s, read+3); //to cut 'R: '
s[len - 4] = 0;
tcp_write(cfd, s, strlen(s));
if(strcmp(token2, read) == 0)
{
tcp_write(cfd, beg_o, strlen(beg_o));
}else
{
tcp_write(cfd, beg_f, strlen(beg_f));
}
token2 is the word that the user is supposed to guess. and beg_o and beg_f are just some printing of 'Congratulation' and 'You failed'.
I did the strlen(token2) + 5, since the token2 is the word that the user should guess + 5 characters for 'R' ':' ' ' '\r' '\n', since those 5 characters are always the same every user inputs like this, only someword is what is dynamic.
In the first iteration its working, but then when the function is called again it is not working anymore (Because the second part of the code is in a big loop)
But I cannot understand why this is not working, sometimes it works sometimes it fails for example the user inputs "L: someword\r\n" and the read in my function is not someword but sometimes is somew sometimes is just so. It is behaving weird.
I'm surprised that compiles, just looking at it. The dynamic array dims are an extension, which you should probably avoid -- just do char read[1024] or something. (Really you should use a #define for that.) But in any case, token2 is not even defined on the first line, so I don't see how the dynamic array dim can be evaluated.
Once you get over that, use a debugger. Step through line by line examining variables until you see where it's gone wrong.
Don't return when r == 0. Wait until you reach count and return only then.
Currently, I have a parent process piping information to children and reading out of their STDOUT through fgetc. I iterate through rounds and store the file pointer from the first round using fdopen and call it again in the next round.
The issue is, in the first round the read_line results in a perfectly fine read, but in the second round the fgetc hits a segfault.
Error Message:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7791330 in __GI___uflow (fp=0x6041b0) at genops.c:381
381 return *(unsigned char *) fp->_IO_read_ptr++;
Reading Function: (I know it's not the best)
To Clarify. It works the first time. Why won't it work the second?
Many other questions seem to just have a different error.
char* read_line(FILE* file,int currentP, Players *players) {
char* result = malloc(sizeof(char) * 80);
int position = 0;
int next = 0;
for (int i = 0; i < players->players[currentP].position; i++) {
next = fgetc(file);
}
while (1) {
next = fgetc(file);
//players->players[currentP].position++;
if (next == '!') {
return "!";
}
if (next == EOF || next == '\n') {
result[position] = '\0';
return result;
} else {
result[position++] = (char)next;
}
}
}
this function ideally takes a string and returns the letters past the first word (or "command") based on an integer. I.e:
input: "write 1234" //valLen = 4 and inputStr = "write 1234"
output: "1234"
"Extractor Function":
char* ExtractValue(unsigned int *valLen, char *inputStr)
{
int sizeToAllocate = strlen(inputStr) - (strlen(inputStr) - *valLen) + 1;
unsigned int i = 0;
int count = 0;
/* memory aloc */
printf("Assigning %d bits of space...\n", sizeToAllocate);
char *outStr = (char*)malloc(sizeToAllocate);
if (!outStr)
{
perror("Error allocating memory");
abort();
}
/*sets final string to value entered*/
for (i = 0; i < strlen(inputStr); i++)
{
if (strlen(inputStr) - i <= *valLen)
{
outStr[count] = inputStr[i];
count++;
}
}
return outStr;
}
And it works for the most part; however, the return string is always proceeded by garbage values for some reason. It seems as if malloc() initializes outstr to already used memory (with a bunch of gibberish in it) and the for loop adds characters to the beginning of it. An example would be:
input: "write 1234"
output: "1234══²²²²¼"
Why would it do that? Any explanations greatly appreciated, thanks!
You answered your own question already! malloc just allocates memory and doesn't bother clearing out whatever was there before. You can use calloc to get a block of memory that's been zeroed out, or modify your loop to add a null character at the end of the string.
/*sets final string to value entered*/
for (i = 0; i < strlen(inputStr); i++)
{
if (strlen(inputStr) - i <= *valLen)
{
outStr[count] = inputStr[i];
count++;
}
}
/* null terminate the string */
outStr[count++] = "\0";
This question already has answers here:
Return char[]/string from a function [duplicate]
(5 answers)
Closed 8 years ago.
I am writing a program that returns a string from stdin, but i am getting warning that it returns an adress of local wariable. How can i return the string?
thanks in advance
#include <stdio.h>
char* readLine()
{
int i;
char input[1024];
for(i=0;i<1024;i++)
{
input[i]=fgetc(stdin);
if(input[i]=='\n')
{
break;
}
}
return input;
}
int main()
{
printf("%s",readLine());
return 0;
}
This should work for you:
You can pass input from main as reference:
#include <stdio.h>
char * readLine(char * input, int length) {
int i;
for(i = 0; i < length; i++) {
input[i] = fgetc(stdin);
input[length] = '\0';
if(input[i] == '\n')
break;
}
return input;
}
int main() {
int length = 1024;
char input[length+1];
printf("%s", readLine(input, length));
return 0;
}
Try to do something like that instead:
#include <stdio.h>
char* readLine()
{
int i;
char *input;
if ((input = malloc(sizeof(char) * 1024)) == NULL)
return (NULL);
for(i=0;i<1024;i++)
{
input[i]=fgetc(stdin);
if(input[i]=='\n')
{
input[i] = '\0';
break;
}
}
return input;
}
int main()
{
char *str;
if (str = readLine()) != NULL) {
printf("%s\n", str);
free(str);
}
return 0;
}
}
There is nothing wrong here - that is just a WARNING because usually it is a common mistake of new programmers. I used to run into problems with this usage all the time.
The first thing... this "string" is not null-terminated. You'll want to put at the end of that function something like *(input + i) = '\0'; and make either the array size 1025 or the condition i < 1023 (so that the null character isn't assigned beyond the end of the buffer), because at the moment using this array in a function that expects null termination will cause it to possibly continue past the end of the array, resulting in a memory access violation. Alternately, you could use memset(input,0,1024);, just still make sure that the condition is something like i < 1023 so that the standard input you receive doesn't end up writing all the way to the last null character in the array.
The other problem is that this memory is local, as in it "belongs" to this function. And for the usage you have here, it is probably just fine to use the same memory... if you plan to call the function, do something with the result, and then call the function again, do something with the result... But if you want to keep what's given to you by it, you'll have to either (1) copy the string to another buffer that isn't going to be written to again when the function is called in the future, or (2) make the function allocate a new buffer each time it runs, and then be sure to delete that memory when you're done with it. For example, instead of char input [1024]; (which by the way would have the same pointer for the life of the program, so it's not really necessary to return it each time) you could write char* input = malloc(1024); and later, when the caller is done with the string, you should free(input);. (Of course, the name might not be input in this case since you would probably not want to free the memory in the function whose purpose is to allocate it.)
I will edit this later with code showing changes.
I'm very new to C and I'm still learning the basics. I'm creating an application that reads in a text file and breaks down the words individually. My intention will be to count the amount of times each word occurs.
Anyway, the last do-while loop in the code below executes fine, and then crashes. This loop prints memory address to this word (pointer) and then prints the word. It accomplishes this fine, and then crashes on the last iteration. My intention is to push this memory address into a singly linked list, albeit once it's stopped crashing.
Also, just a quick mention regarding the array sizes below; I yet figured out how to set the correct size needed to hold the word character array etc because you must define the size before the array is filled, and I don't know how to do this. Hence why I've set them to 1024.
#include<stdio.h>
#include<string.h>
int main (int argc, char **argv) {
FILE * pFile;
int c;
int n = 0;
char *wp;
char wordArray[1024];
char delims[] = " "; // delims spaces in the word array.
char *result = NULL;
result = strtok(wordArray, delims);
char holder[1024];
pFile=fopen (argv[1],"r");
if (pFile == NULL) perror ("Error opening file");
else {
do {
c = fgetc (pFile);
wordArray[n] = c;
n++;
} while (c != EOF);
n = 0;
fclose (pFile);
do {
result = strtok(NULL, delims);
holder[n] = *result; // holder stores the value of 'result', which should be a word.
wp = &holder[n]; // wp points to the address of 'holder' which holds the 'result'.
n++;
printf("Pointer value = %d\n", wp); // Prints the address of holder.
printf("Result is \"%s\"\n", result); // Prints the 'result' which is a word from the array.
//sl_push_front(&wp); // Push address onto stack.
} while (result != NULL);
}
return 0;
}
Please ignore the bad program structure, as I mentioned, I'm new to this!
Thanks
As others have pointed out, your second loop attempts to dereference result before you check for it being NULL. Restructure your code as follows:
result = strtok( wordArray, delims ); // do this *after* you have read data into
// wordArray
while( result != NULL )
{
holder[n] = *result;
...
result = strtok( NULL, delims );
}
Although...
You're attempting to read the entire contents of the file into memory before breaking it up into words; that's not going to work for files bigger than the size of your buffer (currently 1K). If I may make a suggestion, change your code such that you're reading individual words as you go. Here's an example that breaks the input stream up into words delimited by whitespace (blanks, newlines, tabs, etc.) and punctuation (period, comma, etc.):
#include <stdio.h>
#include <ctype.h>
int main(int argc, char **argv)
{
char buffer[1024];
int c;
size_t n = 0;
FILE *input = stdin;
if( argc > 1 )
{
input = fopen( argv[1], "r");
if (!input)
input = stdin;
}
while(( c = fgetc(input)) != EOF )
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
fclose(input);
return 0;
}
No warranties express or implied (having pounded this out before 7:00 a.m.). But it should give you a flavor of how to parse a file as you go. If nothing else, it avoids using strtok, which is not the greatest of tools for parsing input. You should be able to adapt this general structure to your code. For best results, you should abstract that out into its own function:
int getNextWord(FILE *stream, char *buf, size_t bufsize)
{
int c;
size_t n = 0;
while(( c = fgetc(input)) != EOF && n < bufsize)
{
if (isspace(c) || ispunct(c))
{
if (n > 0)
{
buf[n] = 0;
n = 0;
}
}
else
{
buffer[n++] = c;
}
}
if (n > 0)
{
buffer[n] = 0;
printf("read word %s\n", buffer);
}
if (n == 0)
return 0;
else
return 1;
}
and you would call it like
void foo(void)
{
char word[SOME_SIZE];
...
while (getNextWord(inFile, word, sizeof word))
{
do_something_with(word);
}
...
}
If you expect in your do...while code, that result could be null (this is the condition for loop break), how do you think this code-line:
holder[n] = *result;
must work? It seems to me, that it is the reason for crashing in your program.
Change do while loop to while
use
while (condition)
{
}
instead of
do {
}while(condition)
It is crashing because you are trying to derefrance a NULL pointer result in do while loop.
I work mostly with Objective-C and was just looking at your question for fun, but I may have a solution.
Before setting n=0; after your first do-while loop, create another variable called totalWords and set it equal to n, totalWords can be declared anywhere within the file (except within one of the do-while loops), but can be defined at the top to the else block since its lifetime is short:
totalWords = n;
then you can set n back to zero:
n = 0;
Your conditional for the final do-while loop should then say:
...
} while (n <= ++totalWords);
The logic behind the application will thus say, count the words in the file (there are n words, which is the totalWords in the file). When program prints the results to the console, it will run the second do-while loop, which will run until n is one result past the value of totalWords (this ensures that you print the final word).
Alternately, it is better practice and clearer for other programmers to use a loop and a half:
do {
result = strtok(NULL, delims);
holder[n] = *result;
wp = &holder[n];
printf("Pointer value = %d\n", wp);
printf("Result is \"%s\"\n", result);
//sl_push_front(&wp); // Push address onto stack.
if (n == totalWords) break; // This forces the program to exit the do-while after we have printed the last word
n++; // We only need to increment if we have not reached the last word
// if our logic is bad, we will enter an infinite loop, which will tell us while testing that our logic is bad.
} while (true);