Writing my own Cat function in C
Asked Answered
P

1

6

Hi i don't know how to simulate my own Cat function in C, i know how it works when no arguments are set and i already get it, but my problem is when i tried to open a file and then print itself...

my code until now:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int main(int argc, char* argv[])
{  
    char *a1 = (char*) malloc (sizeof(char));
    int sz, fd,cont=0, cont1=0;
    char *b1 = (char*) malloc (sizeof(char));
    //char *a2 = (char*) malloc (sizeof(char));
    char * a2;
    char *b2 = (char*) malloc (sizeof(char));

    // NO PARAMETERS
    while (argc == 1){      
        sz=read(0, a1, 1);
        b1[cont]=a1[0];

        if(b1[cont]=='\n'){
            write(1,b1,cont);
            write(1,"\n",1);
            b1=NULL;            
        }

        cont=cont+1;
        b1=(char*) realloc(b1, sizeof(char)*cont);
      }

    // 1 PARAMETER (FILE)   /*Here is the problem*/
    if (argc > 1){

        fd=open(argv[1],O_RDONLY);
        a2=fgetc(fd);

        while (a2 != EOF){
            b2[cont1]=a2;
            cont1=cont1+1;
            b2=(char*) realloc (b2, sizeof(char)*cont1+1);
            a2=fgetc(fd);
        }

        write(1,b2,cont);
        b2=NULL;
        close(fd);  
    }

    return 0;
}

What am i supposed to do ?

Pernod answered 5/5, 2015 at 23:30 Comment(7)
Please be more specific about the problem you are having. What exactly is breaking?Systole
it show me this... o2.c:34:3: warning: passing argument 1 of ‘fgetc’ makes pointer from integer without a cast [enabled by default] a2=fgetc(fd); ^ ejercicio_evaluado2.c:34:5: warning: assignment makes pointer from integer without a cast [enabled by default] a2=fgetc(fd); ^ ejercicio_evaluado2.c:35:13: warning: comparison between pointer and integer [enabled by default] while (a2 != EOF){ Pernod
Check if open succeeded. fgetc returns an int, not a char* and expects a FILE* as the parameter, not an int, use fopen or don't use fgetc.Casease
its ok @RetiredNinja but, i need fgetc to use EOF i guessPernod
You can use whatever you like, but you're using it incorrectly as the warnings show you.Casease
Please edit your question to provide additional information there, instead of burying it in comments. Putting it in the question allows others to see it, and allows you to properly format it so that it's readable. While you're doing so, you can also ask an actual question - "what am I supposed to do?" isn't quite there. Thanks.Clare
You can detect EOF using read - in case of EOF, returned size will be 0. (The same applies for the no-argument case, where you are incorrectly ignoring the value of sz.) You can either use fopen and fgetc (or fgets, etc.) or open and read. You cannot use open and then call fgetc on the file descriptor - fgetc is a higher-level function that expects to receive a different kind of object.Compulsive
S
5

If you are using open() and close(), you cannot use fgetc(). You need to use fopen() and fclose() to be able to use fgetc().

Either way, you need a function which can be called with either the standard input (spelled 0 or stdin) or with the file that was opened (fd or fp are conventional names for 'file descriptor' and 'file pointer'). You can specify the output stream too. Hence, for example, the interfaces:

int cat_fd(int ifd, int ofd);
int cat_fp(FILE *ifp, FILE *ofp);

Your main program then calls your chosen function with the standard input and standard output or with the opened file and standard output.


Additionally, you have:

char *a1 = (char*) malloc (sizeof(char));

Ignoring the cast, this is an expensive way of writing:

char a1[1];

Your loops are reading a single character at a time. This is OK with the file streams from <stdio.h>, but is bad for performance if you're using file descriptors. You should be reading block of, say, 4096 characters at a time.

int cat_fd(int ifd, int ofd)
{
    char buffer[4096];
    ssize_t nbytes;
    while ((nbytes = read(ifd, buffer, sizeof(buffer))) > 0)
    {
        if (write(ofd, buffer, nbytes) != nbytes)
            return -1;
    }
    return (nbytes < 0) ? -1 : 0;
}

You don't need the dynamic memory allocation; it is only confusing you and wasting time in the program. The code in the main() function then looks more like:

if (argc == 1)
{
    if (cat_fd(0, 1) != 0)
        fprintf(stderr, "failed to copy standard input\n");
}
else
{
    for (int i = 1; i < argc; i++)
    {
        int fd = open(argv[i], O_RDONLY);
        if (fd < 0)
            fprintf(stderr, "failed to open %s for reading\n", argv[i]);
        else
        {
            if (cat_fd(fd, 1) != 0)
                fprintf(stderr, "failed to copy %d to standard output\n", argv[i]);
            close(fd);
        }
    }
}

Rewriting to use cat_fp() is an exercise for the reader. You might find Tried and true simple file copying code in C relevant.

Saturnian answered 6/5, 2015 at 0:3 Comment(7)
Depending on the environment and the OS, write(ofd, buffer, nbytes) may return a lesser count than nbytes in various circumstances that are not errors. You should keep trying to write the remainder of the buffer until all has been written or an error is returned. It is much more portable to use standard streams from <stdio.h>.Calica
You can do that if you wish — in theory, it is necessary. I've never yet run into a problem with it, but that's not to say there aren't systems out there where it could happen. If the standard output is a socket, it is perhaps more likely, but arranging for standard output to be a socket requires some care. Your call. And I agree that the standard I/O functions are at least as simple and are more portable.Saturnian
arranging for standard output to be a socket requires some care The standard output will be a socket e.g. when the program is run from inetd or similar service. Also, the same should apply to pipes which are frequently used — unless you know the kernel buffers them with an exactly 4K buffer.Compulsive
For pipes, you only need to write not more than the pipe buffer size, which is at least 4k, IIRC. Being run from inetd requires some care. That doesn't happen to every program and doesn't happen by accident.Saturnian
The pipe buffer might accomodate a write of 4k if it is empty, but what about the next write ? What about device drivers ? POSIX is quite clear about the possibility for partial writes. Ignoring this is a source of hard to find bugs. IIRC the situation is different on Windows because the libc write is not a direct map to a system call: it performs a lot of hidden tasks such as the optional line termination translation, signal restart, etc.Calica
See the specification for write(): Write requests to a pipe or FIFO shall be handled in the same way as a regular file with the following exceptions: … Write requests of {PIPE_BUF} bytes or less shall not be interleaved with data from other processes doing writes on the same pipe. Writes of greater than {PIPE_BUF} bytes may have data interleaved, on arbitrary boundaries, with writes by other processes, whether or not the O_NONBLOCK flag of the file status flags is set.Saturnian
The main problem is not so much with pipes or FIFOs, but with signals: If write() is interrupted by a signal after it successfully writes some data, it shall return the number of bytes written.Calica

© 2022 - 2024 — McMap. All rights reserved.