I've got a problem reading a couple of lines from a read-only FIFO. In particular, I have to read two lines — a number n, followed by a \n and a string str — and my C program should write str in a write-only FIFO for n times. This is my attempt.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char *readline(int fd);
int main(int argc, char** argv) {
int in = open(argv[1], O_RDONLY);
mkfifo(argv[2], 0666);
int out = open(argv[2] ,O_WRONLY);
char *line = (char *) malloc(50);
int n;
while (1) {
sscanf(readline(in), "%d", &n);
strcpy(line, readline(in));
int i;
for (i = 0; i < n; i++) {
write(out, line, strlen(line));
write(out, "\n", 1);
}
}
close(in);
close(out);
return 0;
}
char *readline(int fd) {
char *c = (char *) malloc(1);
char line[50];
while (read(fd, c, 1) != 0) {
if (strcmp(c, "\n") == 0) {
break;
}
strcat(line, c);
}
return line;
}
The code is working properly, but it puts a random number of newlines after the last string repetition. Also, this number changes at each execution.
Could someone please give me any help?
Besides the facts that reading character wise and and comparing two characters using "string" comparsion both is far from being efficient, readline() returns a pointer to memory being declared local to readline(), that is line[50] The memory gets deallocated as soon as readline() returns, so accessing it afterwards invokes undefine behaviour.
One possibility to fix this is to declare the buffer to read the line into outside readline() and pass a reference to it down like so:
char * readline(int fd, char * line, size_t size)
{
if ((NULL != line) && (0 < size))
{
char c = 0;
size_t i = 0;
while (read(fd, &c, 1) >0)
{
if ('\n' == c) or (size < i) {
break;
}
line[i] = c;
++i;
}
line [i] = 0;
}
return line;
}
And then call it like this:
char * readline(int fd, char * line, size_t size);
int main(void)
{
...
char line[50] = "";
...
... readline(in, line, sizeof(line) - 1) ...
I have not tried running your code, but in your readline function you have not terminated the line with null ('\0') character. once you hit '\n' character you just breaking the while loop and returning the string line. Try adding '\0' character before returning from the function readline.
Click here for more info.
Your code did not work on my machine, and I'd say you're lucky to get any meaningful results at all.
Here are some problems to consider:
readline returns a locally defined static char buffer (line), which will be destroyed when the function ends and the memory it once occupied will be free to be overwritten by other operations.
If line was not set to null bytes on allocation, strcat would treat its garbage values as characters, and could possibly try to write after its end.
You allocate a 1-byte buffer (c), I suspect, just because you need a char* in read. This is unnecessary (see the code below). What's worse, you do not deallocate it before readline exits, and so it leaks memory.
The while(1) loop would re-read the file and re-print it to the output fifo until the end of time.
You're using some "heavy artillery" - namely, strcat and memory allocation - where there are simpler approaches.
Last, some C standard versions may require that you declare all your variables before using them. See this question.
And here's how I modified your code. Note that, if the second line is longer than 50 characters, this code may also not behave well. There are techniques around the buffer limit, but I don't use any in this example:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
char *readline(int fd, char * buffer);
int main(int argc, char** argv) {
int in = open(argv[1], O_RDONLY);
int out;
int n;
int i;
char line[50];
memset(line, 0, 50);
mkfifo(argv[2], 0666);
out = open(argv[2] ,O_WRONLY);
sscanf(readline(in, line), "%d", &n);
strcpy(line, readline(in, line));
for (i = 0; i < n; i++) {
write(out, line, strlen(line));
write(out, "\n", 1);
}
close(in);
close(out);
return 0;
}
char *readline(int fd, char * buffer) {
char c;
int counter = 0;
while (read(fd, &c, 1) != 0) {
if (c == '\n') {
break;
}
buffer[counter++] = c;
}
return buffer;
}
This works on my box as you described. Compiled with GCC 4.8.2 .
Related
I am writing a program which take a line from user and invert case of letters
The following code works fine
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
int main(void) {
printf("Enter line: ");
char *input_ptr;
size_t input_length;
ssize_t read = (int) getline(&input_ptr, (void *) (&input_length), stdin);
if (read != -1) {
int i = 0;
for (; i < input_length; i++) {
int c = *(input_ptr + i);
if (isupper(c)) {
printf("%c", tolower(c));
} else {
printf("%c", toupper(c));
}
}
} else {
puts("Something Wrong Happened ...");
}
return 0;
}
However, when I change the for loop to while loop:
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
int main(void) {
printf("Enter line: ");
char *input_ptr;
size_t input_length;
ssize_t read = (int) getline(&input_ptr, (void *) (&input_length), stdin);
if (read != -1) {
while (*input_ptr != '\0') {
int c = *input_ptr;
input_ptr++;
if (isupper(c)) {
printf("%c", tolower(c));
} else {
printf("%c", toupper(c));
}
}
} else {
puts("Something Wrong Happened ...");
}
return 0;
}
It says segmentation error after I have entered my line.
May I know what happened? Thanks in advance.
You have not initialised input_ptr so the code has undefined behaviour. You might have passed an invalid buffer address to getline. You should also initialise input_length, so
char *input_ptr = NULL;
size_t input_length = 0;
The function getline() expects either a pointer to memory you allocated yourself, or NULL to indicate that the function should allocate memory.
If *lineptr is set to NULL and *n is set 0 before the call, then getline() will allocate a buffer for storing the line. This buffer should be freed by the user program even if getline() failed.
Note, you should not increment a pointer which you intend to free later.
I am trying to read line by line a standard file input.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 1204
char* readLine(char* buffer){
int i = 0;
for(i; i< BUFFER_SIZE; i++){
printf("%c",buffer[i]);
if( '\n' == buffer[i]){
char* line[124];
memcpy( line, &buffer[0], i-1 );
return *line;
}
}
free(buffer);
}
int doStuffWithLine(char* line){
return 1;
}
int main(int argc, char *argv[])
{
ssize_t aux1;
char *buffer = malloc(sizeof(char)*BUFFER_SIZE);
char *line = malloc(sizeof(char)*BUFFER_SIZE);
while((read(STDIN_FILENO, buffer, BUFFER_SIZE))>0){
line = readLine(buffer);
doStuffWithLine(line);
printf("%s", line);
}
return 0;
}
This is the input file content:
lol1
lol2
lol3
And this is the output of my program:
lol1
Segmentation fault (core dumped)
I want to know how read lines 2 and 3, solve it and a little explanation about what I am doing wrong because I do not understand the problem.
Thank you in advance.
Function read reads in raw bytes and will not terminate your buffer with a string termination character '\0'; Using it then for printf("%s",...), which expects a 0-terminated C-string, yields undefined behaviour (e.g. a crash).
I'd suggest to use fgets instead.
First of all, thank you all that helped me and spent some time trying it.
After spending some hours learning and breaking my brain I have found a solution. In conclusion I am ***** and noob.
If someone is having the same problem I am submitting my code. Easy peasy:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
char* doStuff(char* line){
return line;
}
int main(int argc, char *argv[])
{
char *line = malloc(sizeof(char)*LINE_MAX);
while(fgets(line, LINE_MAX, stdin)!= NULL)
{
line = doStuff(line);
printf("%s", line);
}
return 0;
}
I am looking to create an array of pointers to strings read from a file in C. However when I try to print out the strings copied to stdout, the last line of the file is always left out.
The program also sometimes experiences a segmentation fault which I haven't been able to completely eliminated. It happens about 2 out of 5 times.
Here is my input.c code:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h"
#define MAXLINES 5000
void writelines(char *arr[], int l);
char *read_lines[MAXLINES];
void get_input(const char *fp) {
FILE *contents;
char *line;
char *temp;
size_t len;
ssize_t read;
int i;
i = 0;
contents = fopen(fp, "r");
if (contents == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, contents)) != -1) {
if ((temp = (char *) malloc(strlen(line) + 1)) == NULL) {
printf("Could not allocate required memory.");
exit(EXIT_FAILURE);
}
else {
line[strlen(line) - 1] = '\0';
strcpy(temp, line);
read_lines[i++] = temp;
}
}
fclose(contents);
free(line);
free(temp);
writelines(read_lines, i);
exit(EXIT_SUCCESS);
}
void writelines(char *arr[], int l) {
int i;
for (i = 0; i < l; i++) {
printf("%s\n", arr[i]);
}
}
My main.c file is:
#include <stdio.h>
#include "input.h"
int main(int argc, char *argv[]) {
if (argc == 1)
printf("Please provide a valid source code file.\n");
else
get_input(*(++argv));
return 0;
}
I compile using gcc main.c input.c -Wall with no warnings or errors.
Using gdb I can confirm that the process runs normally.
When it experiences a segmentation fault, the back trace shows a call to strlen that apparently fails.
from the documentation:
If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)
but in your case you're passing an uninitialized value to getline the first time, so getline thinks it can write to that illegal location and this is undefined behaviour (which explains the "It happens about 2 out of 5 times" thing)
The first fix should be to initialize line:
char *line = NULL;
then, why are you creating a copy of line, and you're not freeing line (memory leak) and you're not resetting it to NULL. So next time getline reuses the previous buffer, which may not be long enough to hold the next line.
The fix is just to store the line:
read_lines[i++] = line;
then set line = NULL so getline allocates the proper len for next line. And drop the malloc code, it's useless.
fixed part (you don't need to pass pointer on len it is ignored):
line = NULL;
while ((read = getline(&line, NULL, contents)) != -1) {
read_lines[i++] = line;
line[strcspn(line, "\n")] = 0; // strip off linefeed if there's one
line = NULL;
}
(linefeed strip adapted from Removing trailing newline character from fgets() input)
I am asked to implement my own shell for an Operating System class.
My shell runs every commands fine, except ls that won't return on execve, which is weird because cd, cp, mv, and all the others main commands are returning okay.
ls is still displaying the right output (the list of files in the folder), but just keep running after (execve hangs and needs a carriage return to finish).
All the options like -l, -a are also working correctly, with the same issue.
EDIT: I modified my code in order to completely avoid any memory leaks (I used valgrind to track them), added some comments so you can see what's going on, but ls is still not returning. Here is the updated version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAXPATHLEN 40
#define MAXSIZE 100
#define MAXARGS 10
static char cwd[MAXPATHLEN];
typedef void (*sighandler_t)(int);
void handle_signal(int signo);
void parse_command(char *command, char **arguments);
int main(int argc, char *argv[], char *envp[])
{
int status;
char *command;
char **arguments;
signal(SIGINT, SIG_IGN);
signal(SIGINT, handle_signal);
while(1) {
//Allocating memory
command = calloc(MAXSIZE, sizeof(char));
arguments = calloc(MAXARGS, sizeof(char *));
//Print shell name and cwd
getcwd(cwd,MAXPATHLEN);
printf("[MY_SHELL]:%s$ ", cwd);
parse_command(command, arguments);
//Displays command and arguments
printf("Command is %s\n", command);
int i;
for(i=0; arguments[i] != NULL; ++i){
printf("Argument %d is %s\n", i, arguments[i]);
}
//Fork exec code
if (fork() != 0){
waitpid(1, &status, 0);
} else{
execve(command, arguments, 0);
}
free(command);
for (i=0; arguments[i] != NULL; ++i) {
free(arguments[i]);
}
free(arguments);
}
return 0;
}
void handle_signal(int signo)
{
getcwd(cwd,MAXPATHLEN);
printf("\n[MY_SHELL]:%s$ ", cwd);
fflush(stdout);
}
void parse_command(char *command, char **arguments){
char buf[MAXSIZE];
char env[MAXPATHLEN];
char *tmp;
//Initiate array values to avoid buffer overflows
memset(buf, 0, sizeof(buf));
memset(env, 0, sizeof(env));
//Read command and put it in a buffer
char c = '\0';
int N = 0; //Number of chars in input - shouldn't be more than MAXSIZE
while(1) {
c = getchar();
if (c == '\n')
break;
else{
if (N == MAXSIZE)
break;
buf[N] = c;
}
++N;
}
//Extract command name (e.g "ls"), fetch path to command, append it to command name
tmp = strtok(buf, " ");
strcpy(env, "/bin/");
size_t len1 = strlen(env);
size_t len2 = strlen(tmp);
memcpy(command, env, len1);
memcpy(command + len1, tmp, len2);
//Extracts arguments array: arguments[0] is path+command name
arguments[0] = calloc(strlen(command) + 1, sizeof(char));
strcpy(arguments[0], command);
int i = 1;
while(1){
tmp = strtok(NULL, " ");
if (tmp == NULL)
break;
else{
arguments[i] = calloc(strlen(tmp) + 1, sizeof(char));
strcpy(arguments[i],tmp);
++i;
}
}
}
EDIT 2: This seems to have something to do with STDIN (or STDOUT): similarily than ls, cat makes execve hangs after executing, and I need to carriage return to have my shell line [MY_SHELL]current_working_directory$: line back. Any thoughts on why it is the case ?
In your code, in parse_command() function, you're doing
bzero(arguments, sizeof(char) * MAXARGS);
but at that point of time, arguments is not initialized or allocated memory. So essentially you're trying to write into uninitialized memory. This invokes undefined behaviour.
Same like that, without allocating memory to arguments, you're accessing arguments[0].
Note: As I already mentioned in my comments, do not cast the return value of malloc() and family.
C uses pass by value. That means that after the call to parse_command the value of arguments will still be undefined, since any assignments were made to the local copy. Instead of becoming a three-star programmer I would recommend that you have parse_command return the argument list instead:
char **parse_command(char *command){
char **arguments = malloc(...);
...
return arguments;
}
And in main:
arguments = parse_command(command);
Also look at Sourav Ghosh's answer as he points out some other bugs.
This a code that would reverse the data of a document and save it in the same document itself.
However I am getting a Segmentation Fault.Please Help,I don't know why it gives a SegFault.
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main (int argc,char* argv[])
{
int fd,n,i,j;
char* buf;
if(argc<2)
printf("USAGE: %s file-to-reverse.\n",argv[0]);
fd=open(argv[1], O_RDWR);
if(fd==-1)
printf("ERROR: Cannot reverse %s,file does not exist.\n",argv[1]);
i = 0;
j = n-1;
while(i < j)
{
read(fd,buf,n);
char ib = buf[i];
char jb = buf[j];
jb = i++;
ib = j--;
write(fd,buf,n);
}
free(buf);
close(fd);
}
EDIT1
I tried adding :
#include <sys/stat.h>
struct stat fs;
fstat(fd, &fs);
n= fs.st_size;
buf = malloc(n * sizeof (char));
but now it just duplicates the characters inside the document again and again instead of
reversing them.
You don't allocate, nor initialize buf.
You never initialized n so it could be anything, even negative. Use fstat or some other method to determine the size of the file and store that in n.
Your buffer isn't allocated and n = 0 so you will try to read 0 chars.
This should repair your code :
buf = malloc(10 * sizeof (char));
n = 10;
Resources :
Wikipedia - malloc()
linux.die.net - malloc()
linux.die.net - read()
Regarding your second EDIT - your loop is wrong.
(1) Take the read & write out of the loop - that's why it keeps writing again & again.
(2) You need to seek back to the beginning of the file, otherwise you will just be appending the new data to the end of the file.
(3) You actually have to reverse the chars in the buffer before writing them out.
read(fd, buf, n);
while (i < j)
{
char t = buf[i];
buf[i] = buf[j];
buf[j] = t;
i++;
j--;
}
lseek(fd, 0, SEEK_SET);
write(fd, buf, n);