Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / Preferences / UserPreference.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * $TOG: UserPreference.C /main/7 1998/04/17 11:38:58 mgreess $
25  *
26  * Copyright (c) 1993 HAL Computer Systems International, Ltd.
27  * All rights reserved.  Unpublished -- rights reserved under
28  * the Copyright Laws of the United States.  USE OF A COPYRIGHT
29  * NOTICE IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
30  * OR DISCLOSURE.
31  * 
32  * THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND TRADE
33  * SECRETS OF HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.  USE,
34  * DISCLOSURE, OR REPRODUCTION IS PROHIBITED WITHOUT THE
35  * PRIOR EXPRESS WRITTEN PERMISSION OF HAL COMPUTER SYSTEMS
36  * INTERNATIONAL, LTD.
37  * 
38  *                         RESTRICTED RIGHTS LEGEND
39  * Use, duplication, or disclosure by the Government is subject
40  * to the restrictions as set forth in subparagraph (c)(l)(ii)
41  * of the Rights in Technical Data and Computer Software clause
42  * at DFARS 252.227-7013.
43  *
44  *          HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.
45  *                  1315 Dell Avenue
46  *                  Campbell, CA  95008
47  * 
48  */
49
50
51 #define C_UserPreference
52 #define L_Preferences
53
54 //#ifndef TEST_MODE
55 #define C_EnvMgr
56 #define C_MessageMgr
57 #define L_Managers
58 //#endif
59
60 #include <Prelude.h>
61
62 #include <string.h>
63 #include <ctype.h>
64 #include <stdio.h>
65 #include <unistd.h>
66 #if !defined(hpux) && !defined(__osf__) && !defined(USL)
67 #include <sysent.h>
68 #endif
69 #include <sys/types.h>
70 #include <sys/stat.h>
71
72 #include <errno.h>
73
74 PreferenceRecord *PreferenceRecord::g_head;
75 PreferenceRecord *PreferenceRecord::g_tail;
76 int PreferenceRecord::g_update_count = -1;
77
78
79 PreferenceRecord::PreferenceRecord (const char *key, const char *value)
80 : f_key (strdup (key)),
81   f_value (strdup (value)),
82   f_next (NULL)
83 {
84   if (g_head == NULL)
85     g_head = this;
86   if (g_tail != NULL)
87     g_tail->f_next = this;
88   g_tail = this;
89 }
90
91
92 PreferenceRecord::~PreferenceRecord()
93 {
94   free ((char *) f_key);
95   free ((char *) f_value);
96 }
97
98
99 void
100 PreferenceRecord::set_value (const char *value)
101 {
102   free ((char *) f_value);
103   f_value = strdup (value);
104 }
105
106
107 const char *
108 PreferenceRecord::form_filename()
109 {
110   // Form the file name of the preferences.
111   static char filename[256];
112   if (filename[0] == '\0')
113     {
114       sprintf (filename, "%s/preferences", env().user_path());
115 #if EAM
116       const char *home = env().home();
117       if (home == NULL)
118         throw (CASTEXCEPT Exception());
119       const char *lang = env().lang();
120       if (lang == NULL)
121         throw (CASTEXCEPT Exception());
122
123       sprintf (filename, "%s/.dt/dtinfo/%s/preferences",
124                          home, lang);
125 #endif
126     }
127
128   return (filename);
129 }
130
131
132 void
133 revert_from_backup (const char *filename)
134 {
135   // Failed, so look for the backup file.
136   char backup[256], original[256];
137   sprintf (backup, "%s.bak", filename);
138   struct stat file_info;
139
140   if (stat (backup, &file_info) != -1 &&
141       S_ISREG(file_info.st_mode))
142     {
143       unlink (filename);
144       link (backup, filename);
145     }
146 }
147
148
149 int
150 read_version (FILE *stream)
151 {
152   // Make sure the file is valid.
153   char V = '-';
154   fread (&V, 1, 1, stream);
155   if (V != 'V')
156     return (0);
157   // Nab the version from the file. 
158   int version = 0;
159   fscanf (stream, "%d", &version);
160   return (version);
161 }
162
163
164 int
165 read_update_count (FILE *stream)
166 {
167   char buffer[256], *p;
168   int update_count;
169
170   fgets (buffer, 256, stream);
171   p = buffer;
172   while (*p != ',' && *p != '\0')
173     p++;
174   p++;
175   if (*p == '\0')
176     return (0);
177   sscanf (p, "%d", &update_count);
178   ON_DEBUG (printf ("Update count = %d\n", update_count));
179   return (update_count);
180 }
181
182 void
183 PreferenceRecord::read_prefs()
184 {
185   // Open it and read in the preferences.
186   const char *filename;
187   FILE *stream;
188   int attempt = 0;
189
190   while (attempt < 2)
191     {
192       attempt++;
193       filename = form_filename();
194       // See if it exists.
195       struct stat file_info;
196       int status = stat (filename, &file_info);
197       if (status == -1)
198         {
199           // Check for access failure or IO error. 
200           if (errno == EACCES || errno == EIO)
201             throw (CASTEXCEPT Exception());
202           // It doesn't exists otherwise. 
203           g_update_count = 0;
204           return;
205         }
206       else if (!S_ISREG (file_info.st_mode))
207         throw (CASTEXCEPT Exception());
208       stream = fopen (filename, "r");
209       if (stream == NULL)    // Open failed, something is bogus. 
210         throw (CASTEXCEPT Exception());
211
212       int version = read_version (stream);   // Snag the version.
213       if (version == 0)                      // See if file is ok. 
214         {
215           fclose (stream);
216           if (attempt == 2)   // give up on 2nd attempt 
217             throw (CASTEXCEPT Exception());
218           else
219             revert_from_backup (filename);
220         }
221     }
222
223   g_update_count = read_update_count (stream);
224
225   // Read in the preference lines. 
226   char key[256], *value;
227   while (fgets (key, 256, stream) != NULL)
228     {
229       value = key;
230       while (*value != ':' && *value != '\0')
231         value++;
232       if (*value == '\0')  // Ignore bogus lines. 
233         continue;
234       *value++ = '\0';
235       while (isspace (*value))
236         value++;
237       int len = strlen(value);
238       value[len-1] = '\0';
239       ON_DEBUG (printf ("Got Pref: %s = %s\n", key, value));
240       new PreferenceRecord (key, value);
241     }
242   fclose (stream);
243 }
244
245
246 void
247 PreferenceRecord::write_prefs()
248 {
249   assert (g_update_count != -1);
250
251   const char *filename = form_filename();
252   struct stat file_info;
253   int status = stat (filename, &file_info);
254   // Make sure it's a regular file if it exists. 
255   if (status == 0  && !S_ISREG (file_info.st_mode))
256     throw (CASTEXCEPT Exception());
257
258   // Check the update count to guard against overwrites.
259   // This isn't foolproof, but we can't trust NFS locking
260   // so this will have to do for now.  13:39 15-Sep-93 DJB
261   FILE *stream;
262   int update_count = 0;
263
264   if (status == 0)
265     {
266       // If the file isn't readable, we won't write the prefs. 
267       stream = fopen (filename, "r");
268       if (stream == NULL)
269         throw (CASTEXCEPT Exception());
270
271       if (read_version (stream) != 0)
272         update_count = read_update_count (stream);
273       fclose (stream);
274     }
275
276   if (update_count != g_update_count)
277     {
278       bool doit = message_mgr().
279         question_dialog ("Preferences have changed on disk.\nOverwrite?");
280       if (!doit)
281         return;
282     }
283
284   // Create a backup file from the current preferences, if any.
285   char backup[256];
286   backup[0] = '\0';
287   if (status == 0)
288     {
289       sprintf (backup, "%s.bak", filename);
290       unlink (backup);
291       if (rename (filename, backup) == -1)
292         throw (CASTEXCEPT Exception());
293     }
294   else    // Make sure the parent directory exists.
295     {
296       char dirname[256];
297       sprintf (dirname, "%s", env().user_path());
298       status = stat (dirname, &file_info);
299       if (status == -1)
300         {
301           if (mkdir (dirname, 0777) == -1)
302             throw (CASTEXCEPT Exception());
303         }
304       else if (!S_ISDIR (file_info.st_mode))
305         throw (CASTEXCEPT Exception());
306     }
307
308   // Open the file and write the preferences.
309   stream = fopen (filename, "w");
310   if (stream == NULL)
311     {
312       // Put the backup file back where it came from.
313       if (backup[0] != '\0')
314         rename (backup, filename);
315       throw (CASTEXCEPT Exception());
316     }
317
318   // Write out the preference records. 
319   PreferenceRecord *current = g_head;
320   // First line is version and update count.
321   fprintf (stream, "-1.0, %d  # AUTOMATICALLY GENERATED - DO NOT EDIT!\n",
322            ++g_update_count);
323   while (current != NULL)
324     {
325       if (*(current->f_value) != '\0' &&
326           fprintf(stream, "%s: %s\n", current->f_key, current->f_value) == EOF)
327         {
328           fclose (stream);
329           // Remove the file and restore the backup file. 
330           if (unlink (filename) == 0 && backup[0] != '\0')
331             rename (backup, filename);
332           throw (CASTEXCEPT Exception());
333         }
334       current = current->f_next;
335     }
336
337   // Rewrite first character to indicate file is complete. 
338   fseek (stream, 0L, 0);
339   fwrite ("V", 1, 1, stream);
340
341   fclose (stream);
342 }
343
344
345 PreferenceRecord *
346 PreferenceRecord::lookup (const char *key)
347 {
348   if (g_update_count == -1)
349     {
350       try
351         {
352           read_prefs();
353         }
354       catch_any()
355         {
356           // This will only happen the first time through. 
357           message_mgr().error_dialog ("Unable to read preferences.");
358           g_update_count = 0;
359         }
360       end_try;
361     }
362
363   // Scan through the list of preferences looking for the record. 
364   PreferenceRecord *current = g_head;
365   while (current != NULL)
366     {
367       if (strcmp (key, current->f_key) == 0)
368         return (current);
369       current = current->f_next;
370     }
371
372   // Not found, so create a new record.
373   return (new PreferenceRecord (key, ""));
374 }
375
376
377 UserPreference::UserPreference (const char *key)
378   : f_preference (PreferenceRecord::lookup (key))
379 {
380 }
381
382
383 UserPreference::~UserPreference()
384 {
385 }