APLawrence.com -  Resources for Unix and Linux Systems, Bloggers and the self-employed

A simple file I/O program in C

2009/10/19 by Girish Venkatachalam

Girish Venkatachalam is a UNIX hacker with more than a decade of networking and crypto programming experience. His hobbies include yoga,cycling, cooking and he runs his own business. Details here:

http://gayatri-hitech.com

http://spam-cheetah.com

Doing even the simplest programming tasks in C can be daunting due to its fondness for segfaulting. A C program can be found crashing on printf, command line parsing or nearly every attempt to print a string that is NULL. Or even access it.

Of course C can crash and behave nastily in many other ways too. That is why you need hand holding when you are learning C programming.

And one continues to learn C programming for many many years. I have dealt with this topic in my other articles in this very site. So please refer to those articles for the facets of C that trips newbies.

In any case I had to write this code sample as part of my work. I am presently reverse engineering the MAPI protocol that MS Outlook speaks to MS Exchange. My work does not so much involve reverse engineering since Microsoft has published the entire MAPI protocol in full detail.

So my work mostly involves implementing the specification. And there is a project called OpenChange that has done the bulk of the work by riding on top of Samba project. In spite of all this context and help, I still find the project pretty daunting and challenging.

As part of this work I had to read and write mail attachments. So I thought I will write a simple C program and validate it before integrating with the rest of the complex stuff.

In so doing I felt that this sample might help a beginner C programmer and even some experience hands since file I/O is such a common activity and C is filled with various slips and dangers.

I could have read the file size by reading the file header using the stat(2) system call. But I chose this approach of read(2) returning zero. Of course you could improve this program in many ways. But this program is good in many ways though purists may object to some of my practices.

Using syslog() is a great way to debug and inform the user what is going on. I want to switch over from using printf() to this as this can double up as printf once you open the log with the LOG_PERROR flag.

Once the development phase of the program is over you can remove this flag and all logs go to the logfile alone. It is quite cool.

Of course you can do pretty much everything this does using perl or any other scripting language also. Please look at Sys::Syslog in perl.

In case you are wondering, this program can read and write binary files as well as text files. Shows the power and simplicity of C programming and in an oblique way also the genius/simplicity of UNIX itself.

I will now leave the program to do the rest of the talking.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <err.h>
#include <errno.h>


/* Read and write binary/text from/to the file system */

int
readfile(char *f)
{
        int fd, c, bytes;
        char buf[1024];

        /* Open file for reading */
        fd = open(f, O_RDONLY);
        if(fd < 0) {
               perror("File open for reading");
               exit(128);
        }

        do {
               bytes = read(fd, buf, sizeof(buf));
               /* Once we are done reading then break
               * out of the infinite loop.
               */
               if(bytes == 0)
                       break;
               if(bytes < 0) {
                       perror("read(2)");
                       exit(128);
               }
        
               syslog(LOG_INFO, "Read %d bytes from [%s]", bytes, f);
               /* Spit the file out on STDOUT */
               write(STDOUT_FILENO, buf, bytes);

        } while(1); /* Infinite loop */

        syslog(LOG_INFO, "Finished reading");
        return 0;
}

int
writefile(char *f)
{
        int infd, outfd, c, inbytes, outbytes;
        char buf[1024], outf[1024];

        printf("Please give output file to write to:");
        scanf("%s", outf);


        /* Open file for reading */
        infd = open(f, O_RDONLY);
        if(infd < 0) {
               perror("File open for reading");
               exit(128);
        }


        /* Open file for writing */
        outfd = open(outf, O_CREAT | O_APPEND | O_WRONLY, S_IRWXU );
        if(outfd < 0) {
               perror("File open for writing");
               exit(128);
        }

        syslog(LOG_INFO, "File contents of %s", f);

        do {
               inbytes = read(infd, buf, sizeof(buf));
               /* Once we are done reading then break
               * out of the infinite loop.
               */
               if(inbytes == 0)
                       break;
               if(inbytes < 0) {
                       perror("read(2)");
                       exit(128);
               }
               outbytes = write(outfd, buf, inbytes);
               if(outbytes < 0) {
                       perror("read(2)");
                       exit(128);
               }
               syslog(LOG_INFO, "%d bytes read and %d written",
                   inbytes, outbytes);

        } while(1); /* Infinite loop */

        syslog(LOG_INFO, "Finished writing");
        return 0;
}

int
main(int argc, char **argv)
{
        int fd;
        char *filename;
        
        openlog("fio", LOG_PERROR | LOG_CONS | LOG_PID, LOG_USER);

        if(argc < 3) {
               printf(" %s usage: %s <filename> r | w\n",
                   argv[0], argv[0]);
               exit(128);
        }

        if(argv[2][0] == 'r') {
               readfile(argv[1]);
        } else {
               writefile(argv[1]);
        }

}




Got something to add? Send me email.



4 comments



Increase ad revenue 50-250% with Ezoic


More Articles by © Girish Venkatachalam







Mon Oct 19 13:28:37 2009: 7298   TonyLawrence

gravatar
due to its fondness for segfaulting.

Well, C itself has no such fondness. Our sloppy programming certainly does, but that's not C's fault :-)



Mon Oct 19 21:07:51 2009: 7301   BruceGarlock

gravatar
Just a little 'nitpick' -- on my screen (24" iMac) the yellow code highlighting is difficult to read (the if statements)







Mon Oct 19 21:43:32 2009: 7302   AndrewSmallshaw

gravatar
Segfaults can actually be quite a useful thing - one of the more useful debugging tools for C is ElectricFence whose sole purpose is to cause segfaults and cause them quickly. That helps resolve all kinds of pointer errors in low-level data structures. In general I would prefer my program to crash and burn than come up with obviously incorrect results - it is generally a whole lot easier to debug.

Along the same lines I tend to make fairly extensive use of assert() in my code - assert(0s serve a documentary function regarding prerequisites and expected conditions, but they also help enormously when debugging. In a debugger I almost never use watchpoints and only rarely breakpoints - a debugging session typically involves running the program as normal inside the debugger and waiting for something to blow up. If the tests are smartly placed and liberal enough (without being distracting) you can usually find the problem early enough to see what the problem is straight away.



Mon Oct 19 22:32:19 2009: 7305   TonyLawrence

gravatar
the yellow code highlighting is difficult to read

Just one more reason I don't like color highlighting.

------------------------
Kerio Samepage


Have you tried Searching this site?

Unix/Linux/Mac OS X support by phone, email or on-site: Support Rates

This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more.

Contact us





May you live long enough to regret your opinions - (Tony Lawrence)

UNIX is simple. It just takes a genius to understand its simplicity. (Dennis Ritchie)










This post tagged: