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