Is there a way to make this code concise? - c

I'm currently learning and practicing c, but the exercise I'm doing wants each functions to have 25 lines limit (without changing { } or using single-line if statements!)
Please help if there's a way to make this even shorter.
void ft_write_file(void)
{
char c;
int fd;
int i;
i = 0;
if ((fd = open("write_exam", O_WRONLY | O_TRUNC | O_CREAT, 00777)) == -1)
{
ft_putstr("map error");
return ;
}
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
ft_allocate_g_var(i, c, 0);
i++;
}
int j = 0;
while (j < g_line)
{
while (read(0, &c, 1))
{
write(fd, &c, 1);
if (c == '\n')
break ;
}
j++;
}
close(fd);
}

To start with, don't try to do two things in one function. And try to write your functions with sensible arguments instead of hard-coding their subjects.
For example, your function is really doing two things:
Finding and potentially creating the output file (with a hard-coded name).
Copying the entire contents of one stream (hard-coded to stdin) to another stream.
So you could break that down: (prototypes only)
/* Returns fd or -1 on error */
int open_output(const char* name);
/* Returns number of bytes copied or -1 on error */
ssize_t copy_fd(int fd_dest, int fd_source);
Then your driver could be:
ssize_t copy_stdin_to_file(const char *name)
{
int fd = open_output(name);
if (fd < 0)
{
ft_putstr("Could not open output file");
return -1;
}
ssize_t copied = copy_fd(fd, 0);
if (copied < 0) {
ft_putstr("Could not write data.");
return copied;
}
}

A simple way would be to declare all the variables at the top in one line, for exemple :
char c; int fd; int i; i = 0;
Except from that I dont know, hope it can help a bit a least !

Related

Changing STDOUT to file in ncat source code

I managed to compile ncat. I am using -k option to keep server open. Instead of accepting data to STDOUT, my goal is to write to files instead. So far I was able to write to a file instead of STDOUT but my goal is to loop through new files on each new connection. Right now it is appending to the same filename_0 and f++ is not incrementing. Here is what I have so far. The original code will be below. The difference is in the else clause, basically if n is actually greater than 0. On each loop, n is 512 bytes until the last chunk. I just want to be able to have new files from each new connection. filename_0, filename_1, filename_3, etc.
MODIFIED CODE:
/* Read from a client socket and write to stdout. Return the number of bytes
read from the socket, or -1 on error. */
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
int f = 0;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n, s;
n = ncat_recv(fdn, buf, 512, &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
char filename[20];
snprintf(filename, sizeof(char) * 20, "filename_%i", f);
FILE *fp = fopen(filename, "a");
if (fp == NULL)
{
printf("Could not open file");
return 0;
}
//Write(STDOUT_FILENO, buf, n);
s = fwrite(buf, 1, n, fp);
fclose(fp);
f++;
nbytes += n;
}
} while (pending);
return nbytes;
}
ORIGINAL CODE:
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n;
n = ncat_recv(fdn, buf, sizeof(buf), &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
Write(STDOUT_FILENO, buf, n);
nbytes += n;
}
} while (pending);
return nbytes;
}
I was able to figure out using the other functions involved. i passed a pointer into this function to write to it. the handler is a function i added the open() file pointer to.

copying contents of file to another file n bytes at a time in c

