Ticket #13828: 0003-Improve-the-setlocale-command.patch

File 0003-Improve-the-setlocale-command.patch, 9.5 KB (added by lezsakdomi, 6 years ago)

Varios changes to make it complete. Depends on 0001-Implement-new-setlocale-command.patch

  • src/bin/setlocale.cpp

    From 05c5ca7459f2d13774e5295ab9c3ee1adf88e6d0 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 3/3] Improve the `setlocale' command
    
    Now it allows the user to set the system locale with syntax similar to the
    following:
    
      setlocale en
      setlocale en_US -v posix
      setlocale bs -s cyrl
      setlocale bs_BA -s cyrl
    
    Even multiple locales could be set:
    
      setlocale bs_BA_Cyrl en_GB en_US
    
    It also supports the -l flag to list all available locales, but that
    functionality is improved:
    
    - By default, lists only possible locale codes
    - Introduced the -d (--details) flag which simulates the same
      functionality as the previous version
    
    Two (main) additional changes in usage:
    - Introduced the -h (--help) flag
    - Added long options
    - Added a lot of user-friendly error messages
    
    The code is almost completly rewritten and reformatted
    ---
     src/bin/setlocale.cpp | 267 ++++++++++++++++++++++++++++++++++----------------
     1 file changed, 185 insertions(+), 82 deletions(-)
    
    diff --git a/src/bin/setlocale.cpp b/src/bin/setlocale.cpp
    index f25baadb5e..f2f62fac8b 100644
    a b  
    33 * Distributed under the terms of the MIT License.
    44 *
    55 * Authors:
     6 *      Domonkos Lezsák, lezsakdomi1@gmail.com
    67 *      Andrew Tockman, andy@keyboardfire.com
    78 */
    89
     10#include <getopt.h>
    911#include <stdio.h>
    1012#include <stdlib.h>
    1113#include <string.h>
     
    1820
    1921using BPrivate::MutableLocaleRoster;
    2022
    21 bool matches(const char* a, const char* b)
     23bool
     24matches(const char* a, const char* b)
    2225{
    2326    if (a == NULL && b == NULL)
    2427        return true;
    bool matches(const char* a, const char* b)  
    2831}
    2932
    3033
    31 int main(int argc, char **argv)
     34static const char* kHelpMessage =
     35    "Set system-wide locale\n"
     36    "\n"
     37    "Arguments for long options are mandatory for short options too.\n"
     38    "Arguments for getting help:\n"
     39    "  -h, --help    Print usage message, this help text and exit\n"
     40    "\n"
     41    "Arguments for listing locales:\n"
     42    "  -l, --list    Switch to list mode.\n"
     43    "  -d, --details List in a long, detailed format:\n"
     44    "                code<TAB>language<TAB>country<TAB>script<TAB>variant<TAB>"
     45        "name\n"
     46    "\n"
     47    "Arguments for setting locale:\n"
     48    "  -c, --country Specify country for locale\n"
     49    "  -s, --script  Specify script for locale\n"
     50    "  -x, --variant Specify language variant\n"
     51    "\n"
     52    " Please note, that these can be specified using underscores too.\n"
     53    " Example:\n"
     54    "   setlocale en_US\n"
     55    "   setlocale az_Latn_AZ\n"
     56    " When multiple locales are specified, the seconds gets set as secondary, "
     57        "the third as tertiary, etc.\n"
     58;
     59static const char* kListModeButTrailingArgsMessage =
     60    "Error: List mode specified, but it seems like locales too.\n";
     61static const char* kLocaleNotFound =
     62    "Warning: Locale %s not found.\n";
     63static const char* kNoCodeSpecifiedMessage =
     64    "Error: No language code specified.\n";
     65static const char* kUnknownArgMessage = "Error: Unknown argument: -%c\n";
     66static const char* kUsageMessage =
     67    "Usage: setlocale -l|--list [-d|--details]\n"
     68    "or     setlocale -h|--help\n"
     69    "or     setlocale locale_code[ locale_code[ locale_code ...]] "
     70        "[-c|--country country] [-s|--script script] [-v|--variant variant]\n"
     71;
     72
     73
     74void
     75listLocales(bool detailed = false)
     76{
     77    // query for all available languages
     78    BMessage languages;
     79    BLocaleRoster::Default()->GetAvailableLanguages(&languages);
     80
     81    const char* id;
     82    for (int32 i = 0; languages.FindString("language", i, &id) == B_OK; i++)
     83    {
     84        fputs(id, stdout);
     85        if (detailed) {
     86            BLanguage* language;
     87            if (BLocaleRoster::Default()->GetLanguage(id, &language) == B_OK)
     88            {
     89                // extract all the relevant information about the language
     90                const char* code = language->Code();
     91                const char* country = language->CountryCode();
     92                const char* script = language->ScriptCode();
     93                const char* variant = language->Variant();
     94                BString name; language->GetNativeName(name);
     95
     96                // print the information
     97                putchar('\t');
     98                printf("%s\t", code);
     99                printf("%s\t", country);
     100                printf("%s\t", script ? script : "");
     101                printf("%s\t", variant ? variant : "");
     102                printf("%s", name.String());
     103
     104                delete language;
     105            } else
     106                fprintf(stderr, "Failed to get BLanguage for %s\n", id);
     107        }
     108        putchar('\n');
     109    }
     110}
     111
     112
     113int
     114main(const int argc, char* const argv[])
    32115{
    33     const char* queryCode = NULL;
     116    bool queryList = false;
     117    bool queryDetails = false;
    34118    const char* queryCountry = NULL;
    35119    const char* queryScript = NULL;
    36120    const char* queryVariant = NULL;
    37     bool list = false, err = false;
    38121
    39122    // 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;
     123    {
     124        static const struct option longopts[] = {
     125            { "help", no_argument, NULL, 'h' },
     126            { "list", no_argument, NULL, 'l' },
     127            { "details", no_argument, NULL, 'd' },
     128            { "country", required_argument, NULL, 'c' },
     129            { "script", required_argument, NULL, 's' },
     130            { "variant", required_argument, NULL, 'x' },
     131        };
     132        const char* optstring = "hldc:s:x:";
     133       
     134        for (int c = getopt_long(argc, argv, optstring, longopts, NULL);
     135            c != -1; c = getopt_long(argc, argv, optstring, longopts, NULL))
     136        {
     137            switch (c)
     138            {
     139                case 'h':
     140                    fputs(kUsageMessage, stdout);
     141                    fputs(kHelpMessage, stdout);
     142                    return 0;
     143
     144                case 'l':
     145                    queryList = true;
    55146                    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;
     147
     148                case 'd':
     149                    queryDetails = true;
     150                    break;
     151
     152                case 'c':
     153                    queryCountry = optarg;
     154                    break;
     155
     156                case 's':
     157                    queryScript = optarg;
     158                    break;
     159
     160                case 'x':
     161                    queryVariant = optarg;
     162                    break;
     163
     164                default:
     165                    // error
     166                    fprintf(stderr, kUnknownArgMessage, c);
     167                    fputc('\n', stderr);
     168                    fputs(kUsageMessage, stderr);
     169                    return 1;
     170            }
    68171        }
    69     }
    70172
    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;
    78     }
     173        // Process list mode
     174        if (queryList) {
     175            if (optind != argc) {
     176                fputs(kListModeButTrailingArgsMessage, stderr);
     177                fputc('\n', stderr);
     178                fputs(kUsageMessage, stderr);
     179                return 1;
     180            }
     181            listLocales(queryDetails);
     182            return true;
     183        }
    79184
    80     // query for all available languages
    81     BMessage languages;
    82     BLocaleRoster::Default()->GetAvailableLanguages(&languages);
     185        // Process language codes
     186        {
     187            // Check for number of supplimentary arguments
     188            if (optind == argc) {
     189                fputs(kNoCodeSpecifiedMessage, stderr);
     190                fputc('\n', stderr);
     191                fputs(kUsageMessage, stderr);
     192                return 1;
     193            }
    83194
    84     const char* id;
    85     for (int32 i = 0; languages.FindString("language", i, &id) == B_OK; ++i) {
    86         BLanguage* language;
    87         if (BLocaleRoster::Default()->GetLanguage(id, &language) == B_OK) {
    88             // extract all the relevant information about the language
    89             BString name;
    90             language->GetNativeName(name);
    91             const char* code = language->Code();
    92             const char* country = language->CountryCode();
    93             const char* script = language->ScriptCode();
    94             const char* variant = language->Variant();
    95 
    96             // if we're listing, print the information
    97             if (list)
    98                 printf("%s\t%s\t%s\t%s\t%s\n",
    99                     code,
    100                     country ? country : "",
    101                     script ? script : "",
    102                     variant ? variant : "",
    103                     name.String());
    104             else if (!strcasecmp(code, queryCode)
    105                 && matches(country, queryCountry)
    106                 && matches(script, queryScript)
    107                 && matches(variant, queryVariant)) {
    108                 // found a match! set the new locale
     195            // And do the job
     196            {
    109197                BMessage preferred;
    110                 preferred.AddString("language", id);
     198                for (int i = optind; i < argc; i++)
     199                {
     200                    BString targetCode(argv[i]);
     201
     202                    if (queryCountry != NULL)
     203                        targetCode += BString(queryCountry).Prepend('_', 1);
     204
     205                    if (queryVariant != NULL)
     206                        targetCode += BString(queryVariant).Prepend('_', 1);
     207
     208                    if (queryScript != NULL)
     209                        targetCode += BString(queryScript).Prepend('_', 1);
     210
     211                    {
     212                        BLanguage* language;
     213                        if (BLocaleRoster::Default()->GetLanguage(
     214                                targetCode.String(), &language) == B_OK) {
     215                            // found a match! set the new locale
     216                            preferred.AddString("language",
     217                                targetCode.String());
     218                        } else {
     219                            fprintf(stderr, kLocaleNotFound,
     220                                targetCode.String());
     221                        }
     222                    }
     223                }
    111224                MutableLocaleRoster::Default()->
    112225                    SetPreferredLanguages(&preferred);
    113                 printf("Locale successfully set to %s\n", name.String());
    114                 delete language;
    115226                return 0;
    116227            }
    117 
    118             delete language;
    119         } else
    120             fprintf(stderr, "Failed to get BLanguage for %s\n", id);
     228        }
    121229    }
    122230
    123     // if we've gotten this far and the -l flag wasn't passed, the requested
    124     // locale wasn't found
    125     if (!list) {
    126         fprintf(stderr, "Locale not found\n");
    127         return 1;
    128     }
    129231
    130     return 0;
     232    // if we've gotten this far, then some error happened
     233    return 2;
    131234}