So I'm trying to implement a basic FIFO pipeline in C using mkfifo().
Here are my code classes so far:
main.c:
int main(int argc, char *argv[]) {
char *path = "/tmp/fifo";
pid_t pid;
setlinebuf(stdout);
unlink(path);
mkfifo(path, 0600);
pid = fork();
if (pid == 0) {
client(path);
} else {
server(path);
}
return(0);
}
client.c:
void client(char *path) {
char *input;
input = (char *)malloc(200 * sizeof(char));
read(STDIN_FILENO, input, 200);
struct Message message;
message = protocol(input); //protocol simply takes an input string and formats it
char number = message.server;
char* string;
string = message.string;
int fd;
fd = open(path, O_WRONLY);
write(fd, string, sizeof(string));
printf("Client send: %s\n", string);
close(fd);
return;
}
server.c:
void server(char *path) {
int fd;
char *input;
input = (char *)malloc(200 * sizeof(char));
fd = open(path, O_RDONLY);
read(fd, input, sizeof(input));
printf("Server receive: %s\n", input);
close(fd);
return;
}
Now the pipeline is working, but for some reason the server only receives part of the message. For example if we get the following string from the protocol: "HELLO WORLD" We get the following output:
Server receive: HELLO WO
Client send: HELLO WORLD
The server should receive the whole message, but it isn't. What am I doing wrong? Thank you for any help!
I notice you have skimped the usually essential checking of return values from open and read and write. If you had, you might have noticed the error in this line
write(fd, string, sizeof(string));
Because string is a pointer, you are sending 8 bytes (the size of the pointer). You should use strlen(string) or that +1, depending on whether the terminator needs to be sent.
write(fd, string, strlen(string));
You repeat the mistake in your recent unwise edit:
read(fd, input, sizeof(input));
You would be better sticking with the original #define and using that for both buffer allocation and read request size.
Related
My program has a consumer and multiple producers. The producers each read a different file and write their content into a FIFO in N-sized chunks, with a leading parameter for the consumer to interpret.
The consumer is supposed to take these chunks and compose an output file where each line corresponds to one producer. The leading parameter from the chunk is used to determine the owner of the chunk and where to write it (it's a line number number in the output file).
My problem is, even though it works mostly fine when there's one producer, any more make the resulting file a mess. Also there are some unexpected excessive \n but they aren't critical.
This is my expected output:
aaaaa1a aaaaaaa2a aaa3a aaaaaaaaaaa4a
bbbbbbbbbbb1b bbbbbbb2b bbbbbbbbbbbbbb3b bbbbbbb4b bbbbbbbbbb5b bb6b
cccccccccc1c cccc2c cccccccc3c ccccc4c ccccccccc5c ccccccccccccc6c
but that's what I get:
aaaaa1a aaaaaaa2a aaa3a aaaaaaaaaaa4a2 bbbbbbb43 cccccccc53 cccccccc2 bbbbbbbb2 b5b bb6b3 cccc6c2
bbbbbbbbbbb1b bbbbbbb2b bbbbbbbbbbbbbb3b
cccccccccc1c cccc2c cccccccc3c ccccc4c c
There's an unexpected cutoff in the later lines and the chunks become mixed up.
I think it's a problem with how I handle the named pipes, because I'm printing the "raw input" before further processing and I can see that I'm reading invalid data from the pipe. But AFAIK Linux has atomic writes for small chunks of data for FIFO. Maybe the reads aren't caring about the writes and that's where lies the problem?
Consumer code:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
char *filename;
size_t getFileSize(FILE *fp) {
fseek(fp, 0, SEEK_END);
size_t len = ftell(fp);
rewind(fp);
printf("length %ld \n", len);
return len;
}
int nthFunctionCall = 1;
void printFile(FILE *file) {
char *fileContent = NULL;
if(file != NULL) {
size_t size = getFileSize(file);
fileContent = malloc((size /* + 1*/) * sizeof(char));
fread(fileContent, 1, size, file);
//fileContent[size + 1] = '\0'; ?
}
printf("FILE CONTENT: \n%s\n", fileContent);
}
void writeToFile(long targetLineNumber, char *text) {
FILE *temp = fopen("temp", "w");
if(temp == NULL) {
perror("can't create temp");
exit(-1);
}
char *fileContents = NULL;
FILE *file = fopen(filename, "r");
if(file != NULL) {
size_t size = getFileSize(file);
fileContents = malloc((size + 1) * sizeof(char));
fread(fileContents, 1, size, file);
fileContents[size] = '\0'; // tbh, I don't know whether I should do this or not.
fclose(file);
}
char *fileContentsCpy = fileContents;
printf("FILE CONTENT:\n %s\n", fileContents);
printf("%d Text to save %s\n", nthFunctionCall, text);
char *currentLineFromFile;
size_t processedLineNumber;
for (processedLineNumber = 1; (currentLineFromFile = strsep(&fileContents, "\n")) != NULL; processedLineNumber++) {
printf("%d targetLineNumber %ld processedLineNumber %ld \n", nthFunctionCall, targetLineNumber, processedLineNumber);
printf("%d copy the current line into temp: %s\n", nthFunctionCall, currentLineFromFile);
fputs(currentLineFromFile, temp);
if(processedLineNumber == targetLineNumber) {
printf("%d add text to line %ld: %s\n", nthFunctionCall, processedLineNumber, text);
fputs(text, temp);
}
fputs("\n", temp);
fflush(temp);
}
printf("%d Finished loop with: targetLineNumber %ld processedLineNumber %ld \n", nthFunctionCall, targetLineNumber, processedLineNumber);
if(targetLineNumber >= processedLineNumber) {
for (int j = 0; j < (targetLineNumber - processedLineNumber); ++j) {
fputs("\n", temp);
}
printf("%d added text: %s\n", nthFunctionCall, text);
fputs(text, temp);
fflush(temp);
}
fclose(temp);
if(fileContentsCpy != NULL) free(fileContentsCpy);
nthFunctionCall++;
remove(filename);
rename("temp", filename);
printf("One iteration end\n");
}
int numberLength(size_t number) {
int len = 0;
while(number > 0) {
number /= 10;
len++;
}
return len;
}
int main(int argc, char **argv)
{
if (argc < 4) {
fprintf(stderr, "testConsument <fifo_path> <file_to_save_in> <chunk size>\n");
exit(-1);
}
char *myfifo = argv[1];
filename = argv[2];
int numberToRead = atoi(argv[3]);
int fd = open(myfifo, O_RDONLY);
perror("sdada test consument");
char *str1 = calloc(100, sizeof(char));
while (read(fd, str1, numberToRead + 3) > 0) {
long lineNumber;
printf("length: %ld raw input: %s\n", strlen(str1), str1);
sscanf(str1, "%ld", &lineNumber);
char* content = str1 + numberLength(lineNumber) + 1; // lines should be of the format "<number> <chunk-sized-word>\0"
printf("add to line %ld content : %s \n", lineNumber, content);
writeToFile(lineNumber, content);
sleep(1);
free(str1);
str1 = calloc(100, sizeof(char));
printf("#################\n");
}
free(str1);
close(fd);
FILE *res = fopen(filename, "r");
printFile(res);
fclose(res);
return 0;
}
Producer code:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
size_t getFileSize(FILE *fp) {
fseek(fp, 0, SEEK_END);
size_t len = ftell(fp);
rewind(fp);
return len;
}
int main(int argc, char **argv) {
if (argc < 5) {
fprintf(stderr, "producer <fifo_path> <line_number_to_save_in> <input_file> <chunk_size>\n");
exit(-1);
}
char *myfifo = argv[1];
char *lineNumber = argv[2];
int numberToRead = atoi(argv[4]);
mkfifo(myfifo, 0666);
int fd = open(myfifo, O_WRONLY);
char *someFilePath = argv[3];
FILE *somFile = fopen(someFilePath, "r");
char *buf = calloc(numberToRead, sizeof(char));
size_t size = 1;
while ((fread(buf, size, numberToRead, somFile) > 0)) {
char *buf2 = calloc((numberToRead + 3), sizeof(char));
strcat(buf2, lineNumber); strcat(buf2, " "); strcat(buf2, buf); strcat(buf2, "\0");
while (strstr(buf2, "\n")) {
buf2[strcspn(buf2, "\n")] = ' ';
}
printf("SENDING: %s\n", buf2);
fflush(stdout);
write(fd, buf2, numberToRead + 3);
sleep(2);
free(buf);
free(buf2);
buf = calloc(numberToRead, sizeof(char));
}
write(fd, lineNumber, 2);
close(fd);
return 0;
}
After running both the producer and the consumer the communication should start working and after some time there should be an output file. After each such execution you have to manually remove the file, because I didn't really consider the situation where it has existed before.
Example start (each line should be in a different terminal):
./producer '/tmp/fifo3' 3 'file1' 10
./producer '/tmp/fifo3' 2 'file1' 10
./producer '/tmp/fifo3' 1 'file1' 10
./testConsument '/tmp/fifo3' 'output' 10
There are a lot of debug prints, I'm not sure if they are helpful or not but I'm leaving them in.
The problem you face is that having several producers attached to a shared resource (the fifo) you need to control how the accesses are done to be able to control that the consumer gets the data in the proper sequence. The only help you get from the kernel is at the write(2) system call level (the kernel locks the inode of the destination fifo during the time the system call is being executed) So, if you are making short writes, the easiest approach is to group together all the data you are going to put in the fifo and write(2) it all in one single call to write.
If you opt for a more complex solution, then you need to use some kind of mutex/semaphore/whatever to control who has exclusive access to the fifo for writting, as other processes must wait for it to release the lock before starting to write.
Also, don't try to use stdio if you are using this approach. The stdio package only writes data when flushing a buffer, and this happens differently for the output terminal than for a fifo, it depends on the actua buffer size it is using and you don't have an exact and clear idea on when it is happening. This means you cannot use fprintf(3) and friends.
Finally, if you use the atomicity of write(2), then have in mind that a fifo is a limited resource, that can buffer data, and will break a write(2) call if you try to write a big amount of data in one shot (this meaning a single write(2) call) you can get a partial write and you will not be able to recover from this because in the mean time other producers can have access to the fifo and be writting on it (which will break your writting structure) As a rule of thumb, try to recude your messages to a small number of kilobytes (4kb or 8kb being a good upper limit, to be portable to different unices)
The problem laid with the line
write(fd, lineNumber, 2);
near the end in the producer program. It sent unnecessary data which wasn't meaningful in any way and wasn't interpreted properly.
After removing it the program works as intended (except for the unexpected new lines, but they aren't that bad and they have happened before).
I am trying to write a struct to a .dat file and when I open it it shows me this:
"1^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#041720881^#^#^#^#^#^#^#^#^#^#^#Denko^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#Brenko^#^#^#^#^#^#^#^#^#^#^#^#^#^#13.07.2000^#^#^#^#^#^#^#^#^#^#
"
It adds random symbols between the actual values. And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty. But I followed the instructions and guides I read.
Using fwrite or similar is not an option since I have to work with these specific functions write() and read().
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
struct info{
char id[20];
char telefon[20];
char ime[20];
char priimek[20];
char datum[20];
};
int main(int argc, char* argv[]) {
struct info dude =
{
"01",
"041720881",
"Denko",
"Brenko",
"13.07.2000"
};
struct info dude2 =
{
"02",
"041581734",
"Denko",
"Badenko",
"13.07.1990"
};
if(strcmp(argv[1], "-c") == 0){
int fd = open("xpo.dat", O_CREAT| O_APPEND | S_IRWXG, 0666);
if(fd == -1){
perror("Error while creating file");
exit(EXIT_FAILURE);
}
}
else if(strcmp(argv[1], "-o") == 0){
struct stat sizefile;
int fd = open("xpo.dat", O_RDWR);
if(fd == -1){
perror("Error while opening file");
exit(EXIT_FAILURE);
}
fstat(fd,&sizefile);
int wr = write(fd, &dude,sizeof(struct info));
char buf[101];
int sz = read(fd, buf, 100);
buf[sz] = '\0';
if(sz == -1) {
perror("Error while creating file");
exit(EXIT_FAILURE);
}
printf("%s", buf);
int cl = close(fd);
}
return 0;
}
The struct contains 100 chars of data. But you are setting only some of them. When you set ime as Denko, the first six chars are set as 'D', 'e', 'n', 'k', 'o','\0'. The remaining 14 are not initialized (or rather initialized implicitly, see #dave_thompson_085's comment below).
If you want to omit those chars, you cannot write the struct as one block. Either write each field separately, or concatenate the fields into a string and write it instead.
As stated in the comments and in the accepted answer, you have some issues, the why and the what is already talked about and explained.
I would like to add some more information:
And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty.
What happens is that you are reading from the end of the file, if you want to read after you write without closing and reopening the file, you can, but you'll need to reposition the offset of the opened file to the beginning using lseek.
Another thing to note is that if you want to write the data as a structure you then need to read it as a structure as well, not as a string.
Taking that in consideration your code could look more or less like this (skipping return value validations, but you should do it, as you know):
//...
else if(strcmp(argv[1], "-o") == 0){
int fd = open("xpo.dat", O_RDWR);
int wr = write(fd, &dude, sizeof dude);
lseek(fd, 0, SEEK_SET); // set the reading position to the beginning of the file
struct info buf;
wr = read(fd, &buf, sizeof buf);
int cl = close(fd);
printf("%s %s %s %s %s", buf.id, buf.telefon, buf.ime, buf.priimek, buf.datum);
}
//...
If you prefer it as a string you can easily concatenate it using something like snprintf or similar.
In an exercise problem, I am required to build a client program (write first) that opens a .txt file, put each line and the total bytes of each line into a struct variable and then send it out to the server program (read first). Right after this is done, the client program will also receive a struct file (similarly only has char * and int attributes) from the server program.
// Below are global variables in both programs
#define BUFSIZE 1024
struct info_pack
{
char line[BUFSIZE]; // the line to receive messages
int bytes; // the bytes of data transferred
};
char fifo_path[] = "./my_fifo";
struct info_pack info_w; // the info_pack for writing each line in text.txt
struct info_pack info_r; // the info_pack for reading feedback info_pack sent from the server program
First is the client program:
// the main() in the client program
int main()
{
int fd;
int i = 0, index = 1, bytes = 0, line_length, fifo_read;
char *file_path = "/home/text.txt";
FILE *fd2;
mkfifo(fifo_path, 0666);
if ((fd2 = fopen(file_path, "r")) < 0)
{
perror("Opening file");
return -1;
}
else
{
printf("Successfully open the target file\n");
while (fgets(info.line, BUFSIZE, fd2) != NULL)
// the "segmentation fault" error appears right after this line
{
info_w.bytes = strlen(line);
fd = open(fifo_path, O_WRONLY);
printf("The %d th line sent out is: %s\n%d bytes are sent\n\n",
index, info_w.line, info_w.bytes);
write(fd, &info_w, sizeof(info_w) + 1);
close(fd);
fd = open(fifo_path, O_RDONLY);
fifo_read = read(fd, &info_r, sizeof(info_r));
close(fd);
if (fifo_read > 0)
{
printf("Feedback: %s\nand %d bytes are returned\n", info_r.line, info_r.bytes);
}
}
printf("All data is successfully transfered\n");
}
return 0;
}
Then is the server program
// the main() in the server program
int main()
{
int fd, fifo_read;
int line_length;
char *feedback = "SUCCESS";
strcpy(info_w.line, feedback);
info_w.bytes = strlen(feedback);
// define a constant info_pack variable to send to the client program
if (mkfifo(fifo_path, 0666) < 0)
{
perror("client end: ");
exit(-1);
}
while (1)
// This server program will wait for any one single client's message
// This server program can only be terminated by manually input signals (like ^\)
{
fd = open(fifo_path, O_RDONLY);
printf("waiting for client's message\n");
fifo_read = read(fd, &info_r, sizeof(info_r));
close(fd);
if (fifo_read > 0)
// if receive the struct variable, print all of its attributes
{
if (info_r == NULL)
printf("Found no lines sent from the client\n");
else
printf("Read from fifo:\n %s\n(in info)%d bytes read (actually)%d bytes read\n", info_r.line, info_r.bytes, fifo_read);
}
else
{
sleep(1);
printf("Fail to read data from the client\n");
}
// Because of the error in client program this server program
// always pause here
fd = open(fifo_path, O_WRONLY);
printf("Now writing feedback to the client\n");
write(fd, info_w, sizeof(info_w));
close(fd);
}
}
Could anyone explain why the segmentation fault error appears in the client program? Then I can test if the both the client and the server can co-op properly.By the way, I read this post already but, in this post, it is a one-time data stream and I cannot find any hints in it.
I'm trying to send a file descriptor between a socketpair with the code pasted below. This code is from: http://www.techdeviancy.com/uds.html. I am running on Ubuntu 16.04 64-bit.
The problem is that the file descriptor received for my run of the program is "3" and not "4". I also cannot read any data from it in the receiving process. Why is it not working?
The console output looks like this:
Parent at work
FILE TO SEND HAS DESCRIPTOR: 4
Parent read: [[hello phil
]]
Child at play
Read 3!
Done: 0 Success!
Parent exits
Code:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
int send_fd(int socket, int fd_to_send)
{
struct msghdr socket_message;
struct iovec io_vector[1];
struct cmsghdr *control_message = NULL;
char message_buffer[1];
/* storage space needed for an ancillary element with a paylod of length is CMSG_SPACE(sizeof(length)) */
char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];
int available_ancillary_element_buffer_space;
/* at least one vector of one byte must be sent */
message_buffer[0] = 'F';
io_vector[0].iov_base = message_buffer;
io_vector[0].iov_len = 1;
/* initialize socket message */
memset(&socket_message, 0, sizeof(struct msghdr));
socket_message.msg_iov = io_vector;
socket_message.msg_iovlen = 1;
/* provide space for the ancillary data */
available_ancillary_element_buffer_space = CMSG_SPACE(sizeof(int));
memset(ancillary_element_buffer, 0, available_ancillary_element_buffer_space);
socket_message.msg_control = ancillary_element_buffer;
socket_message.msg_controllen = available_ancillary_element_buffer_space;
/* initialize a single ancillary data element for fd passing */
control_message = CMSG_FIRSTHDR(&socket_message);
control_message->cmsg_level = SOL_SOCKET;
control_message->cmsg_type = SCM_RIGHTS;
control_message->cmsg_len = CMSG_LEN(sizeof(int));
*((int *) CMSG_DATA(control_message)) = fd_to_send;
return sendmsg(socket, &socket_message, 0);
}
int recv_fd(int socket)
{
int sent_fd, available_ancillary_element_buffer_space;
struct msghdr socket_message;
struct iovec io_vector[1];
struct cmsghdr *control_message = NULL;
char message_buffer[1];
char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];
/* start clean */
memset(&socket_message, 0, sizeof(struct msghdr));
memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int)));
/* setup a place to fill in message contents */
io_vector[0].iov_base = message_buffer;
io_vector[0].iov_len = 1;
socket_message.msg_iov = io_vector;
socket_message.msg_iovlen = 1;
/* provide space for the ancillary data */
socket_message.msg_control = ancillary_element_buffer;
socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
if(recvmsg(socket, &socket_message, MSG_CMSG_CLOEXEC) < 0)
return -1;
if(message_buffer[0] != 'F')
{
/* this did not originate from the above function */
return -1;
}
if((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
{
/* we did not provide enough space for the ancillary element array */
return -1;
}
/* iterate ancillary elements */
for(control_message = CMSG_FIRSTHDR(&socket_message);
control_message != NULL;
control_message = CMSG_NXTHDR(&socket_message, control_message))
{
if( (control_message->cmsg_level == SOL_SOCKET) &&
(control_message->cmsg_type == SCM_RIGHTS) )
{
sent_fd = *((int *) CMSG_DATA(control_message));
return sent_fd;
}
}
return -1;
}
int main(int argc, char **argv)
{
const char *filename = "/tmp/z7.c";
if (argc > 1)
filename = argv[1];
int sv[2];
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) != 0)
fprintf(stderr,"Failed to create Unix-domain socket pair\n");
int pid = fork();
if (pid > 0) // in parent
{
fprintf(stderr,"Parent at work\n");
close(sv[1]);
int sock = sv[0];
int fd = open(filename, O_RDONLY);
if (fd < 0)
fprintf(stderr,"Failed to open file %s for reading %s\n", filename, strerror(errno));
fprintf(stderr,"FILE TO SEND HAS DESCRIPTOR: %d\n",fd);
/* Read some data to demonstrate that file offset is passed */
char buffer[32];
int nbytes = read(fd, buffer, sizeof(buffer));
if (nbytes > 0)
fprintf(stderr,"Parent read: [[%.*s]]\n", nbytes, buffer);
send_fd(sock, fd);
close(fd);
sleep(4);
fprintf(stderr,"Parent exits\n");
}
else // in child
{
fprintf(stderr,"Child at play\n");
close(sv[0]);
int sock = sv[1];
sleep(2);
int fd = recv_fd(sock);
printf("Read %d!\n", fd);
char buffer[256];
ssize_t nbytes;
while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0) {
fprintf(stderr,"WRITING: %d\n",nbytes);
write(1, buffer, nbytes);
}
printf("Done: %d %s!\n",nbytes,strerror(errno));
close(fd);
}
return 0;
}
The file offset is shared by both processes. So when the parent process reads until EOF, there's nothing left for the child process to read.
This is the same as when two processes inherit a file descriptor from a parent, e.g. the shell command:
{ echo first cat; cat ; echo second cat ; cat ; } < filename
The first cat command will read all of the file, and the second cat will have nothing to read.
Quoting Richard Stevens (Programming UNIX networks):
"It is normal that the descriptor number in the receiving process differs from the descriptor number in the sending process. Passing a descriptor isn't passing the descriptor number, instead a new descriptor is created in the receiving process that points to the same file entry in the kernel as the descriptor that was sent by the transmitting process."
Barmar said was right.
And I complete some code to make thing right.
That is seek to begin of the file:
lseek(fd, 0,SEEK_SET);
Code snippet
int fd = recv_fd(sock);
printf("Read %d!\n", fd);
lseek(fd, 0,SEEK_SET);
char buffer[256];
ssize_t nbytes;
I have a problem at my C-lecture skill practice. My exercise is to read a text document (which is in the same directory like the program) char by char and write it reversed (so from the end to the beginning, char by char) at the Terminal (i have to work at Ubuntu).
Unfortunately it doesn't work - "read" only reads newline-chars (\n).
Can you find my mistake?
#include <sys/stat.h> //mode_t: accessing rights for the file
#include <fcntl.h> //for I/O
#include <unistd.h> //for file descriptors
#include <string.h> //for strlen
short const EXIT_FAILURE = 1;
short const EXIT_SUCCESS = 0;
char const* USAGE_CMD = "usage: write_file filename string_to_write\n";
char const* ERR_OPEN = "error in open\n";
char const* ERR_READ = "error in reading\n";
char const* ERR_CLOSE = "error in close\n";
char const* ERR_WRITE = "error in write\n";
int main(int argc, char** argv){
int fd = open(argv[1], O_RDONLY);
if(fd == -1){
write(STDERR_FILENO, ERR_OPEN, strlen(ERR_OPEN));
return EXIT_FAILURE;
}
int two_char_back = (-1)*sizeof(char); //shift-value for char
int one_back = -1; //shift-value for "no shift"
int length = lseek(fd, one_back, SEEK_END);//setting to one before oef
int i = 0; //for the loop
char buffer;
char* pbuffer = &buffer; //buffer for writing
while (i < length){
if (read(fd, pbuffer, sizeof(buffer)) == -1){ //READING
write(STDERR_FILENO, ERR_READ, strlen(ERR_READ));
return EXIT_FAILURE;
}
if(write(STDOUT_FILENO, pbuffer, sizeof(buffer)) == -1){ //WRITING
write(STDERR_FILENO, ERR_WRITE, strlen(ERR_WRITE));
return EXIT_FAILURE;
}
lseek(fd, two_char_back, SEEK_CUR); //STEPPING
i++;
}
if(close(fd) == -1){ //CLOSING
write(STDERR_FILENO, ERR_CLOSE, strlen(ERR_CLOSE));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
This is wrong:
int two_char_back = (-1)*sizeof(char);
sizeof(char) is 1, you need -2
Haven't tried running it, but looks like two_char_back should be -2. The read advances the cursor, so -1 keeps reading the same one.
Also, just an option, you could make it more efficient by reading the whole file in then reversing it, then writing.
You have a typo in following line:
int two_char_back = (-1)*sizeof(char);
It must be:
int two_char_back = (-2)*sizeof(char);
As read() increments a cursor, you are actually reading the same character all the time e.g:
example text
^
|
After reading:
example text
^
|
After seeking:
example text
^
|
Thanks for your advices a lot!
& Thanks to my colleagues!
Now it works but I created kind of a new version, here it is:
#include <sys/stat.h> //mode_t: accessing rights for the file
#include <fcntl.h> //for I/O
#include <unistd.h> //for file descriptors
#include <string.h> //for strlen
short const EXIT_FAILURE = 1;
short const EXIT_SUCCESS = 0;
char const* USAGE_CMD = "usage: write_file filename string_to_write\n";
char const* ERR_OPEN = "error in open\n";
char const* ERR_READ = "error in reading\n";
char const* ERR_CLOSE = "error in close\n";
char const* ERR_WRITE = "error in write\n";
int main(int argc, char** argv){
int fd = open(argv[1], O_RDONLY); //OPENING
if(fd == -1){
write(STDERR_FILENO, ERR_OPEN, strlen(ERR_OPEN));
return EXIT_FAILURE;
}
int file_size = lseek(fd, 0, SEEK_END); //setting to eof
int i = file_size-1; //for the loop, runs from the end to the start
char buffer;
//the files runs from the end to the back
do{
i--;
lseek(fd, i, SEEK_SET); //STEPPING from the start
if (read(fd, &buffer, sizeof(buffer)) != sizeof(buffer)){ //READING
write(STDERR_FILENO, ERR_READ, strlen(ERR_READ));
return EXIT_FAILURE;
}
if(write(STDOUT_FILENO, &buffer, sizeof(buffer)) != sizeof(buffer)){ //WRITING
write(STDERR_FILENO, ERR_WRITE, strlen(ERR_WRITE));
return EXIT_FAILURE;
}
}while (i != 0);
buffer = '\n';
write(STDOUT_FILENO, &buffer, sizeof(buffer));//no error-det. due to fixed value
if(close(fd) == -1){ //CLOSING
write(STDERR_FILENO, ERR_CLOSE, strlen(ERR_CLOSE));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}