Trying to copy the contents of a file to another file by copying n bytes at a time in c. I believe the code below works for copying one byte at a time but am not sure how to make it work for n number of bytes, have tried making a character array of size n and changing the read/write functions to read(sourceFile , &c, n) and write(destFile , &c, n), but the buffer doesn't appear to work that way.
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
void File_Copy(int sourceFile, int destFile, int n){
char c;
while(read(sourceFile , &c, 1) != 0){
write(destFile , &c, 1);
}
}
int main(){
int fd, fd_destination;
fd = open("source_file.txt", O_RDONLY); //opening files to be read/created and written to
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT);
clock_t begin = clock(); //starting clock to time the copying function
File_Copy(fd, fd_destination, 100); //copy function
clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; //timing display
return 0;
}
how to make it work for n number of bytes
Just read N number of bytes and copy that many bytes that you successfully read.
#define N 4096
void File_Copy(int sourceFile, int destFile, int n){
char c[N];
const size_t csize = sizeof(c)/sizeof(*c);
while (1) {
const ssize_t readed = read(sourceFile, c, csize);
if (readed <= 0) {
// nothing more to read
break;
}
// copy to destination that many bytes we read
const ssize_t written = write(destFile, c, readed);
if (written != readed) {
// we didn't transfer everything and destFile should be blocking
// handle error
abort();
}
}
}
You want to copy a buffer of size n at once:
void File_Copy(int sourceFile, int destFile, int n){
char c[n];
ssize_t st;
while((st = read(sourceFile , c, n)) > 0){
write(destFile , c, st);
}
}
Note, that not necessarily n bytes are always copied at once, it might be less. And you also have to check the return value of write() and handle the situation, when less bytes were written, as it fits your needs.
One example is a loop:
while (st > 0) {
int w = write(destFile, c, st);
if (w < 0) {
perror("write");
return;
}
st -= w;
}
Another issue: When you create the destination file here
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT);
you do not specify the third mode parameter. This leads to a random mode, which might lead to this open() to fail the next time. So better add a valid mode, for example like this:
fd_destination = open("destination_file.txt", O_RDWR | O_CREAT, 0644);
This might have distorted your test results.
This is my version using lseek (no loop required):
It relies on read and write always processing the complete buffer and never a part of it (I don't know if this is guaranteed).
void File_Copy(int sourceFile, int destFile)
{
off_t s = lseek(sourceFile, 0, SEEK_END);
lseek(sourceFile, 0, SEEK_SET);
char* c = malloc(s);
if (read(sourceFile, c, s) == s)
write(destFile, c, s);
free(c);
}
The following code does not rely on this assumption and can also be used for file descriptors not supporting lseek.
void File_Copy(int sourceFile, int destFile, int n)
{
char* c = malloc(n);
while (1)
{
ssize_t readStatus = read(sourceFile, c, n);
if (readStatus == -1)
{
printf("error, read returned -1, errno: %d\n", errno);
return;
}
if (readStatus == 0)
break; // EOF
ssize_t bytesWritten = 0;
while (bytesWritten != readStatus)
{
ssize_t writeStatus = write(destFile, c + bytesWritten, readStatus - bytesWritten);
if (writeStatus == -1)
{
printf("error, write returned -1, errno is %d\n", errno);
return;
}
bytesWritten += writeStatus;
if (bytesWritten > readStatus) // should not be possible
{
printf("how did 'bytesWritten > readStatus' happen?");
return;
}
}
}
free(c);
}
On my system (PCIe SSD) I get best performance with a buffer between 1MB and 4MB (you can also use dd to find this size). Bigger buffers don't make sense. And you need big files (try 50GB) to see the effect.

Bad file number in simple main function with pipes

I am still a baby in C world and I was doing some "system" programming in order to do some exercises when I stumbled upon some error which is obvious, but I can't find the problem within my application
This is the code
const int __WRITE_ERROR = 44;
const int __READ_ERROR = 43;
const int READ = 0;
const int WRITE = 1;
const int MAX = 1024;
int main(int argc, char *argv[]) {
int fd[2], n;
char buff[MAX];
if(pipe(&fd[2]) < 0)
exit(__PIPE_ERROR);
printf("Hello, from pipe: write: %d and read: %d\n", fd[WRITE], fd[READ]);
if(write(fd[WRITE], "Hello World\n", 12) != 12) {
printf("Explanation: %i", errno); // <- constantly comes here with errno 9 for some reason.
exit(__WRITE_ERROR);
}
if((n = read(fd[READ], buff, MAX)) != 0)
exit(__READ_ERROR);
write(1, buff, n);
exit(0);
}
Could you help me out, cause I ran out of options, thanks.
There is a problem with:
if (pipe(&fd[2]) < 0)
It should be instead:
if (pipe(fd) < 0)
The former is passing to pipe() the address of one element past of the bounds of the array fd (i.e.: the &fd[2]).
Also, read() and write() return the number of bytes read or written, respectively. However, if an error occurs -1 is returned for both functions.
As someone already pointed out first mistake was
if (pipe(&fd[2]) < 0)
had to be
if (pipe(fd) < 0)
then read was failing because of wrong conition
if((n = read(fd[READ], buff, MAX)) != 0)
should have been
if((n = read(fd[READ], buff, MAX)) <= 0)

How to prevent infinite loop writing to file

This is a c code to create a copy of a file or directory, compiled using gcc in fedora 19. It runs but it doesn't stop and I can see the new file created keeps on increasing in size ridiculously. What is wrong with this code?
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
char buffer[2048];
int version = 1;
int main(int argc, char *argv[])
{
void copy (int, int);
int fdold, fdnew;
if (argc != 3)
{
printf("insufficient arguments !!! \n");
return (1);
}
fdold = open(argv[1], O_RDONLY);
if (fdold == -1)
{
printf("cannot open file %s\n",argv[1]);
return (1);
}
fdnew = creat(argv[2],0666);
if (fdnew == -1)
{
printf("cannot create file %s\n",argv[2]);
return (1);
}
copy (fdold, fdnew)
return (0);
}
void copy (int fdold, int fdnew)
{
int count;
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
}
You never update count and you keep writing the same data over and over again. In this code:
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
You read from the file descriptor once, pulling in count bytes, and while it's greater than 0 (which is presumably is), you keep writing the buffer out to the new file. You never read any more data from the old file. If you can see the file getting bigger and bigger, you might also be able to see (depending on how the content gets flushed to disk) the same content repeated over and over again.
What you actually need to be doing is something more like this:
while there is data to read from the old file
read it into a buffer
while there is data in the buffer to write to the new file
write data from the buffer into the new file
In slightly less pseudo-codish, but highly untested form, I think you'd be looking for something sort of like this:
int count = 0;
int written;
while ( 0 < (count = read(fdold, buffer, sizeof(buffer))) ) {
written = 0;
while ( count > (written += write(fdnew, buffer+written, count-written))) );
}
The outer loop makes sure that you read until there's nothing left to read, and the inner while make sure that you call write until written is as big as count, that is, until you've written all the bytes that you read. This is “clever” code, but it's actually too clever; you actually need to check whether written is -1, or else you'll start doing some strange things.
Something with more error checking, and hopefully more idiomatic, might be:
for ( int count_in = -1; count_in != 0; ) {
if ( -1 == (count_in = read(fd, buf, bufsize))) {
perror("Problem reading from file");
exit(-1);
}
else {
for ( int count_out = 0, out = 0; count_out < count_in; count_out += out ) {
if ( -1 == (out = write(fd, buf+count_out, count_in-count_out)) ) {
perror("Problem writing to file");
exit(-1);
}
}
}
}

