Search code examples
cgetoptgetopt-longargc

How to pass escape sequence like tab and newline char as command line argument in C programming getopt_long?


I almost reached end of my code, after a lot of search I didn't find solution no where, I want to supply escape sequence like '\t', '\n' to my program like how awk and perl program takes, and finally I want to use them as printf or sprintf format string

This is what I tried so far, please note I need to have variable delim and rs should be pointer.

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


 int main (int argc, char **argv)
 {
   int c;

   char *delim = ",", *rs = "\n\r";

   while (1)
     {
       static struct option long_options[] =
         {

           {"delim",  required_argument, 0, 'd'},
           {"row_sep",  required_argument, 0, 'r'},
           {0, 0, 0, 0}
         };

       int option_index = 0;

       c = getopt_long (argc, argv, "df",
                        long_options, &option_index);


       if (c == -1)
         break;

       switch (c)
         {
         case 0:

           if (long_options[option_index].flag != 0)
             break;
           printf ("option %s", long_options[option_index].name);
           if (optarg)
             printf (" with arg %s", optarg);
           printf ("\n");
           break;

         case 'd':
           delim = optarg;
           break;

         case 'r':
           rs = optarg;
           break;

         case '?':
           break;

         default:
           abort ();
         }
     }


   /* Print any remaining command line arguments (not options). */
   if (optind < argc)
     {
       printf ("non-option ARGV-elements: ");
       while (optind < argc)
         printf ("%s ", argv[optind++]);
       putchar ('\n');
     }



 /* Test input argument */


 printf("This is test%ssome text%s",delim,rs);



   exit (0);
 }

When I compile and execute I get output like this

 $ gcc argument.c

 $ ./a.out --delim="\t"
 This is test\tsome text

 $ ./a.out --delim="\t" --row_sep="\n"
 This is test\tsome text\n

I expect it to print tab and newline instead of '\t' and '\n' as original

Kindly someone help me.


Solution

  • Some code somewhere has to translate backslash-t and backslash-n into tab and newline. You can have the shell do it (if it is Bash or supports ANSI C quoting):

    ./a.out --delim=$'\t'
    ./a.out --delim=$'\t' --row_sep=$'\n'
    

    Or use the printf command (distinct from, though related to, the printf() function); this avoids using any Bashisms:

    ./a.out --delim="$(printf '\t')"
    ./a.out --delim="$(printf '\t')" --row_sep="$(printf '\n')"
    

    Or, indeed, you can simply type the characters at the command line. Entering tab needs you to type Control-VControl-I to avoid filename completion.

    $ ./a.out --delim='^V^I'
    $ ./a.out --delim='^V^I' --row_sep='
    > '
    

    This is less clear, though; I'd use one of the previous two mechanisms in preference.

    Or you can do it in your program. That's a little harder, but not much. I have a rather comprehensive function, cstrlit_chr() that does most of that job (it doesn't handle Unicode escapes like \u0123 or \U00012345), but it isn't standard and it is in a file that's 238 lines long with the comments and test code, etc (just over 100 non-blank, non-comment lines of C code in the function, which could be compressed a bit if I wanted to spend the time on it), so it's a bit big to add it here.


    Can you please show me cstrlit_chr() example for the same?

    The header documenting the interface says:

    /* Convert C String Literal in (str..end] (excluding surrounding quotes) */
    /* to string, returning length of string, or -1 if conversion error, or */
    /* -2 if there is not enough room for the output */
    extern int cstrlit_str(const char *str, const char *end, char *buffer, size_t buflen);
    /* Convert C Character Literal in (str..end] (excluding surrounding quotes) */
    /* to character, returning converted char or -1 if string is invalid. */
    /* If non-null, eptr is set to first non-converted (or non-convertible) character */
    extern int cstrlit_chr(const char *str, const char *end, char const ** const eptr);
    
    /* Convert character to C Character Literal. */
    /* buffer[0] = '\0' if there isn't enough room in buffer */
    extern void chr_cstrlit(unsigned char c, char *buffer, size_t buflen);
    /* Convert string to C String Literal */
    extern void str_cstrlit(const char *str, char *buffer, size_t buflen);
    

    So, cstrlit_chr() is one of a set of four functions. However, it is pretty easy to use:

     const char *endptr;
     int c = cstrlit_char(argv[i], argv[i]+strlen(argv[i]), &endptr);
    

    If argv[i] contains backslash and t, then c will be assigned the value of '\t' (which is usually control-I or 9). If it contains backslash and n, then c will be assigned the value of '\n' (which is usually control-J or 10).

    The value in endptr tells you what's the next character to be interpreted.