Original patch as attached on the bugreport
[midnight-commander.git] / src / profile.c
blobcbd60e536d39e5b066a60615fa551d04d49e8b8e
1 /*
2 * Initialization-File Functions.
4 * From the Wine project
6 Copyright (C) 1993, 1994, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
7 2007 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include <sys/types.h>
32 #include "global.h"
33 #include "profile.h"
35 #define STRSIZE 4096
36 #define overflow (next == &CharBuffer [STRSIZE-1])
38 enum { FirstBrace, OnSecHeader, IgnoreToEOL, KeyDef, KeyDefOnKey, KeyValue };
40 typedef struct TKeys {
41 char *KeyName;
42 char *Value;
43 struct TKeys *link;
44 } TKeys;
46 typedef struct TSecHeader {
47 char *AppName;
48 TKeys *Keys;
49 struct TSecHeader *link;
50 } TSecHeader;
52 typedef struct TProfile {
53 char *FileName;
54 TSecHeader *Section;
55 struct TProfile *link;
56 } TProfile;
58 static TProfile *Base = 0;
60 static TProfile *
61 find_loaded (const char *FileName, TSecHeader ** section)
63 TProfile *p = Base;
65 while (p) {
66 if (!g_strcasecmp (FileName, p->FileName)) {
67 *section = p->Section;
68 return p;
70 p = p->link;
72 return NULL;
75 #define TRANSLATION_CHAR '\200'
77 static char *
78 str_untranslate_newline_dup (char *s)
80 int l = 0;
81 char *p = s, *q;
82 g_return_val_if_fail(s, NULL);
83 while (*p) {
84 l++;
85 l += (*p == '\n' || *p == TRANSLATION_CHAR);
86 p++;
88 q = p = g_malloc (l + 1);
89 if (!q)
90 return 0;
91 for (;;) {
92 switch (*s) {
93 case '\n':
94 *p++ = TRANSLATION_CHAR;
95 *p++ = 'n';
96 break;
97 case TRANSLATION_CHAR:
98 if (s[1] == 'n' || s[1] == TRANSLATION_CHAR)
99 *p++ = TRANSLATION_CHAR;
100 *p++ = TRANSLATION_CHAR;
101 break;
102 case '\0':
103 *p = '\0';
104 return q;
105 break;
106 default:
107 *p++ = *s;
109 s++;
111 return 0; /* not reached */
114 static char *
115 str_translate_newline_dup (char *s)
117 char *p, *q;
118 g_return_val_if_fail(s,NULL);
119 q = p = g_malloc (strlen (s) + 1);
120 if (!q)
121 return 0;
122 while (*s) {
123 if (*s == TRANSLATION_CHAR) {
124 switch (*(++s)) {
125 case 'n':
126 *p++ = '\n';
127 break;
128 case TRANSLATION_CHAR:
129 *p++ = TRANSLATION_CHAR;
130 break;
131 case '\0':
132 *p++ = TRANSLATION_CHAR;
133 *p++ = '\0';
134 return q;
135 default:
136 *p++ = TRANSLATION_CHAR;
137 *p++ = *s;
139 } else {
140 *p++ = *s;
142 s++;
144 *p = '\0';
145 return q; /* not reached */
148 static TSecHeader *load (const char *file)
150 FILE *f;
151 int state;
152 TSecHeader *SecHeader = 0;
153 char CharBuffer [STRSIZE];
154 char *next = NULL; /* Not needed */
155 int c;
157 if ((f = fopen (file, "r"))==NULL)
158 return NULL;
160 state = FirstBrace;
161 while ((c = getc (f)) != EOF){
162 if (c == '\r') /* Ignore Carriage Return */
163 continue;
165 switch (state){
167 case OnSecHeader:
168 if (c == ']' || overflow){
169 *next = '\0';
170 next = CharBuffer;
171 SecHeader->AppName = g_strdup (CharBuffer);
172 state = IgnoreToEOL;
173 } else
174 *next++ = c;
175 break;
177 case IgnoreToEOL:
178 if (c == '\n'){
179 state = KeyDef;
180 next = CharBuffer;
182 break;
184 case FirstBrace:
185 case KeyDef:
186 case KeyDefOnKey:
187 if (c == '['){
188 TSecHeader *temp;
190 temp = SecHeader;
191 SecHeader = g_new (TSecHeader, 1);
192 SecHeader->link = temp;
193 SecHeader->Keys = 0;
194 state = OnSecHeader;
195 next = CharBuffer;
196 break;
198 if (state == FirstBrace) /* On first pass, don't allow dangling keys */
199 break;
201 if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
202 break;
204 if (c == '\n' || overflow) {
205 /* Abort Definition */
206 next = CharBuffer;
207 break;
210 if (c == '=' || overflow){
211 TKeys *temp;
213 temp = SecHeader->Keys;
214 *next = '\0';
215 SecHeader->Keys =g_new (TKeys, 1);
216 SecHeader->Keys->link = temp;
217 SecHeader->Keys->KeyName = g_strdup (CharBuffer);
218 state = KeyValue;
219 next = CharBuffer;
220 } else {
221 *next++ = c;
222 state = KeyDefOnKey;
224 break;
226 case KeyValue:
227 if (overflow || c == '\n'){
228 *next = '\0';
229 SecHeader->Keys->Value = str_translate_newline_dup (CharBuffer);
230 state = c == '\n' ? KeyDef : IgnoreToEOL;
231 next = CharBuffer;
232 #ifdef DEBUG
233 printf ("[%s] (%s)=%s\n", SecHeader->AppName,
234 SecHeader->Keys->KeyName, SecHeader->Keys->Value);
235 #endif
236 } else
237 *next++ = c;
238 break;
240 } /* switch */
242 } /* while ((c = getc (f)) != EOF) */
244 switch (state) {
245 case KeyValue:
246 *next = '\0';
247 SecHeader->Keys->Value = str_translate_newline_dup (CharBuffer);
248 break;
249 case OnSecHeader: { /* Broken initialization file */
250 TSecHeader *link = SecHeader->link;
251 g_free (SecHeader);
252 SecHeader = link;
253 fprintf (stderr, "Warning: Corrupted initialization file `%s'\n",
254 file);
255 break;
259 fclose (f);
260 return SecHeader;
263 static void new_key (TSecHeader *section, const char *KeyName, const char *Value)
265 TKeys *key;
267 key = g_new (TKeys, 1);
268 key->KeyName = g_strdup (KeyName);
269 key->Value = g_strdup (Value);
270 key->link = section->Keys;
271 section->Keys = key;
274 static const char *
275 GetSetProfileChar (int set, const char *AppName, const char *KeyName,
276 const char *Default, const char *FileName)
279 TProfile *Current;
280 TSecHeader *section;
281 TKeys *key;
283 Current = find_loaded (FileName, &section);
284 if (!Current) {
285 Current = g_new (TProfile, 1);
286 Current->link = Base;
287 Current->FileName = g_strdup (FileName);
288 Current->Section = load (FileName);
289 Base = Current;
290 section = Current->Section;
293 /* Start search */
294 for (; section; section = section->link){
295 if (section->AppName == 0 || g_strcasecmp (section->AppName, AppName))
296 continue;
297 for (key = section->Keys; key; key = key->link){
298 if ( g_strcasecmp (key->KeyName, KeyName))
299 continue;
300 if (set){
301 g_free (key->Value);
302 key->Value = g_strdup (Default);
304 return key->Value;
306 /* If getting the information, then don't write the information
307 to the INI file, need to run a couple of tests with windog */
308 /* No key found */
309 if (set){
310 new_key (section, KeyName, Default);
311 return 0;
315 /* Non existent section */
316 if (set && Default){
317 section = g_new (TSecHeader, 1);
318 section->AppName = g_strdup (AppName);
319 section->Keys = 0;
320 new_key (section, KeyName, Default);
321 section->link = Current->Section;
322 Current->Section = section;
324 return Default;
327 static short GetSetProfile (int set, const char * AppName, const char * KeyName,
328 const char * Default, char * ReturnedString,
329 short Size, const char * FileName)
332 const char *s;
334 s = GetSetProfileChar (set, AppName, KeyName, Default, FileName);
335 if (!set)
336 g_strlcpy (ReturnedString, s, Size);
338 return 1;
341 short GetPrivateProfileString (const char * AppName, const char * KeyName,
342 const char * Default, char * ReturnedString,
343 short Size, const char * FileName)
345 return (GetSetProfile (0, AppName, KeyName, Default, ReturnedString, Size, FileName));
348 const char *get_profile_string (const char *AppName, const char *KeyName, const char *Default,
349 const char *FileName)
351 return GetSetProfileChar (0, AppName, KeyName, Default, FileName);
354 int GetPrivateProfileInt (const char * AppName, const char * KeyName, int Default,
355 const char * File)
357 char IntBuf [BUF_TINY];
358 char buf [BUF_TINY];
360 g_snprintf (buf, sizeof (buf), "%d", Default);
362 /* Check the exact semantic with the SDK */
363 GetPrivateProfileString (AppName, KeyName, buf, IntBuf, BUF_TINY, File);
364 if (! g_strcasecmp (IntBuf, "true"))
365 return 1;
366 if (! g_strcasecmp (IntBuf, "yes"))
367 return 1;
368 return (int) atol (IntBuf);
371 int WritePrivateProfileString (const char * AppName, const char * KeyName, const char * String,
372 const char * FileName)
374 return GetSetProfile (1, AppName, KeyName, String, NULL, 0, FileName);
377 static void dump_keys (FILE * profile, TKeys * p)
379 char *t;
380 if (!p)
381 return;
382 dump_keys (profile, p->link);
383 t = str_untranslate_newline_dup (p->Value);
384 fprintf (profile, "%s=%s\n", p->KeyName, t);
385 g_free (t);
388 static void dump_sections (FILE *profile, TSecHeader *p)
390 if (!p)
391 return;
392 dump_sections (profile, p->link);
393 if (p->AppName [0]){
394 fprintf (profile, "\n[%s]\n", p->AppName);
395 dump_keys (profile, p->Keys);
399 static void dump_profile (TProfile *p)
401 FILE *profile;
403 if (!p)
404 return;
405 dump_profile (p->link);
406 /* .ado: p->FileName can be empty, it's better to jump over */
407 if (p->FileName[0] != (char) 0)
408 if ((profile = fopen (p->FileName, "w")) != NULL){
409 dump_sections (profile, p->Section);
410 fclose (profile);
415 * Must be called at the end of wine run
418 void sync_profiles (void)
420 dump_profile (Base);
423 static void free_keys (TKeys *p)
425 if (!p)
426 return;
427 free_keys (p->link);
428 g_free (p->KeyName);
429 g_free (p->Value);
430 g_free (p);
433 static void free_sections (TSecHeader *p)
435 if (!p)
436 return;
437 free_sections (p->link);
438 free_keys (p->Keys);
439 g_free (p->AppName);
440 p->link = 0;
441 p->Keys = 0;
442 g_free (p);
445 static void free_profile (TProfile *p)
447 if (!p)
448 return;
449 free_profile (p->link);
450 free_sections (p->Section);
451 g_free (p->FileName);
452 g_free (p);
455 void free_profile_name (const char *s)
457 TProfile *p;
459 if (!s)
460 return;
462 for (p = Base; p; p = p->link){
463 if (strcmp (s, p->FileName) == 0){
464 free_sections (p->Section);
465 p->Section = 0;
466 p->FileName [0] = 0;
467 return;
472 void free_profiles (void)
474 free_profile (Base);
477 void *profile_init_iterator (const char *appname, const char *file)
479 TProfile *Current;
480 TSecHeader *section;
482 Current = find_loaded (file, &section);
483 if (!Current) {
484 Current = g_new (TProfile, 1);
485 Current->link = Base;
486 Current->FileName = g_strdup (file);
487 Current->Section = load (file);
488 Base = Current;
489 section = Current->Section;
491 for (; section; section = section->link){
492 if ( g_strcasecmp (section->AppName, appname))
493 continue;
494 return section->Keys;
496 return 0;
499 void *profile_iterator_next (void *s, char **key, char **value)
501 TKeys *keys = (TKeys *) s;
503 if (keys){
504 *key = keys->KeyName;
505 *value = keys->Value;
506 keys = keys->link;
508 return keys;
511 void profile_clean_section (const char *appname, const char *file)
513 TSecHeader *section;
515 /* We assume the user has called one of the other initialization funcs */
516 if (!find_loaded (file, &section)){
517 fprintf (stderr,"Warning: profile_clean_section called before init\n");
518 return;
520 /* We only disable the section, so it will still be freed, but it */
521 /* won't be find by further walks of the structure */
523 for (; section; section = section->link){
524 if ( g_strcasecmp (section->AppName, appname))
525 continue;
526 section->AppName [0] = 0;
530 int profile_has_section (const char *section_name, const char *profile)
532 TSecHeader *section;
534 /* We assume the user has called one of the other initialization funcs */
535 if (!find_loaded (profile, &section)){
536 return 0;
538 for (; section; section = section->link){
539 if ( g_strcasecmp (section->AppName, section_name))
540 continue;
541 return 1;
543 return 0;
546 void profile_forget_profile (const char *file)
548 TProfile *p;
550 for (p = Base; p; p = p->link){
551 if ( g_strcasecmp (file, p->FileName))
552 continue;
553 p->FileName [0] = 0;