Pipe communication in C problems

I'm trying to write some code which uses pipes to communicate between a parent process and it's children. However, my pipe seems to give up after the first time I use it (that is, it stops working after the first use of the pipe). I'm not really sure how to fix this problem, and any help will be greatly appreciated. I also know that some of the coding practice I am using in this are not really ideal (mainly use of sleep).
const int READ = 0;
const int WRITE = 1;
char* COOP = "Criminal cooperates\n";
char* SIL = "Criminal doesn't talk\n";
char* reader(int);
void writer(int, char *c);
int main()
{
int c1pipe1[2];
int c1pipe2[2];
int c2pipe1[2];
int c2pipe2[2];
int c1sentence = 0;
int c2sentence = 0;
int r;
int c;
pipe(c1pipe1);
pipe(c1pipe2);
pipe(c2pipe1);
pipe(c2pipe2);
int C2;
int C1 = fork();
if(C1 > 0)
C2 = fork();
if(C1 < 0 || C2 < 0) //error
{
perror("fork() failed");
exit(1);
}
else if(C1 == 0)
{
close(c1pipe1[WRITE]);
close(c1pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C1 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c1pipe2[WRITE], "1");
else
writer(c1pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else if(C2 == 0)
{
close(c2pipe1[WRITE]);
close(c2pipe2[READ]);
for(c = 0; c < 10; c++)
{
r = rand();
//printf("C2 rand = %d\n", r%2);
if(r % 2 == 1)
writer(c2pipe2[WRITE], "1");
else
writer(c2pipe2[WRITE], "0");
sleep(1);
}
exit(0);
}
else //parent
{
int buff1; //stores choice of c1
int buff2; //stores choice of c2
close(c1pipe1[READ]);
close(c1pipe2[WRITE]);
close(c2pipe1[READ]);
close(c2pipe2[WRITE]);
for(c = 0; c< 10; c++)
{
buff1 = atoi(reader(c1pipe2[READ]));
buff2 = atoi(reader(c2pipe2[READ]));
printf("C1's \(%d)\ choice trial %d : %d\n", C1, c+1, buff1);
printf("C2's \(%d)\ choice trial %d : %d\n", C2, c+1, buff2);
if(buff1 && buff2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if(buff1 || buff2) // one cooperates, one is silent
{
if(buff1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
else if(!(buff1 && buff2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
sleep(1);
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
exit(0);
}
exit(0);
}
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
char* reader(int pipe_read_fd)
{
open(pipe_read_fd);
// Allocate buffer to store
// result of read
int buffer_size = 1024;
char buffer[buffer_size];
// Keep reading until we exhaust
// buffer or reach end of file
int i = 0;
while (i < buffer_size
&& read(pipe_read_fd, &buffer[i], 1) > 0)
{ i++; }
if (i < buffer_size) {
// Add null termination
buffer[i] = '\0';
} else {
// We exhausted buffer
fprintf(stderr, "Warning: buffer full.\n");
buffer[buffer_size-1] = '\0';
}
//printf("%s", buffer);
// Close the pipe
close(pipe_read_fd);
return buffer;
}
You need to close more of the pipes. The child processes must close every pipe file descriptor that they are not using. You have 8 pipe file descriptors; each child process has to close 6 of those - at least! You would be very well advised not to create all the pipes up front as you have done - it is complicated to control things and get all the right descriptors closed.
Looking at the code more closely, the parent does not write messages to the child processes, so you have twice as many pipes as you need - you only need one pipe for each child process to write back to the parent with.
You also do not open() already open file descriptors to the pipes...but how did you get the code to compile? You must be missing the correct header (#include <fcntl.h>) for open() and compiling without enough warning options enabled.
Your variables COOP and SIL are unused in the code presented.
Your writer() function not only mistakenly tries to open an already closed file descriptor, it also closes it, which means that there is no way to send back the extra messages after the first. You should only close the file descriptor once finished - after the loop in the main program for each child. This is why you only see one message.
It is also worth getting into the habit of error-checking the return from every system call that can fail. There are a few that can't fail - getpid() is one such. But I/O operations are notorious for failing for reasons outside the direct control of the program (or, in this case, within the control of the program), so you should check that writes succeed. When you get back an EBADF - bad file descriptor - error, you know something is up.
You have similar problems with close() (and open()) in reader(), plus the additional problem that you attempt to return a pointer to a local automatic variable - which is not a good idea, ever. Again, a decent compiler (like GCC) with warnings enabled will tell you about such things. I used this command to compile your program:
gcc -O -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
pipe.c -o pipe
Your child processes are always going to generate the same sequence of (pseudo-)random numbers, which isn't very exciting. You should probably use something like:
srand(getpid());
to ensure they get different sequences.
Your reader() function is both not enthusiastic enough and too enthusiastic about reading the data. You read a single byte at a time, but you then loop to accumulate single bytes, so the code waits around for all 10 results to be known, and then spits everything out at once. Since a 32-bit integer can store a number up to 1,111,111,111 without problem, you would get just one number back from your call to atoi() on the first iteration, which isn't quite what you wanted.
Reads and writes on pipes are atomic - in the sense that if the writing process writes 6 bytes and the reading process attempts to read more than 6 bytes, then the packet of 6 bytes will be returned by a single read, even if there are other bytes in the pipe waiting to be read; those extra bytes will be returned on subsequent calls to read().
So, your reader() function should be passed in a buffer to use, along with its size; the code should attempt to read that buffer size; it should null terminate what it does receive; it can return the pointer to the buffer it was passed; it should error check the returned value from read().
The code for the two child processes is essentially the same - you should use an appropriately parameterized function rather than writing out the code twice.
Putting it all together, you end up with something like this (which works fine for me on MacOS X 10.6.6 with GCC 4.5.2):
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
const int READ = 0;
const int WRITE = 1;
static char* reader(int fd, char *buffer, size_t bufsiz);
static void writer(int fd, const char *c);
static void child_process(int *my_pipe, int *his_pipe);
static void err_exit(const char *fmt, ...)
{
va_list args;
int errnum = errno;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int c1pipe[2];
int c2pipe[2];
int c1sentence = 0;
int c2sentence = 0;
int c;
if (pipe(c1pipe) != 0 || pipe(c2pipe) != 0)
err_exit("Failed to open a pipe\n");
int C2 = 0;
int C1 = fork();
if (C1 > 0)
C2 = fork();
if (C1 < 0 || C2 < 0) //error
err_exit("fork() failed\n");
else if (C1 == 0)
child_process(c1pipe, c2pipe);
else if (C2 == 0)
child_process(c2pipe, c1pipe);
else //parent
{
int choice1; //stores choice of c1
int choice2; //stores choice of c2
char buffer1[BUFSIZ];
char buffer2[BUFSIZ];
close(c1pipe[WRITE]);
close(c2pipe[WRITE]);
for (c = 0; c< 10; c++)
{
choice1 = atoi(reader(c1pipe[READ], buffer1, sizeof(buffer1)));
choice2 = atoi(reader(c2pipe[READ], buffer2, sizeof(buffer1)));
printf("C1's (%d) choice trial %d : %d\n", C1, c+1, choice1);
printf("C2's (%d) choice trial %d : %d\n", C2, c+1, choice2);
if (choice1 && choice2) //c1 and c2 cooperate with police
{
c1sentence = c1sentence + 6;
c2sentence = c2sentence + 6;
}
else if (!(choice1 && choice2)) //both c1 and c2 are silent
{
c1sentence = c1sentence + 1;
c2sentence = c2sentence + 1;
}
else if (choice1) // if c1 cooperates and c2 is silent
{
c1sentence = c1sentence + 0;
c2sentence = c2sentence + 10;
}
else // if c2 cooperates and c1 is silent
{
c1sentence = c1sentence + 10;
c2sentence = c2sentence + 0;
}
}
printf("C1 is in jail for %d years total\n", c1sentence);
printf("C2 is in jail for %d years total\n", c2sentence);
}
return(0);
}
static void writer(int pipe_write_fd, const char *c)
{
int len = strlen(c);
if (write(pipe_write_fd, c, len) != len)
err_exit("Write failed\n");
}
static char* reader(int pipe_read_fd, char *buffer, size_t bufsiz)
{
int i = read(pipe_read_fd, buffer, bufsiz-1);
if (i < 0)
err_exit("Read failed\n");
buffer[i] = '\0';
return buffer;
}
static void child_process(int *my_pipe, int *his_pipe)
{
int c;
srand(getpid());
close(my_pipe[READ]);
close(his_pipe[READ]);
close(his_pipe[WRITE]);
for (c = 0; c < 10; c++)
{
writer(my_pipe[WRITE], ((rand() % 2) == 1) ? "1" : "0");
sleep(1);
}
close(my_pipe[WRITE]);
}
Note how the error routine captures errno early - to avoid damaging it. It is one of the perils of using global variables; they may change when you call a function. Don't use them when you can avoid them (but note that you can't avoid using errno completely, in general).
void writer(int pipe_write_fd, char *c)
{
open(pipe_write_fd);
char* choice = c;
// Write to the pipe
write(pipe_write_fd, choice, strlen(choice));
// Close the pipe
// (Sends 'end of file' to reader)
close(pipe_write_fd);
}
I'm not sure which function open you are trying yo use but the usual one takes a filename and returns a file descriptor. In any case you are discarding the return value so I suppose that this doesn't matter.
What is clear is that you close the pipe immediately after the first write so it is "correct" that the next write will fail; the pipe has been closed.
If you fix this problem then the next problem is that reader will, one byte at a time, all of the available input - up to 1024 bytes - before closing the read pipe. As reader is called in a loop, the read attempt in the second iteration will fail.

Resources