(root)/
gettext-0.22.4/
libtextstyle/
examples/
color-hello/
hello.c
       1  /* Example program for GNU libtextstyle.
       2     Copyright (C) 2018-2019 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2018.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Source code of the C program.  */
      19  
      20  #include <textstyle.h>
      21  
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <unistd.h>
      26  #include <pwd.h>
      27  #include <sys/stat.h>
      28  
      29  #include "names.c"
      30  
      31  /* Returns the record for a given first name, or NULL if not found.  */
      32  static const struct first_name *
      33  find_first_name (const char *name)
      34  {
      35    size_t i = 0;
      36    size_t j = sizeof (names) / sizeof (names[0]);
      37    /* Loop invariants:
      38       1. j > i,
      39       2. If name is in the table, then at an index >= i, < j.  */
      40    while (j - i > 1)
      41      {
      42        size_t k = i + (j - i) / 2;
      43        if (strcmp (names[k].name, name) <= 0)
      44          i = k;
      45        else
      46          j = k;
      47      }
      48    /* Here j = i + 1.  */
      49    if (strcmp (names[i].name, name) == 0)
      50      return &names[i];
      51    else
      52      return NULL;
      53  }
      54  
      55  enum sex
      56  {
      57    UNKNOWN,
      58    MALE,
      59    FEMALE
      60  };
      61  
      62  /* Returns the known sex for a given first name, or UNKNOWN if unknown.  */
      63  static enum sex
      64  classify_first_name (const char *name)
      65  {
      66    const struct first_name *p = find_first_name (name);
      67    if (p)
      68      {
      69        if (p->p_boy >= 0.99f)
      70          return MALE;
      71        if (p->p_boy <= 0.01f)
      72          return FEMALE;
      73      }
      74    return UNKNOWN;
      75  }
      76  
      77  /* Returns the full name of the current user, or NULL if unknown.  */
      78  static const char *
      79  get_fullname (void)
      80  {
      81    const char *name = getlogin ();
      82    if (name != NULL)
      83      {
      84        struct passwd *pwd = getpwnam (name);
      85        if (pwd != NULL)
      86          {
      87            const char *gecos = pwd->pw_gecos;
      88            if (gecos != NULL)
      89              {
      90                /* Use the part before the first comma.
      91                   See <https://en.wikipedia.org/wiki/Gecos_field>.  */
      92                const char *comma = strchr (gecos, ',');
      93                if (comma != NULL)
      94                  {
      95                    char *part = (char *) malloc (comma - gecos + 1);
      96                    if (part != NULL)
      97                      {
      98                        memcpy (part, gecos, comma - gecos);
      99                        part[comma - gecos] = '\0';
     100                        return part;
     101                      }
     102                  }
     103                else
     104                  return gecos;
     105              }
     106          }
     107      }
     108    return NULL;
     109  }
     110  
     111  int
     112  main (int argc, char *argv[])
     113  {
     114    const char *program_name = argv[0];
     115    const char *fullname = NULL;
     116    int i;
     117  
     118    /* Parse the command-line arguments.  */
     119    for (i = 1; i < argc; i++)
     120      {
     121        const char *arg = argv[i];
     122        if (strncmp (arg, "--color=", 8) == 0)
     123          handle_color_option (arg + 8);
     124        else if (strncmp (arg, "--style=", 8) == 0)
     125          handle_style_option (arg + 8);
     126        else if (arg[0] == '-')
     127          {
     128            fprintf (stderr, "%s: invalid argument: %s\n", program_name, arg);
     129            exit (1);
     130          }
     131        else
     132          fullname = arg;
     133      }
     134  
     135    /* Handle the --color=test special argument.  */
     136    if (color_test_mode)
     137      {
     138        print_color_test ();
     139        exit (0);
     140      }
     141  
     142    if (color_mode == color_yes
     143        || (color_mode == color_tty
     144            && isatty (STDOUT_FILENO)
     145            && getenv ("NO_COLOR") == NULL)
     146        || color_mode == color_html)
     147      {
     148        /* Find the style file.  */
     149        style_file_prepare ("HELLO_STYLE", "HELLO_STYLESDIR", STYLESDIR,
     150                            "hello-default.css");
     151        /* As a fallback, use the default in the current directory.  */
     152        {
     153          struct stat statbuf;
     154  
     155          if (style_file_name == NULL || stat (style_file_name, &statbuf) < 0)
     156            style_file_name = "hello-default.css";
     157        }
     158      }
     159    else
     160      /* No styling.  */
     161      style_file_name = NULL;
     162  
     163    /* Create a terminal output stream that uses this style file.  */
     164    styled_ostream_t stream =
     165      (color_mode == color_html
     166       ? html_styled_ostream_create (file_ostream_create (stdout),
     167                                     style_file_name)
     168       : styled_ostream_create (STDOUT_FILENO, "(stdout)", TTYCTL_AUTO,
     169                                style_file_name));
     170  
     171    /* Determine the full name of the user.  */
     172    if (fullname == NULL)
     173      fullname = get_fullname ();
     174    if (fullname != NULL)
     175      {
     176        ostream_write_str (stream, "Hello ");
     177  
     178        /* Associate the entire full name in CSS class 'name'.  */
     179        styled_ostream_begin_use_class (stream, "name");
     180  
     181        const char *fullname_end = fullname + strlen (fullname);
     182  
     183        /* Determine the extent of the first name in the full name.  */
     184        const char *firstname_start;
     185        const char *firstname_end;
     186        {
     187          /* The full name can be of the form "FAMILYNAME, FIRSTNAME".  */
     188          const char *comma = strchr (fullname, ',');
     189          if (comma != NULL)
     190            {
     191              firstname_start = comma + 1;
     192              while (*firstname_start == ' ')
     193                firstname_start++;
     194              firstname_end = fullname_end;
     195            }
     196          else
     197            {
     198              /* Or it can be of the form "X. FIRSTNAME Y. FAMILYNAME".  */
     199              firstname_start = fullname;
     200              for (;;)
     201                {
     202                  const char *space = strchr (firstname_start, ' ');
     203                  if (space == NULL)
     204                    {
     205                      firstname_end = fullname_end;
     206                      break;
     207                    }
     208                  if (space == firstname_start || space[-1] == '.')
     209                    firstname_start = space + 1;
     210                  else
     211                    {
     212                      firstname_end = space;
     213                      break;
     214                    }
     215                }
     216            }
     217          while (firstname_end > firstname_start && firstname_end[-1] == ' ')
     218            firstname_end--;
     219        }
     220  
     221        /* Output the part of the full name before the first name.  */
     222        ostream_write_mem (stream, fullname, firstname_start - fullname);
     223  
     224        /* Guess the sex, based on the first name.  */
     225        char *firstname = (char *) malloc (firstname_end - firstname_start + 1);
     226        memcpy (firstname, firstname_start, firstname_end - firstname_start);
     227        firstname[firstname_end - firstname_start] = '\0';
     228        enum sex guessed_sex = classify_first_name (firstname);
     229        free (firstname);
     230  
     231        /* Associate the first name with the appropriate CSS class.  */
     232        switch (guessed_sex)
     233          {
     234          case MALE:
     235            styled_ostream_begin_use_class (stream, "boy-name");
     236            break;
     237          case FEMALE:
     238            styled_ostream_begin_use_class (stream, "girl-name");
     239            break;
     240          default:
     241            break;
     242          }
     243  
     244        /* Output the first name.  */
     245        ostream_write_mem (stream, firstname_start,
     246                           firstname_end - firstname_start);
     247  
     248        /* Terminate the first name.  */
     249        switch (guessed_sex)
     250          {
     251          case MALE:
     252            styled_ostream_end_use_class (stream, "boy-name");
     253            break;
     254          case FEMALE:
     255            styled_ostream_end_use_class (stream, "girl-name");
     256            break;
     257          default:
     258            break;
     259          }
     260  
     261        /* Output the part of the full name after the first name.  */
     262        ostream_write_mem (stream, firstname_end, fullname_end - firstname_end);
     263  
     264        /* Terminate the name.  */
     265        styled_ostream_end_use_class (stream, "name");
     266  
     267        ostream_write_str (stream, "!\n");
     268      }
     269    else
     270      ostream_write_str (stream, "Hello!\n");
     271  
     272    /* Flush and close the terminal stream.  */
     273    styled_ostream_free (stream);
     274  
     275    return 0;
     276  }