Ticket #13828: 0002-Improve-argument-parsing.patch

File 0002-Improve-argument-parsing.patch, 5.1 KB (added by lezsakdomi, 6 years ago)

Use getopt

  • src/bin/setlocale.cpp

    From 93f91ae965528b6de4381f9ff6972343ba74b4f2 Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Domonkos=20Lezs=C3=A1k?= <lezsakdomi1@gmail.com>
    Date: Wed, 6 Dec 2017 15:46:18 +0100
    Subject: [PATCH] Improve argument parsing
    
    Core change:
    Parse arguments with getopt and usage message
    
    New functionality:
    - POSIX-compilant argument handling
    - Print usage message on all kind of errors
    - Add -h option
    - Add long options (--script and --variant)
    - Rename `-v` to `-x`: Get ready for `--verbose`
    - Print user-friendly error messages, when something argument-related
      happens
    - Store these messages in constants, so get ready for translations
      (not really a feature-change)
    ---
     src/bin/setlocale.cpp | 127 ++++++++++++++++++++++++++++++++++++--------------
     1 file changed, 92 insertions(+), 35 deletions(-)
    
    diff --git a/src/bin/setlocale.cpp b/src/bin/setlocale.cpp
    index f25baadb5e..388fd89b73 100644
    a b  
    66 *      Andrew Tockman, andy@keyboardfire.com
    77 */
    88
     9#include <getopt.h>
    910#include <stdio.h>
    1011#include <stdlib.h>
    1112#include <string.h>
    bool matches(const char* a, const char* b)  
    2728    return !strcasecmp(a, b);
    2829}
    2930
     31static const char* helpMessage =
     32    "Set system-wide locale\n"
     33    "\n"
     34    "Arguments for long options are mandatory for short options too.\n"
     35    "Arguments for getting help:\n"
     36    "  -h, --help    Print usage message, this help text and exit\n"
     37    "\n"
     38    "Arguments for listing locales:\n"
     39    "  -l, --list    Switch to list mode.\n"
     40    "                Output format: language<TAB>country<TAB>script<TAB>variant<TAB>...name...<NEWLINE>\n"
     41    "\n"
     42    "Arguments for setting locale:\n"
     43    "  -s, --script  Specify script for locale\n"
     44    "  -x, --variant Specify language variant\n"
     45;
     46static const char* noCodeSpecifiedMessage = "Error: No language code specified.\n";
     47static const char* tooManyTrailingArgsMessage = "Error: To many (%d) trailing arguments specified. Only a language code (and maybe a country code) allowed.\n";
     48static const char* unknownArgMessage = "Error: Unknown argument: -%c\n";
     49static const char* usageMessage =
     50    "Usage: setlocale -l|--list\n"
     51    "or     setlocale -h|--help\n"
     52    "or     setlocale language [country] [-s|--script script] [-v|--variant variant]\n"
     53;
    3054
    3155int main(int argc, char **argv)
    3256{
    int main(int argc, char **argv)  
    3458    const char* queryCountry = NULL;
    3559    const char* queryScript = NULL;
    3660    const char* queryVariant = NULL;
    37     bool list = false, err = false;
     61    bool list = false;
    3862
    3963    // parse command line
    40     for (int i = 1; i < argc; ++i) {
    41         if (*argv[i] == '-') {
    42             // parse a command line flag
    43             if (!strcmp(argv[i], "--list"))
    44                 list = true;
    45             else if (argv[i][1] && !argv[i][2]) {
    46                 // found a single character flag
    47                 if (argv[i][1] == 'l')
    48                     list = true;
    49                 else if (argv[i][1] == 's')
    50                     queryScript = argv[++i];
    51                 else if (argv[i][1] == 'v')
    52                     queryVariant = argv[++i];
    53                 else {
    54                     err = true;
    55                     break;
    56                 }
    57             } else
    58                 err = false; // unknown flag
    59         } else if (queryCode == NULL)
    60             // bare argument; fill up code and then country, erroring on
    61             // anything more than two arguments
    62             queryCode = argv[i];
    63         else if (queryCountry == NULL)
    64             queryCountry = argv[i];
    65         else {
    66             err = true;
    67             break;
     64    {
     65        static const struct option longopts[] = {
     66            { "help", no_argument, NULL, 'h' },
     67            { "list", no_argument, NULL, 'l' },
     68            { "script", required_argument, NULL, 's' },
     69            { "variant", required_argument, NULL, 'x' },
     70        };
     71        const char* optstring = "hls:x:";
     72
     73        int c;
     74        while ((c = getopt_long(argc, argv, optstring, longopts, NULL)) != -1)
     75        {
     76            switch (c) {
     77                case 'h':
     78                    puts(usageMessage);
     79                    puts(helpMessage);
     80                    return 0;
     81
     82                case 'l':
     83                    list = true;
     84                    break;
     85
     86                case 's':
     87                    queryScript = optarg;
     88                    break;
     89
     90                case 'x':
     91                    queryVariant = optarg;
     92                    break;
     93
     94                default:
     95                    fprintf(stderr, unknownArgMessage, c);
     96                    fputc('\n', stderr);
     97                    fputs(usageMessage, stderr);
     98                    return 1;
     99            }
    68100        }
    69     }
    70101
    71     // check for proper command line, requiring exactly one of list or query
    72     if (err || ((queryCode == NULL) ^ list)) {
    73         fprintf(stderr,
    74             "Usage:\n"
    75             "  setlocale -l|--list\n"
    76             "  setlocale language [country] [-s script] [-v variant]\n");
    77         return 1;
     102        {
     103            // check for proper command line, requiring exactly one of list or query
     104            if (list) {
     105                if (argc != optind) {
     106                    fputs(listModeButTrailingArgsMessage, stderr);
     107                    fputc('\n', stderr);
     108                    fputs(usageMessage, stderr);
     109                    return 1;
     110                }
     111            } else {
     112                int numRemainOptions = argc-optind;
     113                switch (numRemainOptions) {
     114                    case 2:
     115                        queryCountry = argv[optind+1]; //fallthru
     116                    case 1:
     117                        queryCode = argv[optind];
     118                        break;
     119
     120                    case 0:
     121                        if (list) break;
     122                        fputs(noCodeSpecifiedMessage, stderr);
     123                        fputc('\n', stderr);
     124                        fputs(usageMessage, stderr);
     125                        return 1;
     126
     127                    default:
     128                        fprintf(stderr, tooManyTrailingArgsMessage, numRemainOptions);
     129                        fputc('\n', stderr);
     130                        fputs(usageMessage, stderr);
     131                        return 1;
     132                }
     133            }
     134        }
    78135    }
    79136
    80137    // query for all available languages