diff --git a/dlls/shell32/xdg.c b/dlls/shell32/xdg.c index 71666509da..188d21c82c 100644 --- a/dlls/shell32/xdg.c +++ b/dlls/shell32/xdg.c @@ -290,6 +290,58 @@ static int dskentry_encode(const char *value, char *output) return num_written; } +/****************************************************************************** + * dskentry_decode [internal] + * + * Unescape the characters that can be escaped according to the desktop entry spec. + * The output parameter may be NULL. Then only the number of characters will + * be computers. + * + * RETURNS + * The number of characters after unescaping the special characters, including the + * terminating NUL. + */ +static int dskentry_decode(const char *value, int len, char *output) +{ + int pos = 0; + int count = 0; + while (posstr, str2, str1->len) != 0) + return FALSE; + if (str2[str1->len] != 0) + return FALSE; + return TRUE; +} + +static void free_entries_list(PARSED_ENTRY *first) +{ + PARSED_ENTRY *next; + while (first) + { + next = first->next; + SHFree(first); + first = next; + } +} + +void XDG_FreeParsedFile(XDG_PARSED_FILE *parsed) +{ + PARSED_GROUP *group, *next; + if (!parsed) + return; + free_entries_list(parsed->head_comments); + + group = parsed->groups; + while (group) + { + next = group->next; + free_entries_list(group->entries); + SHFree(group); + group = next; + } +} + +#define LINE_GROUP 1 +#define LINE_ENTRY 2 +#define LINE_COMMENT 3 + +static int parse_line(char *content, PARSED_ENTRY *output, int *outType) +{ + char *end; + + ZeroMemory(output, sizeof(PARSED_ENTRY)); + end = strchr(content, '\n'); + if (end == NULL) + end = content + strlen(content) - 1; + + if (*content == '#') + { + *outType = LINE_COMMENT; + output->equals.str = content; + output->equals.len = end - content; + if (*end != '\n') + output->equals.len++; + } + else if (*content == '[') + { + char *last_char = end; + + *outType = LINE_GROUP; + + /* the standard says nothing about skipping white spaces but e.g. KDE accepts such files */ + while (isspace(*last_char)) + last_char--; + if (*last_char != ']') + return -1; + output->name.str = content + 1; + output->name.len = last_char - (content + 1); + } + else + { + /* 'name = value' line */ + char *equal, *eq_begin, *eq_end; + + *outType = LINE_ENTRY; + + equal = strchr(content, '='); + if (equal == NULL || equal > end) + return -1; + for (eq_begin = equal-1; isspace(*eq_begin) && eq_begin >= content; eq_begin--) + ; + for (eq_end = equal+1; isspace(*eq_end) && *eq_end != '\n'; eq_end++) + ; + + output->name.str = content; + output->name.len = eq_begin - content + 1; + + output->equals.str = eq_begin + 1; + output->equals.len = eq_end - eq_begin - 1; + + output->value.str = eq_end; + output->value.len = end - eq_end; + + if (*end != '\n') + output->value.len++; + } + return end - content + 1; +} + +XDG_PARSED_FILE *XDG_ParseDesktopFile(int fd) +{ + struct stat stats; + XDG_PARSED_FILE *parsed = NULL; + PARSED_ENTRY **curr_entry; + PARSED_GROUP **curr_group; + BOOL is_in_group = FALSE; + + int pos = 0; + + if (fstat(fd, &stats) == -1) goto failed; + parsed = SHAlloc(sizeof(XDG_PARSED_FILE)); + if (parsed == NULL) goto failed; + parsed->groups = NULL; + parsed->head_comments = NULL; + parsed->contents = SHAlloc(stats.st_size+1); + if (parsed->contents == NULL) goto failed; + + curr_entry = &parsed->head_comments; + curr_group = &parsed->groups; + + if (read(fd, parsed->contents, stats.st_size) == -1) goto failed; + parsed->contents[stats.st_size] = 0; + + while (pos < stats.st_size) + { + PARSED_ENTRY statement; + int type, size; + + size = parse_line(parsed->contents + pos, &statement, &type); + if (size == -1) goto failed; + if (size == 0) + break; + pos += size; + + switch (type) + { + case LINE_GROUP: + { + PARSED_GROUP *group = SHAlloc(sizeof(PARSED_GROUP)); + if (group == NULL) goto failed; + is_in_group = TRUE; + + group->name = statement.name; + group->entries = NULL; + group->next = NULL; + *curr_group = group; + curr_group = &group->next; + curr_entry = &group->entries; + break; + } + + case LINE_ENTRY: + if (!is_in_group) goto failed; + /* fall through */ + case LINE_COMMENT: + { + PARSED_ENTRY *new_stat = SHAlloc(sizeof(PARSED_ENTRY)); + if (new_stat == NULL) goto failed; + *new_stat = statement; + new_stat->next = NULL; + *curr_entry = new_stat; + curr_entry = &new_stat->next; + break; + } + } + } + return parsed; + +failed: + XDG_FreeParsedFile(parsed); + return NULL; +} + +char *XDG_GetStringValue(XDG_PARSED_FILE *file, const char *group_name, const char *value_name, DWORD dwFlags) +{ + PARSED_GROUP *group; + PARSED_ENTRY *entry; + + for (group = file->groups; group; group = group->next) + { + if (!parsed_str_eq(&group->name, group_name)) + continue; + + for (entry = group->entries; entry; entry = entry->next) + if (entry->name.str != NULL && parsed_str_eq(&entry->name, value_name)) + { + int len; + char *ret; + + len = dskentry_decode(entry->value.str, entry->value.len, NULL); + ret = SHAlloc(len); + if (ret == NULL) return NULL; + dskentry_decode(entry->value.str, entry->value.len, ret); + if (dwFlags & XDG_URLENCODE) + url_decode(ret, ret); + return ret; + } + } + + return NULL; +} diff --git a/dlls/shell32/xdg.h b/dlls/shell32/xdg.h index 90f10f72d2..6c083c66e3 100644 --- a/dlls/shell32/xdg.h +++ b/dlls/shell32/xdg.h @@ -32,6 +32,12 @@ int XDG_MakeDirs(const char *path); #define XDG_URLENCODE 0x1 BOOL XDG_WriteDesktopStringEntry(int fd, const char *keyName, DWORD dwFlags, const char *value); +typedef struct tagXDG_PARSED_FILE XDG_PARSED_FILE; + +XDG_PARSED_FILE *XDG_ParseDesktopFile(int fd); +char *XDG_GetStringValue(XDG_PARSED_FILE *file, const char *group_name, const char *value_name, DWORD dwFlags); +void XDG_FreeParsedFile(XDG_PARSED_FILE *file); + /* implemented in trash.c */ typedef struct tagTRASH_ELEMENT TRASH_ELEMENT;