utilunix.c(mc_tmpdir): Check return value of getpwuid() for NULL.
[midnight-commander.git] / src / profile.c
blob30e86baaad70d9f11127aa76cef565cb8d69d9ad
1 /*
2 * Initialization-File Functions.
4 * From the Wine project
6 Copyright (C) 1993, 1994 Miguel de Icaza.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <config.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include "global.h"
28 #include "profile.h"
30 #define STRSIZE 4096
31 #define overflow (next == &CharBuffer [STRSIZE-1])
33 enum { FirstBrace, OnSecHeader, IgnoreToEOL, KeyDef, KeyDefOnKey, KeyValue };
35 typedef struct TKeys {
36 char *KeyName;
37 char *Value;
38 struct TKeys *link;
39 } TKeys;
41 typedef struct TSecHeader {
42 char *AppName;
43 TKeys *Keys;
44 struct TSecHeader *link;
45 } TSecHeader;
47 typedef struct TProfile {
48 char *FileName;
49 TSecHeader *Section;
50 struct TProfile *link;
51 } TProfile;
53 static TProfile *Base = 0;
55 static TProfile *
56 find_loaded (const char *FileName, TSecHeader ** section)
58 TProfile *p = Base;
60 while (p) {
61 if (!g_strcasecmp (FileName, p->FileName)) {
62 *section = p->Section;
63 return p;
65 p = p->link;
67 return NULL;
70 #define TRANSLATION_CHAR '\200'
72 static char *
73 str_untranslate_newline_dup (char *s)
75 int l = 0;
76 char *p = s, *q;
77 g_return_val_if_fail(s, NULL);
78 while (*p) {
79 l++;
80 l += (*p == '\n' || *p == TRANSLATION_CHAR);
81 p++;
83 q = p = g_malloc (l + 1);
84 if (!q)
85 return 0;
86 for (;;) {
87 switch (*s) {
88 case '\n':
89 *p++ = TRANSLATION_CHAR;
90 *p++ = 'n';
91 break;
92 case TRANSLATION_CHAR:
93 if (s[1] == 'n' || s[1] == TRANSLATION_CHAR)
94 *p++ = TRANSLATION_CHAR;
95 *p++ = TRANSLATION_CHAR;
96 break;
97 case '\0':
98 *p = '\0';
99 return q;
100 break;
101 default:
102 *p++ = *s;
104 s++;
106 return 0; /* not reached */
109 static char *
110 str_translate_newline_dup (char *s)
112 char *p, *q;
113 g_return_val_if_fail(s,NULL);
114 q = p = g_malloc (strlen (s) + 1);
115 if (!q)
116 return 0;
117 while (*s) {
118 if (*s == TRANSLATION_CHAR) {
119 switch (*(++s)) {
120 case 'n':
121 *p++ = '\n';
122 break;
123 case TRANSLATION_CHAR:
124 *p++ = TRANSLATION_CHAR;
125 break;
126 case '\0':
127 *p++ = TRANSLATION_CHAR;
128 *p++ = '\0';
129 return q;
130 default:
131 *p++ = TRANSLATION_CHAR;
132 *p++ = *s;
134 } else {
135 *p++ = *s;
137 s++;
139 *p = '\0';
140 return q; /* not reached */
143 static TSecHeader *load (const char *file)
145 FILE *f;
146 int state;
147 TSecHeader *SecHeader = 0;
148 char CharBuffer [STRSIZE];
149 char *next = ""; /* Not needed */
150 int c;
152 if ((f = fopen (file, "r"))==NULL)
153 return NULL;
155 state = FirstBrace;
156 while ((c = getc (f)) != EOF){
157 if (c == '\r') /* Ignore Carriage Return */
158 continue;
160 switch (state){
162 case OnSecHeader:
163 if (c == ']' || overflow){
164 *next = '\0';
165 next = CharBuffer;
166 SecHeader->AppName = g_strdup (CharBuffer);
167 state = IgnoreToEOL;
168 } else
169 *next++ = c;
170 break;
172 case IgnoreToEOL:
173 if (c == '\n'){
174 state = KeyDef;
175 next = CharBuffer;
177 break;
179 case FirstBrace:
180 case KeyDef:
181 case KeyDefOnKey:
182 if (c == '['){
183 TSecHeader *temp;
185 temp = SecHeader;
186 SecHeader = g_new (TSecHeader, 1);
187 SecHeader->link = temp;
188 SecHeader->Keys = 0;
189 state = OnSecHeader;
190 next = CharBuffer;
191 break;
193 if (state == FirstBrace) /* On first pass, don't allow dangling keys */
194 break;
196 if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
197 break;
199 if (c == '\n' || overflow) {
200 /* Abort Definition */
201 next = CharBuffer;
202 break;
205 if (c == '=' || overflow){
206 TKeys *temp;
208 temp = SecHeader->Keys;
209 *next = '\0';
210 SecHeader->Keys =g_new (TKeys, 1);
211 SecHeader->Keys->link = temp;
212 SecHeader->Keys->KeyName = g_strdup (CharBuffer);
213 state = KeyValue;
214 next = CharBuffer;
215 } else {
216 *next++ = c;
217 state = KeyDefOnKey;
219 break;
221 case KeyValue:
222 if (overflow || c == '\n'){
223 *next = '\0';
224 SecHeader->Keys->Value = str_translate_newline_dup (CharBuffer);
225 state = c == '\n' ? KeyDef : IgnoreToEOL;
226 next = CharBuffer;
227 #ifdef DEBUG
228 printf ("[%s] (%s)=%s\n", SecHeader->AppName,
229 SecHeader->Keys->KeyName, SecHeader->Keys->Value);
230 #endif
231 } else
232 *next++ = c;
233 break;
235 } /* switch */
237 } /* while ((c = getc (f)) != EOF) */
239 switch (state) {
240 case KeyValue:
241 *next = '\0';
242 SecHeader->Keys->Value = str_translate_newline_dup (CharBuffer);
243 break;
244 case OnSecHeader: { /* Broken initialization file */
245 TSecHeader *link = SecHeader->link;
246 g_free (SecHeader);
247 SecHeader = link;
248 fprintf (stderr, "Warning: Corrupted initialization file `%s'\n",
249 file);
250 break;
254 fclose (f);
255 return SecHeader;
258 static void new_key (TSecHeader *section, const char *KeyName, const char *Value)
260 TKeys *key;
262 key = g_new (TKeys, 1);
263 key->KeyName = g_strdup (KeyName);
264 key->Value = g_strdup (Value);
265 key->link = section->Keys;
266 section->Keys = key;
269 static char *
270 GetSetProfileChar (int set, const char *AppName, char *KeyName,
271 char *Default, char *FileName)
274 TProfile *Current;
275 TSecHeader *section;
276 TKeys *key;
278 Current = find_loaded (FileName, &section);
279 if (!Current) {
280 Current = g_new (TProfile, 1);
281 Current->link = Base;
282 Current->FileName = g_strdup (FileName);
283 Current->Section = load (FileName);
284 Base = Current;
285 section = Current->Section;
288 /* Start search */
289 for (; section; section = section->link){
290 if (section->AppName == 0 || g_strcasecmp (section->AppName, AppName))
291 continue;
292 for (key = section->Keys; key; key = key->link){
293 if ( g_strcasecmp (key->KeyName, KeyName))
294 continue;
295 if (set){
296 g_free (key->Value);
297 key->Value = g_strdup (Default);
299 return key->Value;
301 /* If getting the information, then don't write the information
302 to the INI file, need to run a couple of tests with windog */
303 /* No key found */
304 if (set){
305 new_key (section, KeyName, Default);
306 return 0;
310 /* Non existent section */
311 if (set && Default){
312 section = g_new (TSecHeader, 1);
313 section->AppName = g_strdup (AppName);
314 section->Keys = 0;
315 new_key (section, KeyName, Default);
316 section->link = Current->Section;
317 Current->Section = section;
319 return Default;
322 static short GetSetProfile (int set, const char * AppName, char * KeyName,
323 char * Default, char * ReturnedString,
324 short Size, char * FileName)
327 char *s;
329 s = GetSetProfileChar (set, AppName, KeyName, Default, FileName);
330 if (!set){
331 ReturnedString [Size-1] = 0;
332 g_strlcpy (ReturnedString, s, Size-1);
334 return 1;
337 short GetPrivateProfileString (const char * AppName, char * KeyName,
338 char * Default, char * ReturnedString,
339 short Size, char * FileName)
341 return (GetSetProfile (0, AppName, KeyName, Default, ReturnedString, Size, FileName));
344 char *get_profile_string (const char *AppName, char *KeyName, char *Default,
345 char *FileName)
347 return GetSetProfileChar (0, AppName, KeyName, Default, FileName);
350 int GetPrivateProfileInt (const char * AppName, char * KeyName, int Default,
351 char * File)
353 char IntBuf [BUF_TINY];
354 char buf [BUF_TINY];
356 g_snprintf (buf, sizeof (buf), "%d", Default);
358 /* Check the exact semantic with the SDK */
359 GetPrivateProfileString (AppName, KeyName, buf, IntBuf, BUF_TINY, File);
360 if (! g_strcasecmp (IntBuf, "true"))
361 return 1;
362 if (! g_strcasecmp (IntBuf, "yes"))
363 return 1;
364 return (int) atol (IntBuf);
367 int WritePrivateProfileString (const char * AppName, char * KeyName, char * String,
368 char * FileName)
370 return GetSetProfile (1, AppName, KeyName, String, "", 0, FileName);
373 static void dump_keys (FILE * profile, TKeys * p)
375 char *t;
376 if (!p)
377 return;
378 dump_keys (profile, p->link);
379 t = str_untranslate_newline_dup (p->Value);
380 fprintf (profile, "%s=%s\n", p->KeyName, t);
381 g_free (t);
384 static void dump_sections (FILE *profile, TSecHeader *p)
386 if (!p)
387 return;
388 dump_sections (profile, p->link);
389 if (p->AppName [0]){
390 fprintf (profile, "\n[%s]\n", p->AppName);
391 dump_keys (profile, p->Keys);
395 static void dump_profile (TProfile *p)
397 FILE *profile;
399 if (!p)
400 return;
401 dump_profile (p->link);
402 /* .ado: p->FileName can be empty, it's better to jump over */
403 if (p->FileName[0] != (char) 0)
404 if ((profile = fopen (p->FileName, "w")) != NULL){
405 dump_sections (profile, p->Section);
406 fclose (profile);
411 * Must be called at the end of wine run
414 void sync_profiles (void)
416 dump_profile (Base);
419 static void free_keys (TKeys *p)
421 if (!p)
422 return;
423 free_keys (p->link);
424 g_free (p->KeyName);
425 g_free (p->Value);
426 g_free (p);
429 static void free_sections (TSecHeader *p)
431 if (!p)
432 return;
433 free_sections (p->link);
434 free_keys (p->Keys);
435 g_free (p->AppName);
436 p->link = 0;
437 p->Keys = 0;
438 g_free (p);
441 static void free_profile (TProfile *p)
443 if (!p)
444 return;
445 free_profile (p->link);
446 free_sections (p->Section);
447 g_free (p->FileName);
448 g_free (p);
451 void free_profile_name (char *s)
453 TProfile *p;
455 if (!s)
456 return;
458 for (p = Base; p; p = p->link){
459 if (strcmp (s, p->FileName) == 0){
460 free_sections (p->Section);
461 p->Section = 0;
462 p->FileName [0] = 0;
463 return;
468 void free_profiles (void)
470 free_profile (Base);
473 void *profile_init_iterator (char *appname, char *file)
475 TProfile *Current;
476 TSecHeader *section;
478 Current = find_loaded (file, &section);
479 if (!Current) {
480 Current = g_new (TProfile, 1);
481 Current->link = Base;
482 Current->FileName = g_strdup (file);
483 Current->Section = load (file);
484 Base = Current;
485 section = Current->Section;
487 for (; section; section = section->link){
488 if ( g_strcasecmp (section->AppName, appname))
489 continue;
490 return section->Keys;
492 return 0;
495 void *profile_iterator_next (void *s, char **key, char **value)
497 TKeys *keys = (TKeys *) s;
499 if (keys){
500 *key = keys->KeyName;
501 *value = keys->Value;
502 keys = keys->link;
504 return keys;
507 void profile_clean_section (const char *appname, char *file)
509 TSecHeader *section;
511 /* We assume the user has called one of the other initialization funcs */
512 if (!find_loaded (file, &section)){
513 fprintf (stderr,"Warning: profile_clean_section called before init\n");
514 return;
516 /* We only disable the section, so it will still be freed, but it */
517 /* won't be find by further walks of the structure */
519 for (; section; section = section->link){
520 if ( g_strcasecmp (section->AppName, appname))
521 continue;
522 section->AppName [0] = 0;
526 int profile_has_section (char *section_name, char *profile)
528 TSecHeader *section;
530 /* We assume the user has called one of the other initialization funcs */
531 if (!find_loaded (profile, &section)){
532 return 0;
534 for (; section; section = section->link){
535 if ( g_strcasecmp (section->AppName, section_name))
536 continue;
537 return 1;
539 return 0;
542 void profile_forget_profile (char *file)
544 TProfile *p;
546 for (p = Base; p; p = p->link){
547 if ( g_strcasecmp (file, p->FileName))
548 continue;
549 p->FileName [0] = 0;