1 /*
2 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
3 * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net>
4 * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
5 * Copyright 2005 Richard Wilson <info@tinct.net>
6 * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
7 * Copyright 2004-2009 John Tytgat <joty@netsurf-browser.org>
8 *
9 * This file is part of NetSurf, http://www.netsurf-browser.org/
10 *
11 * NetSurf is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * NetSurf is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include <stdbool.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <unixlib/local.h>
30 #include <fpu_control.h>
31 #include <oslib/help.h>
32 #include <oslib/uri.h>
33 #include <oslib/inetsuite.h>
34 #include <oslib/pdriver.h>
35 #include <oslib/osfile.h>
36 #include <oslib/hourglass.h>
37 #include <oslib/osgbpb.h>
38 #include <oslib/osbyte.h>
39 #include <oslib/osmodule.h>
40 #include <oslib/osfscontrol.h>
41
42 #include "utils/utils.h"
43 #include "utils/nsoption.h"
44 #include "utils/log.h"
45 #include "utils/messages.h"
46 #include "utils/file.h"
47 #include "utils/filename.h"
48 #include "utils/url.h"
49 #include "utils/corestrings.h"
50 #include "netsurf/fetch.h"
51 #include "netsurf/misc.h"
52 #include "netsurf/content.h"
53 #include "netsurf/netsurf.h"
54 #include "netsurf/browser_window.h"
55 #include "netsurf/cookie_db.h"
56 #include "netsurf/url_db.h"
57 #include "desktop/save_complete.h"
58 #include "desktop/hotlist.h"
59 #include "content/backing_store.h"
60
61 #include "riscos/gui.h"
62 #include "riscos/bitmap.h"
63 #include "riscos/wimputils.h"
64 #include "riscos/hotlist.h"
65 #include "riscos/buffer.h"
66 #include "riscos/textselection.h"
67 #include "riscos/print.h"
68 #include "riscos/save.h"
69 #include "riscos/dialog.h"
70 #include "riscos/wimp.h"
71 #include "riscos/message.h"
72 #include "riscos/help.h"
73 #include "riscos/query.h"
74 #include "riscos/window.h"
75 #include "riscos/iconbar.h"
76 #include "riscos/local_history.h"
77 #include "riscos/global_history.h"
78 #include "riscos/cookies.h"
79 #include "riscos/pageinfo.h"
80 #include "riscos/wimp_event.h"
81 #include "riscos/uri.h"
82 #include "riscos/url_protocol.h"
83 #include "riscos/mouse.h"
84 #include "riscos/ucstables.h"
85 #include "riscos/filetype.h"
86 #include "riscos/font.h"
87 #include "riscos/toolbar.h"
88 #include "riscos/content-handlers/artworks.h"
89 #include "riscos/content-handlers/draw.h"
90 #include "riscos/content-handlers/sprite.h"
91
92 bool riscos_done = false;
93
94 extern bool ro_plot_patterned_lines;
95
96 int os_version = 0;
97
98 const char * const __dynamic_da_name = "NetSurf"; /**< For UnixLib. */
99 int __dynamic_da_max_size = 128 * 1024 * 1024; /**< For UnixLib. */
100 int __feature_imagefs_is_file = 1; /**< For UnixLib. */
101 /* default filename handling */
102 int __riscosify_control = __RISCOSIFY_NO_SUFFIX |
103 __RISCOSIFY_NO_REVERSE_SUFFIX;
104 #ifndef __ELF__
105 extern int __dynamic_num;
106 #endif
107
108 const char * NETSURF_DIR;
109
110 static const char *task_name = "NetSurf";
111 #define CHOICES_PREFIX "<Choices$Write>.WWW.NetSurf."
112
113 ro_gui_drag_type gui_current_drag_type;
114 wimp_t task_handle; /**< RISC OS wimp task handle. */
115 static clock_t gui_last_poll; /**< Time of last wimp_poll. */
116 osspriteop_area *gui_sprites; /**< Sprite area containing pointer and hotlist sprites */
117
118 #define DIR_SEP ('.')
119
120 /**
121 * Accepted wimp user messages.
122 */
123 static ns_wimp_message_list task_messages = {
124 message_HELP_REQUEST,
125 {
126 message_DATA_SAVE,
127 message_DATA_SAVE_ACK,
128 message_DATA_LOAD,
129 message_DATA_LOAD_ACK,
130 message_DATA_OPEN,
131 message_PRE_QUIT,
132 message_SAVE_DESKTOP,
133 message_MENU_WARNING,
134 message_MENUS_DELETED,
135 message_WINDOW_INFO,
136 message_CLAIM_ENTITY,
137 message_DATA_REQUEST,
138 message_DRAGGING,
139 message_DRAG_CLAIM,
140 message_MODE_CHANGE,
141 message_PALETTE_CHANGE,
142 message_FONT_CHANGED,
143 message_URI_PROCESS,
144 message_URI_RETURN_RESULT,
145 message_INET_SUITE_OPEN_URL,
146 #ifdef WITH_PLUGIN
147 message_PLUG_IN_OPENING,
148 message_PLUG_IN_CLOSED,
149 message_PLUG_IN_RESHAPE_REQUEST,
150 message_PLUG_IN_FOCUS,
151 message_PLUG_IN_URL_ACCESS,
152 message_PLUG_IN_STATUS,
153 message_PLUG_IN_BUSY,
154 message_PLUG_IN_STREAM_NEW,
155 message_PLUG_IN_STREAM_WRITE,
156 message_PLUG_IN_STREAM_WRITTEN,
157 message_PLUG_IN_STREAM_DESTROY,
158 message_PLUG_IN_OPEN,
159 message_PLUG_IN_CLOSE,
160 message_PLUG_IN_RESHAPE,
161 message_PLUG_IN_STREAM_AS_FILE,
162 message_PLUG_IN_NOTIFY,
163 message_PLUG_IN_ABORT,
164 message_PLUG_IN_ACTION,
165 /* message_PLUG_IN_INFORMED, (not provided by oslib) */
166 #endif
167 message_PRINT_SAVE,
168 message_PRINT_ERROR,
169 message_PRINT_TYPE_ODD,
170 message_HOTLIST_ADD_URL,
171 message_HOTLIST_CHANGED,
172 0
173 }
174 };
175
176
177 static struct
178 {
179 int width; /* in OS units */
180 int height;
181 } screen_info;
182
183
184 /**
185 * Callback to translate resource to full url for RISC OS.
186 *
187 * Transforms a resource: path into a full URL. The returned URL is
188 * used as the target for a redirect. The caller takes ownership of
189 * the returned nsurl including unrefing it when finished with it.
190 *
191 * \param path The path of the resource to locate.
192 * \return A string containing the full URL of the target object or
193 * NULL if no suitable resource can be found.
194 */
gui_get_resource_url(const char * path)195 static nsurl *gui_get_resource_url(const char *path)
196 {
197 static const char base_url[] = "file:///NetSurf:/Resources/";
198 const char *lang;
199 size_t path_len, length;
200 char *raw;
201 nsurl *url = NULL;
202
203 /* Map paths first */
204 if (strcmp(path, "adblock.css") == 0) {
205 path = "AdBlock";
206
207 } else if (strcmp(path, "default.css") == 0) {
208 path = "CSS";
209
210 } else if (strcmp(path, "quirks.css") == 0) {
211 path = "Quirks";
212
213 } else if (strcmp(path, "favicon.ico") == 0) {
214 path = "Icons/content.png";
215
216 } else if (strcmp(path, "user.css") == 0) {
217 /* Special case; this file comes from Choices: */
218 nsurl_create("file:///Choices:WWW/NetSurf/User", &url);
219 return url;
220 }
221
222 path_len = strlen(path);
223
224 lang = ro_gui_default_language();
225
226 /* Find max URL length */
227 length = SLEN(base_url) +
228 strlen(lang) + 1 + /* <lang> + / */
229 path_len + 1; /* + NUL */
230
231 raw = malloc(length);
232 if (raw != NULL) {
233 /* Insert base URL */
234 char *ptr = memcpy(raw, base_url, SLEN(base_url));
235 ptr += SLEN(base_url);
236
237 /* Add language directory to URL, for translated files */
238 /* TODO: handle non-html translated files */
239 if (path_len > SLEN(".html") &&
240 strncmp(path + path_len - SLEN(".html"),
241 ".html", SLEN(".html")) == 0) {
242 ptr += sprintf(ptr, "%s/", lang);
243 }
244
245 /* Add filename to URL */
246 memcpy(ptr, path, path_len);
247 ptr += path_len;
248
249 /* Terminate string */
250 *ptr = '\0';
251
252 nsurl_create(raw, &url);
253 free(raw);
254 }
255
256 return url;
257 }
258
259
260 /**
261 * Set colour option from wimp.
262 *
263 * \param opts The netsurf options.
264 * \param wimp wimp colour value
265 * \param option the netsurf option enum.
266 * \param def_colour The default colour value to use.
267 * \return NSERROR_OK on success or error code.
268 */
269 static nserror
set_colour_from_wimp(struct nsoption_s * opts,wimp_colour wimp,enum nsoption_e option,colour def_colour)270 set_colour_from_wimp(struct nsoption_s *opts,
271 wimp_colour wimp,
272 enum nsoption_e option,
273 colour def_colour)
274 {
275 os_error *error;
276 os_PALETTE(20) palette;
277
278 error = xwimp_read_true_palette((os_palette *) &palette);
279 if (error != NULL) {
280 NSLOG(netsurf, INFO,
281 "xwimp_read_palette: 0x%x: %s",
282 error->errnum,
283 error->errmess);
284 } else {
285 /* entries are in B0G0R0LL */
286 def_colour = palette.entries[wimp] >> 8;
287 }
288
289 opts[option].value.c = def_colour;
290
291 return NSERROR_OK;
292 }
293
294
295 /**
296 * Set option defaults for riscos frontend
297 *
298 * @param defaults The option table to update.
299 * @return error status.
300 *
301 * @todo The wimp_COLOUR_... values here map the colour definitions to
302 * parts of the RISC OS desktop palette. In places this is fairly
303 * arbitrary, and could probably do with re-checking.
304 */
set_defaults(struct nsoption_s * defaults)305 static nserror set_defaults(struct nsoption_s *defaults)
306 {
307 /* Set defaults for absent option strings */
308 nsoption_setnull_charp(ca_bundle, strdup("NetSurf:Resources.ca-bundle"));
309 nsoption_setnull_charp(cookie_file, strdup("NetSurf:Cookies"));
310 nsoption_setnull_charp(cookie_jar, strdup(CHOICES_PREFIX "Cookies"));
311
312 if (nsoption_charp(ca_bundle) == NULL ||
313 nsoption_charp(cookie_file) == NULL ||
314 nsoption_charp(cookie_jar) == NULL) {
315 NSLOG(netsurf, INFO, "Failed initialising default options");
316 return NSERROR_BAD_PARAMETER;
317 }
318
319 /* RISC OS platform does not generally benefit from disc cache
320 * so the default should be off.
321 */
322 nsoption_set_uint(disc_cache_size, 0);
323
324 /* Override core default treeview font size with 12 pt.
325 * TODO: 12 is the normal desktop font size, but users might run
326 * with something different.
327 */
328 nsoption_set_int(treeview_font_size, 12 * 10);
329
330 /* set default system colours for riscos ui */
331 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_ActiveBorder, 0x00000000);
332 set_colour_from_wimp(defaults, wimp_COLOUR_CREAM, NSOPTION_sys_colour_ActiveCaption, 0x00dddddd);
333 set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_AppWorkspace, 0x00eeeeee);
334 set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_Background, 0x00aa0000);/* \TODO -- Check */
335 set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_ButtonFace, 0x00aaaaaa);
336 set_colour_from_wimp(defaults, wimp_COLOUR_DARK_GREY, NSOPTION_sys_colour_ButtonHighlight, 0x00cccccc);/* \TODO -- Check */
337 set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ButtonShadow, 0x00bbbbbb);
338 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_ButtonText, 0x00000000);
339 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_CaptionText, 0x00000000);
340 set_colour_from_wimp(defaults, wimp_COLOUR_MID_LIGHT_GREY, NSOPTION_sys_colour_GrayText, 0x00777777);/* \TODO -- Check */
341 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_Highlight, 0x00ee0000);
342 set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_HighlightText, 0x00000000);
343 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InactiveBorder, 0x00000000);
344 set_colour_from_wimp(defaults, wimp_COLOUR_LIGHT_GREY, NSOPTION_sys_colour_InactiveCaption, 0x00ffffff);
345 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InactiveCaptionText, 0x00cccccc);
346 set_colour_from_wimp(defaults, wimp_COLOUR_CREAM, NSOPTION_sys_colour_InfoBackground, 0x00aaaaaa);
347 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_InfoText, 0x00000000);
348 set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_Menu, 0x00aaaaaa);
349 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_MenuText, 0x00000000);
350 set_colour_from_wimp(defaults, wimp_COLOUR_LIGHT_GREY, NSOPTION_sys_colour_Scrollbar, 0x00aaaaaa);/* \TODO -- Check */
351 set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ThreeDDarkShadow, 0x00555555);
352 set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_ThreeDFace, 0x00dddddd);
353 set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_ThreeDHighlight, 0x00aaaaaa);
354 set_colour_from_wimp(defaults, wimp_COLOUR_WHITE, NSOPTION_sys_colour_ThreeDLightShadow, 0x00999999);
355 set_colour_from_wimp(defaults, wimp_COLOUR_MID_DARK_GREY, NSOPTION_sys_colour_ThreeDShadow, 0x00777777);
356 set_colour_from_wimp(defaults, wimp_COLOUR_VERY_LIGHT_GREY, NSOPTION_sys_colour_Window, 0x00aaaaaa);
357 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_WindowFrame, 0x00000000);
358 set_colour_from_wimp(defaults, wimp_COLOUR_BLACK, NSOPTION_sys_colour_WindowText, 0x00000000);
359
360 return NSERROR_OK;
361 }
362
363
364
365
366 /**
367 * Create intermediate directories for Choices and User Data files
368 */
ro_gui_create_dirs(void)369 static void ro_gui_create_dirs(void)
370 {
371 char buf[256];
372 char *path;
373
374 /* Choices */
375 path = getenv("NetSurf$ChoicesSave");
376 if (!path)
377 die("Failed to find NetSurf Choices save path");
378
379 snprintf(buf, sizeof(buf), "%s", path);
380 netsurf_mkdir_all(buf);
381
382 /* URL */
383 snprintf(buf, sizeof(buf), "%s", nsoption_charp(url_save));
384 netsurf_mkdir_all(buf);
385
386 /* Hotlist */
387 snprintf(buf, sizeof(buf), "%s", nsoption_charp(hotlist_save));
388 netsurf_mkdir_all(buf);
389
390 /* Recent */
391 snprintf(buf, sizeof(buf), "%s", nsoption_charp(recent_save));
392 netsurf_mkdir_all(buf);
393
394 /* Theme */
395 snprintf(buf, sizeof(buf), "%s", nsoption_charp(theme_save));
396 netsurf_mkdir_all(buf);
397 /* and the final directory part (as theme_save is a directory) */
398 xosfile_create_dir(buf, 0);
399 }
400
401
402 /**
403 * Ensures the gui exits cleanly.
404 */
ro_gui_cleanup(void)405 static void ro_gui_cleanup(void)
406 {
407 ro_gui_buffer_close();
408 xhourglass_off();
409 /* Uninstall NetSurf-specific fonts */
410 xos_cli("FontRemove NetSurf:Resources.Fonts.");
411 }
412
413
414 /**
415 * Handles a signal
416 */
ro_gui_signal(int sig)417 static void ro_gui_signal(int sig)
418 {
419 static const os_error error = { 1, "NetSurf has detected a serious "
420 "error and must exit. Please submit a bug report, "
421 "attaching the browser log file." };
422 os_colour old_sand, old_glass;
423
424 ro_gui_cleanup();
425
426 xhourglass_on();
427 xhourglass_colours(0x0000ffff, 0x000000ff, &old_sand, &old_glass);
428 nsoption_dump(stderr, NULL);
429 /*rufl_dump_state();*/
430
431 #ifndef __ELF__
432 /* save WimpSlot and DA to files if NetSurf$CoreDump exists */
433 int used;
434 xos_read_var_val_size("NetSurf$CoreDump", 0, 0, &used, 0, 0);
435 if (used) {
436 int curr_slot;
437 xwimp_slot_size(-1, -1, &curr_slot, 0, 0);
438 NSLOG(netsurf, INFO, "saving WimpSlot, size 0x%x", curr_slot);
439 xosfile_save("$.NetSurf_Slot", 0x8000, 0,
440 (byte *) 0x8000,
441 (byte *) 0x8000 + curr_slot);
442
443 if (__dynamic_num != -1) {
444 int size;
445 byte *base_address;
446 xosdynamicarea_read(__dynamic_num, &size,
447 &base_address, 0, 0, 0, 0, 0);
448 NSLOG(netsurf, INFO,
449 "saving DA %i, base %p, size 0x%x",
450 __dynamic_num,
451 base_address,
452 size);
453 xosfile_save("$.NetSurf_DA",
454 (bits) base_address, 0,
455 base_address,
456 base_address + size);
457 }
458 }
459 #else
460 /* Save WimpSlot and UnixLib managed DAs when UnixEnv$coredump
461 * defines a coredump directory. */
462 const _kernel_oserror *err = __unixlib_write_coredump (NULL);
463 if (err != NULL)
464 NSLOG(netsurf, INFO, "Coredump failed: %s", err->errmess);
465 #endif
466
467 xhourglass_colours(old_sand, old_glass, 0, 0);
468 xhourglass_off();
469
470 __write_backtrace(sig);
471
472 xwimp_report_error_by_category(&error,
473 wimp_ERROR_BOX_GIVEN_CATEGORY |
474 wimp_ERROR_BOX_CATEGORY_ERROR <<
475 wimp_ERROR_BOX_CATEGORY_SHIFT,
476 "NetSurf", "!netsurf",
477 (osspriteop_area *) 1, "Quit", 0);
478 xos_cli("Filer_Run <Wimp$ScrapDir>.WWW.NetSurf.Log");
479
480 _Exit(sig);
481 }
482
483
484 /**
485 * Read a "line" from an Acorn URI file.
486 *
487 * \param fp file pointer to read from
488 * \param b buffer for line, size 400 bytes
489 * \return true on success, false on EOF
490 */
ro_gui_uri_file_parse_line(FILE * fp,char * b)491 static bool ro_gui_uri_file_parse_line(FILE *fp, char *b)
492 {
493 int c;
494 unsigned int i = 0;
495
496 c = getc(fp);
497 if (c == EOF)
498 return false;
499
500 /* skip comment lines */
501 while (c == '#') {
502 do { c = getc(fp); } while (c != EOF && 32 <= c);
503 if (c == EOF)
504 return false;
505 do { c = getc(fp); } while (c != EOF && c < 32);
506 if (c == EOF)
507 return false;
508 }
509
510 /* read "line" */
511 do {
512 if (i == 399)
513 return false;
514 b[i++] = c;
515 c = getc(fp);
516 } while (c != EOF && 32 <= c);
517
518 /* skip line ending control characters */
519 while (c != EOF && c < 32)
520 c = getc(fp);
521
522 if (c != EOF)
523 ungetc(c, fp);
524
525 b[i] = 0;
526 return true;
527 }
528
529
530 /**
531 * Parse an Acorn URI file.
532 *
533 * \param file_name file to read
534 * \param uri_title pointer to receive title data, or NULL for no data
535 * \return URL from file, or 0 on error and error reported
536 */
ro_gui_uri_file_parse(const char * file_name,char ** uri_title)537 static char *ro_gui_uri_file_parse(const char *file_name, char **uri_title)
538 {
539 /* See the "Acorn URI Handler Functional Specification" for the
540 * definition of the URI file format. */
541 char line[400];
542 char *url = NULL;
543 FILE *fp;
544
545 *uri_title = NULL;
546 fp = fopen(file_name, "rb");
547 if (!fp) {
548 NSLOG(netsurf, INFO, "fopen(\"%s\", \"rb\"): %i: %s",
549 file_name, errno, strerror(errno));
550 ro_warn_user("LoadError", strerror(errno));
551 return 0;
552 }
553
554 /* "URI" */
555 if (!ro_gui_uri_file_parse_line(fp, line) || strcmp(line, "URI") != 0)
556 goto uri_syntax_error;
557
558 /* version */
559 if (!ro_gui_uri_file_parse_line(fp, line) ||
560 strspn(line, "0123456789") != strlen(line))
561 goto uri_syntax_error;
562
563 /* URI */
564 if (!ro_gui_uri_file_parse_line(fp, line))
565 goto uri_syntax_error;
566
567 url = strdup(line);
568 if (!url) {
569 ro_warn_user("NoMemory", 0);
570 fclose(fp);
571 return 0;
572 }
573
574 /* title */
575 if (!ro_gui_uri_file_parse_line(fp, line))
576 goto uri_free;
577 if (uri_title && line[0] && ((line[0] != '*') || line[1])) {
578 *uri_title = strdup(line);
579 if (!*uri_title) /* non-fatal */
580 ro_warn_user("NoMemory", 0);
581 }
582 fclose(fp);
583
584 return url;
585
586 uri_free:
587 free(url);
588
589 uri_syntax_error:
590 fclose(fp);
591 ro_warn_user("URIError", 0);
592 return 0;
593 }
594
595
596 /**
597 * Parse an ANT URL file.
598 *
599 * \param file_name file to read
600 * \return URL from file, or 0 on error and error reported
601 */
ro_gui_url_file_parse(const char * file_name)602 static char *ro_gui_url_file_parse(const char *file_name)
603 {
604 char line[400];
605 char *url;
606 FILE *fp;
607
608 fp = fopen(file_name, "r");
609 if (!fp) {
610 NSLOG(netsurf, INFO, "fopen(\"%s\", \"r\"): %i: %s",
611 file_name, errno, strerror(errno));
612 ro_warn_user("LoadError", strerror(errno));
613 return 0;
614 }
615
616 if (!fgets(line, sizeof line, fp)) {
617 if (ferror(fp)) {
618 NSLOG(netsurf, INFO, "fgets: %i: %s", errno,
619 strerror(errno));
620 ro_warn_user("LoadError", strerror(errno));
621 } else
622 ro_warn_user("LoadError", messages_get("EmptyError"));
623 fclose(fp);
624 return 0;
625 }
626
627 fclose(fp);
628
629 if (line[strlen(line) - 1] == '\n')
630 line[strlen(line) - 1] = '\0';
631
632 url = strdup(line);
633 if (!url) {
634 ro_warn_user("NoMemory", 0);
635 return 0;
636 }
637
638 return url;
639 }
640
641
642 /**
643 * Parse an IEURL file.
644 *
645 * \param file_name file to read
646 * \return URL from file, or 0 on error and error reported
647 */
ro_gui_ieurl_file_parse(const char * file_name)648 static char *ro_gui_ieurl_file_parse(const char *file_name)
649 {
650 char line[400];
651 char *url = 0;
652 FILE *fp;
653
654 fp = fopen(file_name, "r");
655 if (!fp) {
656 NSLOG(netsurf, INFO, "fopen(\"%s\", \"r\"): %i: %s",
657 file_name, errno, strerror(errno));
658 ro_warn_user("LoadError", strerror(errno));
659 return 0;
660 }
661
662 while (fgets(line, sizeof line, fp)) {
663 if (strncmp(line, "URL=", 4) == 0) {
664 if (line[strlen(line) - 1] == '\n')
665 line[strlen(line) - 1] = '\0';
666 url = strdup(line + 4);
667 if (!url) {
668 fclose(fp);
669 ro_warn_user("NoMemory", 0);
670 return 0;
671 }
672 break;
673 }
674 }
675 if (ferror(fp)) {
676 NSLOG(netsurf, INFO, "fgets: %i: %s", errno, strerror(errno));
677 ro_warn_user("LoadError", strerror(errno));
678 fclose(fp);
679 return 0;
680 }
681
682 fclose(fp);
683
684 if (!url)
685 ro_warn_user("URIError", 0);
686
687 return url;
688 }
689
690
691 /**
692 * Handle Message_DataOpen (double-click on file in the Filer).
693 *
694 * \param message The wimp message to open.
695 */
ro_msg_dataopen(wimp_message * message)696 static void ro_msg_dataopen(wimp_message *message)
697 {
698 int file_type = message->data.data_xfer.file_type;
699 char *url = 0;
700 os_error *oserror;
701 nsurl *urlns;
702 nserror error;
703 size_t len;
704
705 switch (file_type) {
706 case 0xb28: /* ANT URL file */
707 url = ro_gui_url_file_parse(message->data.data_xfer.file_name);
708 error = nsurl_create(url, &urlns);
709 free(url);
710 break;
711
712 case 0xfaf: /* HTML file */
713 error = netsurf_path_to_nsurl(message->data.data_xfer.file_name,
714 &urlns);
715 break;
716
717 case 0x1ba: /* IEURL file */
718 url = ro_gui_ieurl_file_parse(message->
719 data.data_xfer.file_name);
720 error = nsurl_create(url, &urlns);
721 free(url);
722 break;
723
724 case 0x2000: /* application */
725 len = strlen(message->data.data_xfer.file_name);
726 if (len < 9 || strcmp(".!NetSurf",
727 message->data.data_xfer.file_name + len - 9))
728 return;
729
730 if (nsoption_charp(homepage_url) &&
731 nsoption_charp(homepage_url)[0]) {
732 error = nsurl_create(nsoption_charp(homepage_url),
733 &urlns);
734 } else {
735 error = nsurl_create(NETSURF_HOMEPAGE, &urlns);
736 }
737 break;
738
739 default:
740 return;
741 }
742
743 /* send DataLoadAck */
744 message->action = message_DATA_LOAD_ACK;
745 message->your_ref = message->my_ref;
746 oserror = xwimp_send_message(wimp_USER_MESSAGE, message, message->sender);
747 if (oserror) {
748 NSLOG(netsurf, INFO, "xwimp_send_message: 0x%x: %s",
749 oserror->errnum, oserror->errmess);
750 ro_warn_user("WimpError", oserror->errmess);
751 return;
752 }
753
754 if (error != NSERROR_OK) {
755 ro_warn_user(messages_get_errorcode(error), 0);
756 return;
757 }
758
759 /* create a new window with the file */
760 error = browser_window_create(BW_CREATE_HISTORY,
761 urlns,
762 NULL,
763 NULL,
764 NULL);
765 nsurl_unref(urlns);
766 if (error != NSERROR_OK) {
767 ro_warn_user(messages_get_errorcode(error), 0);
768 }
769 }
770
771
772 /**
773 * Handle Message_DataLoad (file dragged in).
774 */
ro_msg_dataload(wimp_message * message)775 static void ro_msg_dataload(wimp_message *message)
776 {
777 int file_type = message->data.data_xfer.file_type;
778 char *urltxt = NULL;
779 char *title = NULL;
780 struct gui_window *g;
781 os_error *oserror;
782 nsurl *url;
783 nserror error;
784
785 g = ro_gui_window_lookup(message->data.data_xfer.w);
786 if (g) {
787 if (ro_gui_window_dataload(g, message))
788 return;
789 }
790 else {
791 g = ro_gui_toolbar_lookup(message->data.data_xfer.w);
792 if (g && ro_gui_toolbar_dataload(g, message))
793 return;
794 }
795
796 switch (file_type) {
797 case FILETYPE_ACORN_URI:
798 urltxt = ro_gui_uri_file_parse(message->data.data_xfer.file_name,
799 &title);
800 error = nsurl_create(urltxt, &url);
801 free(urltxt);
802 break;
803
804 case FILETYPE_ANT_URL:
805 urltxt = ro_gui_url_file_parse(message->data.data_xfer.file_name);
806 error = nsurl_create(urltxt, &url);
807 free(urltxt);
808 break;
809
810 case FILETYPE_IEURL:
811 urltxt = ro_gui_ieurl_file_parse(message->data.data_xfer.file_name);
812 error = nsurl_create(urltxt, &url);
813 free(urltxt);
814 break;
815
816 case FILETYPE_HTML:
817 case FILETYPE_JNG:
818 case FILETYPE_CSS:
819 case FILETYPE_MNG:
820 case FILETYPE_GIF:
821 case FILETYPE_BMP:
822 case FILETYPE_ICO:
823 case osfile_TYPE_DRAW:
824 case FILETYPE_PNG:
825 case FILETYPE_JPEG:
826 case osfile_TYPE_SPRITE:
827 case osfile_TYPE_TEXT:
828 case FILETYPE_ARTWORKS:
829 case FILETYPE_SVG:
830 /* display the actual file */
831 error = netsurf_path_to_nsurl(message->data.data_xfer.file_name, &url);
832 break;
833
834 default:
835 return;
836 }
837
838 /* report error to user */
839 if (error != NSERROR_OK) {
840 ro_warn_user(messages_get_errorcode(error), 0);
841 return;
842 }
843
844
845 if (g) {
846 error = browser_window_navigate(g->bw,
847 url,
848 NULL,
849 BW_NAVIGATE_HISTORY,
850 NULL,
851 NULL,
852 NULL);
853 } else {
854 error = browser_window_create(BW_CREATE_HISTORY,
855 url,
856 NULL,
857 NULL,
858 NULL);
859 }
860 nsurl_unref(url);
861 if (error != NSERROR_OK) {
862 ro_warn_user(messages_get_errorcode(error), 0);
863 }
864
865
866 /* send DataLoadAck */
867 message->action = message_DATA_LOAD_ACK;
868 message->your_ref = message->my_ref;
869 oserror = xwimp_send_message(wimp_USER_MESSAGE, message,
870 message->sender);
871 if (oserror) {
872 NSLOG(netsurf, INFO, "xwimp_send_message: 0x%x: %s",
873 oserror->errnum, oserror->errmess);
874 ro_warn_user("WimpError", oserror->errmess);
875 return;
876 }
877
878 }
879
880
881 /**
882 * Ensure that the filename in a data transfer message is NULL terminated
883 * (some applications, especially BASIC programs use CR)
884 *
885 * \param message message to be corrected
886 */
ro_msg_terminate_filename(wimp_full_message_data_xfer * message)887 static void ro_msg_terminate_filename(wimp_full_message_data_xfer *message)
888 {
889 const char *ep = (char*)message + message->size;
890 char *p = message->file_name;
891
892 if ((size_t)message->size >= sizeof(*message))
893 ep = (char*)message + sizeof(*message) - 1;
894
895 while (p < ep && *p >= ' ') p++;
896 *p = '\0';
897 }
898
899
900 /**
901 * Handle Message_DataSave
902 */
ro_msg_datasave(wimp_message * message)903 static void ro_msg_datasave(wimp_message *message)
904 {
905 wimp_full_message_data_xfer *dataxfer = (wimp_full_message_data_xfer*)message;
906
907 /* remove ghost caret if drag-and-drop protocol was used */
908 // ro_gui_selection_drag_reset();
909
910 ro_msg_terminate_filename(dataxfer);
911
912 if (ro_gui_selection_prepare_paste_datasave(dataxfer))
913 return;
914
915 switch (dataxfer->file_type) {
916 case FILETYPE_ACORN_URI:
917 case FILETYPE_ANT_URL:
918 case FILETYPE_IEURL:
919 case FILETYPE_HTML:
920 case FILETYPE_JNG:
921 case FILETYPE_CSS:
922 case FILETYPE_MNG:
923 case FILETYPE_GIF:
924 case FILETYPE_BMP:
925 case FILETYPE_ICO:
926 case osfile_TYPE_DRAW:
927 case FILETYPE_PNG:
928 case FILETYPE_JPEG:
929 case osfile_TYPE_SPRITE:
930 case osfile_TYPE_TEXT:
931 case FILETYPE_ARTWORKS:
932 case FILETYPE_SVG: {
933 os_error *error;
934
935 dataxfer->your_ref = dataxfer->my_ref;
936 dataxfer->size = offsetof(wimp_full_message_data_xfer, file_name) + 16;
937 dataxfer->action = message_DATA_SAVE_ACK;
938 dataxfer->est_size = -1;
939 memcpy(dataxfer->file_name, "<Wimp$Scrap>", 13);
940
941 error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message*)dataxfer, message->sender);
942 if (error) {
943 NSLOG(netsurf, INFO,
944 "xwimp_send_message: 0x%x: %s",
945 error->errnum,
946 error->errmess);
947 ro_warn_user("WimpError", error->errmess);
948 }
949 }
950 break;
951 }
952 }
953
954
955 /**
956 * Handle Message_DataSaveAck.
957 */
ro_msg_datasave_ack(wimp_message * message)958 static void ro_msg_datasave_ack(wimp_message *message)
959 {
960 ro_msg_terminate_filename((wimp_full_message_data_xfer*)message);
961
962 if (ro_print_ack(message))
963 return;
964
965 switch (gui_current_drag_type) {
966 case GUI_DRAG_DOWNLOAD_SAVE:
967 ro_gui_download_datasave_ack(message);
968 break;
969
970 case GUI_DRAG_SAVE:
971 ro_gui_save_datasave_ack(message);
972 gui_current_drag_type = GUI_DRAG_NONE;
973 break;
974
975 default:
976 break;
977 }
978
979 gui_current_drag_type = GUI_DRAG_NONE;
980 }
981
982
983 /**
984 * Handle PreQuit message
985 *
986 * \param message PreQuit message from Wimp
987 */
ro_msg_prequit(wimp_message * message)988 static void ro_msg_prequit(wimp_message *message)
989 {
990 if (!ro_gui_prequit()) {
991 os_error *error;
992
993 /* we're objecting to the close down */
994 message->your_ref = message->my_ref;
995 error = xwimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE,
996 message, message->sender);
997 if (error) {
998 NSLOG(netsurf, INFO, "xwimp_send_message: 0x%x:%s",
999 error->errnum, error->errmess);
1000 ro_warn_user("WimpError", error->errmess);
1001 }
1002 }
1003 }
1004
1005
1006 /**
1007 * Handle SaveDesktop message.
1008 *
1009 * \param message SaveDesktop message from Wimp.
1010 */
ro_msg_save_desktop(wimp_message * message)1011 static void ro_msg_save_desktop(wimp_message *message)
1012 {
1013 os_error *error;
1014
1015 error = xosgbpb_writew(message->data.save_desktopw.file,
1016 (const byte*)"Run ", 4, NULL);
1017 if (!error) {
1018 error = xosgbpb_writew(message->data.save_desktopw.file,
1019 (const byte*)NETSURF_DIR, strlen(NETSURF_DIR), NULL);
1020 if (!error)
1021 error = xos_bputw('\n', message->data.save_desktopw.file);
1022 }
1023
1024 if (error) {
1025 NSLOG(netsurf, INFO, "xosgbpb_writew/xos_bputw: 0x%x:%s",
1026 error->errnum, error->errmess);
1027 ro_warn_user("SaveError", error->errmess);
1028
1029 /* we must cancel the save by acknowledging the message */
1030 message->your_ref = message->my_ref;
1031 error = xwimp_send_message(wimp_USER_MESSAGE_ACKNOWLEDGE,
1032 message, message->sender);
1033 if (error) {
1034 NSLOG(netsurf, INFO, "xwimp_send_message: 0x%x:%s",
1035 error->errnum, error->errmess);
1036 ro_warn_user("WimpError", error->errmess);
1037 }
1038 }
1039 }
1040
1041
1042 /**
1043 * Handle WindowInfo message (part of the iconising protocol)
1044 *
1045 * \param message WindowInfo message from the Iconiser
1046 */
ro_msg_window_info(wimp_message * message)1047 static void ro_msg_window_info(wimp_message *message)
1048 {
1049 wimp_full_message_window_info *wi;
1050 struct gui_window *g;
1051
1052 /* allow the user to turn off thumbnail icons */
1053 if (!nsoption_bool(thumbnail_iconise))
1054 return;
1055
1056 wi = (wimp_full_message_window_info*)message;
1057 g = ro_gui_window_lookup(wi->w);
1058
1059 /* ic_<task name> will suffice for our other windows */
1060 if (g) {
1061 ro_gui_window_iconise(g, wi);
1062 ro_gui_dialog_close_persistent(wi->w);
1063 }
1064 }
1065
1066
1067 /**
1068 * Get screen properties following a mode change.
1069 */
ro_gui_get_screen_properties(void)1070 static void ro_gui_get_screen_properties(void)
1071 {
1072 static const ns_os_vdu_var_list vars = {
1073 os_MODEVAR_XWIND_LIMIT,
1074 {
1075 os_MODEVAR_YWIND_LIMIT,
1076 os_MODEVAR_XEIG_FACTOR,
1077 os_MODEVAR_YEIG_FACTOR,
1078 os_VDUVAR_END_LIST
1079 }
1080 };
1081 os_error *error;
1082 int vals[4];
1083
1084 error = xos_read_vdu_variables(PTR_OS_VDU_VAR_LIST(&vars), vals);
1085 if (error) {
1086 NSLOG(netsurf, INFO, "xos_read_vdu_variables: 0x%x: %s",
1087 error->errnum, error->errmess);
1088 ro_warn_user("MiscError", error->errmess);
1089 return;
1090 }
1091 screen_info.width = (vals[0] + 1) << vals[2];
1092 screen_info.height = (vals[1] + 1) << vals[3];
1093 }
1094
1095
1096 /**
1097 * Warn the user if Inet$Resolvers is not set.
1098 */
ro_gui_check_resolvers(void)1099 static void ro_gui_check_resolvers(void)
1100 {
1101 char *resolvers;
1102 resolvers = getenv("Inet$Resolvers");
1103 if (resolvers && resolvers[0]) {
1104 NSLOG(netsurf, INFO, "Inet$Resolvers '%s'", resolvers);
1105 } else {
1106 NSLOG(netsurf, INFO, "Inet$Resolvers not set or empty");
1107 ro_warn_user("Resolvers", 0);
1108 }
1109 }
1110
1111
1112 /**
1113 * Initialise the RISC OS specific GUI.
1114 *
1115 * \param argc The number of command line arguments.
1116 * \param argv The string vector of command line arguments.
1117 */
gui_init(int argc,char ** argv)1118 static nserror gui_init(int argc, char** argv)
1119 {
1120 struct {
1121 void (*sigabrt)(int);
1122 void (*sigfpe)(int);
1123 void (*sigill)(int);
1124 void (*sigint)(int);
1125 void (*sigsegv)(int);
1126 void (*sigterm)(int);
1127 void (*sigoserror)(int);
1128 } prev_sigs;
1129 char path[40];
1130 os_error *error;
1131 int length;
1132 char *nsdir_temp;
1133 byte *base;
1134 nsurl *url;
1135 nserror ret;
1136 bool open_window;
1137
1138 /* re-enable all FPU exceptions/traps except inexact operations,
1139 * which we're not interested in, and underflow which is incorrectly
1140 * raised when converting an exact value of 0 from double-precision
1141 * to single-precision on FPEmulator v4.09-4.11 (MVFD F0,#0:MVFS F0,F0)
1142 * - UnixLib disables all FP exceptions by default */
1143
1144 _FPU_SETCW(_FPU_IEEE & ~(_FPU_MASK_PM | _FPU_MASK_UM));
1145
1146 xhourglass_start(1);
1147
1148 /* read OS version for code that adapts to conform to the OS
1149 * (remember that it's preferable to check for specific features
1150 * being present) */
1151 xos_byte(osbyte_IN_KEY, 0, 0xff, &os_version, NULL);
1152
1153 /* the first release version of the A9home OS is incapable of
1154 plotting patterned lines (presumably a fault in the hw acceleration) */
1155 if (!xosmodule_lookup("VideoHWSMI", NULL, NULL, &base, NULL, NULL)) {
1156 #if 0 // this fault still hasn't been fixed, so disable patterned lines for all versions until it has
1157 const char *help = (char*)base + ((int*)base)[5];
1158 while (*help > 9) help++;
1159 while (*help == 9) help++;
1160 if (!memcmp(help, "0.55", 4))
1161 #endif
1162 ro_plot_patterned_lines = false;
1163 }
1164
1165 /* Create our choices directories */
1166 ro_gui_create_dirs();
1167
1168 /* Register exit and signal handlers */
1169 atexit(ro_gui_cleanup);
1170 prev_sigs.sigabrt = signal(SIGABRT, ro_gui_signal);
1171 prev_sigs.sigfpe = signal(SIGFPE, ro_gui_signal);
1172 prev_sigs.sigill = signal(SIGILL, ro_gui_signal);
1173 prev_sigs.sigint = signal(SIGINT, ro_gui_signal);
1174 prev_sigs.sigsegv = signal(SIGSEGV, ro_gui_signal);
1175 prev_sigs.sigterm = signal(SIGTERM, ro_gui_signal);
1176 prev_sigs.sigoserror = signal(SIGOSERROR, ro_gui_signal);
1177
1178 if (prev_sigs.sigabrt == SIG_ERR || prev_sigs.sigfpe == SIG_ERR ||
1179 prev_sigs.sigill == SIG_ERR ||
1180 prev_sigs.sigint == SIG_ERR ||
1181 prev_sigs.sigsegv == SIG_ERR ||
1182 prev_sigs.sigterm == SIG_ERR ||
1183 prev_sigs.sigoserror == SIG_ERR)
1184 die("Failed registering signal handlers");
1185
1186 /* Load in UI sprites */
1187 gui_sprites = ro_gui_load_sprite_file("NetSurf:Resources.Sprites");
1188 if (!gui_sprites)
1189 die("Unable to load Sprites.");
1190
1191 /* Find NetSurf directory */
1192 nsdir_temp = getenv("NetSurf$Dir");
1193 if (!nsdir_temp)
1194 die("Failed to locate NetSurf directory");
1195 NETSURF_DIR = strdup(nsdir_temp);
1196 if (!NETSURF_DIR)
1197 die("Failed duplicating NetSurf directory string");
1198
1199 /* Initialise filename allocator */
1200 filename_initialise();
1201
1202 /* Initialise save complete functionality */
1203 save_complete_init();
1204
1205 /* Initialise the font subsystem */
1206 nsfont_init();
1207
1208 /* Load in visited URLs, Cookies, and hostlist */
1209 urldb_load(nsoption_charp(url_path));
1210 urldb_load_cookies(nsoption_charp(cookie_file));
1211 hotlist_init(nsoption_charp(hotlist_path),
1212 nsoption_bool(external_hotlists) ?
1213 NULL :
1214 nsoption_charp(hotlist_save));
1215
1216 /* Initialise with the wimp */
1217 error = xwimp_initialise(wimp_VERSION_RO38, task_name,
1218 PTR_WIMP_MESSAGE_LIST(&task_messages), 0,
1219 &task_handle);
1220 if (error) {
1221 NSLOG(netsurf, INFO, "xwimp_initialise: 0x%x: %s",
1222 error->errnum, error->errmess);
1223 die(error->errmess);
1224 }
1225 /* Register message handlers */
1226 ro_message_register_route(message_HELP_REQUEST,
1227 ro_gui_interactive_help_request);
1228 ro_message_register_route(message_DATA_OPEN,
1229 ro_msg_dataopen);
1230 ro_message_register_route(message_DATA_SAVE,
1231 ro_msg_datasave);
1232 ro_message_register_route(message_DATA_SAVE_ACK,
1233 ro_msg_datasave_ack);
1234 ro_message_register_route(message_PRE_QUIT,
1235 ro_msg_prequit);
1236 ro_message_register_route(message_SAVE_DESKTOP,
1237 ro_msg_save_desktop);
1238 ro_message_register_route(message_DRAGGING,
1239 ro_gui_selection_dragging);
1240 ro_message_register_route(message_DRAG_CLAIM,
1241 ro_gui_selection_drag_claim);
1242 ro_message_register_route(message_WINDOW_INFO,
1243 ro_msg_window_info);
1244
1245 /* Initialise global information */
1246 ro_gui_get_screen_properties();
1247 ro_gui_wimp_get_desktop_font();
1248
1249 /* Issue a *Desktop to poke AcornURI into life */
1250 if (getenv("NetSurf$Start_URI_Handler"))
1251 xwimp_start_task("Desktop", 0);
1252
1253 /* Open the templates */
1254 if ((length = snprintf(path, sizeof(path),
1255 "NetSurf:Resources.%s.Templates",
1256 nsoption_charp(language))) < 0 || length >= (int)sizeof(path))
1257 die("Failed to locate Templates resource.");
1258 error = xwimp_open_template(path);
1259 if (error) {
1260 NSLOG(netsurf, INFO, "xwimp_open_template failed: 0x%x: %s",
1261 error->errnum, error->errmess);
1262 die(error->errmess);
1263 }
1264
1265 /* Initialise themes before dialogs */
1266 ro_gui_theme_initialise();
1267
1268 /* Initialise dialog windows (must be after UI sprites are loaded) */
1269 ro_gui_dialog_init();
1270
1271 /* Initialise download window */
1272 ro_gui_download_init();
1273
1274 /* Initialise menus */
1275 ro_gui_menu_init();
1276
1277 /* Initialise query windows */
1278 ro_gui_query_init();
1279
1280 /* Initialise toolbars */
1281 ro_toolbar_init();
1282
1283 /* Initialise url bar module */
1284 ro_gui_url_bar_init();
1285
1286 /* Initialise browser windows */
1287 ro_gui_window_initialise();
1288
1289 /* Done with the templates file */
1290 wimp_close_template();
1291
1292 /* Create Iconbar icon and menus */
1293 ro_gui_iconbar_initialise();
1294
1295 /* Finally, check Inet$Resolvers for sanity */
1296 ro_gui_check_resolvers();
1297
1298 open_window = nsoption_bool(open_browser_at_startup);
1299
1300 /* parse command-line arguments */
1301 if (argc == 2) {
1302 NSLOG(netsurf, INFO, "parameters: '%s'", argv[1]);
1303 /* this is needed for launching URI files */
1304 if (strcasecmp(argv[1], "-nowin") == 0) {
1305 return NSERROR_OK;
1306 }
1307 ret = nsurl_create(NETSURF_HOMEPAGE, &url);
1308 }
1309 else if (argc == 3) {
1310 NSLOG(netsurf, INFO, "parameters: '%s' '%s'", argv[1],
1311 argv[2]);
1312 open_window = true;
1313
1314 /* HTML files */
1315 if (strcasecmp(argv[1], "-html") == 0) {
1316 ret = netsurf_path_to_nsurl(argv[2], &url);
1317 }
1318 /* URL files */
1319 else if (strcasecmp(argv[1], "-urlf") == 0) {
1320 char *urlf = ro_gui_url_file_parse(argv[2]);
1321 if (!urlf) {
1322 NSLOG(netsurf, INFO, "allocation failed");
1323 die("Insufficient memory for URL");
1324 }
1325 ret = nsurl_create(urlf, &url);
1326 free(urlf);
1327 }
1328 /* ANT URL Load */
1329 else if (strcasecmp(argv[1], "-url") == 0) {
1330 ret = nsurl_create(argv[2], &url);
1331 }
1332 /* Unknown => exit here. */
1333 else {
1334 NSLOG(netsurf, INFO, "Unknown parameters: '%s' '%s'",
1335 argv[1], argv[2]);
1336 return NSERROR_BAD_PARAMETER;
1337 }
1338 }
1339 /* get user's homepage (if configured) */
1340 else if (nsoption_charp(homepage_url) &&
1341 nsoption_charp(homepage_url)[0]) {
1342 ret = nsurl_create(nsoption_charp(homepage_url), &url);
1343 }
1344 /* default homepage */
1345 else {
1346 ret = nsurl_create(NETSURF_HOMEPAGE, &url);
1347 }
1348
1349 /* check for url creation error */
1350 if (ret != NSERROR_OK) {
1351 return ret;
1352 }
1353
1354 if (open_window) {
1355 ret = browser_window_create(BW_CREATE_HISTORY,
1356 url,
1357 NULL,
1358 NULL,
1359 NULL);
1360 }
1361 nsurl_unref(url);
1362
1363 return ret;
1364 }
1365
1366
1367 /**
1368 * Determine the default language to use.
1369 *
1370 * RISC OS has no standard way of determining which language the user prefers.
1371 * We have to guess from the 'Country' setting.
1372 */
ro_gui_default_language(void)1373 const char *ro_gui_default_language(void)
1374 {
1375 char path[40];
1376 const char *lang;
1377 int country;
1378 os_error *error;
1379
1380 /* choose a language from the configured country number */
1381 error = xosbyte_read(osbyte_VAR_COUNTRY_NUMBER, &country);
1382 if (error) {
1383 NSLOG(netsurf, INFO, "xosbyte_read failed: 0x%x: %s",
1384 error->errnum, error->errmess);
1385 country = 1;
1386 }
1387 switch (country) {
1388 case 7: /* Germany */
1389 case 30: /* Austria */
1390 case 35: /* Switzerland (70% German-speaking) */
1391 lang = "de";
1392 break;
1393 case 6: /* France */
1394 case 18: /* Canada2 (French Canada?) */
1395 lang = "fr";
1396 break;
1397 case 34: /* Netherlands */
1398 lang = "nl";
1399 break;
1400 default:
1401 lang = "en";
1402 break;
1403 }
1404 sprintf(path, "NetSurf:Resources.%s", lang);
1405 if (is_dir(path))
1406 return lang;
1407 return "en";
1408 }
1409
1410
1411 /**
1412 * Create a nsurl from a RISC OS pathname.
1413 *
1414 * Perform the necessary operations on a path to generate a nsurl.
1415 *
1416 * @param[in] path The RISC OS pathname to convert.
1417 * @param[out] url_out pointer to recive the nsurl, The returned url must be
1418 * unreferenced by the caller.
1419 * @return NSERROR_OK and the url is placed in \a url or error code on faliure.
1420 */
ro_path_to_nsurl(const char * path,struct nsurl ** url_out)1421 static nserror ro_path_to_nsurl(const char *path, struct nsurl **url_out)
1422 {
1423 int spare;
1424 char *canonical_path; /* canonicalised RISC OS path */
1425 char *unix_path; /* unix path */
1426 char *escaped_path;
1427 os_error *error;
1428 nserror ret;
1429 int urllen;
1430 char *url; /* resulting url */
1431
1432 /* calculate the canonical risc os path */
1433 error = xosfscontrol_canonicalise_path(path, 0, 0, 0, 0, &spare);
1434 if (error) {
1435 NSLOG(netsurf, INFO,
1436 "xosfscontrol_canonicalise_path failed: 0x%x: %s",
1437 error->errnum,
1438 error->errmess);
1439 ro_warn_user("PathToURL", error->errmess);
1440 return NSERROR_NOT_FOUND;
1441 }
1442
1443 canonical_path = malloc(1 - spare);
1444 if (canonical_path == NULL) {
1445 free(canonical_path);
1446 return NSERROR_NOMEM;
1447 }
1448
1449 error = xosfscontrol_canonicalise_path(path, canonical_path, 0, 0, 1 - spare, 0);
1450 if (error) {
1451 NSLOG(netsurf, INFO,
1452 "xosfscontrol_canonicalise_path failed: 0x%x: %s",
1453 error->errnum,
1454 error->errmess);
1455 ro_warn_user("PathToURL", error->errmess);
1456 free(canonical_path);
1457 return NSERROR_NOT_FOUND;
1458 }
1459
1460 /* create a unix path from the cananocal risc os one */
1461 unix_path = __unixify(canonical_path, __RISCOSIFY_NO_REVERSE_SUFFIX, NULL, 0, 0);
1462
1463 if (unix_path == NULL) {
1464 NSLOG(netsurf, INFO, "__unixify failed: %s", canonical_path);
1465 free(canonical_path);
1466 return NSERROR_BAD_PARAMETER;
1467 }
1468 free(canonical_path);
1469
1470 /* url escape the unix path */
1471 ret = url_escape(unix_path, false, "/", &escaped_path);
1472 if (ret != NSERROR_OK) {
1473 free(unix_path);
1474 return ret;
1475 }
1476 free(unix_path);
1477
1478 /* convert the escaped unix path into a url */
1479 urllen = strlen(escaped_path) + FILE_SCHEME_PREFIX_LEN + 1;
1480 url = malloc(urllen);
1481 if (url == NULL) {
1482 NSLOG(netsurf, INFO, "Unable to allocate url");
1483 free(escaped_path);
1484 return NSERROR_NOMEM;
1485 }
1486
1487 if (*escaped_path == '/') {
1488 snprintf(url, urllen, "%s%s",
1489 FILE_SCHEME_PREFIX, escaped_path + 1);
1490 } else {
1491 snprintf(url, urllen, "%s%s",
1492 FILE_SCHEME_PREFIX, escaped_path);
1493 }
1494 free(escaped_path);
1495
1496 ret = nsurl_create(url, url_out);
1497 free(url);
1498
1499 return ret;
1500 }
1501
1502
1503 /**
1504 * Create a path from a nsurl using posix file handling.
1505 *
1506 * @param[in] url The url to encode.
1507 * @param[out] path_out A string containing the result path which should
1508 * be freed by the caller.
1509 * @return NSERROR_OK and the path is written to \a path or error code
1510 * on faliure.
1511 */
ro_nsurl_to_path(struct nsurl * url,char ** path_out)1512 static nserror ro_nsurl_to_path(struct nsurl *url, char **path_out)
1513 {
1514 lwc_string *urlpath;
1515 size_t unpath_len;
1516 char *unpath;
1517 char *path;
1518 bool match;
1519 lwc_string *scheme;
1520 nserror res;
1521 char *r;
1522
1523 if ((url == NULL) || (path_out == NULL)) {
1524 return NSERROR_BAD_PARAMETER;
1525 }
1526
1527 scheme = nsurl_get_component(url, NSURL_SCHEME);
1528
1529 if (lwc_string_caseless_isequal(scheme, corestring_lwc_file,
1530 &match) != lwc_error_ok)
1531 {
1532 return NSERROR_BAD_PARAMETER;
1533 }
1534 lwc_string_unref(scheme);
1535 if (match == false) {
1536 return NSERROR_BAD_PARAMETER;
1537 }
1538
1539 urlpath = nsurl_get_component(url, NSURL_PATH);
1540 if (urlpath == NULL) {
1541 return NSERROR_BAD_PARAMETER;
1542 }
1543
1544 res = url_unescape(lwc_string_data(urlpath),
1545 lwc_string_length(urlpath),
1546 &unpath_len,
1547 &unpath);
1548 lwc_string_unref(urlpath);
1549 if (res != NSERROR_OK) {
1550 return res;
1551 }
1552
1553 /* RISC OS path should not be more than 100 characters longer */
1554 path = malloc(unpath_len + 100);
1555 if (path == NULL) {
1556 free(unpath);
1557 return NSERROR_NOMEM;
1558 }
1559
1560 r = __riscosify(unpath, 0, __RISCOSIFY_NO_SUFFIX,
1561 path, unpath_len + 100, 0);
1562 free(unpath);
1563 if (r == NULL) {
1564 free(path);
1565 return NSERROR_NOMEM;
1566 }
1567
1568 *path_out = path;
1569
1570 return NSERROR_OK;
1571 }
1572
1573
1574 /**
1575 * Ensures output logging stream is correctly configured.
1576 */
nslog_stream_configure(FILE * fptr)1577 static bool nslog_stream_configure(FILE *fptr)
1578 {
1579 /* set log stream to be non-buffering */
1580 setbuf(fptr, NULL);
1581
1582 return true;
1583 }
1584
1585
1586 /**
1587 * Close down the gui (RISC OS).
1588 */
gui_quit(void)1589 static void gui_quit(void)
1590 {
1591 urldb_save_cookies(nsoption_charp(cookie_jar));
1592 urldb_save(nsoption_charp(url_save));
1593 ro_gui_window_quit();
1594 ro_gui_local_history_finalise();
1595 ro_gui_global_history_finalise();
1596 ro_gui_pageinfo_finalise();
1597 ro_gui_hotlist_finalise();
1598 ro_gui_cookies_finalise();
1599 ro_gui_saveas_quit();
1600 ro_gui_url_bar_fini();
1601 rufl_quit();
1602 free(gui_sprites);
1603 xwimp_close_down(task_handle);
1604 xhourglass_off();
1605 }
1606
1607
1608 /**
1609 * Handle Close_Window_Request events.
1610 */
ro_gui_close_window_request(wimp_close * close)1611 static void ro_gui_close_window_request(wimp_close *close)
1612 {
1613 if (ro_gui_alt_pressed())
1614 ro_gui_window_close_all();
1615 else {
1616 if (ro_gui_wimp_event_close_window(close->w))
1617 return;
1618 ro_gui_dialog_close(close->w);
1619 }
1620 }
1621
1622
1623 /**
1624 * Handle key press paste callback.
1625 */
ro_gui_keypress_cb(void * pw)1626 static void ro_gui_keypress_cb(void *pw)
1627 {
1628 wimp_key *key = (wimp_key *) pw;
1629
1630 if (ro_gui_wimp_event_keypress(key) == false) {
1631 os_error *error = xwimp_process_key(key->c);
1632 if (error) {
1633 NSLOG(netsurf, INFO, "xwimp_process_key: 0x%x: %s",
1634 error->errnum, error->errmess);
1635 ro_warn_user("WimpError", error->errmess);
1636 }
1637 }
1638
1639 free(key);
1640 }
1641
1642
1643 /**
1644 * Handle gui keypress.
1645 */
ro_gui_keypress(wimp_key * key)1646 static void ro_gui_keypress(wimp_key *key)
1647 {
1648 if (key->c == wimp_KEY_ESCAPE &&
1649 (gui_current_drag_type == GUI_DRAG_SAVE ||
1650 gui_current_drag_type == GUI_DRAG_DOWNLOAD_SAVE)) {
1651
1652 /* Allow Escape key to be used for cancelling a drag
1653 * save (easier than finding somewhere safe to abort
1654 * the drag)
1655 */
1656 ro_gui_drag_box_cancel();
1657 gui_current_drag_type = GUI_DRAG_NONE;
1658 } else if (key->c == 22 /* Ctrl-V */) {
1659 wimp_key *copy;
1660
1661 /* Must copy the keypress as it's on the stack */
1662 copy = malloc(sizeof(wimp_key));
1663 if (copy == NULL)
1664 return;
1665 memcpy(copy, key, sizeof(wimp_key));
1666
1667 ro_gui_selection_prepare_paste(key->w, ro_gui_keypress_cb, copy);
1668 } else if (ro_gui_wimp_event_keypress(key) == false) {
1669 os_error *error = xwimp_process_key(key->c);
1670 if (error) {
1671 NSLOG(netsurf, INFO, "xwimp_process_key: 0x%x: %s",
1672 error->errnum, error->errmess);
1673 ro_warn_user("WimpError", error->errmess);
1674 }
1675 }
1676 }
1677
1678
1679 /**
1680 * Handle the three User_Message events.
1681 */
ro_gui_user_message(wimp_event_no event,wimp_message * message)1682 static void ro_gui_user_message(wimp_event_no event, wimp_message *message)
1683 {
1684 /* attempt automatic routing */
1685 if (ro_message_handle_message(event, message))
1686 return;
1687
1688 switch (message->action) {
1689 case message_DATA_LOAD:
1690 ro_msg_terminate_filename((wimp_full_message_data_xfer*)message);
1691
1692 if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
1693 if (ro_print_current_window)
1694 ro_print_dataload_bounce(message);
1695 } else if (ro_gui_selection_prepare_paste_dataload(
1696 (wimp_full_message_data_xfer *) message) == false) {
1697 ro_msg_dataload(message);
1698 }
1699 break;
1700
1701 case message_DATA_LOAD_ACK:
1702 if (ro_print_current_window)
1703 ro_print_cleanup();
1704 break;
1705
1706 case message_MENU_WARNING:
1707 ro_gui_menu_warning((wimp_message_menu_warning *)
1708 &message->data);
1709 break;
1710
1711 case message_MENUS_DELETED:
1712 ro_gui_menu_message_deleted((wimp_message_menus_deleted *)
1713 &message->data);
1714 break;
1715
1716 case message_CLAIM_ENTITY:
1717 ro_gui_selection_claim_entity((wimp_full_message_claim_entity*)message);
1718 break;
1719
1720 case message_DATA_REQUEST:
1721 ro_gui_selection_data_request((wimp_full_message_data_request*)message);
1722 break;
1723
1724 case message_MODE_CHANGE:
1725 ro_gui_get_screen_properties();
1726 rufl_invalidate_cache();
1727 break;
1728
1729 case message_PALETTE_CHANGE:
1730 break;
1731
1732 case message_FONT_CHANGED:
1733 ro_gui_wimp_get_desktop_font();
1734 break;
1735
1736 case message_URI_PROCESS:
1737 if (event != wimp_USER_MESSAGE_ACKNOWLEDGE)
1738 ro_uri_message_received(message);
1739 break;
1740 case message_URI_RETURN_RESULT:
1741 ro_uri_bounce(message);
1742 break;
1743 case message_INET_SUITE_OPEN_URL:
1744 if (event == wimp_USER_MESSAGE_ACKNOWLEDGE) {
1745 ro_url_bounce(message);
1746 }
1747 else {
1748 ro_url_message_received(message);
1749 }
1750 break;
1751 #ifdef WITH_PLUGIN
1752 case message_PLUG_IN_OPENING:
1753 plugin_opening(message);
1754 break;
1755 case message_PLUG_IN_CLOSED:
1756 plugin_closed(message);
1757 break;
1758 case message_PLUG_IN_RESHAPE_REQUEST:
1759 plugin_reshape_request(message);
1760 break;
1761 case message_PLUG_IN_FOCUS:
1762 break;
1763 case message_PLUG_IN_URL_ACCESS:
1764 plugin_url_access(message);
1765 break;
1766 case message_PLUG_IN_STATUS:
1767 plugin_status(message);
1768 break;
1769 case message_PLUG_IN_BUSY:
1770 break;
1771 case message_PLUG_IN_STREAM_NEW:
1772 plugin_stream_new(message);
1773 break;
1774 case message_PLUG_IN_STREAM_WRITE:
1775 break;
1776 case message_PLUG_IN_STREAM_WRITTEN:
1777 plugin_stream_written(message);
1778 break;
1779 case message_PLUG_IN_STREAM_DESTROY:
1780 break;
1781 case message_PLUG_IN_OPEN:
1782 if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
1783 plugin_open_msg(message);
1784 break;
1785 case message_PLUG_IN_CLOSE:
1786 if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
1787 plugin_close_msg(message);
1788 break;
1789 case message_PLUG_IN_RESHAPE:
1790 case message_PLUG_IN_STREAM_AS_FILE:
1791 case message_PLUG_IN_NOTIFY:
1792 case message_PLUG_IN_ABORT:
1793 case message_PLUG_IN_ACTION:
1794 break;
1795 #endif
1796 case message_PRINT_SAVE:
1797 if (event == wimp_USER_MESSAGE_ACKNOWLEDGE)
1798 ro_print_save_bounce(message);
1799 break;
1800 case message_PRINT_ERROR:
1801 ro_print_error(message);
1802 break;
1803 case message_PRINT_TYPE_ODD:
1804 ro_print_type_odd(message);
1805 break;
1806 case message_HOTLIST_CHANGED:
1807 ro_gui_hotlist_add_cleanup();
1808 break;
1809 case message_QUIT:
1810 riscos_done = true;
1811 break;
1812 }
1813 }
1814
1815
1816 /**
1817 * Process a Wimp_Poll event.
1818 *
1819 * \param event wimp event number
1820 * \param block parameter block
1821 */
ro_gui_handle_event(wimp_event_no event,wimp_block * block)1822 static void ro_gui_handle_event(wimp_event_no event, wimp_block *block)
1823 {
1824 switch (event) {
1825 case wimp_NULL_REASON_CODE:
1826 ro_gui_throb();
1827 ro_mouse_poll();
1828 break;
1829
1830 case wimp_REDRAW_WINDOW_REQUEST:
1831 ro_gui_wimp_event_redraw_window(&block->redraw);
1832 break;
1833
1834 case wimp_OPEN_WINDOW_REQUEST:
1835 ro_gui_open_window_request(&block->open);
1836 break;
1837
1838 case wimp_CLOSE_WINDOW_REQUEST:
1839 ro_gui_close_window_request(&block->close);
1840 break;
1841
1842 case wimp_POINTER_LEAVING_WINDOW:
1843 ro_mouse_pointer_leaving_window(&block->leaving);
1844 break;
1845
1846 case wimp_POINTER_ENTERING_WINDOW:
1847 ro_gui_wimp_event_pointer_entering_window(&block->entering);
1848 break;
1849
1850 case wimp_MOUSE_CLICK:
1851 ro_gui_wimp_event_mouse_click(&block->pointer);
1852 break;
1853
1854 case wimp_USER_DRAG_BOX:
1855 ro_mouse_drag_end(&block->dragged);
1856 break;
1857
1858 case wimp_KEY_PRESSED:
1859 ro_gui_keypress(&(block->key));
1860 break;
1861
1862 case wimp_MENU_SELECTION:
1863 ro_gui_menu_selection(&(block->selection));
1864 break;
1865
1866 /* Scroll requests fall back to a generic handler because we
1867 * might get these events for any window from a scroll-wheel.
1868 */
1869
1870 case wimp_SCROLL_REQUEST:
1871 if (!ro_gui_wimp_event_scroll_window(&(block->scroll)))
1872 ro_gui_scroll(&(block->scroll));
1873 break;
1874
1875 case wimp_USER_MESSAGE:
1876 case wimp_USER_MESSAGE_RECORDED:
1877 case wimp_USER_MESSAGE_ACKNOWLEDGE:
1878 ro_gui_user_message(event, &(block->message));
1879 break;
1880 }
1881 }
1882
1883
1884 /**
1885 * Poll the RISC OS wimp for events.
1886 */
riscos_poll(void)1887 static void riscos_poll(void)
1888 {
1889 wimp_event_no event;
1890 wimp_block block;
1891 const wimp_poll_flags mask = wimp_MASK_LOSE | wimp_MASK_GAIN | wimp_SAVE_FP;
1892 os_t track_poll_offset;
1893
1894 /* Poll wimp. */
1895 xhourglass_off();
1896 track_poll_offset = ro_mouse_poll_interval();
1897 if (sched_active || (track_poll_offset > 0)) {
1898 os_t t = os_read_monotonic_time();
1899
1900 if (track_poll_offset > 0) {
1901 t += track_poll_offset;
1902 } else {
1903 t += 10;
1904 }
1905
1906 if (sched_active && (sched_time - t) < 0) {
1907 t = sched_time;
1908 }
1909
1910 event = wimp_poll_idle(mask, &block, t, 0);
1911 } else {
1912 event = wimp_poll(wimp_MASK_NULL | mask, &block, 0);
1913 }
1914
1915 xhourglass_on();
1916 gui_last_poll = clock();
1917 ro_gui_handle_event(event, &block);
1918
1919 /* Only run scheduled callbacks on a null poll
1920 * We cannot do this in the null event handler, as that may be called
1921 * from gui_multitask(). Scheduled callbacks must only be run from the
1922 * top-level.
1923 */
1924 if (event == wimp_NULL_REASON_CODE) {
1925 schedule_run();
1926 }
1927
1928 ro_gui_window_update_boxes();
1929 }
1930
1931
1932 /**
1933 * Handle Open_Window_Request events.
1934 */
ro_gui_open_window_request(wimp_open * open)1935 void ro_gui_open_window_request(wimp_open *open)
1936 {
1937 os_error *error;
1938
1939 if (ro_gui_wimp_event_open_window(open))
1940 return;
1941
1942 error = xwimp_open_window(open);
1943 if (error) {
1944 NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
1945 error->errnum, error->errmess);
1946 ro_warn_user("WimpError", error->errmess);
1947 return;
1948 }
1949 }
1950
1951
1952 /**
1953 * source bounce callback.
1954 */
ro_gui_view_source_bounce(wimp_message * message)1955 static void ro_gui_view_source_bounce(wimp_message *message)
1956 {
1957 char *filename;
1958 os_error *error;
1959 char command[256];
1960
1961 /* run the file as text */
1962 filename = ((wimp_full_message_data_xfer *)message)->file_name;
1963 sprintf(command, "@RunType_FFF %s", filename);
1964 error = xwimp_start_task(command, 0);
1965 if (error) {
1966 NSLOG(netsurf, INFO, "xwimp_start_task failed: 0x%x: %s",
1967 error->errnum, error->errmess);
1968 ro_warn_user("WimpError", error->errmess);
1969 }
1970 }
1971
1972
1973 /**
1974 * Send the source of a content to a text editor.
1975 */
ro_gui_view_source(struct hlcache_handle * c)1976 void ro_gui_view_source(struct hlcache_handle *c)
1977 {
1978 os_error *error;
1979 char *temp_name;
1980 wimp_full_message_data_xfer message;
1981 int objtype;
1982 bool done = false;
1983
1984 const uint8_t *source_data;
1985 size_t source_size;
1986
1987 if (!c) {
1988 ro_warn_user("MiscError", "No document source");
1989 return;
1990 }
1991
1992 source_data = content_get_source_data(c, &source_size);
1993
1994 if (!source_data) {
1995 ro_warn_user("MiscError", "No document source");
1996 return;
1997 }
1998
1999 /* try to load local files directly. */
2000 if (netsurf_nsurl_to_path(hlcache_handle_get_url(c), &temp_name) == NSERROR_OK) {
2001 error = xosfile_read_no_path(temp_name, &objtype, 0, 0, 0, 0);
2002 if ((!error) && (objtype == osfile_IS_FILE)) {
2003 snprintf(message.file_name, 212, "%s", temp_name);
2004 message.file_name[211] = '\0';
2005 done = true;
2006 }
2007 free(temp_name);
2008 }
2009 if (!done) {
2010 /* We cannot release the requested filename until after it
2011 * has finished being used. As we can't easily find out when
2012 * this is, we simply don't bother releasing it and simply
2013 * allow it to be re-used next time NetSurf is started. The
2014 * memory overhead from doing this is under 1 byte per
2015 * filename. */
2016 char *r;
2017 char full_name[256];
2018 const char *filename = filename_request();
2019 if (!filename) {
2020 ro_warn_user("NoMemory", 0);
2021 return;
2022 }
2023
2024 snprintf(full_name, 256, "%s/%s", TEMP_FILENAME_PREFIX,
2025 filename);
2026 full_name[255] = '\0';
2027 r = __riscosify(full_name, 0, __RISCOSIFY_NO_SUFFIX,
2028 message.file_name, 212, 0);
2029 if (r == 0) {
2030 NSLOG(netsurf, INFO, "__riscosify failed");
2031 return;
2032 }
2033 message.file_name[211] = '\0';
2034
2035 error = xosfile_save_stamped(message.file_name,
2036 ro_content_filetype(c),
2037 (byte *) source_data,
2038 (byte *) source_data + source_size);
2039 if (error) {
2040 NSLOG(netsurf, INFO,
2041 "xosfile_save_stamped failed: 0x%x: %s",
2042 error->errnum,
2043 error->errmess);
2044 ro_warn_user("MiscError", error->errmess);
2045 return;
2046 }
2047 }
2048
2049 /* begin the DataOpen protocol */
2050 message.your_ref = 0;
2051 message.size = 44 + ((strlen(message.file_name) + 4) & (~3u));
2052 message.action = message_DATA_OPEN;
2053 message.w = 0;
2054 message.i = 0;
2055 message.pos.x = 0;
2056 message.pos.y = 0;
2057 message.est_size = 0;
2058 message.file_type = 0xfff;
2059 ro_message_send_message(wimp_USER_MESSAGE_RECORDED,
2060 (wimp_message*)&message, 0,
2061 ro_gui_view_source_bounce);
2062 }
2063
2064
2065 /**
2066 * Broadcast an URL that we can't handle.
2067 */
gui_launch_url(struct nsurl * url)2068 static nserror gui_launch_url(struct nsurl *url)
2069 {
2070 /* Try ant broadcast */
2071 ro_url_broadcast(nsurl_access(url));
2072 return NSERROR_OK;
2073 }
2074
2075
2076 /**
2077 * Choose the language to use.
2078 */
ro_gui_choose_language(void)2079 static void ro_gui_choose_language(void)
2080 {
2081 /* if option_language exists and is valid, use that */
2082 if (nsoption_charp(language)) {
2083 char path[40];
2084 if (2 < strlen(nsoption_charp(language)))
2085 nsoption_charp(language)[2] = 0;
2086 sprintf(path, "NetSurf:Resources.%s", nsoption_charp(language));
2087
2088 if (is_dir(path)) {
2089 nsoption_setnull_charp(accept_language,
2090 strdup(nsoption_charp(language)));
2091 return;
2092 }
2093 nsoption_set_charp(language, NULL);
2094 }
2095
2096 nsoption_set_charp(language, strdup(ro_gui_default_language()));
2097 if (nsoption_charp(language) == NULL)
2098 die("Out of memory");
2099 nsoption_set_charp(accept_language, strdup(nsoption_charp(language)));
2100 if (nsoption_charp(accept_language) == NULL)
2101 die("Out of memory");
2102 }
2103
2104
2105 /**
2106 * Display a warning for a serious problem (eg memory exhaustion).
2107 *
2108 * \param warning message key for warning message
2109 * \param detail additional message, or 0
2110 */
ro_warn_user(const char * warning,const char * detail)2111 nserror ro_warn_user(const char *warning, const char *detail)
2112 {
2113 NSLOG(netsurf, INFO, "%s %s", warning, detail);
2114
2115 if (dialog_warning) {
2116 char warn_buffer[300];
2117 snprintf(warn_buffer, sizeof warn_buffer, "%s %s",
2118 messages_get(warning),
2119 detail ? detail : "");
2120 warn_buffer[sizeof warn_buffer - 1] = 0;
2121 ro_gui_set_icon_string(dialog_warning, ICON_WARNING_MESSAGE,
2122 warn_buffer, true);
2123 xwimp_set_icon_state(dialog_warning, ICON_WARNING_HELP,
2124 wimp_ICON_DELETED, wimp_ICON_DELETED);
2125 ro_gui_dialog_open(dialog_warning);
2126 xos_bell();
2127 } else {
2128 /* probably haven't initialised (properly), use a
2129 non-multitasking error box */
2130 os_error error;
2131 snprintf(error.errmess, sizeof error.errmess, "%s %s",
2132 messages_get(warning),
2133 detail ? detail : "");
2134 error.errmess[sizeof error.errmess - 1] = 0;
2135 xwimp_report_error_by_category(&error,
2136 wimp_ERROR_BOX_OK_ICON |
2137 wimp_ERROR_BOX_GIVEN_CATEGORY |
2138 wimp_ERROR_BOX_CATEGORY_ERROR <<
2139 wimp_ERROR_BOX_CATEGORY_SHIFT,
2140 "NetSurf", "!netsurf",
2141 (osspriteop_area *) 1, 0, 0);
2142 }
2143
2144 return NSERROR_OK;
2145 }
2146
2147
2148 /**
2149 * Display an error and exit.
2150 *
2151 * Should only be used during initialisation.
2152 */
die(const char * const error)2153 void die(const char * const error)
2154 {
2155 os_error warn_error;
2156
2157 NSLOG(netsurf, INFO, "%s", error);
2158
2159 warn_error.errnum = 1; /* \todo: reasonable ? */
2160 strncpy(warn_error.errmess, messages_get(error),
2161 sizeof(warn_error.errmess)-1);
2162 warn_error.errmess[sizeof(warn_error.errmess)-1] = '\0';
2163 xwimp_report_error_by_category(&warn_error,
2164 wimp_ERROR_BOX_OK_ICON |
2165 wimp_ERROR_BOX_GIVEN_CATEGORY |
2166 wimp_ERROR_BOX_CATEGORY_ERROR <<
2167 wimp_ERROR_BOX_CATEGORY_SHIFT,
2168 "NetSurf", "!netsurf",
2169 (osspriteop_area *) 1, 0, 0);
2170 exit(EXIT_FAILURE);
2171 }
2172
2173
2174 /**
2175 * Test whether it's okay to shutdown, prompting the user if not.
2176 *
2177 * \return true iff it's okay to shutdown immediately
2178 */
ro_gui_prequit(void)2179 bool ro_gui_prequit(void)
2180 {
2181 return ro_gui_download_prequit();
2182 }
2183
2184
2185 /**
2186 * Generate a riscos path from one or more component elemnts.
2187 *
2188 * Constructs a complete path element from passed components. The
2189 * second (and subsequent) components have a slash substituted for all
2190 * riscos directory separators.
2191 *
2192 * If a string is allocated it must be freed by the caller.
2193 *
2194 * @param[in,out] str pointer to string pointer if this is NULL enough
2195 * storage will be allocated for the complete path.
2196 * @param[in,out] size The size of the space available if \a str not
2197 * NULL on input and if not NULL set to the total
2198 * output length on output.
2199 * @param[in] nelm The number of elements.
2200 * @param[in] ap The elements of the path as string pointers.
2201 * @return NSERROR_OK and the complete path is written to str
2202 * or error code on faliure.
2203 */
riscos_mkpath(char ** str,size_t * size,size_t nelm,va_list ap)2204 static nserror riscos_mkpath(char **str, size_t *size, size_t nelm, va_list ap)
2205 {
2206 const char *elm[16];
2207 size_t elm_len[16];
2208 size_t elm_idx;
2209 char *fname;
2210 size_t fname_len = 0;
2211 char *curp;
2212 size_t idx;
2213
2214 /* check the parameters are all sensible */
2215 if ((nelm == 0) || (nelm > 16)) {
2216 return NSERROR_BAD_PARAMETER;
2217 }
2218 if ((*str != NULL) && (size == NULL)) {
2219 /* if the caller is providing the buffer they must say
2220 * how much space is available.
2221 */
2222 return NSERROR_BAD_PARAMETER;
2223 }
2224
2225 /* calculate how much storage we need for the complete path
2226 * with all the elements.
2227 */
2228 for (elm_idx = 0; elm_idx < nelm; elm_idx++) {
2229 elm[elm_idx] = va_arg(ap, const char *);
2230 /* check the argument is not NULL */
2231 if (elm[elm_idx] == NULL) {
2232 return NSERROR_BAD_PARAMETER;
2233 }
2234 elm_len[elm_idx] = strlen(elm[elm_idx]);
2235 fname_len += elm_len[elm_idx];
2236 }
2237 fname_len += nelm; /* allow for separators and terminator */
2238
2239 /* ensure there is enough space */
2240 fname = *str;
2241 if (fname != NULL) {
2242 if (fname_len > *size) {
2243 return NSERROR_NOSPACE;
2244 }
2245 } else {
2246 fname = malloc(fname_len);
2247 if (fname == NULL) {
2248 return NSERROR_NOMEM;
2249 }
2250 }
2251
2252 /* copy the elements in with directory separator */
2253 curp = fname;
2254
2255 /* first element is not altered */
2256 memmove(curp, elm[0], elm_len[0]);
2257 curp += elm_len[0];
2258 /* ensure there is a delimiter */
2259 if (curp[-1] != DIR_SEP) {
2260 *curp = DIR_SEP;
2261 curp++;
2262 }
2263
2264 /* subsequent elemnts have slashes substituted with directory
2265 * separators.
2266 */
2267 for (elm_idx = 1; elm_idx < nelm; elm_idx++) {
2268 for (idx = 0; idx < elm_len[elm_idx]; idx++) {
2269 if (elm[elm_idx][idx] == DIR_SEP) {
2270 *curp = '/';
2271 } else {
2272 *curp = elm[elm_idx][idx];
2273 }
2274 curp++;
2275 }
2276 *curp = DIR_SEP;
2277 curp++;
2278 }
2279 curp[-1] = 0; /* NULL terminate */
2280
2281 assert((curp - fname) <= (int)fname_len);
2282
2283 *str = fname;
2284 if (size != NULL) {
2285 *size = fname_len;
2286 }
2287
2288 return NSERROR_OK;
2289
2290 }
2291
2292
2293 /**
2294 * Get the basename of a file using posix path handling.
2295 *
2296 * This gets the last element of a path and returns it. The returned
2297 * element has all forward slashes translated into riscos directory
2298 * separators.
2299 *
2300 * @param[in] path The path to extract the name from.
2301 * @param[in,out] str Pointer to string pointer if this is NULL enough
2302 * storage will be allocated for the path element.
2303 * @param[in,out] size The size of the space available if \a
2304 * str not NULL on input and set to the total
2305 * output length on output.
2306 * @return NSERROR_OK and the complete path is written to str
2307 * or error code on faliure.
2308 */
riscos_basename(const char * path,char ** str,size_t * size)2309 static nserror riscos_basename(const char *path, char **str, size_t *size)
2310 {
2311 const char *leafname;
2312 char *fname;
2313 char *temp;
2314
2315 if (path == NULL) {
2316 return NSERROR_BAD_PARAMETER;
2317 }
2318
2319 leafname = strrchr(path, DIR_SEP);
2320 if (!leafname) {
2321 leafname = path;
2322 } else {
2323 leafname += 1;
2324 }
2325
2326 fname = strdup(leafname);
2327 if (fname == NULL) {
2328 return NSERROR_NOMEM;
2329 }
2330
2331 /** @todo check this leafname translation is actually required */
2332 /* and s/\//\./g */
2333 for (temp = fname; *temp != 0; temp++) {
2334 if (*temp == '/') {
2335 *temp = DIR_SEP;
2336 }
2337 }
2338
2339 *str = fname;
2340 if (size != NULL) {
2341 *size = strlen(fname);
2342 }
2343 return NSERROR_OK;
2344 }
2345
2346
2347 /**
2348 * Ensure that all directory elements needed to store a filename exist.
2349 *
2350 * Given a path of x.y.z directories x and x.y will be created.
2351 *
2352 * @param fname The filename to ensure the path to exists.
2353 * @return NSERROR_OK on success or error code on failure.
2354 */
riscos_mkdir_all(const char * fname)2355 static nserror riscos_mkdir_all(const char *fname)
2356 {
2357 char *dname;
2358 char *cur;
2359
2360 dname = strdup(fname);
2361
2362 cur = dname;
2363 while ((cur = strchr(cur, '.'))) {
2364 *cur = '\0';
2365 xosfile_create_dir(dname, 0);
2366 *cur++ = '.';
2367 }
2368
2369 free(dname);
2370
2371 return NSERROR_OK;
2372 }
2373
2374 /**
2375 * Find screen size in OS units.
2376 */
ro_gui_screen_size(int * width,int * height)2377 void ro_gui_screen_size(int *width, int *height)
2378 {
2379 *width = screen_info.width;
2380 *height = screen_info.height;
2381 }
2382
2383
2384 /**
2385 * Send the debug dump of a content to a text editor.
2386 */
ro_gui_dump_browser_window(struct browser_window * bw)2387 void ro_gui_dump_browser_window(struct browser_window *bw)
2388 {
2389 os_error *error;
2390
2391 /* open file for dump */
2392 FILE *stream = fopen("<Wimp$ScrapDir>.WWW.NetSurf.dump", "w");
2393 if (!stream) {
2394 NSLOG(netsurf, INFO, "fopen: errno %i", errno);
2395 ro_warn_user("SaveError", strerror(errno));
2396 return;
2397 }
2398
2399 browser_window_debug_dump(bw, stream, CONTENT_DEBUG_RENDER);
2400
2401 fclose(stream);
2402
2403 /* launch file in editor */
2404 error = xwimp_start_task("Filer_Run <Wimp$ScrapDir>.WWW.NetSurf.dump",
2405 0);
2406 if (error) {
2407 NSLOG(netsurf, INFO, "xwimp_start_task failed: 0x%x: %s",
2408 error->errnum, error->errmess);
2409 ro_warn_user("WimpError", error->errmess);
2410 }
2411 }
2412
2413
2414 static struct gui_file_table riscos_file_table = {
2415 .mkpath = riscos_mkpath,
2416 .basename = riscos_basename,
2417 .nsurl_to_path = ro_nsurl_to_path,
2418 .path_to_nsurl = ro_path_to_nsurl,
2419 .mkdir_all = riscos_mkdir_all,
2420 };
2421
2422 static struct gui_fetch_table riscos_fetch_table = {
2423 .filetype = fetch_filetype,
2424
2425 .get_resource_url = gui_get_resource_url,
2426 .mimetype = fetch_mimetype,
2427 };
2428
2429 static struct gui_misc_table riscos_misc_table = {
2430 .schedule = riscos_schedule,
2431
2432 .quit = gui_quit,
2433 .launch_url = gui_launch_url,
2434 .present_cookies = ro_gui_cookies_present,
2435 };
2436
2437
get_cachepath(void)2438 static char *get_cachepath(void)
2439 {
2440 char *cachedir;
2441 char *cachepath = NULL;
2442 nserror ret;
2443
2444 cachedir = getenv("Cache$Dir");
2445 if ((cachedir == NULL) || (cachedir[0] == 0)) {
2446 NSLOG(netsurf, INFO, "cachedir was null");
2447 return NULL;
2448 }
2449 ret = netsurf_mkpath(&cachepath, NULL, 2, cachedir, "NetSurf");
2450 if (ret != NSERROR_OK) {
2451 return NULL;
2452 }
2453 return cachepath;
2454 }
2455
2456 /**
2457 * Normal entry point from RISC OS.
2458 */
main(int argc,char ** argv)2459 int main(int argc, char** argv)
2460 {
2461 char *cachepath;
2462 char path[40];
2463 int length;
2464 os_var_type type;
2465 int used = -1; /* slightly better with older OSLib versions */
2466 os_error *error;
2467 nserror ret;
2468 struct netsurf_table riscos_table = {
2469 .misc = &riscos_misc_table,
2470 .window = riscos_window_table,
2471 .clipboard = riscos_clipboard_table,
2472 .download = riscos_download_table,
2473 .fetch = &riscos_fetch_table,
2474 .file = &riscos_file_table,
2475 .utf8 = riscos_utf8_table,
2476 .search = riscos_search_table,
2477 .llcache = filesystem_llcache_table,
2478 .bitmap = riscos_bitmap_table,
2479 .layout = riscos_layout_table,
2480 };
2481
2482 ret = netsurf_register(&riscos_table);
2483 if (ret != NSERROR_OK) {
2484 die("NetSurf operation table failed registration");
2485 }
2486
2487 /* Consult NetSurf$Logging environment variable to decide if logging
2488 * is required. */
2489 error = xos_read_var_val_size("NetSurf$Logging", 0, os_VARTYPE_STRING,
2490 &used, NULL, &type);
2491 if (error != NULL || type != os_VARTYPE_STRING || used != -2) {
2492 verbose_log = true;
2493 } else {
2494 char logging_env[2];
2495 error = xos_read_var_val("NetSurf$Logging", logging_env,
2496 sizeof(logging_env), 0, os_VARTYPE_STRING,
2497 &used, NULL, &type);
2498 if (error != NULL || logging_env[0] != '0') {
2499 verbose_log = true;
2500 } else {
2501 verbose_log = false;
2502 }
2503 }
2504
2505 /* initialise logging. Not fatal if it fails but not much we
2506 * can do about it either.
2507 */
2508 nslog_init(nslog_stream_configure, &argc, argv);
2509
2510 /* user options setup */
2511 ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
2512 if (ret != NSERROR_OK) {
2513 die("Options failed to initialise");
2514 }
2515 nsoption_read("NetSurf:Choices", NULL);
2516 nsoption_commandline(&argc, argv, NULL);
2517
2518 /* Choose the interface language to use */
2519 ro_gui_choose_language();
2520
2521 /* select language-specific Messages */
2522 if (((length = snprintf(path,
2523 sizeof(path),
2524 "NetSurf:Resources.%s.Messages",
2525 nsoption_charp(language))) < 0) ||
2526 (length >= (int)sizeof(path))) {
2527 die("Failed to locate Messages resource.");
2528 }
2529
2530 /* initialise messages */
2531 messages_add_from_file(path);
2532
2533 /* obtain cache path */
2534 cachepath = get_cachepath();
2535
2536 /* common initialisation */
2537 ret = netsurf_init(cachepath);
2538 free(cachepath);
2539 if (ret != NSERROR_OK) {
2540 die("NetSurf failed to initialise core");
2541 }
2542
2543 artworks_init();
2544 draw_init();
2545 sprite_init();
2546
2547 /* Load some extra RISC OS specific Messages */
2548 messages_add_from_file("NetSurf:Resources.LangNames");
2549
2550 ret = gui_init(argc, argv);
2551 if (ret != NSERROR_OK) {
2552 ro_warn_user(messages_get_errorcode(ret), 0);
2553 }
2554
2555 while (!riscos_done) {
2556 riscos_poll();
2557 }
2558
2559 netsurf_exit();
2560 nsoption_finalise(nsoptions, nsoptions_default);
2561
2562 /* finalise logging */
2563 nslog_finalise();
2564
2565 return 0;
2566 }
2567