chiark / gitweb /
gpg agent lockup fix: Interrupt main loop when active_connections_value==0
[gnupg2.git] / common / helpfile.c
1 /* helpfile.c - GnuPG's helpfile feature
2  *      Copyright (C) 2007 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <https://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <stdlib.h>
32
33
34 #include "util.h"
35 #include "i18n.h"
36 #include "membuf.h"
37
38
39 /* Try to find KEY in the file FNAME.  */
40 static char *
41 findkey_fname (const char *key, const char *fname)
42 {
43   gpg_error_t err = 0;
44   FILE *fp;
45   int lnr = 0;
46   int c;
47   char *p, line[256];
48   int in_item = 0;
49   membuf_t mb = MEMBUF_ZERO;
50
51   fp = fopen (fname, "r");
52   if (!fp)
53     {
54       if (errno != ENOENT)
55         {
56           err = gpg_error_from_syserror ();
57           log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
58         }
59       return NULL;
60     }
61
62   while (fgets (line, DIM(line)-1, fp))
63     {
64       lnr++;
65
66       if (!*line || line[strlen(line)-1] != '\n')
67         {
68           /* Eat until end of line. */
69           while ( (c=getc (fp)) != EOF && c != '\n')
70             ;
71           err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
72                            : GPG_ERR_INCOMPLETE_LINE);
73           log_error (_("file '%s', line %d: %s\n"),
74                      fname, lnr, gpg_strerror (err));
75         }
76       else
77         line[strlen(line)-1] = 0; /* Chop the LF. */
78
79     again:
80       if (!in_item)
81         {
82           /* Allow for empty lines and spaces while not in an item. */
83           for (p=line; spacep (p); p++)
84             ;
85           if (!*p || *p == '#')
86             continue;
87           if (*line != '.' || spacep(line+1))
88             {
89               log_info (_("file '%s', line %d: %s\n"),
90                         fname, lnr, _("ignoring garbage line"));
91               continue;
92             }
93           trim_trailing_spaces (line);
94           in_item = 1;
95           if (!strcmp (line+1, key))
96             {
97               /* Found.  Start collecting.  */
98               init_membuf (&mb, 1024);
99             }
100           continue;
101         }
102
103       /* If in an item only allow for comments in the first column
104          and provide ". " as an escape sequence to allow for
105          leading dots and hash marks in the actual text.  */
106       if (*line == '#')
107         continue;
108       if (*line == '.')
109         {
110           if (spacep(line+1))
111             p = line + 2;
112           else
113             {
114               trim_trailing_spaces (line);
115               in_item = 0;
116               if (is_membuf_ready (&mb))
117                 break;        /* Yep, found and collected the item.  */
118               if (!line[1])
119                 continue;     /* Just an end of text dot. */
120               goto again;     /* A new key line.  */
121             }
122         }
123       else
124         p = line;
125
126       if (is_membuf_ready (&mb))
127         {
128           put_membuf_str (&mb, p);
129           put_membuf (&mb, "\n", 1);
130         }
131
132     }
133   if ( !err && ferror (fp) )
134     {
135       err = gpg_error_from_syserror ();
136       log_error (_("error reading '%s', line %d: %s\n"),
137                  fname, lnr, gpg_strerror (err));
138     }
139
140   fclose (fp);
141   if (is_membuf_ready (&mb))
142     {
143       /* We have collected something.  */
144       if (err)
145         {
146           xfree (get_membuf (&mb, NULL));
147           return NULL;
148         }
149       else
150         {
151           put_membuf (&mb, "", 1);  /* Terminate string.  */
152           return get_membuf (&mb, NULL);
153         }
154     }
155   else
156     return NULL;
157 }
158
159
160 /* Try the help files depending on the locale.  */
161 static char *
162 findkey_locale (const char *key, const char *locname,
163                 int only_current_locale, const char *dirname)
164 {
165   const char *s;
166   char *fname, *ext, *p;
167   char *result;
168
169   fname = xtrymalloc (strlen (dirname) + 6 + strlen (locname) + 4 + 1);
170   if (!fname)
171     return NULL;
172   ext = stpcpy (stpcpy (fname, dirname), "/help.");
173   /* Search with locale name and territory.  ("help.LL_TT.txt") */
174   if (strchr (locname, '_'))
175     {
176       strcpy (stpcpy (ext, locname), ".txt");
177       result = findkey_fname (key, fname);
178     }
179   else
180     result = NULL;  /* No territory.  */
181
182   if (!result)
183     {
184       /* Search with just the locale name - if any. ("help.LL.txt") */
185       if (*locname)
186         {
187           for (p=ext, s=locname; *s && *s != '_';)
188             *p++ = *s++;
189           strcpy (p, ".txt");
190           result = findkey_fname (key, fname);
191         }
192       else
193         result = NULL;
194     }
195
196   if (!result && (!only_current_locale || !*locname) )
197     {
198       /* Last try: Search in file without any locale info.  ("help.txt") */
199       strcpy (ext, "txt");
200       result = findkey_fname (key, fname);
201     }
202
203   xfree (fname);
204   return result;
205 }
206
207
208 /* Return a malloced help text as identified by KEY.  The system takes
209    the string from an UTF-8 encoded file to be created by an
210    administrator or as distributed with GnuPG.  On a GNU or Unix
211    system the entry is searched in these files:
212
213      /etc/gnupg/help.LL.txt
214      /etc/gnupg/help.txt
215      /usr/share/gnupg/help.LL.txt
216      /usr/share/gnupg/help.txt
217
218    Here LL denotes the two digit language code of the current locale.
219    If ONLY_CURRENT_LOCALE is set, the function won't fallback to the
220    english valiant ("help.txt") unless that locale has been requested.
221
222    The help file needs to be encoded in UTF-8, lines with a '#' in the
223    first column are comment lines and entirely ignored.  Help keys are
224    identified by a key consisting of a single word with a single dot
225    as the first character.  All key lines listed without any
226    intervening lines (except for comment lines) lead to the same help
227    text.  Lines following the key lines make up the actual hep texts.
228
229 */
230
231 char *
232 gnupg_get_help_string (const char *key, int only_current_locale)
233 {
234   static const char *locname;
235   char *result;
236
237   if (!locname)
238     {
239       char *buffer, *p;
240       int count = 0;
241       const char *s = gnupg_messages_locale_name ();
242       buffer = xtrystrdup (s);
243       if (!buffer)
244         locname = "";
245       else
246         {
247           for (p = buffer; *p; p++)
248             if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/)
249               *p = 0;
250             else if (*p == '_')
251               {
252                 if (count++)
253                   *p = 0;  /* Also cut at a underscore in the territory.  */
254               }
255           locname = buffer;
256         }
257     }
258
259   if (!key || !*key)
260     return NULL;
261
262   result = findkey_locale (key, locname, only_current_locale,
263                            gnupg_sysconfdir ());
264   if (!result)
265     result = findkey_locale (key, locname, only_current_locale,
266                              gnupg_datadir ());
267
268   if (result)
269     trim_trailing_spaces (result);
270
271   return result;
272 }