### Bug Summary

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

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name filter_files.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D BUILD_WSUTIL -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D wsutil_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/build/wsutil -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-truncation -Wno-format-nonliteral -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-10-13-100350-3623-1 -x c /builds/wireshark/wireshark/wsutil/filter_files.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* filter_files.c
2 | * Code for reading and writing the filters file.
3 | *
4 | * Wireshark - Network traffic analyzer
5 | * By Gerald Combs <gerald@wireshark.org>
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) {
1 | Control jumps to 'case DMACROS_LIST:' at line 130 | →
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;
2 | ← | Execution continues on line 140 | →
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)) {
3 | ← | Taking false branch | →
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++) {
4 | ← | Loop condition is true. Entering loop body | →
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))
5 | ← | Taking false branch | →
194 | break; /* Nothing more to read */
195 | if (c == '\n')
6 | ← | Taking false branch | →
196 | continue; /* Blank line. */
197 | if (c == '#') {
7 | ← | Assuming the condition is true | →
8 | ← | Taking true branch | →
198 | /* Comment. */
199 | while (c != '\n')
9 | ← | Loop condition is true. Entering loop body | →
11 | ← | Loop condition is true. Entering loop body | →
200 | c = getc(ff); /* skip to the end of the line */
10 | ← | Assuming this stream operation fails | →
12 | ← | File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
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 | }