user7823016
user7823016

Reputation: 167

How to combine two files into a third using only linux system calls?

I am a complete noob when it comes to linux system calls interacting with c-code. So far I have been able to open a single file, but that's about it. I'm unsure of how I would take a second file and combine both of those into a third.

For example, I have file1 with simple text contents, and file2 with the same, how could I combine both contents into file3 using only linux system calls? I know I have to use lseek to change the pointer, but unsure of how to utilize that.

here is what I have so far... I apologize for the scarcity:

This takes file1 and copies it to file2, I believe

#include <fcntl.h>
#include <unistd.h>

int copyfile(const char *file1, const char *file2)
{
    int infile, outfile;
    ssize_t nread;
    char buffer[BUFSIZE]

    if( (infile = open(file1, O_RDONLY)) == -1 )
        return (-1);

    if( (infile = open(file2, O_WRONLY|O_CREATE|O_TRUNC, PERM)) == -1 )
    {
        close (infile);
        return (-2);
    }

    /*read from file1 BUFSIZE chars at a time*/
    while ( nread = read (infile, buffer, BUFSIZE) )
    {
        // write buffer to output file
        if (write (outfile, buffer, nread) < nread)
        {
            close(infile);
            close(outfile);
            return (-3);
        }
    }
    close (infile)
    close (outfile)

    if (nread == -1)
        return (-4);
    else
        return(0);
}

The files will be entered within the terminal as such:

lastnameCat.c file1 file2 file3

such that file1 and file2 are added together, and sent into file3.

Upvotes: 0

Views: 1217

Answers (2)

Gillespie
Gillespie

Reputation: 6561

Based on your comments, it sounds like this doesn't have to be a C program as long as it is user friendly. As long as you can guarantee that it will be run in linux, just create a shell script and name it whatever you want. You could even give the shell script the same name as your c program executable and users wouldn't be able to tell the difference:

#!/bin/bash

cat $1 $2 > $3

Say that you name this script lastnameCat and make it executable with chmod +x ./lastnameCat. From then on you could simply do:

$ ./lastnameCat file1 file2 file3

You could also name this script lastnameCat.c if you wanted, but that is a bit deceptive in my opinion since it is not a C file, it is a bash script.

Upvotes: 0

Graeme
Graeme

Reputation: 3041

You can use the copy_file_range system call for this. It is faster than using read and write calls as the copying is done inside the kernel. From the man page:

The copy_file_range() system call performs an in-kernel copy between two file descriptors without the additional cost of transferring data from the kernel to user space and then back into the kernel.

Here is an example of using it:

#define _GNU_SOURCE
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <unistd.h>

int do_copy(int infd, int outfd)
{
    ssize_t bytes = 0;
    do
    {
        bytes = copy_file_range(infd, NULL, outfd, NULL, SSIZE_MAX, 0);
    }
    while(SSIZE_MAX == bytes);

    return bytes;
}

int concatenate(const char *inpath1, const char *inpath2, const char *outpath)
{
    int infd1 = -1;
    int infd2 = -1;
    int outfd = -1;
    int res = -1;

    infd1 = open(inpath1, O_RDONLY);
    if(infd1 < 0)
        goto close;

    infd2 = open(inpath2, O_RDONLY);
    if(infd2 < 0)
        goto close;

    outfd = open(outpath, O_WRONLY|O_CREAT|O_TRUNC);
    if(outfd < 0)
        goto close;

    res = do_copy(infd1, outfd);
    if(res < 0)
        goto close;

    res = do_copy(infd2, outfd);

close:
    if(infd1 >= 0)
        close(infd1);

    if(infd2 >= 0)
        close(infd2);

    if(outfd >= 0)
        close(outfd);

    return res;
}

The loop in do_copy allows for very large files which may exceed the maximum copy possible in a single call.

Upvotes: 2

Related Questions