Click here to Skip to main content
12,501,692 members (66,834 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

33.5K views
414 downloads
21 bookmarked
Posted

A Typical Linux C Application: Standard I/O ports allowing simple IPC pipe based

, 16 Oct 2008 Public Domain
Rate this:
Please Sign up or sign in to vote.
Demonstrates how to create a simple application that interacts with terminal standard I/O ports to provide an IPC pipe based between processes

Figure 1: Piping makeupper with 2 other processes

Introduction

A typical Linux (or UNIX) terminal application, like cat command, provides three ways to input data:

  • By passing a file name as argument: cat file.txt <enter>
  • By entering in canonical mode: cat <enter>
    • Canonical mode is a terminal input mode processed in units of lines. That means the program reads the information after a new line (CRLF) or EOF (End-Of-File) character has been entered.
  • By creating a pipe connection to send data to a process (or receive from it):

    cat file.txt | more <enter>

This article demonstrates how to create a small application (makeupper) that allows those three ways of inputting data.

Note: The source code should compile without errors in every UNIX flavor or Linux distribution.

Terminal Input/Output

In Linux and all other UNIX flavors (like IBM-AIX), there is no difference between reading data from a file and reading data from a terminal or between writing data to a file and writing data to screen - specially if the data consists only of strings of characters.

A terminal provides three I/O ports:

  • standard input: by default is the keyboard
  • standard output: by default is the terminal screen
  • standard error: by default is the terminal screen

There are two sets of functions to handle those I/O ports:

  • The low-level I/O system calls: that use file descriptors to access standard input, output and error.
  • The standard I/O library: that use streams implemented as a pointer to FILE* structure (#include <stdio.h>).

Figure 2: Low-level system calls and standard I/O library functions

Note: You can get more information about low-level system calls: read, write, open, select, close.

The source code contains a small application named test.c that illustrates the use of low-level system calls and standard I/O library functions by printing a string to terminal screen (standard output). The source code is the simplest possible:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main ( int _argc, char *_argv[] )
{
    char *ptr = "This is an output test!\n";

    printf( ptr );
    fprintf( stdout, ptr );
    write( 1, ptr, strlen(ptr) );

    return 0;
}

To build test.c by using the C compiler:

cc -o test test.c

When you execute ./test, you get the following output:

Figure 3: ./test output

You see? The string "This is an output test!" pointed by ptr is printed to screen (standard output) by using three different functions:

  • printf: Part of standard I/O library that by default always prints a string to standard output
  • fprintf: Part of standard I/O library. Notice the first argument, stdout stream redirect ptr string to standard output (or screen).
  • write: Low-level system call. Notice the first argument, 1 which is the file descriptor for standard output.

Makeupper Sample

There are two more samples in the source code: makeupper.c and makeupper2.c. They do the same thing. The difference is that makeupper.c uses low-level system calls and makeupper2.c uses standard I/O library.

makeupper sample is very simple. Its purpose is to capitalize letters in inputted data and print to standard output. However, it demonstrates how to read from standard input and from a file too. The first sample, makeupper.c, uses low-level system calls:

/* Makeupper.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int main ( int _argc, char *_argv[] )
{
    int  fd_main = 0;
    char ch = 0;

    fd_main = _argc == 1 ? 0 : open(_argv[1], O_RDONLY);

    if ( fd_main == -1 )
    {
       perror("input");
       exit(-1);
    }

    while ( read( fd_main, &ch , 1 ) )
    {
       ch = toupper(ch);
       write( 1, &ch, 1 );
    }

    close(fd_main);

    return 0;
}

Notice the line that reads:

fd_main = _argc == 1 ? 0 : open(_argv[1], O_RDONLY);

If no file path argument is passed then fd_main assumes standard input descriptor. open system call is used to open a file when it is passed as argument.

makeupper2.c uses standard I/O library functions instead of system calls:

/* Makeupper2.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main ( int _argc, char *_argv[] )
{
    FILE *f_file = NULL;
    char ch = 0;

    f_file = _argc == 1 ? stdin : fopen(_argv[1], "r");

    if ( f_file == NULL )
    {
       perror("input");
       exit(-1);
    }

    while ( !feof(f_file) )
    if ( (ch = fgetc(f_file)) > 0 )
        fputc( toupper(ch), stdout );

    fclose(f_file);

    return 0;
}

To build both samples:

  • cc -o makeupper makeupper.c
  • cc -o makeupper2 makeupper2.c

By executing ./makeupper (or ./makeupper2) without arguments, it starts in Canonical Mode. After you press enter, makeupper receives the line. To quit Canonical Mode, you should press CTRL+D or CTRL+\:

Figure 4: ./makeupper - canonical mode

You can also pass a file path:

Figure 5: ./makeupper - printing test.c contents to standard output

Or makeupper can receive the output from a process in its standard input:

Figure 6: ./makeupper2 - ls -ltr output is redirected from screen (standard output) to standard input of makeupper2 (via PIPE)

Redirecting I/O

You can redirect standard output and error to files or devices (/dev/...). To illustrate that type and compile the following code (you can name it test2.c):

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

int main ( int _argc, char *_argv[] )
{
    char *p1 = "printed to standard output via stdlib.\n";
    char *p2 = "printed to standard error via stdlib.\n";
    char *p3 = "printed to standard output via low-level calls.\n";
    char *p4 = "printed to standard error low-level calls.\n";

    fprintf( stdout, p1 );
    fprintf( stderr, p2 );

    write ( 1, p3, strlen(p3) );
    write ( 2, p4, strlen(p4) );

    return 0;
}

There are four strings pointed for four pointers. p1 and p3 are printed to standard output and p2 and p4 are printed to standard error.

To build it:

cc -o test2 test.c

By executing ./test2, we have:

  1. ./test2 - you get all strings printed to screen.
  2. ./test2 >file.txt - redirects standard output to file.txt. Notice only p1 and p3 strings are saved in file.txt.
  3. ./test2 2>file.txt redirects standard error to file.txt. Notice only p2 and p4 strings are saved in file.txt.
  4. ./test2 >file.txt 2>&1 redirects both output and error to file.txt. Notice the trick 2>&1 (means redirect standard error to standard output).

If you want to append to existing file: ./test2 >>file.txt or ./test2 >>file.txt 2>&1.

Hope this helps.

History

  • 14th October, 2008: First version

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

Ciro Sisman Pereira
Software Developer (Senior)
Brazil Brazil
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160919.1 | Last Updated 16 Oct 2008
Article Copyright 2008 by Ciro Sisman Pereira
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid