/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Save.c */ /* */ /* Purpose: Save functions for the browser. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 04-Dec-96: Created. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "flex.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "window.h" #include "menu.h" #include "saveas.h" #include "svcprint.h" #include "Global.h" #include "Utils.h" #include "ChoiceDefs.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "Filetypes.h" #include "Fontmanage.h" #include "Hotlist.h" #include "Protocols.h" #include "URLutils.h" #include "Save.h" /* Static function prototypes */ static int save_save_string (FILE * file, char * string); static int save_save_colour (FILE * file, int colour); static int save_save_number (FILE * file, int number); static int save_save_yes_no (FILE * file, int yes); static int save_save_general (FILE * file, int how_many, const int numbers[], const char * strings[], int match, int def); /* Locals */ static char * last_path = NULL; /*************************************************/ /* save_record_path() */ /* */ /* Record the given pathname for future use. No */ /* errors are raised if the memory allocation to */ /* do this fails. */ /* */ /* Note that <Wimp$Scrap>... and */ /* <Wimp$ScrapDir>... are two exceptions - paths */ /* starting with these are not recorded. */ /* */ /* Parameters: Pointer to a pathname to record. */ /*************************************************/ void save_record_path(const char * path) { if (!path) return; /* Check for the special cases */ if (!strncmp(path, "<Wimp$Scrap>", 12) || !strncmp(path, "<Wimp$ScrapDir>", 15)) return; /* Otherwise, record the path, failing silently if */ /* we can't claim enough memory for it. */ free(last_path); last_path = malloc(strlen(path) + 1); if (last_path) strcpy(last_path, path); } /*************************************************/ /* save_return_last_path() */ /* */ /* Used mostly for filling in writables of save */ /* dialogues with paths based on previous saves. */ /* */ /* Returns: Pointer to the last pathname that */ /* was used for saving, or NULL if */ /* no such record is available. */ /*************************************************/ const char * save_return_last_path(void) { return last_path; } /*************************************************/ /* save_save_source() */ /* */ /* Saves the document source for a given browser */ /* to the given path, setting the filetype as */ /* HTML or text as appropriate. */ /* */ /* Parameters: Pointer to a null-terminated */ /* pathname to save to; */ /* */ /* Pointer to a browser_data struct */ /* owning the source to save. */ /*************************************************/ _kernel_oserror * save_save_source(char * path, browser_data * b) { _kernel_oserror * e; /* Sanity checks */ if (!b || !b->source || !path || !*path) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_save_source: %p, %p (source %p)", path, b, b ? b->source : NULL); return &erb; #else return NULL; #endif } save_record_path(path); /* Save the file - lock flex heap to ensure save transfer */ flex_set_budge(0); e = _swix(OS_File, _INR(0,2) | _INR(4,5), 10, /* Save block of memory as a typed file */ path, b->page_is_text ? FileType_TEXT : FileType_HTML, b->source, ((char *) b->source) + flex_size((flex_ptr) &b->source)); /* Unlock flex */ flex_set_budge(1); return e; } /*************************************************/ /* save_transfer_source() */ /* */ /* Save a browser's page source as an HTML file, */ /* through a RAM transfer buffer with */ /* Wimp_TransferBlock. */ /* */ /* Intended to be called as a response to a */ /* Message_RAMFetch from another task. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the document source; */ /* */ /* Pointer to an int, in which the */ /* amount of data transferred so */ /* far should be stored on entry; */ /* */ /* Pointer to the WimpMessage struct */ /* corresponding to the */ /* Message_RAMFetch that led to this */ /* function being called. */ /* */ /* Returns: The contents of the int holding */ /* the amount of data transferred */ /* prior to the function call are */ /* updated with the new amount */ /* transferred. Callers should use */ /* this in any future calls, and it */ /* *must* be checked for a value of */ /* -1, which indicates the transfer */ /* is complete (so any tidying up */ /* should be done if there is an */ /* error returned, or if the int is */ /* filled in with a value of -1). */ /* */ /* Assumes: The various pointers may be NULL, */ /* though if they are the function */ /* does nothing (it just exits). */ /*************************************************/ _kernel_oserror * save_transfer_source(browser_data * b, int * transferred, WimpMessage * m) { _kernel_oserror * e = NULL; int size = save_source_size(b); int left; int write; /* Sanity check */ if (!b || !b->source || !transferred || !m) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_transfer_source: %p (source %p), %p, %p", b, b ? b->source : NULL, transferred, m); return &erb; #else return NULL; #endif } left = size - *transferred; /* If we have data to transfer, do so */ if (left >= 0) { /* Use either the buffer size, or the bytes left, whichever */ /* is the smallest. */ write = left > m->data.ram_fetch.buffer_size ? m->data.ram_fetch.buffer_size : left; if (write) { /* Transfer the data - must lock the flex heap for the */ /* duration of this... */ flex_set_budge(0); e = wimp_transfer_block(task_handle, (char *) b->source + (*transferred), m->hdr.sender, m->data.ram_fetch.buffer, write); /* Unlock flex and report any errors */ flex_set_budge(1); if (e) return e; } /* If we have any data left to send, reply with a Message_RAMTransmit */ e = protocols_atats_send_ram_transmit(m, write, write < m->data.ram_fetch.buffer_size); /* Increment the transferred counter */ *transferred += write; left -= write; } /* Finished */ return e; } /*************************************************/ /* save_source_size() */ /* */ /* Returns the size that a given HTML source */ /* document would take on disc. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* owning the source to save. */ /* */ /* Returns: Size the file will be. */ /*************************************************/ int save_source_size(browser_data * b) { /* Sanity check */ if (!b || !b->source) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_source_size: %p (source %p)", b, b ? b->source : NULL); show_error_ret(&erb); #endif return 0; } /* For now this is trivially simple, but in future it */ /* could take account of, for example, inserting a */ /* <BASE> tag into the document. */ return flex_size((flex_ptr) &b->source); } /*************************************************/ /* save_save_object() */ /* */ /* Called when a fetch is to be spooled to disc. */ /* Handles saving as much data as is already */ /* fetched, and setting the relevant parts of */ /* the given browser_data structure up with the */ /* output file details. */ /* */ /* Parameters: Pointer to a null-terminated */ /* pathname to save to; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the object to save. */ /*************************************************/ _kernel_oserror * save_save_object(char * path, browser_data * b) { /* Sanity check */ if (!b || !path || !*path) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_save_object: %p, %p", path, b); return &erb; #else return NULL; #endif } save_record_path(path); /* If using a small fetch window, set the title to the save pathname */ if (b->small_fetch) { char title[Limits_Title]; StrNCpy0(title, path); /* Don't treat any errors here as fatal */ show_error_ret(window_set_title(0, b->self_id, title)); } /* Open the file */ b->save_file = fopen(path, "wb"); if (!b->save_file) { fetch_stop(b, 0); RetLastE; } else { int bytes; /* Set the filetype to DEADDEAD, to represent an incomplete */ /* file (particularly good on later Filers, which display */ /* a special sprite for this). */ _swix(OS_File, _INR(0,2), 2, /* Set load address */ path, 0xdeaddead); _swix(OS_File, _INR(0,1) | _IN(3), 3, /* Set exec address */ path, 0xdeaddead); if (b->source) { /* Any data in the source store represents already */ /* fetched bits of the file. Must lock flex down */ /* over the save to make sure the heap doesn't */ /* shift over the call to fwrite. */ flex_set_budge(0); bytes = fwrite(b->source, 1, flex_size((flex_ptr) &b->source), b->save_file); flex_set_budge(1); /* If we didn't transfer as much as we expected, complain */ if (bytes != flex_size((flex_ptr) &b->source)) { /* Report any errors */ fetch_stop(b, 0); RetLastE; } else { /* Otherwise, get rid of the data in the source store */ /* as it's been written to the file. */ flex_free((flex_ptr) &b->source); b->source = NULL; } } } return NULL; } /*************************************************/ /* save_object_size() */ /* */ /* Returns the estimated size that a given */ /* object will be after being spooled through */ /* the fetcher. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the object to save. */ /* */ /* Returns: *Estimated* size of the object. */ /*************************************************/ int save_object_size(browser_data * b) { /* For now, we just don't know this information... */ return 4096; } /*************************************************/ /* save_save_uri() */ /* */ /* Save the contents of a link as a URI file. */ /* */ /* Parameters: Pointer to a null-terminated */ /* pathname to save to; */ /* */ /* Pointer to the URI to save; */ /* */ /* Pointer to the URL title, or NULL */ /* / null string for no title; */ /* */ /* 0 to save as a URI file, else */ /* save as a non-terminated string */ /* with type FileType_URL (ANT */ /* suite URL file). */ /*************************************************/ _kernel_oserror * save_save_uri(char * path, char * url, char * title, int write_url) { _kernel_oserror * e = NULL; FILE * file; /* Sanity check */ if (title && !*title) title = NULL; if (!path || !*path || !url || !*url) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_save_uri: %p, %p, %d", path, url, write_url); return &erb; #else return NULL; #endif } save_record_path(path); /* Try and open the file */ file = fopen(path, "wb"); if (!file) RetLastE; /* Write the contents */ if (!write_url) { /* URI file */ if ( fprintf(file, "URI\t100\n\t# %s v", lookup_token("_TaskName",1,0)) < 0 ) erb = *_kernel_last_oserror(), e = &erb; else if ( fprintf(file, "%s\n\n\t%s", lookup_token("Version:(Unknown!)",0,0), url) < 0 ) erb = *_kernel_last_oserror(), e = &erb; else if ( fprintf(file, "\n\t%s", title ? title : "*") < 0 ) erb = *_kernel_last_oserror(), e = &erb; } else { /* URL file */ if (fprintf(file, url) < 0) erb = *_kernel_last_oserror(), e = &erb; } /* Close the file and return any error that may have */ /* happened during the file writing stage */ fclose(file); if (e) return &erb; /* Exit via. setting the filetype */ return _swix(OS_File, _INR(0,2), 18, /* Set type of named object */ path, write_url ? FileType_URL : FileType_URI); } /*************************************************/ /* save_transfer_uri() */ /* */ /* Save the contents of a link as a URI file, */ /* through a RAM transfer buffer with */ /* Wimp_TransferBlock. */ /* */ /* Intended to be called as a response to a */ /* Message_RAMFetch from another task. */ /* */ /* Parameters: Pointer to the URL to save; */ /* */ /* Pointer to the URL title, or NULL */ /* / null string for no title; */ /* */ /* 0 to save as a URI file, else */ /* save as a non-terminated string */ /* with type FileType_URL (ANT */ /* suite URL file); */ /* */ /* Pointer to an int, in which the */ /* amount of data transferred so */ /* far should be stored on entry; */ /* */ /* Pointer to the WimpMessage struct */ /* corresponding to the */ /* Message_RAMFetch that led to this */ /* function being called. */ /* */ /* Returns: The contents of the int holding */ /* the amount of data transferred */ /* prior to the function call are */ /* updated with the new amount */ /* transferred. Callers should use */ /* this in any future calls, and it */ /* *must* be checked for a value of */ /* -1, which indicates the transfer */ /* is complete (so any tidying up */ /* should be done if there is an */ /* error returned, or if the int is */ /* filled in with a value of -1). */ /* */ /* Assumes: The various pointers may be NULL, */ /* though if they are the function */ /* does nothing (it just exits). */ /*************************************************/ _kernel_oserror * save_transfer_uri(char * url, char * title, int write_url, int * transferred, WimpMessage * m) { _kernel_oserror * e = NULL; char * uri_file = NULL; int size = save_uri_size(url, title, write_url); int left; int write; /* Sanity check */ if (title && !*title) title = NULL; if (!url || !*url || !transferred || !m) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_transfer_uri: %p, %d, %p, %p", url, write_url, transferred, m); return &erb; #else return NULL; #endif } left = size - *transferred; /* Each time the function is called, rebuild the URI file in a */ /* temporary buffer. */ if (size > 0 && left > 0) { uri_file = malloc(size + 1); /* + 1 to account for terminators */ if (!uri_file) { e = make_no_memory_error(10); goto save_transfer_uri_exit; } /* Build the URI or URL file */ if (!write_url) { int written; /* URI file */ written = sprintf(uri_file, "URI\t100\n\t# %s v", lookup_token("_TaskName",1,0)); written += sprintf(uri_file + written, "%s\n\n\t%s", lookup_token("Version:(Unknown!)",0,0), url); written += sprintf(uri_file + written, "\n\t%s", title ? title : "*"); } else { /* URL file */ strcpy(uri_file, url); } } /* If we have data to transfer, do so */ if (left >= 0) { /* Use either the buffer size, or the bytes left, whichever */ /* is the smallest. */ write = left > m->data.ram_fetch.buffer_size ? m->data.ram_fetch.buffer_size : left; if (write) { /* Transfer the data */ e = wimp_transfer_block(task_handle, uri_file + (*transferred), m->hdr.sender, m->data.ram_fetch.buffer, write); if (e) goto save_transfer_uri_exit; } /* If we have any data left to send, reply with a Message_RAMTransmit */ e = protocols_atats_send_ram_transmit(m, write, write < m->data.ram_fetch.buffer_size); /* Increment the transferred counter */ *transferred += write; left -= write; } /* Finished */ save_transfer_uri_exit: /* Free the URI file buffer, if we claimed one */ if (uri_file) free (uri_file); return e; } /*************************************************/ /* save_uri_size() */ /* */ /* Returns the size, in bytes, that a URI or URL */ /* file will be. */ /* */ /* Parameters: Pointer to the URL that would be */ /* saved; */ /* */ /* Pointer to the URL title, or NULL */ /* / null string for no title; */ /* */ /* 0 to find the length of a URI */ /* file, 1 to find the length of a */ /* URL file (as in save_save_uri). */ /* */ /* Returns: The size, in bytes, that the file */ /* will be. */ /*************************************************/ int save_uri_size(char * url, char * title, int write_url) { int len = 0; /* Sanity check */ if (title && !*title) title = NULL; if (!url || !*url) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Invalid parameters to save_uri_size: %p, %d", url, write_url); show_error_ret(&erb); #endif return 0; } if (!write_url) { /* URI file */ len = strlen("URI\t100\n\t# "); len += strlen(lookup_token("_TaskName",1,0)); len += strlen(" v"); len += strlen(lookup_token("Version:(Unknown!)",0,0)); len += strlen("\n\n\t"); len += strlen(url); len += strlen("\n\t"); len += strlen(title ? title : "*"); } else { /* URL file */ len = strlen(url); /* (No terminator needed in URL files, so no '+ 1') */ } return len; } /*************************************************/ /* save_build_messages_path() */ /* */ /* Builds a pathname in a malloc block through */ /* which the Choices or Controls file may be */ /* accessed. The caller is responsible for */ /* freeing the block. */ /* */ /* Parameters: 0 for the Choices file (to load), */ /* 1 for the Controls file (to */ /* load), 2 for the Choices file (to */ /* save), 3 for the Controls file */ /* (to save). */ /* */ /* Returns: Pointer to a null-terminated */ /* pathname, in a malloc block, or */ /* NULL if there was an error. */ /*************************************************/ char * save_build_messages_path(int which) { int len, exists; char * path; char * sysvar; char * defpath; len = strlen(lookup_token("_TaskName", 1, 0)); len += sizeof("$ControlsFile"); /* Longer than "$ChoicesFile"! */ sysvar = malloc(len); if (!sysvar) return NULL; else strcpy(sysvar, tokens); switch (which) { case 0: { strcat(sysvar, "$ChoicesFile"); defpath = ".Choices"; } break; case 1: { strcat(sysvar, "$ControlsFile"); defpath = ".Controls"; } break; case 2: { strcat(sysvar, "$ChoicesSave"); defpath = ".Choices"; } break; case 3: { strcat(sysvar, "$ControlsSave"); defpath = ".Controls"; } break; default: { #ifdef TRACE erb.errnum = Utils_Error_Custom_Fatal; sprintf(erb.errmess, "In open_messages_file, passed unrecognised parameter value '%d'", which); /* OK, so you'll get the above error and then callers will */ /* probably think this call ran out of memory due to the */ /* NULL return (assuming the below call doesn't cause an */ /* immediate exit - it depends on what stage of */ /* initialisation the browser is at). But at least you get */ /* to see what is happening. */ show_error_ret(&erb); free(sysvar); return NULL; #else free(sysvar); return NULL; #endif } } /* First, find the system variable length. Must use _kernel_swi */ /* here for various reasons. */ { _kernel_swi_regs r; r.r[0] = (int) sysvar; r.r[1] = (int) NULL; r.r[2] = -1; r.r[3] = 0; r.r[4] = 0; /* Equivalent to getenv(), but the RISC OS implementation evaluates */ /* the system variable as an expression which we don't want (at */ /* least, not under RISC OS); hence the direct use of the SWI. */ _kernel_swi(OS_ReadVarVal, &r, &r); len = -r.r[2]; /* This includes a terminator */ } if (!len) { /* Variable doesn't exist */ len = strlen(task_dir) + strlen(defpath) + 1; exists = 0; } else exists = 1; /* Allocate space */ path = calloc(len + 1, 1); if (!path) { free(sysvar); return NULL; } /* Read the variable or copy the data in */ if (exists) _swix(OS_ReadVarVal, _INR(0,4), sysvar, /* Variable name */ path, /* Buffer */ len, /* Size of buffer */ 0, /* Name pointer (0 for 1st call) */ 4); /* Variable type (4 = literal string) */ else { strcpy(path, task_dir); strcat(path, defpath); } free(sysvar); return path; } /*************************************************/ /* save_save_string() */ /* */ /* Output a string */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* String number to write. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /*************************************************/ static int save_save_string(FILE * file, char * string) { int length; if ((length = strlen(string)) == 0) return 0; if (fprintf(file, "%s", string) < length) return EOF; return length; } /*************************************************/ /* save_save_colour() */ /* */ /* Output a colour number in the format seen in */ /* the Choices file, to a given file. */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* Colour number to write. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /*************************************************/ static int save_save_colour(FILE * file, int colour) { if (fprintf(file, "0x%08x", colour) < 10) return EOF; return 10; } /*************************************************/ /* save_save_number() */ /* */ /* Output a number as ASCII, to a given file. */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* Number to write. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /*************************************************/ static int save_save_number(FILE * file, int number) { char buffer[64]; int len; len = sprintf(buffer, "%d", number); if (fprintf(file, "%s", buffer) < len) return EOF; return len; } /*************************************************/ /* save_save_yes_no() */ /* */ /* Output either 'yes' or 'no' to a given file. */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* 0 to write 'no', else 'yes'. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /*************************************************/ static int save_save_yes_no(FILE * file, int yes) { if (!yes) { if (fprintf(file, "no") < 2) return EOF; } else { if (fprintf(file, "yes") < 3) return EOF; } return !yes ? 2 : 3; } /*************************************************/ /* save_save_font() */ /* */ /* Saves a typeface definition in the form */ /* required by the browser font manager. */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* The name of the typeface to save. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /* */ /* Assumes: No sanity checking is given on */ /* any of the supplied parameters... */ /*************************************************/ static int save_save_font(FILE * file, char * typefacename) { fm_typeface * tfptr; tfptr = fm_find_typeface(typefacename); if (!tfptr) return EOF; return fprintf(file, "%s=%s:%s:%s:%s;%s", tfptr->name, tfptr->fontnames[0], tfptr->fontnames[1], tfptr->fontnames[2], tfptr->fontnames[3], tfptr->alternative); } /*************************************************/ /* save_save_general() */ /* */ /* Save a general item to the given file. This */ /* is for items with a discrete set of values */ /* that map to a discrete set of strings. */ /* */ /* Parameters: Pointer to a FILE struct holding */ /* info on the file to write to; */ /* */ /* The number of discrete values */ /* that the item may take; */ /* */ /* Pointer to an array of the above */ /* size holding the values; */ /* */ /* Pointer to an array of (char *)'s */ /* pointing to the strings that map */ /* to the values in the int array; */ /* */ /* Value to match out of those given */ /* in the int array; */ /* */ /* Default item (from 1 to the value */ /* given in the second parameter) to */ /* use if the match value is not */ /* found in the int array. */ /* */ /* Returns: Number of characters written, or */ /* EOF if there was an error. */ /* */ /* Assumes: No sanity checking is given on */ /* any of the supplied parameters... */ /*************************************************/ static int save_save_general(FILE * file, int how_many, const int numbers[], const char * strings[], int match, int def) { int i, found = 0; const char * string = NULL; int sl; /* Try to find the match value in the array of ints */ for (i = 0; i < how_many && !found; i++) { if (numbers[i] == match) string = strings[i], found = 1; } /* If not found, raise an error in TRACE builds, and pick */ /* the given default string */ if (!found) { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Match value '%d' not found in save_save_general (", match); for ( i = 0; i < how_many && strlen(erb.errmess) + strlen(strings[i]) + 9 < sizeof(erb.errmess); i++ ) { strcat(erb.errmess, strings[i]); if (i != how_many - 1) strcat(erb.errmess, ", "); if (i == how_many - 2) strcat(erb.errmess, "or "); } strcat(erb.errmess, ")."); show_error_ret(&erb); #endif if (def > how_many) def = how_many; string = strings[def - 1]; } /* Output the item to the file */ if (!string) return EOF; sl = strlen(string); if (fprintf(file, "%s", string) < sl) return EOF; return sl; } /*************************************************/ /* save_save_choices() */ /* */ /* Saves out the Choices file to either the */ /* given path or to the path constructed in */ /* save_build_messages_page. Uses the global */ /* Choices block as the source of what should be */ /* written. */ /* */ /* Parameters: Pointer to a null-terminated path */ /* to write the file to, or NULL for */ /* default (see description above). */ /*************************************************/ _kernel_oserror * save_save_choices(char * path) { _kernel_oserror * e = NULL; FILE * file = NULL; char * out = NULL; char * canonicalised_out = NULL; char * p = NULL; /* Moves through the file loaded into a malloc block. */ char * op = NULL; /* Remembers the start address of the block. */ char * en; /* Remembers the end address of the block. */ int size; int type; #ifdef TRACE if (tl & (1u<<26)) Printf("save_save_choices: Called\n"); #endif /* Find out where to get the file from */ if (path && *path) out = path; else out = save_build_messages_path(2); if (!out) return make_no_memory_error(14); /* Now ensure the path is there */ (utils_canonicalise_path(out, &canonicalised_out)); /* Can get rid of 'out' if we allocated it locally */ if (!path || !*path) { free(out); out = NULL; } if (!canonicalised_out) return make_no_memory_error(14); #ifdef TRACE if (tl & (1u<<26)) Printf("save_save_choices: Working with file '%s'\n", canonicalised_out); #endif /* Ensure this path is available */ e = utils_build_tree(canonicalised_out); if (e) goto save_save_choices_read_error; /* Find out how big the file is - for this of course, use */ /* the *load* path, not the canonicalised save path */ { char * load_path = save_build_messages_path(0); if (!load_path) { free(canonicalised_out); return make_no_memory_error(14); } e = _swix(OS_File, _INR(0,1) | _OUT(0) | _OUT(4), 17, load_path, &type, &size); /* Throw the local path away - want to minimise how many */ /* malloc blocks we're juggling for fear of opening up a */ /* memory leak in here */ free(load_path); if (e || type != 1) goto save_save_choices_read_error; } /* Allocate memory for the file */ p = malloc(size + 1); if (!p) { free(canonicalised_out); return make_no_memory_error(14); } en = p + size; op = p; /* Read the file into memory - again, use the load path */ /* not the save path. Rebuild this path to avoid having */ /* any more malloc blocks flying about (see comments */ /* earlier) - it doesn't take long to do this, and the */ /* function isn't speed critical anyway. */ { char * load_path = save_build_messages_path(0); if (!load_path) { free(canonicalised_out); free(op); return make_no_memory_error(14); } e = _swix(OS_File, _INR(0,3), 16, load_path, p, 0); free(load_path); if (e) goto save_save_choices_read_error; } /* Force a null terminator at the end, so strchr() calls */ /* (or equivalent code fragments) don't run off the end */ /* of the block. */ p[size] = 0; /* Open it for writing */ file = fopen(canonicalised_out, "wb"); if (!file) goto save_save_choices_write_error; /* Use the original file in memory as a reference for */ /* writing the new one. */ do { /* Comments or white space */ if (*p == '#' || *p < ' ') { /* Output until an end of line marker */ while (*p >= ' ') { if (fputc(*p++, file) == EOF) goto save_save_choices_write_error; } /* Output any control characters - we thus preserve */ /* CR, LF, CR+LF or whatever was in the original file */ while (*p < ' ' && p < en) { if (fputc(*p++, file) == EOF) goto save_save_choices_write_error; } } /* If not comments or white space, must have a token */ else { char * token_end; int len; token_end = p; /* Search for a ':', but don't go off the end of a line */ while (*token_end > 31 && *token_end != ':') token_end++; len = token_end - p; /* Doesn't include ':' in the count */ if (*token_end == ':' && len) { int i, result = 0; for (i = 0; i <= len; i++) /* '<=' to include the ':' */ { if (fputc(p[i], file) == EOF) goto save_save_choices_write_error; } /* Now it just gets tedious...! */ if (!strncmp(p, "HomePage", len)) result = save_save_string(file, choices.home_page); else if (!strncmp(p, "BackColour", len)) result = save_save_colour(file, choices.background_colour); else if (!strncmp(p, "TextColour", len)) result = save_save_colour(file, choices.text_colour); else if (!strncmp(p, "LinkColour", len)) result = save_save_colour(file, choices.link_colour); else if (!strncmp(p, "UsedColour", len)) result = save_save_colour(file, choices.used_colour); else if (!strncmp(p, "FollColour", len)) result = save_save_colour(file, choices.followed_colour); else if (!strncmp(p, "SeleColour", len)) result = save_save_colour(file, choices.selected_colour); else if (!strncmp(p, "SupportTables", len)) result = save_save_yes_no(file, choices.support_tables); else if (!strncmp(p, "TableOuter", len)) { static const char * s[] = { "2d", "auto", "3d", "never" }; static const int v[] = { Choices_TableOuter_Always2D, Choices_TableOuter_Auto, Choices_TableOuter_Always3D, Choices_TableOuter_Never }; result = save_save_general(file, 4, v, s, choices.table_outer, 3); } else if (!strncmp(p, "TableInner", len)) { static const char * s[] = { "2d", "auto", "3d", "never" }; static const int v[] = { Choices_TableInner_Always2D, Choices_TableInner_Auto, Choices_TableInner_Always3D, Choices_TableInner_Never }; result = save_save_general(file, 4, v, s, choices.table_inner, 3); } else if (!strncmp(p, "FontSize", len)) result = save_save_number(file, choices.font_size); else if (!strncmp(p, "TTAspect", len)) result = save_save_number(file, choices.tt_aspect); else if (!strncmp(p, "SystemFont", len)) result = save_save_yes_no(file, choices.system_font); else if (!strncmp(p, "Typeface1", len)) result = save_save_font(file, "fixed"); else if (!strncmp(p, "Typeface2", len)) result = save_save_font(file, "sans"); else if (!strncmp(p, "Typeface3", len)) result = save_save_font(file, "serif"); else if (!strncmp(p, "Encoding", len)) result = save_save_number(file, choices.encoding); else if (!strncmp(p, "UnderlineLinks", len)) result = save_save_yes_no(file, choices.underline_links); else if (!strncmp(p, "UseSourceCols", len)) result = save_save_yes_no(file, choices.use_source_cols); else if (!strncmp(p, "ShowForeground", len)) result = save_save_yes_no(file, choices.show_foreground); else if (!strncmp(p, "ShowBackground", len)) result = save_save_yes_no(file, choices.show_background); else if (!strncmp(p, "LeftMargin", len)) result = save_save_number(file, choices.left_margin); else if (!strncmp(p, "RightMargin", len)) result = save_save_number(file, choices.right_margin); else if (!strncmp(p, "QuoteMargin", len)) result = save_save_number(file, choices.quote_margin); else if (!strncmp(p, "Leading", len)) result = save_save_number(file, choices.leading); else if (!strncmp(p, "LeftIndent", len)) result = save_save_number(file, choices.left_indent); else if (!strncmp(p, "MaxImages", len)) result = save_save_number(file, choices.maximages); else if (!strncmp(p, "ClientPull", len)) result = save_save_yes_no(file, choices.client_pull); else if (!strncmp(p, "SupportFrames", len)) result = save_save_yes_no(file, choices.support_frames); else if (!strncmp(p, "SupportObject", len)) result = save_save_yes_no(file, choices.support_object); else if (!strncmp(p, "PlugInControl", len)) { static const char * s[] = { "never", "viewed", "asap" }; static const int v[] = { Choices_PlugIns_Never, Choices_PlugIns_Viewed, Choices_PlugIns_ASAP }; result = save_save_general(file, 3, v, s, choices.plugin_control, 3); } else if (!strncmp(p, "SeeFetches", len)) result = save_save_yes_no(file, choices.see_fetches); else if (!strncmp(p, "SaveHotlist", len)) { static const char * s[] = { "never", "once", "always" }; static const int v[] = { Choices_SaveHotlist_Never, Choices_SaveHotlist_Once, Choices_SaveHotlist_Always }; result = save_save_general(file, 3, v, s, choices.save_hotlist, 3); } else if (!strncmp(p, "AddHotlist", len)) { static const char * s[] = { "top", "bottom" }; static const int v[] = { Choices_AddHotlist_Top, Choices_AddHotlist_Bottom }; result = save_save_general(file, 2, v, s, choices.add_hotlist, 2); } else if (!strncmp(p, "HotlistType", len)) { static const char * s[] = { "urls", "descriptions" }; static const int v[] = { Choices_HotlistType_URLs, Choices_HotlistType_Descriptions }; result = save_save_general(file, 2, v, s, choices.hotlist_show, 2); } else if (!strncmp(p, "AutoOpenDelay", len)) result = save_save_number(file, choices.auto_open_delay); else if (!strncmp(p, "AutoScrollDelay", len)) result = save_save_number(file, choices.auto_scroll_delay); else if (!strncmp(p, "AutoScrollMargin", len)) result = save_save_number(file, choices.auto_scroll_margin); else if (!strncmp(p, "MaxSize", len)) result = save_save_number(file, choices.max_size / 1024); /* (Convert bytes to K) */ else if (!strncmp(p, "ImageMaxSize", len)) result = save_save_number(file, choices.image_max_size / 1024); else if (!strncmp(p, "ExpiryAge", len)) result = save_save_number(file, choices.expiry_age); else if (!strncmp(p, "ImageExpiryAge", len)) result = save_save_number(file, choices.image_expiry_age); else if (!strncmp(p, "ShowURLs", len)) result = save_save_yes_no(file, choices.show_urls); else if (!strncmp(p, "SaveHistory", len)) { static const char * s[] = { "never", "once", "always" }; static const int v[] = { Choices_SaveHistory_Never, Choices_SaveHistory_Once, Choices_SaveHistory_Always }; result = save_save_general(file, 3, v, s, choices.save_history, 3); } else if (!strncmp(p, "SaveImageHistory", len)) { static const char * s[] = { "never", "once", "always" }; static const int v[] = { Choices_SaveImageHistory_Never, Choices_SaveImageHistory_Once, Choices_SaveImageHistory_Always }; result = save_save_general(file, 3, v, s, choices.save_image_history, 3); } else if (!strncmp(p, "URLbar", len)) result = save_save_yes_no(file, choices.url_bar); else if (!strncmp(p, "ButtonBar", len)) result = save_save_yes_no(file, choices.button_bar); else if (!strncmp(p, "StatusBar", len)) result = save_save_yes_no(file, choices.status_bar); else if (!strncmp(p, "MoveGadgets", len)) { static const char * s[] = { "never", "atend", "during" }; static const int v[] = { Choices_MoveGadgets_Never, Choices_MoveGadgets_AtEnd, Choices_MoveGadgets_During }; result = save_save_general(file, 3, v, s, choices.move_gadgets, 3); } else if (!strncmp(p, "Width", len)) result = save_save_number(file, choices.width); else if (!strncmp(p, "Height", len)) result = save_save_number(file, choices.height); else if (!strncmp(p, "OverrideX", len)) result = save_save_number(file, choices.override_x); else if (!strncmp(p, "OverrideY", len)) result = save_save_number(file, choices.override_y); else if (!strncmp(p, "SolidResize", len)) { static const char * s[] = { "no", "yes", "always" }; static const int v[] = { Choices_SolidResize_No, Choices_SolidResize_Yes, Choices_SolidResize_Always }; result = save_save_general(file, 3, v, s, choices.solid_resize, 3); } else if (!strncmp(p, "FullScreen", len)) result = save_save_yes_no(file, choices.full_screen); else if (!strncmp(p, "HScroll", len)) { static const char * s[] = { "no", "yes", "auto" }; static const int v[] = { Choices_HScroll_No, Choices_HScroll_Yes, Choices_HScroll_Auto }; result = save_save_general(file, 3, v, s, choices.h_scroll, 3); } else if (!strncmp(p, "VScroll", len)) { static const char * s[] = { "no", "yes", "auto" }; static const int v[] = { Choices_VScroll_No, Choices_VScroll_Yes, Choices_VScroll_Auto }; result = save_save_general(file, 3, v, s, choices.v_scroll, 3); } else if (!strncmp(p, "RefoWait", len)) result = save_save_yes_no(file, choices.refo_wait); else if (!strncmp(p, "RefoHang", len)) result = save_save_yes_no(file, choices.refo_hang); else if (!strncmp(p, "RefoTime", len)) result = save_save_number(file, choices.refo_time); else if (!strncmp(p, "FixedPtr", len)) result = save_save_yes_no(file, choices.fixed_pointer); else if (!strncmp(p, "HighlightLks", len)) result = save_save_yes_no(file, choices.highlight_links); else if (!strncmp(p, "KeyboardCtl", len)) result = save_save_yes_no(file, choices.keyboard_ctrl); else if (!strncmp(p, "Clone", len)) result = save_save_yes_no(file, choices.clone); else if (!strncmp(p, "UseProxy", len)) result = save_save_yes_no(file, choices.use_proxy); else if (!strncmp(p, "ProxyAddress", len)) result = save_save_string(file, choices.proxy_address); else if (!strncmp(p, "StartProxy", len)) result = save_save_yes_no(file, choices.start_proxy); #ifndef SINGLE_USER else if (!strncmp(p, "PostIn", len)) result = save_save_string(file, choices.post_in); else if (!strncmp(p, "PostOut", len)) result = save_save_string(file, choices.post_out); #endif else { /* Token we don't understand - output the rest of the line */ #ifdef TRACE if (tl & (1u<<26)) { Printf("save_save_choices: Unrecognised token '"); for (i = 0; i <= len; i++) { Printf("%c", p[i]); } Printf("'\n"); } #endif p += len + 1; while (*p >= ' ' && result != EOF) result = fputc(*p++, file); } if (result == EOF) goto save_save_choices_write_error; /* Skip to the end of the line */ while (*p >= ' ') p++; /* Output any control characters - we thus preserve */ /* CR, LF, CR+LF or whatever was in the original file */ while (*p < ' ' && p < en) { if (fputc(*p++, file) == EOF) goto save_save_choices_write_error; } } else { /* Unrecognised line contents; spit out the whole line */ #ifdef TRACE if (tl & (1u<<26)) { int i = 0; Printf("save_save_choices: Unknown line fragment '"); while (p[i] >= ' ') { Printf("%c", p[i++]); } Printf("'\n"); } #endif while (*p >= ' ') { if (fputc(*p++, file) == EOF) goto save_save_choices_write_error; } } } } while (p < en); /* Close the output file */ fclose(file); /* Set the filetype - not essential, just nice to have, so */ /* let it fail silently. */ _swix(OS_File, _INR(0,2), 18, /* Set type of named object */ canonicalised_out, FileType_TEXT); /* Free the canonicalised path */ free(canonicalised_out); /* Free the old Choices file */ free(op); /* Finished */ #ifdef TRACE if (tl & (1u<<26)) Printf("save_save_choices: Successful\n"); #endif return NULL; /* Error condition exits */ save_save_choices_read_error: if (!path || !*path) free(out); free(op); free(canonicalised_out); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("COChoices:Can't open the Choices file - the preferences cannot be saved.", 0, 0)); #ifdef TRACE if (tl & (1u<<26)) Printf("save_save_choices: Can't open Choices file, exitting\n"); #endif return &erb; save_save_choices_write_error: erb = *_kernel_last_oserror(); if (file) fclose(file); /* We really want to avoid leaving a broken Choices file, so */ /* have a last ditch go at dumping the original file down. */ _swix(OS_File, _INR(0,2) | _INR(4,5), 10, out, FileType_TEXT, op, en); /* Free any temporary blocks */ if (!path || !*path) free(out); free(op); free(canonicalised_out); #ifdef TRACE if (tl & (1u<<26)) Printf("save_save_choices: Exitting with error\n"); #endif return &erb; }