Line data Source code
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 <http://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 0 : findkey_fname (const char *key, const char *fname)
42 : {
43 0 : gpg_error_t err = 0;
44 : FILE *fp;
45 0 : int lnr = 0;
46 : int c;
47 : char *p, line[256];
48 0 : int in_item = 0;
49 0 : membuf_t mb = MEMBUF_ZERO;
50 :
51 0 : fp = fopen (fname, "r");
52 0 : if (!fp)
53 : {
54 0 : if (errno != ENOENT)
55 : {
56 0 : err = gpg_error_from_syserror ();
57 0 : log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
58 : }
59 0 : return NULL;
60 : }
61 :
62 0 : while (fgets (line, DIM(line)-1, fp))
63 : {
64 0 : lnr++;
65 :
66 0 : if (!*line || line[strlen(line)-1] != '\n')
67 : {
68 : /* Eat until end of line. */
69 0 : while ( (c=getc (fp)) != EOF && c != '\n')
70 : ;
71 0 : err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
72 : : GPG_ERR_INCOMPLETE_LINE);
73 0 : log_error (_("file '%s', line %d: %s\n"),
74 : fname, lnr, gpg_strerror (err));
75 : }
76 : else
77 0 : line[strlen(line)-1] = 0; /* Chop the LF. */
78 :
79 : again:
80 0 : if (!in_item)
81 : {
82 : /* Allow for empty lines and spaces while not in an item. */
83 0 : for (p=line; spacep (p); p++)
84 : ;
85 0 : if (!*p || *p == '#')
86 0 : continue;
87 0 : if (*line != '.' || spacep(line+1))
88 : {
89 0 : log_info (_("file '%s', line %d: %s\n"),
90 : fname, lnr, _("ignoring garbage line"));
91 0 : continue;
92 : }
93 0 : trim_trailing_spaces (line);
94 0 : in_item = 1;
95 0 : if (!strcmp (line+1, key))
96 : {
97 : /* Found. Start collecting. */
98 0 : init_membuf (&mb, 1024);
99 : }
100 0 : 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 0 : if (*line == '#')
107 0 : continue;
108 0 : if (*line == '.')
109 : {
110 0 : if (spacep(line+1))
111 0 : p = line + 2;
112 : else
113 : {
114 0 : trim_trailing_spaces (line);
115 0 : in_item = 0;
116 0 : if (is_membuf_ready (&mb))
117 : break; /* Yep, found and collected the item. */
118 0 : if (!line[1])
119 0 : continue; /* Just an end of text dot. */
120 0 : goto again; /* A new key line. */
121 : }
122 : }
123 : else
124 0 : p = line;
125 :
126 0 : if (is_membuf_ready (&mb))
127 : {
128 0 : put_membuf_str (&mb, p);
129 0 : put_membuf (&mb, "\n", 1);
130 : }
131 :
132 : }
133 0 : if ( !err && ferror (fp) )
134 : {
135 0 : err = gpg_error_from_syserror ();
136 0 : log_error (_("error reading '%s', line %d: %s\n"),
137 : fname, lnr, gpg_strerror (err));
138 : }
139 :
140 0 : fclose (fp);
141 0 : if (is_membuf_ready (&mb))
142 : {
143 : /* We have collected something. */
144 0 : if (err)
145 : {
146 0 : xfree (get_membuf (&mb, NULL));
147 0 : return NULL;
148 : }
149 : else
150 : {
151 0 : put_membuf (&mb, "", 1); /* Terminate string. */
152 0 : return get_membuf (&mb, NULL);
153 : }
154 : }
155 : else
156 0 : return NULL;
157 : }
158 :
159 :
160 : /* Try the help files depending on the locale. */
161 : static char *
162 0 : 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 0 : fname = xtrymalloc (strlen (dirname) + 6 + strlen (locname) + 4 + 1);
170 0 : if (!fname)
171 0 : return NULL;
172 0 : ext = stpcpy (stpcpy (fname, dirname), "/help.");
173 : /* Search with locale name and territory. ("help.LL_TT.txt") */
174 0 : if (strchr (locname, '_'))
175 : {
176 0 : strcpy (stpcpy (ext, locname), ".txt");
177 0 : result = findkey_fname (key, fname);
178 : }
179 : else
180 0 : result = NULL; /* No territory. */
181 :
182 0 : if (!result)
183 : {
184 : /* Search with just the locale name - if any. ("help.LL.txt") */
185 0 : if (*locname)
186 : {
187 0 : for (p=ext, s=locname; *s && *s != '_';)
188 0 : *p++ = *s++;
189 0 : strcpy (p, ".txt");
190 0 : result = findkey_fname (key, fname);
191 : }
192 : else
193 0 : result = NULL;
194 : }
195 :
196 0 : if (!result && (!only_current_locale || !*locname) )
197 : {
198 : /* Last try: Search in file without any locale info. ("help.txt") */
199 0 : strcpy (ext, "txt");
200 0 : result = findkey_fname (key, fname);
201 : }
202 :
203 0 : xfree (fname);
204 0 : 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 0 : gnupg_get_help_string (const char *key, int only_current_locale)
233 : {
234 : static const char *locname;
235 : char *result;
236 :
237 0 : if (!locname)
238 : {
239 : char *buffer, *p;
240 0 : int count = 0;
241 0 : const char *s = gnupg_messages_locale_name ();
242 0 : buffer = xtrystrdup (s);
243 0 : if (!buffer)
244 0 : locname = "";
245 : else
246 : {
247 0 : for (p = buffer; *p; p++)
248 0 : if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/)
249 0 : *p = 0;
250 0 : else if (*p == '_')
251 : {
252 0 : if (count++)
253 0 : *p = 0; /* Also cut at a underscore in the territory. */
254 : }
255 0 : locname = buffer;
256 : }
257 : }
258 :
259 0 : if (!key || !*key)
260 0 : return NULL;
261 :
262 0 : result = findkey_locale (key, locname, only_current_locale,
263 : gnupg_sysconfdir ());
264 0 : if (!result)
265 0 : result = findkey_locale (key, locname, only_current_locale,
266 : gnupg_datadir ());
267 :
268 0 : if (result)
269 0 : trim_trailing_spaces (result);
270 :
271 0 : return result;
272 : }
|