| 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 <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) { | |||
| ||||
| 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 | } |