Book Image

Linux System Programming Techniques

By : Jack-Benny Persson
Book Image

Linux System Programming Techniques

By: Jack-Benny Persson

Overview of this book

Linux is the world's most popular open source operating system (OS). Linux System Programming Techniques will enable you to extend the Linux OS with your own system programs and communicate with other programs on the system. The book begins by exploring the Linux filesystem, its basic commands, built-in manual pages, the GNU compiler collection (GCC), and Linux system calls. You'll then discover how to handle errors in your programs and will learn to catch errors and print relevant information about them. The book takes you through multiple recipes on how to read and write files on the system, using both streams and file descriptors. As you advance, you'll delve into forking, creating zombie processes, and daemons, along with recipes on how to handle daemons using systemd. After this, you'll find out how to create shared libraries and start exploring different types of interprocess communication (IPC). In the later chapters, recipes on how to write programs using POSIX threads and how to debug your programs using the GNU debugger (GDB) and Valgrind will also be covered. By the end of this Linux book, you will be able to develop your own system programs for Linux, including daemons, tools, clients, and filters.
Table of Contents (14 chapters)

Reading from stdin

In this recipe, we'll learn how to write a program in C that reads from standard input. Doing so enables your programs to take input from other programs via a pipe, making them easier to use as a filter, thus making them more useful in the long run.

Getting ready

You'll need the GCC compiler and preferably the Bash shell for this recipe, although it should work with any shell.

To fully understand the program that we are about to write, you should look at an ASCII table, an example of which can be found at the following URL: https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch2/ascii-table.md.

How to do it…

In this recipe, we will write a program that takes single words as input, converts their cases (uppercase into lower and lowercase into upper), and prints the result to standard output. Let's get started:

  1. Write the following code into a file and save it as case-changer.c. In this program, we use fgets() to read characters from stdin. We then use a for loop to loop over the input, character by character. Before we start the next loop with the next line of input, we must zero out the arrays using memset():
    #include <stdio.h>
    #include <string.h>
    int main(void)
    {
        char c[20] = { 0 };
        char newcase[20] = { 0 };
        int i;
        while(fgets(c, sizeof(c), stdin) != NULL)
        {
            for(i=0; i<=sizeof(c); i++)
            {
                /* Upper case to lower case */
                if ( (c[i] >= 65) && (c[i] <= 90) )
                {
                    newcase[i] = c[i] + 32;
                }
                /* Lower case to upper case */
                if ( (c[i] >= 97 && c[i] <= 122) )
                {
                    newcase[i] = c[i] - 32;
                }
            }
            printf("%s\n", newcase);
            /* zero out the arrays so there are no
               left-overs in the next run */
            memset(c, 0, sizeof(c));
            memset(newcase, 0, sizeof(newcase));
        }
        return 0;
    }
  2. Compile the program:
    $> gcc case-changer.c -o case-changer
  3. Try it out by typing some words in it. Quit the program by pressing Ctrl + D:
    $> ./case-changer
    hello
    HELLO
    AbCdEf
    aBcDeF
  4. Now, try to pipe some input to it, for example, the first five lines from ls:
    $> ls / | head -n 5 | ./case-changer
    BIN
    BOOT
    DEV
    ETC
    HOME
  5. Let's try to pipe some uppercase words into it from a manual page:
    $> man ls | egrep '^[A-Z]+$' | ./case-changer 
    name
    synopsis
    description
    author
    copyrigh

How it works…

First, we created two character arrays of 20 bytes each and initialize them to 0.

Then, we used fgets(), wrapped in a while loop, to read characters from standard input. The fgets() function reads characters until it reaches a newline character or an End Of File (EOF). The characters that are read are stored in the c array, and also returned.

To read more input—that is, more than one word—we continue reading input with the help of the while loop. The while loop won't finish until we either press Ctrl + D or the input stream is empty.

The fgets() function returns the character read on success and NULL on error or when an EOF occurs while no characters have been read (that is, no more input). Let's break down the fgets() function so that we can understand it better:

fgets(c, sizeof(c), stdin)

The first argument, c, is where we store the data. In this case, it's our character array.

The second argument, sizeof(c), is the maximum size we want to read. The fgets() function is safe here; it reads one less than the size we specify. In our case, it will only read 19 characters, leaving room for the null character.

The final and third argument, stdin, is the stream we want to read from—in our case, standard input.

Inside the while loop is where the case conversions are happening, character by character in the for loop. In the first if statement, we check if the current character is an uppercase one. If it is, then we add 32 to the character. For example, if the character is A, then it's represented by 65 in the ASCII table. When we add 32, we get 97, which is a. The same goes for the entire alphabet. It's always 32 characters apart between the uppercase and lowercase versions.

The next if statement does the reverse. If the character is a lowercase one, we subtract 32 and get the uppercase version.

Since we are only checking characters between 65 and 90, and 97 and 122, all other characters are ignored.

Once we printed the result on the screen, we reset the character arrays to all zeros with memset(). If we don't do this, we will have leftover characters in the next run.

Using the program

We tried the program by running it interactively and typing words into it. Each time we hit the Enter key, the word is transformed; the uppercase letters will become lowercase and vice versa.

Then, we piped data to it from the ls command. That output got converted into uppercase letters.

Then, we tried to pipe it uppercase words from the manual page (the headings). All the headings in a manual page are uppercase and start at the beginning of the line. This is what we "grep" for with egrep, and then pipe to our case-changer program.

There's more…

For more information about fgets(), see the manual page, man 3 fgets.

You can write a small program to print a minimum ASCII table for the letters a-z and A-Z. This small program also demonstrates that each character is represented by a number:

ascii-table.c

#include <stdio.h>
int main(void)
{
    char c;
    for (c = 65; c<=90; c++)
    {
        printf("%c = %d    ", c, c); /* upper case */
        printf("%c = %d\n", c+32, c+32); /* lower case */
    }
    return 0;
}