C: getopt_long_only Example: Accessing command line arguments

getopt_long is useful to work with the command line arguments. But while working with getopt_long, – is used for long options and – for short options. getopt_long_only accepts both — and – for long options

  1. rectangle -a -l 12 -b 34: will calculate the area of the rectangle
  2. square -p -l 12 -b 34: will calculate the perimeter of the rectangle
  3. rectangle -a -p -l 12 -b 34: will calculate the area and perimeter of the rectangle
  4. rectangle –area –length 12 –breadth 34: will calculate the area of the rectangle
  5. square -perimeter –length 12 –breadth 34: will calculate the perimeter of the rectangle
  6. rectangle -area -perimeter –length 12 –breadth 34: will calculate the area and perimeter of the rectangle

The program is much like getopt_long.

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

/** Program to calculate the area and perimeter of 
 * a rectangle using command line arguments
 */
void print_usage() {
    printf("Usage: rectangle [ap] -l num -b num\n");
}

int main(int argc, char *argv[]) {
    int opt= 0;
    int area = -1, perimeter = -1, breadth = -1, length =-1;

    //Specifying the expected options
    //The two options l and b expect numbers as argument
    static struct option long_options[] = {
        {"area",      no_argument,       0,  'a' },
        {"perimeter", no_argument,       0,  'p' },
        {"length",    required_argument, 0,  'l' },
        {"breadth",   required_argument, 0,  'b' },
        {0,           0,                 0,  0   }
    };

    int long_index =0;
    while ((opt = getopt_long_only(argc, argv,"", 
                   long_options, &long_index )) != -1) {
        switch (opt) {
             case 'a' : area = 0;
                 break;
             case 'p' : perimeter = 0;
                 break;
             case 'l' : length = atoi(optarg); 
                 break;
             case 'b' : breadth = atoi(optarg);
                 break;
             default: print_usage(); 
                 exit(EXIT_FAILURE);
        }
    }
    if (length == -1 || breadth ==-1) {
        print_usage();
        exit(EXIT_FAILURE);
    }

    // Calculate the area
    if (area == 0) {
        area = length * breadth;
        printf("Area: %d\n",area);
    }

    // Calculate the perimeter
    if (perimeter == 0) {
        perimeter = 2 * (length + breadth);
        printf("Perimeter: %d\n",perimeter);
    }
    return 0;
}

C: getopt_long example: Accessing command line arguments

A command can have both long and short options. getopt is useful only for short options, that are nothing but options of one char (character) long. To support both short and long options like

  1. rectangle -a -l 12 -b 34: will calculate the area of the rectangle
  2. square -p -l 12 -b 34: will calculate the perimeter of the rectangle
  3. rectangle -a -p -l 12 -b 34: will calculate the area and perimeter of the rectangle
  4. rectangle –area –length 12 –breadth 34: will calculate the area of the rectangle
  5. square –perimeter –length 12 –breadth 34: will calculate the perimeter of the rectangle
  6. rectangle –area –perimeter –length 12 –breadth 34: will calculate the area and perimeter of the rectangle

In the following program, the use of getopt_long is shown

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

/** Program to calculate the area and perimeter of 
 * a rectangle using command line arguments
 */
void print_usage() {
    printf("Usage: rectangle [ap] -l num -b num\n");
}

int main(int argc, char *argv[]) {
    int opt= 0;
    int area = -1, perimeter = -1, breadth = -1, length =-1;

    //Specifying the expected options
    //The two options l and b expect numbers as argument
    static struct option long_options[] = {
        {"area",      no_argument,       0,  'a' },
        {"perimeter", no_argument,       0,  'p' },
        {"length",    required_argument, 0,  'l' },
        {"breadth",   required_argument, 0,  'b' },
        {0,           0,                 0,  0   }
    };

    int long_index =0;
    while ((opt = getopt_long(argc, argv,"apl:b:", 
                   long_options, &long_index )) != -1) {
        switch (opt) {
             case 'a' : area = 0;
                 break;
             case 'p' : perimeter = 0;
                 break;
             case 'l' : length = atoi(optarg); 
                 break;
             case 'b' : breadth = atoi(optarg);
                 break;
             default: print_usage(); 
                 exit(EXIT_FAILURE);
        }
    }
    if (length == -1 || breadth ==-1) {
        print_usage();
        exit(EXIT_FAILURE);
    }

    // Calculate the area
    if (area == 0) {
        area = length * breadth;
        printf("Area: %d\n",area);
    }

    // Calculate the perimeter
    if (perimeter == 0) {
        perimeter = 2 * (length + breadth);
        printf("Perimeter: %d\n",perimeter);
    }
    return 0;
}

