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;
}

C: Using scanf and wchar_t to read and print UTF-8 strings

We saw how using scanf and char to read UTF-8 strings led us to some strange answers. So now we need to discuss the solution provided by C.

/** Program to read a single character of different language
using wchar_t array and scanf. The program prints back the
string along with its length
*/

#include 
#include 
#include <wchar.h>
#include 

int main() {

    wchar_t string[100];

    setlocale(LC_ALL, "");

    printf ("Enter a string: ");
    scanf("%ls",string);

    printf("String Entered: %ls: length: %dn", string, wcslen(string));

    return 0;
}

Let’s see the various aspects of this program.

  • Use of wchar_t instead of char. wchar_t is used by C to deal with the characters of various locales. Note that there are various locales other than UTF-8, but most of them focus on a particular language. wchar_t corresponds to a wide character. wchar is wider than char (1 bytes), so it can carry a large number of characters of various languages
  • To read and print a wide character string, we use the %ls format. Instead of %s, we use %ls to work with the UTF-8 characters. This directs printf and scanf to do special treatment (call additional functions) to the entered string
  • Use of wcslen instead of strlen to get the length of the string. C library provides the function wcslen to get the length of wide character strings
  • There are different ways by which a locale needs to be treated. For example, in some cases, the locale treatment just involves treatment with date or current representation. But here we used LC_ALL to deal with all the locale specific features.

Let’s see more. I need to first show that I am using UTF-8
$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

On executing the program, I will first enter the English character a
$ ./a.out
Enter a single character and press enter: a
String Entered: a: length: 1

The output is as expected, we entered a single character. So the length is 1

Next, I use the French character é
$ ./a.out
Enter a single character and press enter: é
String Entered: é: length: 1

So we got the length 1 as expected

Let’s try the same experiment with a Chinese letter 诶
$ ./a.out
Enter a single character and press enter: 诶
String Entered: 诶: length: 1

Once again, we got what we were looking for, the length 1.

Thus, we must ensure that we use the UTF-8 string for our softwares. It is also important to use the right functions

C: Using scanf and char to read UTF-8 strings

As businesses are turning global, softwares are made that are intended to meet the global customers. UTF-8 has now become a de-facto standard for use in the web. There are obvious questions that arise in the minds of a C programmer whether C supports UTF-8 and is it possible to read a UTF-8 content. In this example, I show how scanf and char are used to read a UTF-8 string. But at the end of the post you will understand why char is not a good option for working with UTF-8.

/** Program to read a single character of different language
  using char array and scanf and printing the string
  along with its length
*/

#include 
#include 

int main() {

    char string[10];

    printf ("Enter a single character and press enter: ");
    scanf("%s",string);

    printf("String Entered: %s: length: %dn", string, strlen(string));

    return 0;
}

We see that in the program, we declare a char array of length 10, we read a string and then print the string along with its length.

I need to first show that I am using UTF-8
$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

On executing the program, I will first enter the English character a
$ ./a.out
Enter a single character and press enter: a
String Entered: a: length: 1

The output is as expected, we entered a single character. So the length is 1

Next, I use the French character é
$ ./a.out
Enter a single character and press enter: é
String Entered: é: length: 2

Here comes the difficult part, we see that even though we entered a single character, we get the length of the character as 2.

Let’s try the same experiment with a chinese letter 诶
$ ./a.out
Enter a single character and press enter: 诶
String Entered: 诶: length: 3

The result is bizarre, we see that the length is 3.
How can we explain this?

The first thing we should recall is that the size of char is 8 bits or 1 byte. It means it can only carry 256 values. Consider the vast number of languages and dialects in the world, char is not enough to carry the value. So we need a better mechanism called the UTF-8.

As already discussed, I am using UTF-8 in my terminal. So it is able to handle the characters from different languages, but my program is not capable to. Since it is showing very strange answers about the length of the character entered. So we need a better option

C: Variable Argument List Access

Here, I explained how we can utilize the operator ellipsis (…) to pass variable number of arguments to a function. I have utilised there the concept of pointers to access the variable arguments. The standard C Library provides support to access these arguments. Use for this support
All you need is to know the last argument before the ellipsis operator(At least one argument is must to use variable arguments), let’s call it larg

suppose

    fun(type var1,type var2,...)

is a function, then larg corresponds to var2

Now we need to declare the list using va_list
i.e.,

    va_list al

now initialize it using va_start

    va_start(al,larg);

Now to access each argument, we must know the expected type of the argument

     type var_name = va_arg(al,type);

When we have accessed all the variable arguments, we need to clean up

     va_end(al);

Using standard library, we can easily access the variable arguments

Take a simple example

#include <stdarg.h>
int print(int num_count,...)
/*num_count contains the number of integers passed*/
{
  int i;
  va_list al;
  va_start(al,num_count);
  for(i=1;i<=num_count;i++){
     int val=va_arg(al,int);
     printf("%d ",val);
  }

}

int print_str(const char *str,...)
/*str has the number of strings passed*/
{
  int i;
  va_list al;
  int num_count=atoi(str);
  va_start(al,str);
  int *num=&num_count;
  for(i=1;i<=num_count;i++){
     char *str=va_arg(al,char *);
     printf("%s ",str);
  }

}

int main()
{
   print(3,2,3,4);
   print_str("3","Hi","All","!!!");
}

OUTPUT

2 3 4 Hi All !!!