remove OSF1 support
[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 libraries 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(linux) && !defined(CSRG_BASED) && !defined(sun)
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       snprintf (filename, sizeof(filename),
115                         "%s/preferences", env().user_path());
116 #if EAM
117       const char *home = env().home();
118       if (home == NULL)
119         throw (CASTEXCEPT Exception());
120       const char *lang = env().lang();
121       if (lang == NULL)
122         throw (CASTEXCEPT Exception());
123
124       snprintf (filename, sizeof(filename),
125                         "%s/.dt/dtinfo/%s/preferences", home, lang);
126 #endif
127     }
128
129   return (filename);
130 }
131
132
133 void
134 revert_from_backup (const char *filename)
135 {
136   int ret;
137   // Failed, so look for the backup file.
138   char backup[256];
139   snprintf (backup, sizeof(backup), "%s.bak", filename);
140   struct stat file_info;
141
142   if (stat (backup, &file_info) != -1 &&
143       S_ISREG(file_info.st_mode))
144     {
145       unlink (filename);
146       ret = link (backup, filename);
147       if(ret == 0) throw (CASTEXCEPT Exception());
148     }
149 }
150
151
152 int
153 read_version (FILE *stream)
154 {
155   size_t ret1;
156   int ret2;
157   // Make sure the file is valid.
158   char V = '-';
159   ret1 = fread (&V, 1, 1, stream);
160   if(ret1 == 0) throw (CASTEXCEPT Exception());
161   if (V != 'V')
162     return (0);
163   // Nab the version from the file. 
164   int version = 0;
165   ret2 = fscanf (stream, "%d", &version);
166   if(ret2 == 0) throw (CASTEXCEPT Exception());
167   return (version);
168 }
169
170
171 int
172 read_update_count (FILE *stream)
173 {
174   char *ret;
175   char buffer[256], *p;
176   int update_count;
177
178   ret = fgets (buffer, 256, stream);
179   if(ret == NULL) throw (CASTEXCEPT Exception());
180   p = buffer;
181   while (*p != ',' && *p != '\0')
182     p++;
183   p++;
184   if (*p == '\0')
185     return (0);
186   sscanf (p, "%d", &update_count);
187   ON_DEBUG (printf ("Update count = %d\n", update_count));
188   return (update_count);
189 }
190
191 void
192 PreferenceRecord::read_prefs()
193 {
194   // Open it and read in the preferences.
195   const char *filename;
196   FILE *stream;
197   int attempt = 0;
198
199   while (attempt < 2)
200     {
201       attempt++;
202       filename = form_filename();
203       // See if it exists.
204       struct stat file_info;
205       int status = stat (filename, &file_info);
206       if (status == -1)
207         {
208           // Check for access failure or IO error. 
209           if (errno == EACCES || errno == EIO)
210             throw (CASTEXCEPT Exception());
211           // It doesn't exists otherwise. 
212           g_update_count = 0;
213           return;
214         }
215       else if (!S_ISREG (file_info.st_mode))
216         throw (CASTEXCEPT Exception());
217       stream = fopen (filename, "r");
218       if (stream == NULL)    // Open failed, something is bogus. 
219         throw (CASTEXCEPT Exception());
220
221       int version = read_version (stream);   // Snag the version.
222       if (version == 0)                      // See if file is ok. 
223         {
224           fclose (stream);
225           if (attempt == 2)   // give up on 2nd attempt 
226             throw (CASTEXCEPT Exception());
227           else
228             revert_from_backup (filename);
229         }
230     }
231
232   g_update_count = read_update_count (stream);
233
234   // Read in the preference lines. 
235   char key[256], *value;
236   while (fgets (key, 256, stream) != NULL)
237     {
238       value = key;
239       while (*value != ':' && *value != '\0')
240         value++;
241       if (*value == '\0')  // Ignore bogus lines. 
242         continue;
243       *value++ = '\0';
244       while (isspace ((unsigned char) *value))
245         value++;
246       int len = strlen(value);
247       value[len-1] = '\0';
248       ON_DEBUG (printf ("Got Pref: %s = %s\n", key, value));
249       new PreferenceRecord (key, value);
250     }
251   fclose (stream);
252 }
253
254
255 void
256 PreferenceRecord::write_prefs()
257 {
258   assert (g_update_count != -1);
259
260   const char *filename = form_filename();
261   struct stat file_info;
262   int status = stat (filename, &file_info);
263   // Make sure it's a regular file if it exists. 
264   if (status == 0  && !S_ISREG (file_info.st_mode))
265     throw (CASTEXCEPT Exception());
266
267   // Check the update count to guard against overwrites.
268   // This isn't foolproof, but we can't trust NFS locking
269   // so this will have to do for now.  13:39 15-Sep-93 DJB
270   FILE *stream;
271   int update_count = 0;
272
273   if (status == 0)
274     {
275       // If the file isn't readable, we won't write the prefs. 
276       stream = fopen (filename, "r");
277       if (stream == NULL)
278         throw (CASTEXCEPT Exception());
279
280       if (read_version (stream) != 0)
281         update_count = read_update_count (stream);
282       fclose (stream);
283     }
284
285   if (update_count != g_update_count)
286     {
287       bool doit = message_mgr().
288         question_dialog ((char*)"Preferences have changed on disk.\nOverwrite?");
289       if (!doit)
290         return;
291     }
292
293   // Create a backup file from the current preferences, if any.
294   char backup[256];
295   backup[0] = '\0';
296   if (status == 0)
297     {
298       snprintf (backup, sizeof(backup), "%s.bak", filename);
299       unlink (backup);
300       if (rename (filename, backup) == -1)
301         throw (CASTEXCEPT Exception());
302     }
303   else    // Make sure the parent directory exists.
304     {
305       char dirname[256];
306       snprintf (dirname, sizeof(dirname), "%s", env().user_path());
307       status = stat (dirname, &file_info);
308       if (status == -1)
309         {
310           if (mkdir (dirname, 0777) == -1)
311             throw (CASTEXCEPT Exception());
312         }
313       else if (!S_ISDIR (file_info.st_mode))
314         throw (CASTEXCEPT Exception());
315     }
316
317   // Open the file and write the preferences.
318   stream = fopen (filename, "w");
319   if (stream == NULL)
320     {
321       // Put the backup file back where it came from.
322       if (backup[0] != '\0')
323         rename (backup, filename);
324       throw (CASTEXCEPT Exception());
325     }
326
327   // Write out the preference records. 
328   PreferenceRecord *current = g_head;
329   // First line is version and update count.
330   fprintf (stream, "-1.0, %d  # AUTOMATICALLY GENERATED - DO NOT EDIT!\n",
331            ++g_update_count);
332   while (current != NULL)
333     {
334       if (*(current->f_value) != '\0' &&
335           fprintf(stream, "%s: %s\n", current->f_key, current->f_value) == EOF)
336         {
337           fclose (stream);
338           // Remove the file and restore the backup file. 
339           if (unlink (filename) == 0 && backup[0] != '\0')
340             rename (backup, filename);
341           throw (CASTEXCEPT Exception());
342         }
343       current = current->f_next;
344     }
345
346   // Rewrite first character to indicate file is complete. 
347   fseek (stream, 0L, 0);
348   fwrite ("V", 1, 1, stream);
349
350   fclose (stream);
351 }
352
353
354 PreferenceRecord *
355 PreferenceRecord::lookup (const char *key)
356 {
357   if (g_update_count == -1)
358     {
359       mtry
360         {
361           read_prefs();
362         }
363       mcatch_any()
364         {
365           // This will only happen the first time through. 
366           message_mgr().error_dialog ((char*)"Unable to read preferences.");
367           g_update_count = 0;
368         }
369       end_try;
370     }
371
372   // Scan through the list of preferences looking for the record. 
373   PreferenceRecord *current = g_head;
374   while (current != NULL)
375     {
376       if (strcmp (key, current->f_key) == 0)
377         return (current);
378       current = current->f_next;
379     }
380
381   // Not found, so create a new record.
382   return (new PreferenceRecord (key, ""));
383 }
384
385
386 UserPreference::UserPreference (const char *key)
387   : f_preference (PreferenceRecord::lookup (key))
388 {
389 }
390
391
392 UserPreference::~UserPreference()
393 {
394 }