File: | wsutil/filter_files.c |
Warning: | line 200, column 21 File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* filter_files.c | |||
2 | * Code for reading and writing the filters file. | |||
3 | * | |||
4 | * Wireshark - Network traffic analyzer | |||
5 | * By Gerald Combs <[email protected]> | |||
6 | * Copyright 1998 Gerald Combs | |||
7 | * | |||
8 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
9 | */ | |||
10 | ||||
11 | #include <config.h> | |||
12 | #define WS_LOG_DOMAIN"WSUtil" LOG_DOMAIN_WSUTIL"WSUtil" | |||
13 | #include "filter_files.h" | |||
14 | ||||
15 | #include <stdio.h> | |||
16 | #include <string.h> | |||
17 | #include <errno(*__errno_location ()).h> | |||
18 | ||||
19 | #include <wsutil/file_util.h> | |||
20 | #include <wsutil/filesystem.h> | |||
21 | #include <wsutil/report_message.h> | |||
22 | #include <wsutil/wslog.h> | |||
23 | #include <wsutil/ws_assert.h> | |||
24 | ||||
25 | /* | |||
26 | * Read in a list of filters. | |||
27 | * | |||
28 | * On error, report the error via the UI. | |||
29 | */ | |||
30 | ||||
31 | #define INIT_BUF_SIZE128 128 | |||
32 | ||||
33 | static GList * | |||
34 | add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr) | |||
35 | { | |||
36 | filter_def *filt; | |||
37 | ||||
38 | filt = g_new(filter_def, 1)((filter_def *) g_malloc_n ((1), sizeof (filter_def))); | |||
39 | filt->name = g_strdup(filt_name)g_strdup_inline (filt_name); | |||
40 | filt->strval = g_strdup(filt_expr)g_strdup_inline (filt_expr); | |||
41 | return g_list_prepend(fl, filt); | |||
42 | } | |||
43 | ||||
44 | static void | |||
45 | free_filter_entry(void * data) | |||
46 | { | |||
47 | filter_def *filt = (filter_def*)data; | |||
48 | g_free(filt->name); | |||
49 | g_free(filt->strval); | |||
50 | g_free(filt); | |||
51 | } | |||
52 | ||||
53 | void ws_filter_list_free(filter_list_t *fl) | |||
54 | { | |||
55 | g_list_free_full(fl->list, free_filter_entry); | |||
56 | g_free(fl); | |||
57 | } | |||
58 | ||||
59 | static GList * | |||
60 | remove_filter_entry(GList *fl, GList *fl_entry) | |||
61 | { | |||
62 | filter_def *filt; | |||
63 | ||||
64 | filt = (filter_def *) fl_entry->data; | |||
65 | g_free(filt->name); | |||
66 | g_free(filt->strval); | |||
67 | g_free(filt); | |||
68 | return g_list_remove_link(fl, fl_entry); | |||
69 | } | |||
70 | ||||
71 | static int | |||
72 | skip_whitespace(FILE *ff) | |||
73 | { | |||
74 | int c; | |||
75 | ||||
76 | while ((c = getc(ff)) != EOF(-1) && c != '\n' && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) | |||
77 | ; | |||
78 | return c; | |||
79 | } | |||
80 | ||||
81 | static int | |||
82 | getc_crlf(FILE *ff) | |||
83 | { | |||
84 | int c; | |||
85 | ||||
86 | c = getc(ff); | |||
87 | if (c == '\r') { | |||
88 | /* Treat CR-LF at the end of a line like LF, so that if we're reading | |||
89 | * a Windows-format file on UN*X, we handle it the same way we'd handle | |||
90 | * a UN*X-format file. */ | |||
91 | c = getc(ff); | |||
92 | if (c != EOF(-1) && c != '\n') { | |||
93 | /* Put back the character after the CR, and process the CR normally. */ | |||
94 | ungetc(c, ff); | |||
95 | c = '\r'; | |||
96 | } | |||
97 | } | |||
98 | return c; | |||
99 | } | |||
100 | ||||
101 | filter_list_t * | |||
102 | ws_filter_list_read(filter_list_type_t list_type) | |||
103 | { | |||
104 | const char *ff_name, *ff_description; | |||
105 | char *ff_path; | |||
106 | FILE *ff; | |||
107 | GList *flp = NULL((void*)0); | |||
108 | int c; | |||
109 | char *filt_name, *filt_expr; | |||
110 | int filt_name_len, filt_expr_len; | |||
111 | int filt_name_index, filt_expr_index; | |||
112 | int line = 1; | |||
113 | ||||
114 | filter_list_t *list = g_new(filter_list_t, 1)((filter_list_t *) g_malloc_n ((1), sizeof (filter_list_t))); | |||
115 | list->type = list_type; | |||
116 | list->list = NULL((void*)0); | |||
117 | ||||
118 | switch (list_type) { | |||
| ||||
119 | ||||
120 | case CFILTER_LIST: | |||
121 | ff_name = CFILTER_FILE_NAME"cfilters"; | |||
122 | ff_description = "capture filter"; | |||
123 | break; | |||
124 | ||||
125 | case DFILTER_LIST: | |||
126 | ff_name = DFILTER_FILE_NAME"dfilters"; | |||
127 | ff_description = "display filter"; | |||
128 | break; | |||
129 | ||||
130 | case DMACROS_LIST: | |||
131 | ff_name = DMACROS_FILE_NAME"dmacros"; | |||
132 | ff_description = "display filter macro"; | |||
133 | break; | |||
134 | ||||
135 | default: | |||
136 | ws_assert_not_reached()ws_log_fatal_full("WSUtil", LOG_LEVEL_ERROR, "wsutil/filter_files.c" , 136, __func__, "assertion \"not reached\" failed"); | |||
137 | } | |||
138 | ||||
139 | /* try to open personal "cfilters"/"dfilters" file */ | |||
140 | ff_path = get_persconffile_path(ff_name, true1); | |||
141 | if ((ff = ws_fopenfopen(ff_path, "r")) == NULL((void*)0)) { | |||
142 | /* | |||
143 | * Did that fail because the file didn't exist? | |||
144 | */ | |||
145 | if (errno(*__errno_location ()) != ENOENT2) { | |||
146 | /* | |||
147 | * No. Just give up. | |||
148 | */ | |||
149 | report_warning("Could not open your %s file\n\"%s\": %s.", | |||
150 | ff_description, ff_path, g_strerror(errno(*__errno_location ()))); | |||
151 | g_free(ff_path); | |||
152 | return list; | |||
153 | } | |||
154 | ||||
155 | /* | |||
156 | * Yes. Try to open the global "cfilters/dfilters" file. | |||
157 | */ | |||
158 | g_free(ff_path); | |||
159 | ff_path = get_datafile_path(ff_name); | |||
160 | if ((ff = ws_fopenfopen(ff_path, "r")) == NULL((void*)0)) { | |||
161 | /* | |||
162 | * Well, that didn't work, either. Just give up. | |||
163 | * Report an error if the file existed but we couldn't open it. | |||
164 | */ | |||
165 | if (errno(*__errno_location ()) != ENOENT2) { | |||
166 | report_warning("Could not open your %s file\n\"%s\": %s.", | |||
167 | ff_description, ff_path, g_strerror(errno(*__errno_location ()))); | |||
168 | } | |||
169 | g_free(ff_path); | |||
170 | return list; | |||
171 | } | |||
172 | } | |||
173 | ||||
174 | /* Allocate the filter name buffer. */ | |||
175 | filt_name_len = INIT_BUF_SIZE128; | |||
176 | filt_name = (char *)g_malloc(filt_name_len + 1); | |||
177 | filt_expr_len = INIT_BUF_SIZE128; | |||
178 | filt_expr = (char *)g_malloc(filt_expr_len + 1); | |||
179 | ||||
180 | for (line = 1; ; line++) { | |||
181 | /* Lines in a filter file are of the form | |||
182 | ||||
183 | "name" expression | |||
184 | ||||
185 | where "name" is a name, in quotes - backslashes in the name | |||
186 | escape the next character, so quotes and backslashes can appear | |||
187 | in the name - and "expression" is a filter expression, not in | |||
188 | quotes, running to the end of the line. */ | |||
189 | ||||
190 | /* Skip over leading white space, if any. */ | |||
191 | c = skip_whitespace(ff); | |||
192 | ||||
193 | if (c == EOF(-1)) | |||
194 | break; /* Nothing more to read */ | |||
195 | if (c == '\n') | |||
196 | continue; /* Blank line. */ | |||
197 | if (c == '#') { | |||
198 | /* Comment. */ | |||
199 | while (c != '\n') | |||
200 | c = getc(ff); /* skip to the end of the line */ | |||
| ||||
201 | continue; | |||
202 | } | |||
203 | ||||
204 | /* "c" is the first non-white-space character. | |||
205 | If it's not a quote, it's an error. */ | |||
206 | if (c != '"') { | |||
207 | ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 208, __func__, "'%s' line %d doesn't have a quoted filter name." , ff_path, line); } } while (0) | |||
208 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 208, __func__, "'%s' line %d doesn't have a quoted filter name." , ff_path, line); } } while (0); | |||
209 | while (c != '\n') | |||
210 | c = getc(ff); /* skip to the end of the line */ | |||
211 | continue; | |||
212 | } | |||
213 | ||||
214 | /* Get the name of the filter. */ | |||
215 | filt_name_index = 0; | |||
216 | for (;;) { | |||
217 | c = getc_crlf(ff); | |||
218 | if (c == EOF(-1) || c == '\n') | |||
219 | break; /* End of line - or end of file */ | |||
220 | if (c == '"') { | |||
221 | /* Closing quote. */ | |||
222 | if (filt_name_index >= filt_name_len) { | |||
223 | /* Filter name buffer isn't long enough; double its length. */ | |||
224 | filt_name_len *= 2; | |||
225 | filt_name = (char *)g_realloc(filt_name, filt_name_len + 1); | |||
226 | } | |||
227 | filt_name[filt_name_index] = '\0'; | |||
228 | break; | |||
229 | } | |||
230 | if (c == '\\') { | |||
231 | /* Next character is escaped */ | |||
232 | c = getc_crlf(ff); | |||
233 | if (c == EOF(-1) || c == '\n') | |||
234 | break; /* End of line - or end of file */ | |||
235 | } | |||
236 | /* Add this character to the filter name string. */ | |||
237 | if (filt_name_index >= filt_name_len) { | |||
238 | /* Filter name buffer isn't long enough; double its length. */ | |||
239 | filt_name_len *= 2; | |||
240 | filt_name = (char *)g_realloc(filt_name, filt_name_len + 1); | |||
241 | } | |||
242 | filt_name[filt_name_index] = c; | |||
243 | filt_name_index++; | |||
244 | } | |||
245 | ||||
246 | if (c == EOF(-1)) { | |||
247 | if (!ferror(ff)) { | |||
248 | /* EOF, not error; no newline seen before EOF */ | |||
249 | ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 250, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0) | |||
250 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 250, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0); | |||
251 | } | |||
252 | break; /* nothing more to read */ | |||
253 | } | |||
254 | ||||
255 | if (c != '"') { | |||
256 | /* No newline seen before end-of-line */ | |||
257 | ws_warning("'%s' line %d doesn't have a closing quote.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 258, __func__, "'%s' line %d doesn't have a closing quote." , ff_path, line); } } while (0) | |||
258 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 258, __func__, "'%s' line %d doesn't have a closing quote." , ff_path, line); } } while (0); | |||
259 | continue; | |||
260 | } | |||
261 | ||||
262 | /* Skip over separating white space, if any. */ | |||
263 | c = skip_whitespace(ff); | |||
264 | ||||
265 | if (c == EOF(-1)) { | |||
266 | if (!ferror(ff)) { | |||
267 | /* EOF, not error; no newline seen before EOF */ | |||
268 | ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 269, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0) | |||
269 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 269, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0); | |||
270 | } | |||
271 | break; /* nothing more to read */ | |||
272 | } | |||
273 | ||||
274 | if (c == '\n') { | |||
275 | /* No filter expression */ | |||
276 | ws_warning("'%s' line %d doesn't have a filter expression.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 277, __func__, "'%s' line %d doesn't have a filter expression." , ff_path, line); } } while (0) | |||
277 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 277, __func__, "'%s' line %d doesn't have a filter expression." , ff_path, line); } } while (0); | |||
278 | continue; | |||
279 | } | |||
280 | ||||
281 | /* "c" is the first non-white-space character; it's the first | |||
282 | character of the filter expression. */ | |||
283 | filt_expr_index = 0; | |||
284 | for (;;) { | |||
285 | /* Add this character to the filter expression string. */ | |||
286 | if (filt_expr_index >= filt_expr_len) { | |||
287 | /* Filter expression buffer isn't long enough; double its length. */ | |||
288 | filt_expr_len *= 2; | |||
289 | filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1); | |||
290 | } | |||
291 | filt_expr[filt_expr_index] = c; | |||
292 | filt_expr_index++; | |||
293 | ||||
294 | /* Get the next character. */ | |||
295 | c = getc_crlf(ff); | |||
296 | if (c == EOF(-1) || c == '\n') | |||
297 | break; | |||
298 | } | |||
299 | ||||
300 | if (c == EOF(-1)) { | |||
301 | if (!ferror(ff)) { | |||
302 | /* EOF, not error; no newline seen before EOF */ | |||
303 | ws_warning("'%s' line %d doesn't have a newline.", ff_path,do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 304, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0) | |||
304 | line)do { if (1) { ws_log_full("WSUtil", LOG_LEVEL_WARNING, "wsutil/filter_files.c" , 304, __func__, "'%s' line %d doesn't have a newline.", ff_path , line); } } while (0); | |||
305 | } | |||
306 | break; /* nothing more to read */ | |||
307 | } | |||
308 | ||||
309 | /* We saw the ending newline; terminate the filter expression string */ | |||
310 | if (filt_expr_index >= filt_expr_len) { | |||
311 | /* Filter expression buffer isn't long enough; double its length. */ | |||
312 | filt_expr_len *= 2; | |||
313 | filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1); | |||
314 | } | |||
315 | filt_expr[filt_expr_index] = '\0'; | |||
316 | ||||
317 | /* Add the new filter to the list of filters */ | |||
318 | flp = add_filter_entry(flp, filt_name, filt_expr); | |||
319 | } | |||
320 | if (ferror(ff)) { | |||
321 | report_warning("Error reading your %s file\n\"%s\": %s.", | |||
322 | ff_description, ff_path, g_strerror(errno(*__errno_location ()))); | |||
323 | } | |||
324 | g_free(ff_path); | |||
325 | fclose(ff); | |||
326 | g_free(filt_name); | |||
327 | g_free(filt_expr); | |||
328 | list->list = flp; | |||
329 | return list; | |||
330 | } | |||
331 | ||||
332 | /* | |||
333 | * Add a new filter to the end of a list. | |||
334 | */ | |||
335 | void | |||
336 | ws_filter_list_add(filter_list_t *fl, const char *name, | |||
337 | const char *expression) | |||
338 | { | |||
339 | fl->list = add_filter_entry(fl->list, name, expression); | |||
340 | } | |||
341 | ||||
342 | static int | |||
343 | compare_def(const void *def, const void *name) | |||
344 | { | |||
345 | return g_strcmp0(((filter_def *)def)->name, name); | |||
346 | } | |||
347 | ||||
348 | GList *ws_filter_list_find(filter_list_t *list, const char *name) | |||
349 | { | |||
350 | return g_list_find_custom(list->list, name, compare_def); | |||
351 | } | |||
352 | ||||
353 | /* | |||
354 | * Remove a filter from a list. | |||
355 | */ | |||
356 | bool_Bool | |||
357 | ws_filter_list_remove(filter_list_t *list, const char *name) | |||
358 | { | |||
359 | GList *p; | |||
360 | ||||
361 | p = g_list_find_custom(list->list, name, compare_def); | |||
362 | if (p == NULL((void*)0)) | |||
363 | return false0; | |||
364 | list->list = remove_filter_entry(list->list, p); | |||
365 | return true1; | |||
366 | } | |||
367 | ||||
368 | /* | |||
369 | * Write out a list of filters. | |||
370 | * | |||
371 | * On error, report the error via the UI. | |||
372 | */ | |||
373 | void | |||
374 | ws_filter_list_write(filter_list_t *list) | |||
375 | { | |||
376 | char *pf_dir_path; | |||
377 | const char *ff_name, *ff_description; | |||
378 | char *ff_path, *ff_path_new; | |||
379 | GList *fl; | |||
380 | GList *flpp; | |||
381 | filter_def *filt; | |||
382 | FILE *ff; | |||
383 | unsigned char *p, c; | |||
384 | ||||
385 | switch (list->type) { | |||
386 | ||||
387 | case CFILTER_LIST: | |||
388 | ff_name = CFILTER_FILE_NAME"cfilters"; | |||
389 | ff_description = "capture filter"; | |||
390 | break; | |||
391 | ||||
392 | case DFILTER_LIST: | |||
393 | ff_name = DFILTER_FILE_NAME"dfilters"; | |||
394 | ff_description = "display filter"; | |||
395 | break; | |||
396 | ||||
397 | case DMACROS_LIST: | |||
398 | ff_name = DMACROS_FILE_NAME"dmacros"; | |||
399 | ff_description = "display filter macro"; | |||
400 | break; | |||
401 | ||||
402 | default: | |||
403 | ws_assert_not_reached()ws_log_fatal_full("WSUtil", LOG_LEVEL_ERROR, "wsutil/filter_files.c" , 403, __func__, "assertion \"not reached\" failed"); | |||
404 | return; | |||
405 | } | |||
406 | fl = list->list; | |||
407 | ||||
408 | /* Create the directory that holds personal configuration files, | |||
409 | if necessary. */ | |||
410 | if (create_persconffile_dir(&pf_dir_path) == -1) { | |||
411 | report_failure("Can't create directory\n\"%s\"\nfor filter files: %s.", | |||
412 | pf_dir_path, g_strerror(errno(*__errno_location ()))); | |||
413 | g_free(pf_dir_path); | |||
414 | return; | |||
415 | } | |||
416 | ||||
417 | ff_path = get_persconffile_path(ff_name, true1); | |||
418 | ||||
419 | /* Write to "XXX.new", and rename if that succeeds. | |||
420 | That means we don't trash the file if we fail to write it out | |||
421 | completely. */ | |||
422 | ff_path_new = ws_strdup_printf("%s.new", ff_path)wmem_strdup_printf(((void*)0), "%s.new", ff_path); | |||
423 | ||||
424 | if ((ff = ws_fopenfopen(ff_path_new, "w")) == NULL((void*)0)) { | |||
425 | /* We had an error saving the filter. */ | |||
426 | report_failure("Error saving your %s file\nCouldn't open \"%s\": %s.", | |||
427 | ff_description, ff_path_new, g_strerror(errno(*__errno_location ()))); | |||
428 | g_free(ff_path_new); | |||
429 | g_free(ff_path); | |||
430 | return; | |||
431 | } | |||
432 | flpp = g_list_first(fl); | |||
433 | while (flpp) { | |||
434 | filt = (filter_def *) flpp->data; | |||
435 | ||||
436 | /* Write out the filter name as a quoted string; escape any quotes | |||
437 | or backslashes. */ | |||
438 | putc('"', ff); | |||
439 | for (p = (unsigned char *)filt->name; (c = *p) != '\0'; p++) { | |||
440 | if (c == '"' || c == '\\') | |||
441 | putc('\\', ff); | |||
442 | putc(c, ff); | |||
443 | } | |||
444 | putc('"', ff); | |||
445 | ||||
446 | /* Separate the filter name and value with a space. */ | |||
447 | putc(' ', ff); | |||
448 | ||||
449 | /* Write out the filter expression and a newline. */ | |||
450 | fprintf(ff, "%s\n", filt->strval); | |||
451 | if (ferror(ff)) { | |||
452 | report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.", | |||
453 | ff_description, ff_path_new, g_strerror(errno(*__errno_location ()))); | |||
454 | fclose(ff); | |||
455 | ws_unlinkunlink(ff_path_new); | |||
456 | g_free(ff_path_new); | |||
457 | g_free(ff_path); | |||
458 | return; | |||
459 | } | |||
460 | flpp = flpp->next; | |||
461 | } | |||
462 | if (fclose(ff) == EOF(-1)) { | |||
463 | report_failure("Error saving your %s file\nWrite to \"%s\" failed: %s.", | |||
464 | ff_description, ff_path_new, g_strerror(errno(*__errno_location ()))); | |||
465 | ws_unlinkunlink(ff_path_new); | |||
466 | g_free(ff_path_new); | |||
467 | g_free(ff_path); | |||
468 | return; | |||
469 | } | |||
470 | ||||
471 | #ifdef _WIN32 | |||
472 | /* ANSI C doesn't say whether "rename()" removes the target if it | |||
473 | exists; the Win32 call to rename files doesn't do so, which I | |||
474 | infer is the reason why the MSVC++ "rename()" doesn't do so. | |||
475 | We must therefore remove the target file first, on Windows. | |||
476 | ||||
477 | XXX - ws_rename() should be ws_stdio_rename() on Windows, | |||
478 | and ws_stdio_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING, | |||
479 | so it should remove the target if it exists, so this stuff | |||
480 | shouldn't be necessary. Perhaps it dates back to when we were | |||
481 | calling rename(), with that being a wrapper around Microsoft's | |||
482 | _rename(), which didn't remove the target. */ | |||
483 | if (ws_removeremove(ff_path) < 0 && errno(*__errno_location ()) != ENOENT2) { | |||
484 | /* It failed for some reason other than "it's not there"; if | |||
485 | it's not there, we don't need to remove it, so we just | |||
486 | drive on. */ | |||
487 | report_failure("Error saving your %s file\nCouldn't remove \"%s\": %s.", | |||
488 | ff_description, ff_path, g_strerror(errno(*__errno_location ()))); | |||
489 | ws_unlinkunlink(ff_path_new); | |||
490 | g_free(ff_path_new); | |||
491 | g_free(ff_path); | |||
492 | return; | |||
493 | } | |||
494 | #endif | |||
495 | ||||
496 | if (ws_renamerename(ff_path_new, ff_path) < 0) { | |||
497 | report_failure("Error saving your %s file\nCouldn't rename \"%s\" to \"%s\": %s.", | |||
498 | ff_description, ff_path_new, ff_path, g_strerror(errno(*__errno_location ()))); | |||
499 | ws_unlinkunlink(ff_path_new); | |||
500 | g_free(ff_path_new); | |||
501 | g_free(ff_path); | |||
502 | return; | |||
503 | } | |||
504 | g_free(ff_path_new); | |||
505 | g_free(ff_path); | |||
506 | } |