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