1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/ddfile.c for the Openbox window manager
4 Copyright (c) 2009 Dana Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program 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
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
19 #include "obt/ddfile.h"
28 typedef void (*ObtDDParseGroupFunc
)(const gchar
*group
,
32 typedef struct _ObtDDParseGroup
{
35 ObtDDParseGroupFunc func
;
38 typedef struct _ObtDDParse
{
41 ObtDDParseGroup
*group
;
42 GHashTable
*group_hash
;
57 gchar
*name
; /*!< Specific name for the object (eg Firefox) */
58 gchar
*generic
; /*!< Generic name for the object (eg Web Browser) */
59 gchar
*comment
; /*!< Comment/description to display for the object */
60 gchar
*icon
; /*!< Name/path for an icon for the object */
62 union _ObtDDFileData
{
64 gchar
*exec
; /*!< Executable to run for the app */
65 gchar
*wdir
; /*!< Working dir to run the app in */
66 gboolean term
; /*!< Run the app in a terminal or not */
67 ObtDDFileAppOpen open
;
69 /* XXX gchar**? or something better, a mime struct.. maybe
70 glib has something i can use. */
71 gchar
**mime
; /*!< Mime types the app can open */
73 ObtDDFileAppStartup startup
;
74 gchar
*startup_wmclass
;
84 static void group_free(ObtDDParseGroup
*g
)
87 g_slice_free(ObtDDParseGroup
, g
);
90 /* Displays a warning message including the file name and line number, and
91 sets the boolean @error to true if it points to a non-NULL address.
93 static void parse_error(const gchar
*m
, const ObtDDParse
*const parse
,
97 g_warning("%s at line %lu of input", m
, parse
->lineno
);
99 g_warning("%s at line %lu of file %s",
100 m
, parse
->lineno
, parse
->filename
);
101 if (error
) *error
= TRUE
;
104 /* reads an input string, strips out invalid stuff, and parses
106 static gchar
* parse_string(const gchar
*in
, gboolean locale
,
107 const ObtDDParse
*const parse
,
110 const gint bytes
= strlen(in
);
113 const gchar
*end
, *i
;
115 g_return_val_if_fail(in
!= NULL
, NULL
);
119 for (i
= in
; i
< end
; ++i
) {
120 if ((guchar
)*i
> 126 || (guchar
)*i
< 32) {
121 /* non-control character ascii */
123 parse_error("Invalid bytes in string", parse
, error
);
128 else if (!g_utf8_validate(in
, bytes
, &end
))
129 parse_error("Invalid bytes in localestring", parse
, error
);
131 out
= g_new(char, bytes
+ 1);
135 const gchar
*next
= locale
? g_utf8_find_next_char(i
, end
) : i
+1;
138 case 's': *o
++ = ' '; break;
139 case 'n': *o
++ = '\n'; break;
140 case 't': *o
++ = '\t'; break;
141 case 'r': *o
++ = '\r'; break;
142 case '\\': *o
++ = '\\'; break;
144 parse_error((locale
?
145 "Invalid escape sequence in localestring" :
146 "Invalid escape sequence in string"),
153 else if ((guchar
)*i
>= 127 || (guchar
)*i
< 32) {
154 /* avoid ascii control characters */
155 parse_error("Found control character in string", parse
, error
);
159 memcpy(o
, i
, next
-i
);
168 static gboolean
parse_bool(const gchar
*in
, const ObtDDParse
*const parse
,
171 if (strcmp(in
, "true") == 0)
173 else if (strcmp(in
, "false") != 0)
174 parse_error("Invalid boolean value", parse
, error
);
178 static float parse_numeric(const gchar
*in
, const ObtDDParse
*const parse
,
182 if (sscanf(in
, "%f", &out
) == 0)
183 parse_error("Invalid numeric value", parse
, error
);
187 gboolean
parse_file_line(FILE *f
, gchar
**buf
, gulong
*size
, gulong
*read
,
188 ObtDDParse
*parse
, gboolean
*error
)
190 const gulong BUFMUL
= 80;
195 g_assert(*read
== 0);
197 *buf
= g_new(char, *size
);
200 /* remove everything up to a null zero already in the buffer and shift
201 the rest to the front */
203 for (i
= 0; i
< *read
; ++i
) {
205 (*buf
)[i
-null
-1] = (*buf
)[i
];
206 else if ((*buf
)[i
] == '\0')
212 /* is there already a newline in the buffer? */
213 for (i
= 0; i
< *read
; ++i
)
214 if ((*buf
)[i
] == '\n') {
215 /* turn it into a null zero and done */
220 /* we need to read some more to find a newline */
225 newread
= *buf
+ *read
;
226 ret
= fread(newread
, sizeof(char), *size
-*read
, f
);
227 if (ret
< *size
- *read
&& !feof(f
)) {
228 parse_error("Error reading", parse
, error
);
233 /* strip out null zeros in the input and look for an endofline */
236 for (i
= newread
-*buf
; i
< *read
; ++i
) {
238 (*buf
)[i
] = (*buf
)[i
+null
];
239 if ((*buf
)[i
] == '\0') {
244 else if ((*buf
)[i
] == '\n' && eol
== *size
) {
246 /* turn it into a null zero */
252 /* found an endofline, done */
254 else if (feof(f
) && *read
< *size
) {
255 /* found the endoffile, done (if there is space) */
257 /* stick a null zero on if there is test on the last line */
258 (*buf
)[(*read
)++] = '\0';
265 *buf
= g_renew(char, *buf
, *size
);
271 static void parse_group(const gchar
*buf
, gulong len
,
272 ObtDDParse
*parse
, gboolean
*error
)
278 /* get the group name */
279 group
= g_strndup(buf
+1, len
-2);
280 for (i
= 0; i
< len
-2; ++i
)
281 if ((guchar
)group
[i
] < 32 || (guchar
)group
[i
] >= 127) {
282 /* valid ASCII only */
283 parse_error("Invalid character found", parse
, NULL
);
284 group
[i
] = '\0'; /* stopping before this character */
288 /* make sure it's a new group */
289 g
= g_hash_table_lookup(parse
->group_hash
, group
);
291 parse_error("Duplicate group found", parse
, error
);
295 /* if it's the first group, make sure it's named Desktop Entry */
296 else if (!parse
->group
&& strcmp(group
, "Desktop Entry") != 0)
298 parse_error("Incorrect group found, "
299 "expected [Desktop Entry]",
306 g
= g_slice_new(ObtDDParseGroup
);
309 g_hash_table_insert(parse
->group_hash
, group
, g
);
316 g_print("Found group %s\n", g
->name
);
320 static gboolean
parse_file(ObtDDFile
*dd
, FILE *f
, ObtDDParse
*parse
)
323 gulong bytes
= 0, read
= 0;
324 gboolean error
= FALSE
;
326 while (!error
&& parse_file_line(f
, &buf
, &bytes
, &read
, parse
, &error
)) {
327 /* XXX use the string in buf */
328 gulong len
= strlen(buf
);
329 if (buf
[0] == '#' || buf
[0] == '\0')
330 ; /* ignore comment lines */
331 else if (buf
[0] == '[' && buf
[len
-1] == ']')
332 parse_group(buf
, len
, parse
, &error
);
336 if (buf
) g_free(buf
);
340 ObtDDFile
* obt_ddfile_new_from_file(const gchar
*name
, GSList
*paths
)
348 dd
= g_slice_new(ObtDDFile
);
351 parse
.filename
= NULL
;
354 parse
.group_hash
= g_hash_table_new_full(g_str_hash
,
357 (GDestroyNotify
)group_free
);
360 for (it
= paths
; it
&& !success
; it
= g_slist_next(it
)) {
361 gchar
*path
= g_strdup_printf("%s/%s", (char*)it
->data
, name
);
362 if ((f
= fopen(path
, "r"))) {
363 parse
.filename
= path
;
365 success
= parse_file(dd
, f
, &parse
);
371 obt_ddfile_unref(dd
);
375 g_hash_table_destroy(parse
.group_hash
);
380 void obt_ddfile_ref(ObtDDFile
*dd
)
385 void obt_ddfile_unref(ObtDDFile
*dd
)
388 g_slice_free(ObtDDFile
, dd
);