regedit: Updated Korean resource.
[wine/gsoc_dplay.git] / dlls / shell32 / xdg.c
blob188d21c82c4927fb8b63009c393e7c56f9fa1d3e
1 /*
2 * Generic freedesktop.org support code
4 * Copyright (C) 2006 Mikolaj Zalewski
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <errno.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "shlwapi.h"
32 #include "wine/debug.h"
33 #include "shell32_main.h"
34 #include "xdg.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(xdg);
39 * XDG paths implemented using Desktop Base Directory spec version 0.6
40 * (from http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html)
43 static CRITICAL_SECTION XDG_PathsLock;
44 static CRITICAL_SECTION_DEBUG XDG_PathsLock_Debug =
46 0, 0, &XDG_PathsLock,
47 { &XDG_PathsLock_Debug.ProcessLocksList,
48 &XDG_PathsLock_Debug.ProcessLocksList},
49 0, 0, { (DWORD_PTR)__FILE__ ": XDG_PathsLock"}
51 static CRITICAL_SECTION XDG_PathsLock = { &XDG_PathsLock_Debug, -1, 0, 0, 0, 0 };
53 typedef struct
55 const char *var_name;
56 const char *default_value;
57 } std_path;
59 static const std_path paths[] = {
60 {"XDG_DATA_HOME", "$HOME/.local/share"},
61 {"XDG_CONFIG_HOME", "$HOME/.config"},
62 {"XDG_DATA_DIRS", "/usr/local/share:/usr/share"},
63 {"XDG_CONFIG_DIRS", "/etc/xdg"},
64 {"XDG_CACHE_HOME", "$HOME/.cache"}
67 #define PATHS_COUNT (sizeof(paths)/sizeof(paths[0]))
69 /* will be filled with paths as they are computed */
70 static const char *path_values[PATHS_COUNT] = {
71 NULL,
72 NULL,
73 NULL,
74 NULL,
75 NULL
78 static char *load_path(int path_id)
80 char *env = getenv(paths[path_id].var_name);
81 char *ret;
83 if (env != NULL && env[0]=='/')
85 ret = SHAlloc(strlen(env)+1);
86 if (ret != NULL)
87 lstrcpyA(ret, env);
88 return ret;
91 if (memcmp(paths[path_id].default_value, "$HOME", 5)==0)
93 char *home = getenv("HOME");
94 int len;
96 if (!home) return NULL;
97 ret = SHAlloc(strlen(home)+strlen(paths[path_id].default_value)-5+1);
98 if (ret == NULL) return NULL;
100 lstrcpyA(ret, home);
101 len = strlen(ret);
102 if (len>0 && ret[len-1]=='/')
103 ret[--len]=0;
104 lstrcatA(ret, paths[path_id].default_value+5);
105 return ret;
108 ret = SHAlloc(strlen(paths[path_id].default_value)+1);
109 if (ret != NULL)
110 lstrcpyA(ret, env);
111 return ret;
114 /******************************************************************************
115 * XDG_GetPath [internal]
117 * Get one of the XDG standard patch. The return value shouldn't be modified nor
118 * freed. A return value of NULL means that the memory is exhausted or the input
119 * is invalid
121 * For XDG_DATA_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME the result is a Unix path.
122 * For XDG_DATA_DIRS and XDG_CONFIG_DIRS the result is a colon-separated list of Unix
123 * paths
125 * The paths are guaranteed to start with '/'
127 const char *XDG_GetPath(int path_id)
129 if (path_id >= PATHS_COUNT || path_id < 0)
131 ERR("Invalid path_id %d\n", path_id);
132 return NULL;
135 if (path_values[path_id] != NULL)
136 return path_values[path_id];
137 EnterCriticalSection(&XDG_PathsLock);
138 if (path_values[path_id] == NULL)
139 path_values[path_id] = load_path(path_id);
140 LeaveCriticalSection(&XDG_PathsLock);
141 return path_values[path_id];
144 /******************************************************************************
145 * XDG_GetPath [internal]
147 * Build a string with a subpath of one of the XDG standard paths.
148 * The root can be one of XDG_DATA_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME.
149 * The subpath is a path relative to that root (it shouldn't start with a slash)
151 * The returned path should be freed with SHFree. A return of NULL means that the
152 * memory is exhausted or the parameters are invalid
154 char *XDG_BuildPath(int root_id, const char *subpath)
156 const char *root_path = XDG_GetPath(root_id);
157 char *ret_buffer;
158 int root_len;
160 if (root_id == XDG_DATA_DIRS || root_id == XDG_CONFIG_DIRS)
162 ERR("Invalid path id %d\n", root_id);
163 return NULL;
166 if (root_path == NULL) return NULL;
167 root_len = strlen(root_path);
168 if (root_path[root_len-1]=='/') root_len--;
169 ret_buffer = SHAlloc(root_len+1+strlen(subpath)+1);
170 if (ret_buffer == NULL) return NULL;
171 lstrcpyA(ret_buffer, root_path);
172 ret_buffer[root_len]='/';
173 lstrcpyA(ret_buffer+root_len+1, subpath);
174 return ret_buffer;
177 /******************************************************************************
178 * XDG_MakeDirs [internal]
180 * Checks that all the directories on the specified path exists. If some don't exists
181 * they are created with mask 0700 as required by many the freedeskop.org specs.
182 * If the path doesn't end with '/' it is assumed to be a path to a file and the last
183 * segment is not checked
185 * In case of a failure the errno is always set and can be used e.g for debugging
187 * RETURNS
188 * TRUE on success, FALSE on error
190 BOOL XDG_MakeDirs(const char *path)
192 int last_slash = 0;
193 BOOL success = TRUE;
194 struct stat tmp;
195 char *buffer = SHAlloc(strlen(path)+1);
197 if (buffer == NULL)
199 errno = ENOMEM;
200 return FALSE;
202 lstrcpyA(buffer, path);
204 TRACE("(%s)\n", debugstr_a(path));
205 while (1)
207 char *slash=strchr(buffer+last_slash+1, '/');
208 if (slash==NULL)
209 break;
211 /* cut the string at that position and create the directory if it doesn't exist */
212 *slash=0;
213 TRACE("Checking path %s\n", debugstr_a(buffer));
214 success = (stat(buffer, &tmp)==0);
215 if (!success && errno==ENOENT)
217 TRACE("Creating\n");
218 success = (mkdir(buffer, 0700)==0);
220 if (!success)
222 WARN("Couldn't process directory %s (errno=%d)\n", debugstr_a(buffer), errno);
223 break;
225 *slash='/';
226 last_slash = slash-buffer;
228 SHFree(buffer);
229 return success;
233 * .desktop files functions
237 /******************************************************************************
238 * dskentry_encode [internal]
240 * Escape the characters that can't be present in a desktop entry value like \n, leading
241 * spaces etc. The output parameter may be NULL. Then only the number of characters will
242 * be computers.
244 * RETURNS
245 * The number of characters after escaping the special characters, including the
246 * terminating NUL.
248 static int dskentry_encode(const char *value, char *output)
250 int only_spc = TRUE;
251 int num_written = 0;
252 const char *c;
253 for (c = value; *c; c++)
255 if (only_spc && *c==' ')
257 if (output)
259 *(output++) = '\\';
260 *(output++) = 's';
262 num_written += 2;
263 continue;
265 only_spc = FALSE;
267 if (*c=='\t' || *c=='\r' || *c=='\n' || *c=='\\')
269 if (output)
271 *(output++) = '\\';
272 if (*c=='\t') *(output++) = 't';
273 if (*c=='\r') *(output++) = 'r';
274 if (*c=='\n') *(output++) = 'n';
275 if (*c=='\\') *(output++) = '\\';
277 num_written += 2;
279 else
281 if (output)
282 *(output++)=*c;
283 num_written++;
287 if (output)
288 *(output++) = 0;
289 num_written++;
290 return num_written;
293 /******************************************************************************
294 * dskentry_decode [internal]
296 * Unescape the characters that can be escaped according to the desktop entry spec.
297 * The output parameter may be NULL. Then only the number of characters will
298 * be computers.
300 * RETURNS
301 * The number of characters after unescaping the special characters, including the
302 * terminating NUL.
304 static int dskentry_decode(const char *value, int len, char *output)
306 int pos = 0;
307 int count = 0;
308 while (pos<len)
310 char c;
311 if (value[pos] == '\\' && pos<len-1)
313 pos++;
314 switch (value[pos])
316 case 's': c = ' '; break;
317 case 'n': c = '\n'; break;
318 case 't': c = '\t'; break;
319 case 'r': c = 'r'; break;
320 case '\\': c = '\\'; break;
321 default:
322 /* store both the backslash and the character */
323 if (output)
324 *(output++) = '\\';
325 count++;
326 c = value[pos];
327 break;
330 else
331 c = value[pos];
333 if (output)
334 *(output++) = c;
335 count++;
336 pos++;
339 if (output)
340 *(output++) = 0;
341 count++;
342 return count;
346 /******************************************************************************
347 * url_encode [internal]
349 * URL-encode the given string (i.e. use escape codes like %20). Note that an
350 * URL-encoded string can be used as a value in desktop entry files as all
351 * unsafe characters are escaped.
353 * The output can be NULL. Then only the number of characters will be counted
355 * RETURNS
356 * The number of characters after escaping the special characters, including the
357 * terminating NUL.
359 static int url_encode(const char *value, char *output)
361 static const char *unsafechars = "^&`{}|[]'<>\\#%\"+";
362 static const char *hexchars = "0123456789ABCDEF";
363 int num_written = 0;
364 const char *c;
366 for (c = value; *c; c++)
368 if (*c<=0x20 || *c>=0x7f || strchr(unsafechars, *c))
370 if (output)
372 *(output++) = '%';
373 *(output++) = hexchars[(unsigned)(*c)/16];
374 *(output++) = hexchars[(unsigned)(*c)%16];
376 num_written += 3;
378 else
380 if (output)
381 *(output++) = *c;
382 num_written++;
386 if (output)
387 *(output++) = 0;
388 num_written++;
390 return num_written;
393 static int decode_url_code(const char *c)
395 const char *p1, *p2;
396 int v1, v2;
397 static const char *hexchars = "0123456789ABCDEF";
398 if (*c == 0)
399 return -1;
401 p1 = strchr(hexchars, toupper(*c));
402 p2 = strchr(hexchars, toupper(*(c+1)));
403 if (p1 == NULL || p2 == NULL)
404 return -1;
405 v1 = (int)(p1 - hexchars);
406 v2 = (int)(p2 - hexchars);
407 return (v1<<4) + v2;
410 /******************************************************************************
411 * url_decode [internal]
413 * URL-decode the given string (i.e. unescape codes like %20). The decoded string
414 * will never be longer than the encoded one. The decoding can be done in place - the
415 * output variable can point to the value buffer.
417 * output should not be NULL
419 static void url_decode(const char *value, char *output)
421 const char *c = value;
422 while (*c)
424 if (*c == '%')
426 int v = decode_url_code(c+1);
427 if (v != -1)
429 *(output++) = v;
430 c += 3;
431 continue;
435 *(output++) = *c;
436 c++;
438 *output = 0;
441 static int escape_value(const char *value, DWORD dwFlags, char *output)
443 if (dwFlags & XDG_URLENCODE)
444 return url_encode(value, output);
445 return dskentry_encode(value, output);
448 /******************************************************************************
449 * XDG_WriteDesktopStringEntry [internal]
451 * Writes a key=value pair into the specified file descriptor.
453 * RETURNS
454 * TRUE on success, else FALSE
456 BOOL XDG_WriteDesktopStringEntry(int writer, const char *keyName, DWORD dwFlags, const char *value)
458 int keyLen = lstrlenA(keyName);
459 int valueLen = escape_value(value, dwFlags, NULL);
460 char *string = SHAlloc(keyLen+1+valueLen);
461 BOOL ret;
463 if (string == NULL)
464 return FALSE;
465 lstrcpyA(string, keyName);
466 string[keyLen] = '=';
467 escape_value(value, dwFlags, string+keyLen+1);
468 string[keyLen+1+valueLen-1]='\n'; /* -1 because valueLen contains the last NUL character */
469 ret = (write(writer, string, keyLen+1+valueLen)!=-1);
470 SHFree(string);
471 return ret;
474 typedef struct
476 char *str;
477 int len;
478 } PARSED_STRING;
480 typedef struct tagPARSED_ENTRY PARSED_ENTRY;
481 struct tagPARSED_ENTRY
483 PARSED_STRING name;
484 PARSED_STRING equals;
485 PARSED_STRING value;
486 PARSED_ENTRY *next;
489 typedef struct tagPARSED_GROUP PARSED_GROUP;
490 struct tagPARSED_GROUP
492 PARSED_STRING name;
493 PARSED_ENTRY *entries;
494 PARSED_GROUP *next;
498 struct tagXDG_PARSED_FILE
500 char *contents;
501 PARSED_ENTRY *head_comments;
502 PARSED_GROUP *groups;
505 static BOOL parsed_str_eq(PARSED_STRING *str1, const char *str2)
507 if (strncmp(str1->str, str2, str1->len) != 0)
508 return FALSE;
509 if (str2[str1->len] != 0)
510 return FALSE;
511 return TRUE;
514 static void free_entries_list(PARSED_ENTRY *first)
516 PARSED_ENTRY *next;
517 while (first)
519 next = first->next;
520 SHFree(first);
521 first = next;
525 void XDG_FreeParsedFile(XDG_PARSED_FILE *parsed)
527 PARSED_GROUP *group, *next;
528 if (!parsed)
529 return;
530 free_entries_list(parsed->head_comments);
532 group = parsed->groups;
533 while (group)
535 next = group->next;
536 free_entries_list(group->entries);
537 SHFree(group);
538 group = next;
542 #define LINE_GROUP 1
543 #define LINE_ENTRY 2
544 #define LINE_COMMENT 3
546 static int parse_line(char *content, PARSED_ENTRY *output, int *outType)
548 char *end;
550 ZeroMemory(output, sizeof(PARSED_ENTRY));
551 end = strchr(content, '\n');
552 if (end == NULL)
553 end = content + strlen(content) - 1;
555 if (*content == '#')
557 *outType = LINE_COMMENT;
558 output->equals.str = content;
559 output->equals.len = end - content;
560 if (*end != '\n')
561 output->equals.len++;
563 else if (*content == '[')
565 char *last_char = end;
567 *outType = LINE_GROUP;
569 /* the standard says nothing about skipping white spaces but e.g. KDE accepts such files */
570 while (isspace(*last_char))
571 last_char--;
572 if (*last_char != ']')
573 return -1;
574 output->name.str = content + 1;
575 output->name.len = last_char - (content + 1);
577 else
579 /* 'name = value' line */
580 char *equal, *eq_begin, *eq_end;
582 *outType = LINE_ENTRY;
584 equal = strchr(content, '=');
585 if (equal == NULL || equal > end)
586 return -1;
587 for (eq_begin = equal-1; isspace(*eq_begin) && eq_begin >= content; eq_begin--)
589 for (eq_end = equal+1; isspace(*eq_end) && *eq_end != '\n'; eq_end++)
592 output->name.str = content;
593 output->name.len = eq_begin - content + 1;
595 output->equals.str = eq_begin + 1;
596 output->equals.len = eq_end - eq_begin - 1;
598 output->value.str = eq_end;
599 output->value.len = end - eq_end;
601 if (*end != '\n')
602 output->value.len++;
604 return end - content + 1;
607 XDG_PARSED_FILE *XDG_ParseDesktopFile(int fd)
609 struct stat stats;
610 XDG_PARSED_FILE *parsed = NULL;
611 PARSED_ENTRY **curr_entry;
612 PARSED_GROUP **curr_group;
613 BOOL is_in_group = FALSE;
615 int pos = 0;
617 if (fstat(fd, &stats) == -1) goto failed;
618 parsed = SHAlloc(sizeof(XDG_PARSED_FILE));
619 if (parsed == NULL) goto failed;
620 parsed->groups = NULL;
621 parsed->head_comments = NULL;
622 parsed->contents = SHAlloc(stats.st_size+1);
623 if (parsed->contents == NULL) goto failed;
625 curr_entry = &parsed->head_comments;
626 curr_group = &parsed->groups;
628 if (read(fd, parsed->contents, stats.st_size) == -1) goto failed;
629 parsed->contents[stats.st_size] = 0;
631 while (pos < stats.st_size)
633 PARSED_ENTRY statement;
634 int type, size;
636 size = parse_line(parsed->contents + pos, &statement, &type);
637 if (size == -1) goto failed;
638 if (size == 0)
639 break;
640 pos += size;
642 switch (type)
644 case LINE_GROUP:
646 PARSED_GROUP *group = SHAlloc(sizeof(PARSED_GROUP));
647 if (group == NULL) goto failed;
648 is_in_group = TRUE;
650 group->name = statement.name;
651 group->entries = NULL;
652 group->next = NULL;
653 *curr_group = group;
654 curr_group = &group->next;
655 curr_entry = &group->entries;
656 break;
659 case LINE_ENTRY:
660 if (!is_in_group) goto failed;
661 /* fall through */
662 case LINE_COMMENT:
664 PARSED_ENTRY *new_stat = SHAlloc(sizeof(PARSED_ENTRY));
665 if (new_stat == NULL) goto failed;
666 *new_stat = statement;
667 new_stat->next = NULL;
668 *curr_entry = new_stat;
669 curr_entry = &new_stat->next;
670 break;
674 return parsed;
676 failed:
677 XDG_FreeParsedFile(parsed);
678 return NULL;
681 char *XDG_GetStringValue(XDG_PARSED_FILE *file, const char *group_name, const char *value_name, DWORD dwFlags)
683 PARSED_GROUP *group;
684 PARSED_ENTRY *entry;
686 for (group = file->groups; group; group = group->next)
688 if (!parsed_str_eq(&group->name, group_name))
689 continue;
691 for (entry = group->entries; entry; entry = entry->next)
692 if (entry->name.str != NULL && parsed_str_eq(&entry->name, value_name))
694 int len;
695 char *ret;
697 len = dskentry_decode(entry->value.str, entry->value.len, NULL);
698 ret = SHAlloc(len);
699 if (ret == NULL) return NULL;
700 dskentry_decode(entry->value.str, entry->value.len, ret);
701 if (dwFlags & XDG_URLENCODE)
702 url_decode(ret, ret);
703 return ret;
707 return NULL;