1  /* xgettext Desktop Entry backend.
       2     Copyright (C) 2014, 2018-2020, 2023 Free Software Foundation, Inc.
       3  
       4     This file was written by Daiki Ueno <ueno@gnu.org>, 2014.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #ifdef HAVE_CONFIG_H
      20  # include "config.h"
      21  #endif
      22  
      23  /* Specification.  */
      24  #include "x-desktop.h"
      25  
      26  #include <errno.h>
      27  #include <stdbool.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  
      32  #include "message.h"
      33  #include "xgettext.h"
      34  #include "xg-message.h"
      35  #include "error.h"
      36  #include "error-progname.h"
      37  #include "xalloc.h"
      38  #include "xvasprintf.h"
      39  #include "mem-hash-map.h"
      40  #include "gettext.h"
      41  #include "read-desktop.h"
      42  #include "po-charset.h"
      43  #include "c-ctype.h"
      44  
      45  #define _(s) gettext(s)
      46  
      47  /* ====================== Keyword set customization.  ====================== */
      48  
      49  /* The syntax of a Desktop Entry file is defined at
      50     https://standards.freedesktop.org/desktop-entry-spec/latest/index.html
      51  
      52     Basically, values with 'localestring' type can be localized.
      53     However, the values of 'Icon', while being localizable, are not supported
      54     by xgettext.  See the documentation for more info.
      55  
      56     The type of a value is determined by looking at the key associated
      57     with it.  The list of available keys are listed on:
      58     https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html  */
      59  
      60  static hash_table keywords;
      61  static bool default_keywords = true;
      62  
      63  static void
      64  add_keyword (const char *name, hash_table *keywords, bool is_list)
      65  {
      66    if (name == NULL)
      67      default_keywords = false;
      68    else
      69      {
      70        if (keywords->table == NULL)
      71          hash_init (keywords, 100);
      72  
      73        desktop_add_keyword (keywords, name, is_list);
      74      }
      75  }
      76  
      77  void
      78  x_desktop_keyword (const char *name)
      79  {
      80    add_keyword (name, &keywords, false);
      81  }
      82  
      83  static void
      84  init_keywords (void)
      85  {
      86    if (default_keywords)
      87      {
      88        if (keywords.table == NULL)
      89          hash_init (&keywords, 100);
      90  
      91        desktop_add_default_keywords (&keywords);
      92        default_keywords = false;
      93      }
      94  }
      95  
      96  typedef struct extract_desktop_reader_ty extract_desktop_reader_ty;
      97  struct extract_desktop_reader_ty
      98  {
      99    DESKTOP_READER_TY
     100  
     101    message_list_ty *mlp;
     102  };
     103  
     104  static void
     105  extract_desktop_handle_group (struct desktop_reader_ty *reader,
     106                                const char *group)
     107  {
     108    savable_comment_reset ();
     109  }
     110  
     111  static void
     112  extract_desktop_handle_pair (struct desktop_reader_ty *reader,
     113                               lex_pos_ty *key_pos,
     114                               const char *key,
     115                               const char *locale,
     116                               const char *value)
     117  {
     118    extract_desktop_reader_ty *extract_reader =
     119      (extract_desktop_reader_ty *) reader;
     120    void *keyword_value;
     121  
     122    if (!locale                   /* Skip already translated entry.  */
     123        && hash_find_entry (&keywords, key, strlen (key), &keyword_value) == 0)
     124      {
     125        bool is_list = (bool) keyword_value;
     126  
     127        remember_a_message (extract_reader->mlp, NULL,
     128                            desktop_unescape_string (value, is_list), false,
     129                            false, null_context, key_pos,
     130                            NULL, savable_comment, false);
     131      }
     132    savable_comment_reset ();
     133  }
     134  
     135  static void
     136  extract_desktop_handle_comment (struct desktop_reader_ty *reader,
     137                                  const char *buffer)
     138  {
     139    size_t buflen = strlen (buffer);
     140    size_t bufpos = 0;
     141  
     142    while (bufpos < buflen
     143           && c_isspace (buffer[bufpos]))
     144      ++bufpos;
     145    while (buflen >= bufpos
     146           && c_isspace (buffer[buflen - 1]))
     147      --buflen;
     148    if (bufpos < buflen)
     149      {
     150        char *comment = xstrdup (buffer);
     151        comment[buflen] = 0;
     152        savable_comment_add (&comment[bufpos]);
     153        free (comment);
     154      }
     155  }
     156  
     157  static void
     158  extract_desktop_handle_blank (struct desktop_reader_ty *reader,
     159                                const char *s)
     160  {
     161    savable_comment_reset ();
     162  }
     163  
     164  static desktop_reader_class_ty extract_methods =
     165    {
     166      sizeof (extract_desktop_reader_ty),
     167      NULL,
     168      NULL,
     169      extract_desktop_handle_group,
     170      extract_desktop_handle_pair,
     171      extract_desktop_handle_comment,
     172      extract_desktop_handle_blank
     173    };
     174  
     175  void
     176  extract_desktop (FILE *f,
     177                   const char *real_filename, const char *logical_filename,
     178                   flag_context_list_table_ty *flag_table,
     179                   msgdomain_list_ty *mdlp)
     180  {
     181    desktop_reader_ty *reader = desktop_reader_alloc (&extract_methods);
     182    extract_desktop_reader_ty *extract_reader =
     183      (extract_desktop_reader_ty *) reader;
     184  
     185    init_keywords ();
     186    xgettext_current_source_encoding = po_charset_utf8;
     187  
     188    extract_reader->mlp = mdlp->item[0]->messages;
     189  
     190    desktop_parse (reader, f, real_filename, logical_filename);
     191    desktop_reader_free (reader);
     192  
     193    reader = NULL;
     194  }