C:Working with command line arguments

Almost all of the commands in Linux/Unix have options. An option for a commmand is a mechanism by which you provide additional parameters to the command to change its behavior. Take for example, the command ls is used to list the files in a directory. But to obtain a detailed listing of the files, the option -l is used. Similarly the -a option with ls allows to see all the hidden files (file names starting with .a). Commands with option removes the need for creating multiple commands to achieve a purpose.

To access the command line parameters, make sure that the main function() looks something like this

int main(int argc, char *argv[]) {
}

Now you can write a program which access every parameter that you pass using argv[0], argv[1],…. And the number of command line arguments passed by the user can be obtained from argc

Working with command line arguments in this manner is tedious. Linux/Unix provides the following functions to easily work with the command line arguments

  1. getopt()
  2. getopt_long()
  3. getopt_long_only()

Before delving deep into this topic, let’s take a look what is meant by long arguments. Most of the options in a command have both a long and a short form. For example, to list all the hidden files, one can write ls -a or ls –all. Here -a is a short argument and –a is a long argument. As mentioned earlier, some options may not have a long option. -l option of ls doesn’t have a long option. With this in mind, let’s continue to look at the various functions

  1. getopt()
  2. getopt_long()
  3. getopt_long_only()

C: getopt Example: Accessing command line arguments

The simplest way to work with command line arguments is to use the getopt() function. To understand more about it, first let’s see a command which can calculate the area and perimeter of a rectangle

  1. rectangle a -l 12 -b 34: will calculate the area of the rectangle
  2. square p -l 12 -b 34: will calculate the perimeter of the rectangle
  3. rectangle ap -l 12 -b 34: will calculate the area and perimeter of the rectangle

As we can see, some options take arguments and some do not. Here a and p do not take any argument. But -l and -b take the arguments (number) for length and breadth respectively.

So to distinguish them, getopt provides a mechanism. All the options that require argument will be preceded by a : (colon).

The following program shows this

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

/** Program to calculate the area and perimeter of 
 * a rectangle using command line arguments
 */
void print_usage() {
    printf("Usage: rectangle [ap] -l num -b num\n");
}

int main(int argc, char *argv[]) {
    int option = 0;
    int area = -1, perimeter = -1, breadth = -1, length =-1;

    //Specifying the expected options
    //The two options l and b expect numbers as argument
    while ((option = getopt(argc, argv,"apl:b:")) != -1) {
        switch (option) {
             case 'a' : area = 0;
                 break;
             case 'p' : perimeter = 0;
                 break;
             case 'l' : length = atoi(optarg); 
                 break;
             case 'b' : breadth = atoi(optarg);
                 break;
             default: print_usage(); 
                 exit(EXIT_FAILURE);
        }
    }
    if (length == -1 || breadth ==-1) {
        print_usage();
        exit(EXIT_FAILURE);
    }

    // Calculate the area
    if (area == 0) {
        area = length * breadth;
        printf("Area: %d\n",area);
    }

    // Calculate the perimeter
    if (perimeter == 0) {
        perimeter = 2 * (length + breadth);
        printf("Perimeter: %d\n",perimeter);
    }
    return 0;
}

Error Handling in Programming

Just like separating out the configuration parameters from the program is important, it is also important to separate the error handling mechanism from general running of the program. There are different ways by which errors are handled by the program.

Errors are of various types. There can be computational errors like Division by Zero, file handling errors like missing files, memory allocation errors and device read or write errors. Depends on the context, an error can be fatal or ignorable. Take for example, if a configuration file is missing, it is a fatal error. The program must terminate and report the error to the user.  Of course, your program can have default values for all the required settings and continue. It is a choice left to the programmers. Some programmers prefer to report the missing file as a warning and continue. Take another example of division by zero. One can always check before the division whether the divisor is zero. If it is a zero, it is reported as an error to the user and the program will continue to work (probably asking for the change in input from the user).

