rpcrt4: Document NdrAllocate.
[wine.git] / dlls / shell32 / xdg.c
blob3b1a1bc12018076750deee371e0add239a2226a7
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 "config.h"
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #ifdef HAVE_SYS_STAT_H
27 # include <sys/stat.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <errno.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winreg.h"
37 #include "shlwapi.h"
38 #include "wine/debug.h"
39 #include "shell32_main.h"
40 #include "xdg.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(xdg);
45 * XDG paths implemented using Desktop Base Directory spec version 0.6
46 * (from http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html)
49 static CRITICAL_SECTION XDG_PathsLock;
50 static CRITICAL_SECTION_DEBUG XDG_PathsLock_Debug =
52 0, 0, &XDG_PathsLock,
53 { &XDG_PathsLock_Debug.ProcessLocksList,
54 &XDG_PathsLock_Debug.ProcessLocksList},
55 0, 0, { (DWORD_PTR)__FILE__ ": XDG_PathsLock"}
57 static CRITICAL_SECTION XDG_PathsLock = { &XDG_PathsLock_Debug, -1, 0, 0, 0, 0 };
59 typedef struct
61 const char *var_name;
62 const char *default_value;
63 } std_path;
65 static const std_path paths[] = {
66 {"XDG_DATA_HOME", "$HOME/.local/share"},
67 {"XDG_CONFIG_HOME", "$HOME/.config"},
68 {"XDG_DATA_DIRS", "/usr/local/share:/usr/share"},
69 {"XDG_CONFIG_DIRS", "/etc/xdg"},
70 {"XDG_CACHE_HOME", "$HOME/.cache"}
73 #define PATHS_COUNT (sizeof(paths)/sizeof(paths[0]))
75 /* will be filled with paths as they are computed */
76 static const char *path_values[PATHS_COUNT] = {
77 NULL,
78 NULL,
79 NULL,
80 NULL,
81 NULL
84 static char *load_path(int path_id)
86 char *env = getenv(paths[path_id].var_name);
87 char *ret;
89 if (env != NULL && env[0]=='/')
91 ret = SHAlloc(strlen(env)+1);
92 if (ret != NULL)
93 lstrcpyA(ret, env);
94 return ret;
97 if (memcmp(paths[path_id].default_value, "$HOME", 5)==0)
99 char *home = getenv("HOME");
100 int len;
102 if (!home) return NULL;
103 ret = SHAlloc(strlen(home)+strlen(paths[path_id].default_value)-5+1);
104 if (ret == NULL) return NULL;
106 lstrcpyA(ret, home);
107 len = strlen(ret);
108 if (len>0 && ret[len-1]=='/')
109 ret[--len]=0;
110 lstrcatA(ret, paths[path_id].default_value+5);
111 return ret;
114 ret = SHAlloc(strlen(paths[path_id].default_value)+1);
115 if (ret != NULL)
116 lstrcpyA(ret, paths[path_id].default_value);
117 return ret;
120 /******************************************************************************
121 * XDG_GetPath [internal]
123 * Get one of the XDG standard patch. The return value shouldn't be modified nor
124 * freed. A return value of NULL means that the memory is exhausted or the input
125 * is invalid
127 * For XDG_DATA_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME the result is a Unix path.
128 * For XDG_DATA_DIRS and XDG_CONFIG_DIRS the result is a colon-separated list of Unix
129 * paths
131 * The paths are guaranteed to start with '/'
133 const char *XDG_GetPath(int path_id)
135 if (path_id >= PATHS_COUNT || path_id < 0)
137 ERR("Invalid path_id %d\n", path_id);
138 return NULL;
141 if (path_values[path_id] != NULL)
142 return path_values[path_id];
143 EnterCriticalSection(&XDG_PathsLock);
144 if (path_values[path_id] == NULL)
145 path_values[path_id] = load_path(path_id);
146 LeaveCriticalSection(&XDG_PathsLock);
147 return path_values[path_id];
150 /******************************************************************************
151 * XDG_BuildPath [internal]
153 * Build a string with a subpath of one of the XDG standard paths.
154 * The root can be one of XDG_DATA_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME.
155 * The subpath is a path relative to that root (it shouldn't start with a slash)
157 * The returned path should be freed with SHFree. A return of NULL means that the
158 * memory is exhausted or the parameters are invalid
160 char *XDG_BuildPath(int root_id, const char *subpath)
162 const char *root_path = XDG_GetPath(root_id);
163 char *ret_buffer;
164 int root_len;
166 if (root_id == XDG_DATA_DIRS || root_id == XDG_CONFIG_DIRS)
168 ERR("Invalid path id %d\n", root_id);
169 return NULL;
172 if (root_path == NULL) return NULL;
173 root_len = strlen(root_path);
174 if (root_path[root_len-1]=='/') root_len--;
175 ret_buffer = SHAlloc(root_len+1+strlen(subpath)+1);
176 if (ret_buffer == NULL) return NULL;
177 lstrcpyA(ret_buffer, root_path);
178 ret_buffer[root_len]='/';
179 lstrcpyA(ret_buffer+root_len+1, subpath);
180 return ret_buffer;
183 /******************************************************************************
184 * XDG_MakeDirs [internal]
186 * Checks that all the directories on the specified path exists. If some don't exists
187 * they are created with mask 0700 as required by many the freedeskop.org specs.
188 * If the path doesn't end with '/' it is assumed to be a path to a file and the last
189 * segment is not checked
191 * In case of a failure the errno is always set and can be used e.g for debugging
193 * RETURNS
194 * TRUE on success, FALSE on error
196 BOOL XDG_MakeDirs(const char *path)
198 int last_slash = 0;
199 BOOL success = TRUE;
200 struct stat tmp;
201 char *buffer = SHAlloc(strlen(path)+1);
203 if (buffer == NULL)
205 errno = ENOMEM;
206 return FALSE;
208 lstrcpyA(buffer, path);
210 TRACE("(%s)\n", debugstr_a(path));
211 while (1)
213 char *slash=strchr(buffer+last_slash+1, '/');
214 if (slash==NULL)
215 break;
217 /* cut the string at that position and create the directory if it doesn't exist */
218 *slash=0;
219 TRACE("Checking path %s\n", debugstr_a(buffer));
220 success = (stat(buffer, &tmp)==0);
221 if (!success && errno==ENOENT)
223 TRACE("Creating\n");
224 success = (mkdir(buffer, 0700)==0);
226 if (!success)
228 WARN("Couldn't process directory %s (errno=%d)\n", debugstr_a(buffer), errno);
229 break;
231 *slash='/';
232 last_slash = slash-buffer;
234 SHFree(buffer);
235 return success;
239 * .desktop files functions
243 /******************************************************************************
244 * dskentry_encode [internal]
246 * Escape the characters that can't be present in a desktop entry value like \n, leading
247 * spaces etc. The output parameter may be NULL. Then only the number of characters will
248 * be computers.
250 * RETURNS
251 * The number of characters after escaping the special characters, including the
252 * terminating NUL.
254 static int dskentry_encode(const char *value, char *output)
256 int only_spc = TRUE;
257 int num_written = 0;
258 const char *c;
259 for (c = value; *c; c++)
261 if (only_spc && *c==' ')
263 if (output)
265 *(output++) = '\\';
266 *(output++) = 's';
268 num_written += 2;
269 continue;
271 only_spc = FALSE;
273 if (*c=='\t' || *c=='\r' || *c=='\n' || *c=='\\')
275 if (output)
277 *(output++) = '\\';
278 if (*c=='\t') *(output++) = 't';
279 if (*c=='\r') *(output++) = 'r';
280 if (*c=='\n') *(output++) = 'n';
281 if (*c=='\\') *(output++) = '\\';
283 num_written += 2;
285 else
287 if (output)
288 *(output++)=*c;
289 num_written++;
293 if (output)
294 *(output++) = 0;
295 num_written++;
296 return num_written;
299 /******************************************************************************
300 * dskentry_decode [internal]
302 * Unescape the characters that can be escaped according to the desktop entry spec.
303 * The output parameter may be NULL. Then only the number of characters will
304 * be computers.
306 * RETURNS
307 * The number of characters after unescaping the special characters, including the
308 * terminating NUL.
310 static int dskentry_decode(const char *value, int len, char *output)
312 int pos = 0;
313 int count = 0;
314 while (pos<len)
316 char c;
317 if (value[pos] == '\\' && pos<len-1)
319 pos++;
320 switch (value[pos])
322 case 's': c = ' '; break;
323 case 'n': c = '\n'; break;
324 case 't': c = '\t'; break;
325 case 'r': c = 'r'; break;
326 case '\\': c = '\\'; break;
327 default:
328 /* store both the backslash and the character */
329 if (output)
330 *(output++) = '\\';
331 count++;
332 c = value[pos];
333 break;
336 else
337 c = value[pos];
339 if (output)
340 *(output++) = c;
341 count++;
342 pos++;
345 if (output)
346 *(output++) = 0;
347 count++;
348 return count;
352 /******************************************************************************
353 * url_encode [internal]
355 * URL-encode the given string (i.e. use escape codes like %20). Note that an
356 * URL-encoded string can be used as a value in desktop entry files as all
357 * unsafe characters are escaped.
359 * The output can be NULL. Then only the number of characters will be counted
361 * RETURNS
362 * The number of characters after escaping the special characters, including the
363 * terminating NUL.
365 static int url_encode(const char *value, char *output)
367 static const char unsafechars[] = "^&`{}|[]'<>\\#%\"+";
368 static const char hexchars[] = "0123456789ABCDEF";
369 int num_written = 0;
370 const char *c;
372 for (c = value; *c; c++)
374 if (*c<=0x20 || *c>=0x7f || strchr(unsafechars, *c))
376 if (output)
378 *(output++) = '%';
379 *(output++) = hexchars[(unsigned char)*c / 16];
380 *(output++) = hexchars[(unsigned char)*c % 16];
382 num_written += 3;
384 else
386 if (output)
387 *(output++) = *c;
388 num_written++;
392 if (output)
393 *(output++) = 0;
394 num_written++;
396 return num_written;
399 static int decode_url_code(const char *c)
401 const char *p1, *p2;
402 int v1, v2;
403 static const char hexchars[] = "0123456789ABCDEF";
404 if (*c == 0)
405 return -1;
407 p1 = strchr(hexchars, toupper(*c));
408 p2 = strchr(hexchars, toupper(*(c+1)));
409 if (p1 == NULL || p2 == NULL)
410 return -1;
411 v1 = (int)(p1 - hexchars);
412 v2 = (int)(p2 - hexchars);
413 return (v1<<4) + v2;
416 /******************************************************************************
417 * url_decode [internal]
419 * URL-decode the given string (i.e. unescape codes like %20). The decoded string
420 * will never be longer than the encoded one. The decoding can be done in place - the
421 * output variable can point to the value buffer.
423 * output should not be NULL
425 static void url_decode(const char *value, char *output)
427 const char *c = value;
428 while (*c)
430 if (*c == '%')
432 int v = decode_url_code(c+1);
433 if (v != -1)
435 *(output++) = v;
436 c += 3;
437 continue;
441 *(output++) = *c;
442 c++;
444 *output = 0;
447 static int escape_value(const char *value, DWORD dwFlags, char *output)
449 if (dwFlags & XDG_URLENCODE)
450 return url_encode(value, output);
451 return dskentry_encode(value, output);
454 /******************************************************************************
455 * XDG_WriteDesktopStringEntry [internal]
457 * Writes a key=value pair into the specified file descriptor.
459 * RETURNS
460 * TRUE on success, else FALSE
462 BOOL XDG_WriteDesktopStringEntry(int writer, const char *keyName, DWORD dwFlags, const char *value)
464 int keyLen = lstrlenA(keyName);
465 int valueLen = escape_value(value, dwFlags, NULL);
466 char *string = SHAlloc(keyLen+1+valueLen);
467 BOOL ret;
469 if (string == NULL)
470 return FALSE;
471 lstrcpyA(string, keyName);
472 string[keyLen] = '=';
473 escape_value(value, dwFlags, string+keyLen+1);
474 string[keyLen+1+valueLen-1]='\n'; /* -1 because valueLen contains the last NUL character */
475 ret = (write(writer, string, keyLen+1+valueLen)!=-1);
476 SHFree(string);
477 return ret;
480 typedef struct
482 char *str;
483 int len;
484 } PARSED_STRING;
486 typedef struct tagPARSED_ENTRY PARSED_ENTRY;
487 struct tagPARSED_ENTRY
489 PARSED_STRING name;
490 PARSED_STRING equals;
491 PARSED_STRING value;
492 PARSED_ENTRY *next;
495 typedef struct tagPARSED_GROUP PARSED_GROUP;
496 struct tagPARSED_GROUP
498 PARSED_STRING name;
499 PARSED_ENTRY *entries;
500 PARSED_GROUP *next;
504 struct tagXDG_PARSED_FILE
506 char *contents;
507 PARSED_ENTRY *head_comments;
508 PARSED_GROUP *groups;
511 static BOOL parsed_str_eq(const PARSED_STRING *str1, const char *str2)
513 if (strncmp(str1->str, str2, str1->len) != 0)
514 return FALSE;
515 if (str2[str1->len] != 0)
516 return FALSE;
517 return TRUE;
520 static void free_entries_list(PARSED_ENTRY *first)
522 PARSED_ENTRY *next;
523 while (first)
525 next = first->next;
526 SHFree(first);
527 first = next;
531 void XDG_FreeParsedFile(XDG_PARSED_FILE *parsed)
533 PARSED_GROUP *group, *next;
534 if (!parsed)
535 return;
536 free_entries_list(parsed->head_comments);
538 group = parsed->groups;
539 while (group)
541 next = group->next;
542 free_entries_list(group->entries);
543 SHFree(group);
544 group = next;
548 #define LINE_GROUP 1
549 #define LINE_ENTRY 2
550 #define LINE_COMMENT 3
552 static int parse_line(char *content, PARSED_ENTRY *output, int *outType)
554 char *end;
556 ZeroMemory(output, sizeof(PARSED_ENTRY));
557 end = strchr(content, '\n');
558 if (end == NULL)
559 end = content + strlen(content) - 1;
561 if (*content == '#')
563 *outType = LINE_COMMENT;
564 output->equals.str = content;
565 output->equals.len = end - content;
566 if (*end != '\n')
567 output->equals.len++;
569 else if (*content == '[')
571 char *last_char = end;
573 *outType = LINE_GROUP;
575 /* the standard says nothing about skipping white spaces but e.g. KDE accepts such files */
576 while (isspace(*last_char))
577 last_char--;
578 if (*last_char != ']')
579 return -1;
580 output->name.str = content + 1;
581 output->name.len = last_char - (content + 1);
583 else
585 /* 'name = value' line */
586 char *equal, *eq_begin, *eq_end;
588 *outType = LINE_ENTRY;
590 equal = strchr(content, '=');
591 if (equal == NULL || equal > end)
592 return -1;
593 for (eq_begin = equal-1; isspace(*eq_begin) && eq_begin >= content; eq_begin--)
595 for (eq_end = equal+1; isspace(*eq_end) && *eq_end != '\n'; eq_end++)
598 output->name.str = content;
599 output->name.len = eq_begin - content + 1;
601 output->equals.str = eq_begin + 1;
602 output->equals.len = eq_end - eq_begin - 1;
604 output->value.str = eq_end;
605 output->value.len = end - eq_end;
607 if (*end != '\n')
608 output->value.len++;
610 return end - content + 1;
613 XDG_PARSED_FILE *XDG_ParseDesktopFile(int fd)
615 struct stat stats;
616 XDG_PARSED_FILE *parsed = NULL;
617 PARSED_ENTRY **curr_entry;
618 PARSED_GROUP **curr_group;
619 BOOL is_in_group = FALSE;
621 int pos = 0;
623 if (fstat(fd, &stats) == -1) goto failed;
624 parsed = SHAlloc(sizeof(XDG_PARSED_FILE));
625 if (parsed == NULL) goto failed;
626 parsed->groups = NULL;
627 parsed->head_comments = NULL;
628 parsed->contents = SHAlloc(stats.st_size+1);
629 if (parsed->contents == NULL) goto failed;
631 curr_entry = &parsed->head_comments;
632 curr_group = &parsed->groups;
634 if (read(fd, parsed->contents, stats.st_size) == -1) goto failed;
635 parsed->contents[stats.st_size] = 0;
637 while (pos < stats.st_size)
639 PARSED_ENTRY statement;
640 int type, size;
642 size = parse_line(parsed->contents + pos, &statement, &type);
643 if (size == -1) goto failed;
644 if (size == 0)
645 break;
646 pos += size;
648 switch (type)
650 case LINE_GROUP:
652 PARSED_GROUP *group = SHAlloc(sizeof(PARSED_GROUP));
653 if (group == NULL) goto failed;
654 is_in_group = TRUE;
656 group->name = statement.name;
657 group->entries = NULL;
658 group->next = NULL;
659 *curr_group = group;
660 curr_group = &group->next;
661 curr_entry = &group->entries;
662 break;
665 case LINE_ENTRY:
666 if (!is_in_group) goto failed;
667 /* fall through */
668 case LINE_COMMENT:
670 PARSED_ENTRY *new_stat = SHAlloc(sizeof(PARSED_ENTRY));
671 if (new_stat == NULL) goto failed;
672 *new_stat = statement;
673 new_stat->next = NULL;
674 *curr_entry = new_stat;
675 curr_entry = &new_stat->next;
676 break;
680 return parsed;
682 failed:
683 XDG_FreeParsedFile(parsed);
684 return NULL;
687 char *XDG_GetStringValue(XDG_PARSED_FILE *file, const char *group_name, const char *value_name, DWORD dwFlags)
689 PARSED_GROUP *group;
690 PARSED_ENTRY *entry;
692 for (group = file->groups; group; group = group->next)
694 if (!parsed_str_eq(&group->name, group_name))
695 continue;
697 for (entry = group->entries; entry; entry = entry->next)
698 if (entry->name.str != NULL && parsed_str_eq(&entry->name, value_name))
700 int len;
701 char *ret;
703 len = dskentry_decode(entry->value.str, entry->value.len, NULL);
704 ret = SHAlloc(len);
705 if (ret == NULL) return NULL;
706 dskentry_decode(entry->value.str, entry->value.len, ret);
707 if (dwFlags & XDG_URLENCODE)
708 url_decode(ret, ret);
709 return ret;
713 return NULL;