I was looking at the following example in a book, and it seems to me that it will cause stack smashing:
int read_line(char str[], int n)
{
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
str[i++] = ch;
str[i] = '\0';
return i;
}
If I pass it an array with room for 10 chars, and n = 10, the if-statement will be true up to and including 9, and i will be incremented to 10.
Then, it will write the '\0' character at str[10] which would be just past the end of the array?
It works just fine, though (tried building with gcc on Linux, clang on Mac and VS on Windows).
VS on Windows is the only one showing an error when running the program, even though I have tried setting -fstack-protector in e.g. clang.
Your assessment is correct, the code has undefined behavior if the user types n or more bytes before the newline. There is also a problem if the end of file is encountered before the end of the line: the function will then run an infinite loop.
Here is a corrected version:
#include <stdio.h>
int read_line(char str[], int n) {
int ch, i = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (i + 1 < n)
str[i] = ch;
i++;
}
if (n > 0) {
str[i < n ? i : n - 1] = '\0';
}
if (i == 0 && ch == EOF) {
/* end of file: no input */
return -1;
} else {
/* return the complete line length, excluding the newline */
return i;
}
}
int main() {
char buf[50];
int count = read_line(buf, sizeof buf);
if (count < 0) {
printf("Empty file\n");
} else
if (count >= sizeof buf) {
printf("Line was truncated: %s\n", buf);
} else {
printf("Read %d bytes: %s\n", count, buf);
}
return 0;
}
Related
I am currently learning C and working on a problem that breaks input lines into lengths of n. Below is my current code where n is set to 30. When it reaches the n-th index it replaces that index with ' ' and then line breaks, but it will only do it for the first n characters and I'm unsure what isn't getting rest in order to it to continue making a new line at the nth index.
int getline2(void);
int c, len, cut, counter;
char line[MAXLINE];
main() {
while ((len = getline2()) > 0) {
if (len > BREAK) {
c = 0;
counter = 0;
while (c < len) {
if (line[c] == ' ') {
counter = c;
}
if (counter == BREAK) {
line[counter] = '\n';
counter = 0;
}
counter++;
c++;
}
}
printf("%s", line);
}
return 0;
}
int getline2(void) {
int c, i;
extern char line[];
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
line[i] = c; //i gets incremented at the end of the loop
if (c == '\n') {
line[i] = c;
++i;
}
line[i] = '\0';
return i;
}
Your code is a little too complicated:
you do not need to store the bytes read from the file into an array, just output them one at a time, keeping track of the line length
when the line would become too long, output a newline and reset the count before you output the byte.
also not that none of these global variables deserves to be global.
and the prototype for main should be either int main(), int main(void) or int main(int argc, char *argv[]) or equivalent. main()` is an obsolete syntax that should be avoided.
Here is a modified version:
#include <stdio.h>
#define BREAK 30
int main() {
int c;
int len = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
putchar(c);
len = 0;
} else {
if (len >= BREAK) {
putchar('\n');
len = 0;
}
putchar(c);
len++;
}
}
return 0;
}
I'm relatively new in C and I currently reading Kernighan's book.
One of the problems in the book is to create an algorithm that from a input line output the line if it is more than 10 characters long.
The point is I'm frustrated because I cant find what is wrong with my code. I debugged and recreate it many times but still cant find out what's going on!
The escape character from function getl() is '.' (dot), and sometimes works and other times don't. If you compile it and test you will see:
gcc -Wall -o out 'script.c'
The question header from the book is:
“Exercise 1-17. Write a program to print all input lines that are longer than 10 characters.”
I'm sure that's relatively easy, but I really wanted to know why this algorithm is not working as expected, i think it has something to do with '\n'.
If someone could help me find out what's the problem with the code, I would appreciate it.
Code
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c;
int count;
for (count = 0 ; count < lim-1 && (c = getchar()) != '.' ; count++) {
if (c == '\n') {
line[count] = '\n';
count++;
break;
}
line[count] = c;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
getl(line, MAX);
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
Your code almost works. You just seem to have some repeated lines here and there that confuse things.
Specifically, you are calling getl(line, MAX); twice in a row. The first gets the input, but don't save the count, the second has only an empty stdin buffer to work with so no sensible count is saved from that. Removing the first call that don't save the count fixes your issue.
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c = getchar();
int count;
for (count = 0 ; c != '.' ; count++) {
line[count] = c;
c = getchar();
}
line[count++] = '\n';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
First, you're calling your getl function twice instead of once (you only want to read lines one by one). Fixing that should work.
Then I think you shouldn't add the trailing '\n' to your lines, just print it when your line is longer than 10 characters, in your code, the '\n' will be counted as a character.
Here's the modified code:
#include <stdlib.h>
#include <stdio.h>
#define MAX 10000
int getl(char line[])
{
char c;
int count;
for (count = 0; count < MAX - 1 && (c = getchar()) != '.' ; count++)
{
if (c == '\n')
break;
line[count] = c;
}
line[count] = '\0';
return (count);
}
int main()
{
char line[MAX];
int len = 1;
while (len > 0)
{
len = getl(line);
if (len >= 10)
printf("%s, c = %i\n", line, len);
}
return (0);
}
This should work. https://ideone.com/cXXRUH
#include <stdio.h>
#define MAX 10000
int getline_length(char line[]) {
char ch;
int count = 0;
printf("\nWaiting for INPUT...");
// Using clear while loop to get input, removing redundent complexity
// Either `.` or `\n` consider End Of Line
while(count < MAX-1 && ((ch = getchar()) != '.' || (ch = getchar()) != '\n')) {
line[count++]=ch;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
while(1) {
// reset array before each input
memset(line, 0, sizeof(line));
int len = getline_length(line); //No need to pass limit
if (len >= 10) {
printf("%s", line);
} else {
printf("len < 10");
}
}
return 0;
}
The program should print all the input lines which length is longer than MINLINE 5 and shorter than MAXLINE 10. Ref. K&R book exercise 1.17
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
int main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
printf("%s", lines);
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
Desired outpur should be like this :-
Hello\n
Hello\n
hi\n
excuseMe\n
excuseMe\n
longLineNotToBePrinted\n
done
done
but unexpectedly the program printing lines that are far longer than MAXLINE and sometimes printing those omitting some trailing characters.
For starters this function
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
has undefined behavior because it can store the character '\0' at position maxline that is outside the array lines that has the valid range of indices [0, maxline).
As for your question about the output then if you entered a text that is greater than maxline then the function will return a string that does not contain the new line character '\n'. So the next string will be outputted in the same line.
/* Updated code. Now it is working fine.
Problems were both in main() function and in the function getlines().
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
{
/* As the input line can be longer than MAXLINE and in that case there will be no '\n' escape sequence to be stored in the lines[MAXLINE] array so we have used the if block to flow the control in such a way that when the input line is longer than MAXLINE, the output string will be printed manually with a '\n' *newline character. */
if (length > MAXLINE)
printf("%s\n", lines);
else
printf("%s", lines);
}
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, j, c;
i = 0;
for (j = 0; (c = getchar()) != EOF && c != '\n'; ++j)
{
/* In the for loop this time we didn't use the condition 'j < maxline
-1' as getchar() needs to read the whole input line no matter it's length(can be greater than MAXLINE), rather we have used the 'j < maxline -1' condition as a nested if block inside the for loop. While doing this to keep the getchar() function busy reaching the last input character no matter how long the line is we have used two variable i and j to overcome the problem in such a way that i will be used to store characters in the lines[MAXLINE] array, while j will be increased untill it reaches the end of the line. */
if (j < maxline - 1)
{
i = j;
lines[i] = c;
++i;
}
}
if (c == '\n')
{
if (j < maxline - 1)
{
lines[i] = c;
++i;
++j;
}
else
++j;
}
lines[i] = '\0';
return j;
}
I was trying to implement the K&R strindex program. User will be asked to enter a line, if the line contains the string "boi" in it, program will confirm that the line contained the pattern. The problem is, program confirms some other string/strings.
If i enter "şgb" it will confirm that it contains the string "boi". So far, it only happens with "şgb".
https://onlinegdb.com/SyeeO0mzH
#include <stdio.h>
#define MAXLINE_LENGTH 100
char pattern[] = "boi";
int get_line(char line[], int maxlength);
int str_index(char str[], char substr[]);
int main() {
char line[MAXLINE_LENGTH];
while(get_line(line, MAXLINE_LENGTH) > 0) {
if(str_index(line, pattern) >= 0) {
printf("%s", line);
printf("Pattern found above line\n");
}
}
return 0;
}
int get_line(char line[], int maxlength){
int index = 0, character;
while(--maxlength > 0 && (character = getchar()) != EOF && character != '\n') {
line[index++] = character;
}
if(character == '\n') {
line[index++] = character;
}
line[index] = '\0';
return index;
}
int str_index(char str[], char substr[]) {
int i, j, k;
for(i = 0; str[i] != '\0'; i++) {
for(j = i, k = 0; substr[k] != '\0' && str[j] == substr[k]; j++, k++) ;
if(k > 0) {
return i;
}
}
return -1;
}
boi
boi
Pattern found above line
fbajdobadşgbadf
fbajdobadşgbadf
Pattern found above line
şgb
şgb
Pattern found above line
In str_index, if any character in str is the first character in substr, then, when i is such that str[i] is that character, substr[j] == substr[k] will be true in the first iteration of for(j = i, k = 0;…, and k will be incremented. When that loop ends, k > 0 is true, and return i; will be executed.
You need to modify the code so that it returns i only if all the characters in substr have been matched.
I write a program to solve Exercise 2-2 of K&R.
#include<stdio.h>
#define MAXLINE 1000
/* write an equivalent loop without using && or || */
int Getline(char s[], int lim);
int main()
{
int len;
char line[MAXLINE];
while ((len = Getline(line, MAXLINE)) > 0) {
printf("%s%15s%d\n", line, "length: ", len);
}
return 0;
}
int Getline(char s[], int lim)
{
int flag = 1;
int i = 0, c = 0;
for (i = 0; flag == 1; ++i) {
if (i < lim - 1) {
if ((c = getchar()) != '\n') {
if (c != EOF) {
;
}
else {
flag = 0;
}
}
else {
flag = 0;
}
}
else {
flag = 0;
}
if (flag == 1) {
s[i] = c;
}
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
This program is wrong...in a weird way.
I run this code with redirection like
./2-2 <in
with the in file
Get this line.
Then the output to the screen is countless
G length: 1
It looks like the program stuck in a loop. But when I stop using redirection and just type Get this line. to the terminal, though it is still wrong, the countless output disappeared. Why?
The problem is here:
for (i = 0; flag == 1; ++i) {
^^^
i will always increment to at least 1
before the for-loop ends
so your function will never return 0
Instead of incrementing in a for-loop, only increment after inserting a new element. Like
if (flag == 1) {
s[i] = c;
++i;
}
Instead of a for-loop, you could use a while loop, like:
int i = 0;
while (flag == 1)
{
...
}
The next step is to get rid of the flag and use break insted. Like:
int i = 0;
while (1)
{
if (i >= lim - 1) break;
...
}
Your code will be much shorter and easier to read.
You have also complicated your function quite a bit. If you simply want to get the line redirected from the file, store it in line and insure it is nul-terminated (and without the trailing '\n' - which you shouldn't leave dangling off strings you store), you could do something quite simple like:
int Getline (char *s, int lim)
{
int i = 0; /* char count - length */
while (i < lim - 1) { /* loop while chars fit */
int c = getchar(); /* getchar */
if (c == EOF) /* validate EOF? */
goto done;
if (c == '\n') /* validate newline */
continue;
s[i++] = c; /* good char, increment len */
}
done:;
s[i] = 0; /* nul-terminate */
return i; /* return length */
}
(note: from your comment about not having used break before, then a simple goto works just as well)
Example Use/Output
Given your file containing the line "Get this line."
$ ./bin/equivloop <dat/in
Get this line. length: 14
(note: if you store the newline, then the length would be 15 and that output would be on the next line)