There are various categories of errors and depending on the requirement, we decide what action has to be taken. It is difficult to generalize what must be put in a specific category. But fatal errors normally include the missing of crucial files or libraries, inability to allocate enough memory for computation. There are errors that can be ignored, probably there is a work around for the error. Possible examples include spelling mistakes in a text editor. These are errors with respect to the user, but they don’t have an impact on the software. And often such errors are shown to the user in a light fashioned manner (spelling mistakes underlined red). But spelling mistakes of keywords in a programming language are important errors and can not be ignored, because it will lead to compile time errors. A compiler will return critical errors for such mistakes, but a text editor may not.

Once we have identified the errors and their respective categories, the next step is to determine what action must be taken. The program should

  • Exit
  • Warn the user
  • Signal the user for a change
  • Look for a Workaround
  • Ignore
  • Alert or Email the user
  • Log the errors in a file

So while writing programs, we must

  1. Identify (all) the possible errors
  2. Identify the categories of errors
  3. Action to be taken for every category of error

 

Location of Vim Syntax files for different programming languages

If you use Vim for programming, you find that by default based on the type of the file, Vim enables syntax highlighting. That is if your file name is myfile.c or myfile.C, Vim enables Syntax highlighting for the C language. Likewise, it enables syntax highlighting for a large number of well known (programming) languages. If you are wondering where you can find these syntax highlighting files, you can check the following directory.

Currently in my system, it is located in the following directory

/usr/share/vim/vim73/syntax

Let’s check the directory contents

$ ls /usr/share/vim/vim73/syntax
...
config.vim        idl.vim           plp.vim          tf.vim
conf.vim          indent.vim        plsql.vim        tidy.vim
context.vim       inform.vim        pod.vim          tilde.vim
cpp.vim           initex.vim        postscr.vim      tli.vim
crm.vim           initng.vim        po.vim           tpp.vim
crontab.vim       inittab.vim       povini.vim       trasys.vim
....

Thus for every programming language, they have an associated .vim file. For C++, there is cpp.vim. For crontab entries (/etc/cron.d), there is crontab.vim

Since, I am using Vim 7.3, the above syntax files are located in the vim73. But if you are looking for future versions, you can run the following command to get the location

$ find /usr/share/vim/ -iname "*syntax"
/usr/share/vim/vim73/syntax

Separating out the source and the configuration files

One of the first steps before delving deep into the coding is to make a clear distinction between the source and the configuration files. Every software must give enough options to its users to configure the software behavior. A user may wish to change the interface color, rearrange certain options and reposition certain elements. Sometimes giving this additional power to the users to tweak the softwares is seen as challenging. This is the prime reason that many softwares that allow the users to configure their softwares often give an option “Restore to the Default values”. There is this common fear among the programmers that the naive users will tweak the software in a manner that will make the software unusable.

Another possible reason is that the programmers often tend to hard code all the possible configurations in their programs. This is mostly because of the ease in programming. Instead of reading a value from the configuration file every time an action must be performed, it is easier to work with the hard coded values. And of course, there is an associated delay in reading the configuration value. So most programmers tend to work with values hard coded into their programs.

And then when there is a demand for a change (for example color), they make the change in the code and release a new version. I agree that there is often no clear distinction between what must be available to the user for configuration and what must not be not. And personally I found very less books and papers that talk on the topic. But it can be concluded that ease of programming and fear of software damage are the prime reasons why programmers do not give enough configurable options to their users.

The caveat associated this non-separation is that the programmers often spend a lot of time tweaking with various values. They have to yield to the popular demand. Often, it results in the loss of some potential customers who are not satisfied with the change and move on with other softwares available in the market.

Sometimes it is not intentionally done. Take for example in mission critical systems where every fraction of second counts and the values chosen are made after enough careful research.

To make a clear distinction between what must be configurable and what must be not at the time of development is a challenge in itself. On one hand, you can make every possible option to be configurable, to an extent that you can give the users the choice between algorithms (for example bubble sort, quick sort for sorting). Such an approach seems to be laughable, but an expert knows what is the best algorithm for his/her requirements. And on the other hand, you can hard code, leaving no option for configuration. If you are designing a generic software, there seems to be no perfect option. There must be enough options for the user to configure their software.

Here is what I feel every software development should look like hypothetically

$ ls project
src config

Two separate directories (or some other means of separation) for source code and configuration