]> Dogcows Code - chaz/openbox/blob - obt/ddfile.c
avoid ascii control characters in strings
[chaz/openbox] / obt / ddfile.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 obt/ddfile.c for the Openbox window manager
4 Copyright (c) 2009 Dana Jansens
5
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.
10
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.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "obt/ddfile.h"
20 #include <glib.h>
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
24 #ifdef HAVE_STDIO_H
25 #include <stdio.h>
26 #endif
27
28 typedef struct _ObtDDParse {
29 gchar *filename;
30 gulong lineno;
31 } ObtDDParse;
32
33 typedef enum {
34 DATA_STRING,
35 DATA_LOCALESTRING,
36 DATA_BOOLEAN,
37 DATA_NUMERIC,
38 NUM_DATA_TYPES
39 } ObtDDDataType;
40
41 struct _ObtDDFile {
42 guint ref;
43
44 ObtDDFileType type;
45 gchar *name; /*!< Specific name for the object (eg Firefox) */
46 gchar *generic; /*!< Generic name for the object (eg Web Browser) */
47 gchar *comment; /*!< Comment/description to display for the object */
48 gchar *icon; /*!< Name/path for an icon for the object */
49
50 union _ObtDDFileData {
51 struct {
52 gchar *exec; /*!< Executable to run for the app */
53 gchar *wdir; /*!< Working dir to run the app in */
54 gboolean term; /*!< Run the app in a terminal or not */
55 ObtDDFileAppOpen open;
56
57 /* XXX gchar**? or something better, a mime struct.. maybe
58 glib has something i can use. */
59 gchar **mime; /*!< Mime types the app can open */
60
61 ObtDDFileAppStartup startup;
62 gchar *startup_wmclass;
63 } app;
64 struct {
65 gchar *url;
66 } link;
67 struct {
68 } dir;
69 } d;
70 };
71
72 static void parse_error(const gchar *m, const ObtDDParse *const parse,
73 gboolean *error)
74 {
75 if (!parse->filename)
76 g_warning("%s at line %lu of input\n", m, parse->lineno);
77 else
78 g_warning("%s at line %lu of file %s\n",
79 m, parse->lineno, parse->filename);
80 if (error) *error = TRUE;
81 }
82
83 /* reads an input string, strips out invalid stuff, and parses
84 backslash-stuff */
85 static gchar* parse_string(const gchar *in, gboolean locale,
86 const ObtDDParse *const parse,
87 gboolean *error)
88 {
89 const gint bytes = strlen(in);
90 gboolean backslash;
91 gchar *out, *o;
92 const gchar *end, *i;
93
94 g_return_val_if_fail(in != NULL, NULL);
95
96 if (!locale) {
97 end = in + bytes;
98 for (i = in; i < end; ++i) {
99 if (*i > 126 || *i < 32) { /* non-control character ascii */
100 end = i;
101 parse_error("Invalid bytes in string", parse, error);
102 break;
103 }
104 }
105 }
106 else if (!g_utf8_validate(in, bytes, &end))
107 parse_error("Invalid bytes in localestring", parse, error);
108
109 out = g_new(char, bytes + 1);
110 i = in; o = out;
111 backslash = FALSE;
112 while (i < end) {
113 const gchar *next = locale ? g_utf8_find_next_char(i, end) : i+1;
114 if (backslash) {
115 switch(*i) {
116 case 's': *o++ = ' '; break;
117 case 'n': *o++ = '\n'; break;
118 case 't': *o++ = '\t'; break;
119 case 'r': *o++ = '\r'; break;
120 case '\\': *o++ = '\\'; break;
121 default:
122 parse_error((locale ?
123 "Invalid escape sequence in localestring" :
124 "Invalid escape sequence in string"),
125 parse, error);
126 }
127 backslash = FALSE;
128 }
129 else if (*i == '\\')
130 backslash = TRUE;
131 else if (*i >= 127 || *i < 32) { /* avoid ascii control characters */
132 parse_error("Found control character in string", parse, error);
133 break;
134 }
135 else {
136 memcpy(o, i, next-i);
137 o += next-i;
138 }
139 i = next;
140 }
141 *o = '\0';
142 return o;
143 }
144
145 static gboolean parse_bool(const gchar *in, const ObtDDParse *const parse,
146 gboolean *error)
147 {
148 if (strcmp(in, "true") == 0)
149 return TRUE;
150 else if (strcmp(in, "false") != 0)
151 parse_error("Invalid boolean value", parse, error);
152 return FALSE;
153 }
154
155 static float parse_numeric(const gchar *in, const ObtDDParse *const parse,
156 gboolean *error)
157 {
158 float out = 0;
159 if (sscanf(in, "%f", &out) == 0)
160 parse_error("Invalid numeric value", parse, error);
161 return out;
162 }
163
164 gboolean parse_file_line(FILE *f, gchar **buf, gulong *size, gulong *read,
165 ObtDDParse *parse, gboolean *error)
166 {
167 const gulong BUFMUL = 80;
168 size_t ret;
169 gulong i, null;
170
171 if (*size == 0) {
172 g_assert(*read == 0);
173 *size = BUFMUL;
174 *buf = g_new(char, *size);
175 }
176
177 /* remove everything up to a null zero already in the buffer and shift
178 the rest to the front */
179 null = *size;
180 for (i = 0; i < *read; ++i) {
181 if (null < *size)
182 (*buf)[i-null-1] = (*buf)[i];
183 else if ((*buf)[i] == '\0')
184 null = i;
185 }
186 if (null < *size)
187 *read -= null + 1;
188
189 /* is there already a newline in the buffer? */
190 for (i = 0; i < *read; ++i)
191 if ((*buf)[i] == '\n') {
192 /* turn it into a null zero and done */
193 (*buf)[i] = '\0';
194 return TRUE;
195 }
196
197 /* we need to read some more to find a newline */
198 while (TRUE) {
199 gulong eol;
200 gchar *newread;
201
202 newread = *buf + *read;
203 ret = fread(newread, sizeof(char), *size-*read, f);
204 if (ret < *size - *read && !feof(f)) {
205 parse_error("Error reading", parse, error);
206 return FALSE;
207 }
208 *read += ret;
209
210 /* strip out null zeros in the input and look for an endofline */
211 null = 0;
212 eol = *size;
213 for (i = newread-*buf; i < *read; ++i) {
214 if (null > 0)
215 (*buf)[i] = (*buf)[i+null];
216 if ((*buf)[i] == '\0') {
217 ++null;
218 --(*read);
219 --i; /* try again */
220 }
221 else if ((*buf)[i] == '\n' && eol == *size) {
222 eol = i;
223 /* turn it into a null zero */
224 (*buf)[i] = '\0';
225 }
226 }
227
228 if (eol != *size)
229 /* found an endofline, done */
230 break;
231 else if (feof(f) && *read < *size) {
232 /* found the endoffile, done (if there is space) */
233 if (*read > 0) {
234 /* stick a null zero on if there is test on the last line */
235 (*buf)[(*read)++] = '\0';
236 }
237 break;
238 }
239 else {
240 /* read more */
241 size += BUFMUL;
242 *buf = g_renew(char, *buf, *size);
243 }
244 }
245 return *read > 0;
246 }
247
248 static gboolean parse_file(ObtDDFile *dd, FILE *f, ObtDDParse *parse)
249 {
250 gchar *buf = NULL;
251 gulong bytes = 0, read = 0;
252 gboolean error = FALSE;
253
254 while (parse_file_line(f, &buf, &bytes, &read, parse, &error)) {
255 /* XXX use the string in buf */
256 ++parse->lineno;
257 }
258
259 if (buf) g_free(buf);
260 return !error;
261 }
262
263 ObtDDFile* obt_ddfile_new_from_file(const gchar *name, GSList *paths)
264 {
265 ObtDDFile *dd;
266 ObtDDParse parse;
267 GSList *it;
268 FILE *f;
269
270 dd = g_slice_new(ObtDDFile);
271 dd->ref = 1;
272
273 f = NULL;
274 for (it = paths; it && !f; it = g_slist_next(it)) {
275 gchar *path = g_strdup_printf("%s/%s", (char*)it->data, name);
276 if ((f = fopen(path, "r"))) {
277 parse.filename = path;
278 parse.lineno = 0;
279 if (!parse_file(dd, f, &parse)) f = NULL;
280 }
281 }
282 if (!f) {
283 obt_ddfile_unref(dd);
284 dd = NULL;
285 }
286 return dd;
287 }
288
289 void obt_ddfile_ref(ObtDDFile *dd)
290 {
291 ++dd->ref;
292 }
293
294 void obt_ddfile_unref(ObtDDFile *dd)
295 {
296 if (--dd->ref < 1) {
297 g_slice_free(ObtDDFile, dd);
298 }
299 }
This page took 0.040102 seconds and 4 git commands to generate.