1 #include "links.h"
2 
3 unsigned char system_name[MAX_STR_LEN];
4 
get_system_name()5 void get_system_name()
6 {
7 #ifdef OS2
8 	if (!os_get_system_name(system_name))
9 		return;
10 #endif
11 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
12 	{
13 		struct utsname name;
14 		int rs;
15 		memset(&name, 0, sizeof name);
16 		EINTRLOOP(rs, uname(&name));
17 		if (!rs) {
18 #ifdef OPENVMS
19 			unsigned char * volatile p;
20 #endif
21 			unsigned char *str = init_str();
22 			int l = 0;
23 			add_to_str(&str, &l, name.sysname);
24 			add_to_str(&str, &l, " ");
25 #ifdef OPENVMS
26 			add_to_str(&str, &l, name.version);
27 #else
28 			add_to_str(&str, &l, name.release);
29 #endif
30 			add_to_str(&str, &l, " ");
31 #ifdef OPENVMS
32 			p = name.nodename + sizeof(name.nodename);
33 			if ((unsigned char *)(&name + 1) - p >= 16 && memchr(p, 0, 16))
34 				add_to_str(&str, &l, cast_uchar p);
35 			else
36 #endif
37 			add_to_str(&str, &l, name.machine);
38 			safe_strncpy(system_name, str, MAX_STR_LEN);
39 			mem_free(str);
40 			return;
41 		}
42 	}
43 #endif
44 #ifdef HAVE_POPEN
45 	if (0) {
46 		FILE *f;
47 		unsigned char *p;
48 		memset(system_name, 0, MAX_STR_LEN);
49 		ENULLLOOP(f, popen("uname -srm", "r"));
50 		if (!f) goto fail;
51 		if (fread(system_name, 1, MAX_STR_LEN - 1, f) <= 0) {
52 			pclose(f);
53 			goto fail;
54 		}
55 		pclose(f);
56 		for (p = system_name; *p; p++) if (*p < ' ') {
57 			*p = 0;
58 			break;
59 		}
60 		if (system_name[0]) return;
61 	}
62 	fail:
63 #endif
64 	strcpy(system_name, SYSTEM_NAME);
65 }
66 
67 unsigned char compiler_name[MAX_STR_LEN];
68 
get_compiler_name(void)69 static void get_compiler_name(void)
70 {
71 #if defined(__BORLANDC__)
72 
73 	int w = __BORLANDC__+0;
74 	int v1 = w / 0x100;
75 	int v2 = w / 0x10 % 0x10;
76 	int v3 = w % 0x10;
77 	if (v1 == 4 && v2 < 5) v1 = 3;
78 	if (v1 == 4 && v2 == 5) v2 = 0;
79 
80 	if (!v3) sprintf(cast_char compiler_name, "Borland C %d.%d", v1, v2);
81 	else sprintf(cast_char compiler_name, "Borland C %d.%d.%d", v1, v2, v3);
82 
83 #elif defined(__clang__)
84 
85 #if !defined(__clang_major__) || !defined(__clang_minor__)
86 	sprintf(cast_char compiler_name, "LLVM/Clang");
87 #else
88 	int v1 = __clang_major__+0;
89 	int v2 = __clang_minor__+0;
90 #ifdef __clang_patchlevel__
91 	int v3 = __clang_patchlevel__+0;
92 #else
93 	int v3 = 0;
94 #endif
95 	if (v3 > 0) sprintf(cast_char compiler_name, "LLVM/Clang %d.%d.%d", v1, v2, v3);
96 	else sprintf(cast_char compiler_name, "LLVM/Clang %d.%d", v1, v2);
97 #endif
98 
99 #elif defined(__COMO_VERSION__)
100 
101 	int w = __COMO_VERSION__+0;
102 	int v1 = w / 100;
103 	int v2 = w % 100;
104 	if (!(v2 % 10)) sprintf(cast_char compiler_name, "Comeau C %d.%d", v1, v2 / 10);
105 	else sprintf(cast_char compiler_name, "Comeau C %d.%02d", v1, v2);
106 
107 #elif defined(__convexc__)
108 
109 	sprintf(cast_char compiler_name, "Convex C");
110 
111 #elif defined(_CRAYC)
112 
113 #if !defined(_RELEASE) || !defined(_RELEASE_MINOR)
114 	sprintf(cast_char compiler_name, "Cray C");
115 #else
116 	int v1 = _RELEASE+0;
117 	int v2 = _RELEASE_MINOR+0;
118 	sprintf(cast_char compiler_name, "Cray C %d.%d", v1, v2);
119 #endif
120 
121 #elif defined(__DCC__)
122 
123 #ifndef __VERSION_NUMBER__
124 	sprintf(cast_char compiler_name, "Diab C");
125 #else
126 	int w = __VERSION_NUMBER__+0;
127 	int v1 = w / 1000;
128 	int v2 = w / 100 % 10;
129 	int v3 = w % 100;
130 	sprintf(cast_char compiler_name, "Diab C %d.%d.%02d", v1, v2, v3);
131 #endif
132 
133 #elif defined(__DMC__)
134 
135 	int w = __DMC__+0;
136 	int v1 = w / 0x100;
137 	int v2 = w / 0x10 % 0x10;
138 	int v3 = w % 0x10;
139 	if (!v3) sprintf(cast_char compiler_name, "Digital Mars C %d.%d", v1, v2);
140 	else sprintf(cast_char compiler_name, "Digital Mars C %d.%d.%d", v1, v2, v3);
141 
142 #elif defined(__DECC_VER)
143 
144 	int w = __DECC_VER+0;
145 	int v1 = w / 10000000;
146 	int v2 = w / 100000 % 100;
147 	int v3 = w % 10000;
148 	sprintf(cast_char compiler_name, "DEC C %d.%d-%03d", v1, v2, v3);
149 
150 #elif defined(__ghs__)
151 
152 #ifndef __GHS_VERSION_NUMBER__
153 	sprintf(cast_char compiler_name, "Green Hill C");
154 #else
155 	int w = __GHS_VERSION_NUMBER__+0;
156 	int v1 = w / 100;
157 	int v2 = w / 10 % 10;
158 	int v3 = w % 10;
159 	sprintf(cast_char compiler_name, "Green Hill C %d.%d.%d", v1, v2, v3);
160 #endif
161 
162 #elif defined(__HIGHC__)
163 
164 	sprintf(cast_char compiler_name, "MetaWare High C");
165 
166 #elif defined(__HP_cc)
167 
168 	int w = __HP_cc+0;
169 	int v1 = w / 10000;
170 	int v2 = w / 100 % 100;
171 	int v3 = w % 100;
172 	if (w <= 1) sprintf(cast_char compiler_name, "HP CC");
173 	else sprintf(cast_char compiler_name, "HP CC %d.%02d.%02d", v1, v2, v3);
174 
175 #elif defined(__xlc__)
176 
177 	int w = __xlc__+0;
178 	int v1 = w / 0x100;
179 	int v2 = w % 0x100;
180 	sprintf(cast_char compiler_name, "IBM XL C %X.%X", v1, v2);
181 
182 #elif defined(__IBMC__) && defined(__COMPILER_VER__)
183 
184 	unsigned w = __COMPILER_VER__+0;
185 	int v0 = w / 0x10000000;
186 	int v1 = w / 0x1000000 % 0x10;
187 	int v2 = w / 0x10000 % 0x100;
188 	int v3 = w % 0x10000;
189 	unsigned char *os = !v0 ? "S/370" : v0 == 1 ? "OS/390" : v0 == 4 ? "z/OS" : "";
190 	sprintf(cast_char compiler_name, "IBM%s%s XL C %X.%0X.%X", *os ? " " : "", os, v1, v2, v3);
191 
192 #elif defined(__ICC)
193 
194 	int w = __ICC+0;
195 	int v1 = w / 100;
196 	int v2 = w % 100;
197 	if (!(v2 % 10)) sprintf(cast_char compiler_name, "Intel C %d.%d", v1, v2 / 10);
198 	else sprintf(cast_char compiler_name, "Intel C %d.%02d", v1, v2);
199 
200 #elif defined(__LCC__)
201 
202 	sprintf(cast_char compiler_name, "LCC");
203 
204 #elif defined(__NDPC__)
205 
206 	sprintf(cast_char compiler_name, "Microway NDP C");
207 
208 #elif defined(_MSC_VER)
209 
210 	int w = _MSC_VER+0;
211 	int v1 = w / 100;
212 	int v2 = w % 100;
213 	unsigned char *visual = cast_uchar "";
214 	if (v1 >= 8) {
215 		v1 -= 6;
216 		if (v1 == 2) v1 = 1;
217 		visual = cast_uchar "Visual ";
218 	}
219 	if (!(v2 % 10)) sprintf(cast_char compiler_name, "Microsoft %sC %d.%d", visual, v1, v2 / 10);
220 	else sprintf(cast_char compiler_name, "Microsoft %sC %d.%02d", visual, v1, v2);
221 
222 #elif defined(__MWERKS__)
223 
224 	int w = __MWERKS__+0;
225 	int v1 = w / 0x1000;
226 	int v2 = w / 0x100 % 0x10;
227 	int v3 = w % 0x100;
228 	if (w <= 1) sprintf(cast_char compiler_name, "Metrowerks CodeWarrior");
229 	sprintf(cast_char compiler_name, "Metrowerks CodeWarrior %x.%x.%x", v1, v2, v3);
230 
231 #elif defined(__NWCC__)
232 
233 	sprintf(cast_char compiler_name, "NWCC");
234 
235 #elif defined(__OPEN64__)
236 
237 	unsigned char *n = cast_uchar "Open64 " __OPEN64__;
238 	if (strlen(cast_const_char n) >= sizeof(cast_char compiler_name)) n = cast_uchar "Open64";
239 	strcpy(cast_char compiler_name, cast_const_char n);
240 
241 #elif defined(__PATHSCALE__)
242 
243 	unsigned char *n = cast_uchar "PathScale " __PATHSCALE__;
244 	if (strlen(cast_const_char n) >= sizeof(cast_char compiler_name)) n = cast_uchar "PathScale";
245 	strcpy(cast_char compiler_name, cast_const_char n);
246 
247 #elif defined(__PCC__)
248 
249 	int v1 = __PCC__+0;
250 #ifdef __PCC_MINOR__
251 	int v2 = __PCC_MINOR__+0;
252 #else
253 	int v2 = 0;
254 #endif
255 #ifdef __PCC_MINORMINOR__
256 	int v3 = __PCC_MINORMINOR__+0;
257 #else
258 	int v3 = 0;
259 #endif
260 	sprintf(cast_char compiler_name, "PCC %d.%d.%d", v1, v2, v3);
261 
262 #elif defined(__PGI) || defined(__PGIC__)
263 
264 #if !defined(__PGIC__) || !defined(__PGIC_MINOR__)
265 	sprintf(cast_char compiler_name, "The Portland Group C");
266 #else
267 	int v1 = __PGIC__+0;
268 	int v2 = __PGIC_MINOR__+0;
269 #ifdef __PGIC_PATCHLEVEL__
270 	int v3 = __PGIC_PATCHLEVEL__+0;
271 #else
272 	int v3 = 0;
273 #endif
274 	if (v3 > 0) sprintf(cast_char compiler_name, "The Portland Group C %d.%d.%d", v1, v2, v3);
275 	else sprintf(cast_char compiler_name, "The Portland Group C %d.%d", v1, v2);
276 #endif
277 
278 #elif defined(__SASC__)
279 
280 	int w = __SASC__+0;
281 	int v1 = w / 100;
282 	int v2 = w % 100;
283 	sprintf(cast_char compiler_name, "SAS C %d.%02d", v1, v2);
284 
285 #elif (defined(__sgi) && defined(_COMPILER_VERSION)) || defined(_SGI_COMPILER_VERSION)
286 
287 #ifdef _SGI_COMPILER_VERSION
288 	int w = _SGI_COMPILER_VERSION;
289 #else
290 	int w = _COMPILER_VERSION;
291 #endif
292 	int v1 = w / 100;
293 	int v2 = w / 10 % 10;
294 	int v3 = w % 10;
295 	sprintf(cast_char compiler_name, "MIPSpro %d.%d.%d", v1, v2, v3);
296 
297 #elif defined(__SUNPRO_C)
298 
299 	int w = __SUNPRO_C+0;
300 	int div = w >= 0x1000 ? 0x1000 : 0x100;
301 	int v2_digits = w >= 0x1000 ? 2 : 1;
302 	int v1 = w / div;
303 	int v2 = w % div / 0x10;
304 	int v3 = w % 0x10;
305 	if (!v3) sprintf(cast_char compiler_name, "Sun C %X.%0*X", v1, v2_digits, v2);
306 	else sprintf(cast_char compiler_name, "Sun C %X.%0*X.%X", v1, v2_digits, v2, v3);
307 
308 #elif defined(__SYSC__) && defined(__SYSC_VER__)
309 
310 	int w = __SYSC_VER__+0;
311 	int v1 = w / 10000;
312 	int v2 = w / 100 % 100;
313 	int v3 = w % 100;
314 	sprintf(cast_char compiler_name, "Dignus Systems C %d.%02d.%02d", v1, v2, v3);
315 
316 #elif defined(__TenDRA__)
317 
318 	sprintf(cast_char compiler_name, "TenDRA C");
319 
320 #elif defined(__TINYC__)
321 
322 	sprintf(cast_char compiler_name, "Tiny C");
323 
324 #elif defined(_UCC)
325 
326 #if !defined(_MAJOR_REV) || !defined(_MINOR_REV)
327 	sprintf(cast_char compiler_name, "Ultimate C");
328 #else
329 	int v1 = _MAJOR_REV+0;
330 	int v2 = _MAJOR_REV+0;
331 	sprintf(cast_char compiler_name, "Ultimate C %d.%d", v1, v2);
332 #endif
333 
334 #elif defined(__USLC__)
335 
336 	sprintf(cast_char compiler_name, "USL C");
337 
338 #elif defined(__VAXC)
339 
340 	sprintf(cast_char compiler_name, "VAX C");
341 
342 #elif defined(__VOSC__)
343 
344 	sprintf(cast_char compiler_name, "Stratus VOS C");
345 
346 #elif defined(__WATCOMC__)
347 
348 	int w = __WATCOMC__+0;
349 	int v1 = w / 100;
350 	int v2 = w % 100;
351 	unsigned char *op = cast_uchar "";
352 	if (v1 >= 12) {
353 		v1 -= 11;
354 		op = cast_uchar "Open";
355 	}
356 	if (!(v2 % 10)) sprintf(cast_char compiler_name, "%sWatcom C %d.%d", op, v1, v2 / 10);
357 	else sprintf(cast_char compiler_name, "%sWatcom C %d.%02d", op, v1, v2);
358 
359 #elif defined(__GNUC__)
360 
361 	int v1 = __GNUC__+0;
362 #ifdef __GNUC_MINOR__
363 	int v2 = __GNUC_MINOR__+0;
364 #else
365 	int v2 = -1;
366 #endif
367 #ifdef __GNUC_PATCHLEVEL__
368 	int v3 = __GNUC_PATCHLEVEL__+0;
369 #else
370 	int v3 = 0;
371 #endif
372 #if defined(__llvm__)
373 	unsigned char *prefix = cast_uchar "LLVM/";
374 #else
375 	unsigned char *prefix = cast_uchar "";
376 #endif
377 	if (v1 == 2 && (v2 >= 90 && v2 <= 91)) sprintf(cast_char compiler_name, "%sEGCS 1.%d", prefix, v2 - 90);
378 	else if (v3 > 0 && v2 >= 0) sprintf(cast_char compiler_name, "%sGNU C %d.%d.%d", prefix, v1, v2, v3);
379 	else if (v2 >= 0) sprintf(cast_char compiler_name, "%sGNU C %d.%d", prefix, v1, v2);
380 	else sprintf(cast_char compiler_name, "%sGNU C %d", prefix, v1);
381 
382 #else
383 
384 	strcpy(cast_char compiler_name, "unknown compiler");
385 
386 #endif
387 }
388 
389 extern struct option links_options[];
390 extern struct option html_options[];
391 
392 struct option *all_options[] = { links_options, html_options, NULL, };
393 
p_arse_options(int argc,unsigned char * argv[],struct option ** opt)394 unsigned char *p_arse_options(int argc, unsigned char *argv[], struct option **opt)
395 {
396 	unsigned char *e, *u = NULL;
397 	int i;
398 	for (i = 0; i < argc; i++) {
399 		if (strlen(argv[i]) >= MAXINT) {
400 			fprintf(stderr, "Too long parameter\n");
401 			return NULL;
402 		}
403 	}
404 	while (argc) {
405 		argv++, argc--;
406 		if (argv[-1][0] == '-') {
407 			struct option *options;
408 			struct option **op;
409 			for (op = opt; (options = *op); op++) for (i = 0; options[i].p; i++)
410 				if (options[i].rd_cmd && options[i].cmd_name &&
411 				    !strcasecmp(options[i].cmd_name, &argv[-1][1])) {
412 					if ((e = options[i].rd_cmd(&options[i], &argv, &argc))) {
413 						if (e[0]) fprintf(stderr, "Error parsing option %s: %s\n", argv[-1], e);
414 						return NULL;
415 					}
416 					goto found;
417 				}
418 			uu:
419 			fprintf(stderr, "Unknown option %s\n", argv[-1]);
420 			return NULL;
421 		} else if (!u) u = argv[-1];
422 		else goto uu;
423 		found:;
424 	}
425 	if (u) return u;
426 	return "";
427 }
428 
parse_options(int argc,unsigned char * argv[])429 unsigned char *parse_options(int argc, unsigned char *argv[])
430 {
431 	return p_arse_options(argc, argv, all_options);
432 }
433 
get_token(unsigned char ** line)434 unsigned char *get_token(unsigned char **line)
435 {
436 	unsigned char *s = NULL;
437 	int l = 0;
438 	int escape = 0;
439 	int quote = 0;
440 
441 	while (**line == ' ' || **line == 9) (*line)++;
442 	if (**line) {
443 		for (s = init_str(); **line; (*line)++) {
444 			if (escape)
445 				escape = 0;
446 			else if (**line == '\\') {
447 				escape = 1;
448 				continue;
449 			}
450 			else if (**line == '"') {
451 				quote = !quote;
452 			    	continue;
453 			}
454 			else if ((**line == ' ' || **line == 9) && !quote)
455 				break;
456 			add_chr_to_str(&s, &l, **line);
457 		}
458 	}
459 	return s;
460 }
461 
parse_config_file(unsigned char * name,unsigned char * file,struct option ** opt)462 void parse_config_file(unsigned char *name, unsigned char *file, struct option **opt)
463 {
464 	struct option *options;
465 	struct option **op;
466 	int err = 0;
467 	int line = 0;
468 	unsigned char *e;
469 	int i;
470 	unsigned char *n, *p;
471 	unsigned char *tok;
472 	int nl, pl;
473 	while (file[0]) {
474 		line++;
475 		while (file[0] && (file[0] == ' ' || file[0] == 9)) file++;
476 		n = file;
477 		while (file[0] && file[0] > ' ') file++;
478 		if (file == n) {
479 			if (file[0]) file++;
480 			continue;
481 		}
482 		nl = file - n;
483 		while (file[0] == 9 || file[0] == ' ') file++;
484 		p = file;
485 		while (file[0] && file[0] != 10 && file[0] != 13) file++;
486 		pl = file - p;
487 		if (file[0]) {
488 			if ((file[1] == 10 || file[1] == 13) && file[0] != file[1]) file++;
489 			file++;
490 		}
491 		tok = NULL;
492 		if (n[0] == '#') goto f;
493 		if (!(tok = get_token(&n))) goto f;
494 		nl = strlen(tok);
495 		for (op = opt; (options = *op); op++)
496 		    	for (i = 0; options[i].p; i++) if (options[i].cfg_name && (size_t)nl == strlen(options[i].cfg_name) && !casecmp(tok, options[i].cfg_name, nl)) {
497 				unsigned char *o = memacpy(p, pl);
498 				if ((e = options[i].rd_cfg(&options[i], o))) {
499 					if (e[0]) fprintf(stderr, "Error parsing config file %s, line %d: %s\n", name, line, e), err = 1;
500 				}
501 				mem_free(o);
502 				goto f;
503 			}
504 		fprintf(stderr, "Unknown option in config file %s, line %d\n", name, line);
505 		err = 1;
506 		f:
507 		if (tok) mem_free(tok);
508 	}
509 	if (err) fprintf(stderr, "\007"), sleep(3);
510 }
511 
create_config_string(struct option * options)512 unsigned char *create_config_string(struct option *options)
513 {
514 	unsigned char *s = init_str();
515 	int l = 0;
516 	int i;
517 	add_to_str(&s, &l, "# This file is automatically generated by Links -- please do not edit.");
518 	for (i = 0; options[i].p; i++) if (options[i].wr_cfg)
519 		options[i].wr_cfg(&options[i], &s, &l);
520 	add_to_str(&s, &l, NEWLINE);
521 	return s;
522 }
523 
524 #define FILE_BUF	1024
525 
526 unsigned char cfg_buffer[FILE_BUF];
527 
read_config_file(unsigned char * name)528 unsigned char *read_config_file(unsigned char *name)
529 {
530 	int h, r;
531 	int l = 0;
532 	unsigned char *s;
533 	int rs;
534 	EINTRLOOP(h, open(name, O_RDONLY | O_NOCTTY));
535 	if (h == -1) return NULL;
536 	set_bin(h);
537 	s = init_str();
538 	while ((r = hard_read(h, cfg_buffer, FILE_BUF)) > 0) {
539 		int i;
540 		for (i = 0; i < r; i++) if (!cfg_buffer[i]) cfg_buffer[i] = ' ';
541 		add_bytes_to_str(&s, &l, cfg_buffer, r);
542 	}
543 	if (r == -1) mem_free(s), s = NULL;
544 	EINTRLOOP(rs, close(h));
545 	return s;
546 }
547 
write_to_config_file(unsigned char * name,unsigned char * c)548 int write_to_config_file(unsigned char *name, unsigned char *c)
549 {
550 	int rr;
551 	int h, w;
552 	int count = 0;
553 	int tmp_namel;
554 	unsigned char *tmp_name;
555 	int rs, err;
556 try_new_count:
557 	tmp_namel = 0;
558 	tmp_name = init_str();
559 	add_to_str(&tmp_name, &tmp_namel, name);
560 	for (w = tmp_namel - 1; w >= 0; w--) {
561 		if (dir_sep(tmp_name[w]))
562 			break;
563 		if (tmp_name[w] == '.') {
564 			if (w <= tmp_namel - 2) {
565 				tmp_name[w + 2] = 0;
566 				tmp_namel = w + 2;
567 			}
568 			break;
569 		}
570 	}
571 	add_num_to_str(&tmp_name, &tmp_namel, count);
572 	EINTRLOOP(h, open(tmp_name, O_WRONLY | O_NOCTTY | O_CREAT | O_TRUNC | O_EXCL, 0600));
573 	if (h == -1) {
574 		if (errno == EEXIST && count < MAXINT) {
575 			count++;
576 			mem_free(tmp_name);
577 			goto try_new_count;
578 		}
579 		mem_free(tmp_name);
580 		return get_error_from_errno(errno);
581 	}
582 	set_bin(h);
583 	rr = strlen(c);
584 	if (hard_write(h, c, rr) != rr) {
585 		err = errno;
586 		EINTRLOOP(rs, close(h));
587 		goto unlink_err;
588 	}
589 	EINTRLOOP(rs, close(h));
590 	if (rs) {
591 		err = errno;
592 		goto unlink_err;
593 	}
594 #if defined(OPENVMS)
595 	/* delete all versions of the file */
596 	count = 0;
597 	do {
598 		EINTRLOOP(rs, unlink(name));
599 	} while (!rs && ++count < 65536);
600 #elif !defined(RENAME_OVER_EXISTING_FILES)
601 	EINTRLOOP(rs, unlink(name));
602 #endif
603 	EINTRLOOP(rs, rename(tmp_name, name));
604 	if (rs) {
605 		err = errno;
606 		goto unlink_err;
607 	}
608 	mem_free(tmp_name);
609 	return 0;
610 
611 	unlink_err:
612 	EINTRLOOP(rs, unlink(cast_const_char tmp_name));
613 	mem_free(tmp_name);
614 	return get_error_from_errno(err);
615 }
616 
617 #ifdef OPENVMS
translate_vms_to_unix(unsigned char ** str)618 void translate_vms_to_unix(unsigned char **str)
619 {
620 	unsigned char *n;
621 	if (!*str || strchr(cast_const_char *str, '/')) return;
622 	n = cast_uchar decc$translate_vms(cast_const_char *str);
623 	if (!n || (int)n == -1) return;
624 	mem_free(*str);
625 	*str = stracpy(n);
626 }
627 #endif
628 
get_home(int * n)629 unsigned char *get_home(int *n)
630 {
631 	struct stat st;
632 	int rs;
633 	unsigned char *home = NULL;
634 	unsigned char *home_links;
635 	unsigned char *config_dir = stracpy(getenv("CONFIG_DIR"));
636 
637 	if (n) *n = 1;
638 #ifdef WIN32
639 	if (!home) {
640 		home = stracpy(getenv("APPDATA"));
641 #ifdef HAVE_CYGWIN_CONV_PATH
642 		/*
643 		 * Newer Cygwin complains about windows-style path, so
644 		 * we have to convert it.
645 		 */
646 		if (home) {
647 			unsigned char *new_path;
648 			ssize_t sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, home, NULL, 0);
649 			if (sz < 0)
650 				goto skip_path_conv;
651 			new_path = mem_alloc(sz);
652 			sz = cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, home, new_path, sz);
653 			if (sz < 0) {
654 				mem_free(new_path);
655 				goto skip_path_conv;
656 			}
657 			mem_free(home);
658 			home = new_path;
659 skip_path_conv:;
660 		}
661 #endif
662 		if (home) {
663 			EINTRLOOP(rs, stat(home, &st));
664 			if (rs || !S_ISDIR(st.st_mode)) {
665 				mem_free(home);
666 				home = NULL;
667 			}
668 		}
669 	}
670 #endif
671 	if (!home) home = stracpy(getenv("HOME"));
672 #ifdef WIN32
673 /* When we run in Cygwin without Cygwin environment, it reports home "/".
674    Unfortunatelly, it can't write anything to that directory */
675 	if (home && !strcmp(home, "/")) {
676 		mem_free(home);
677 		home = NULL;
678 	}
679 #endif
680 #ifdef OPENVMS
681 	if (!home) home = stracpy(cast_uchar "/SYS$LOGIN");
682 	translate_vms_to_unix(&home);
683 	translate_vms_to_unix(&config_dir);
684 #endif
685 	if (!home) {
686 		int i;
687 		home = stracpy(path_to_exe);
688 		if (!home) {
689 			if (config_dir) mem_free(config_dir);
690 			return NULL;
691 		}
692 		for (i = strlen(home) - 1; i >= 0; i--) if (dir_sep(home[i])) {
693 			home[i + 1] = 0;
694 			goto br;
695 		}
696 		home[0] = 0;
697 		br:;
698 	}
699 	while (home[0] && home[1] && dir_sep(home[strlen(home) - 1])) home[strlen(home) - 1] = 0;
700 	if (home[0]) add_to_strn(&home, "/");
701 	home_links = stracpy(home);
702 	if (config_dir) {
703 		add_to_strn(&home_links, config_dir);
704 		while (home_links[0] && dir_sep(home_links[strlen(home_links) - 1])) home_links[strlen(home_links) - 1] = 0;
705 		EINTRLOOP(rs, stat(home_links, &st));
706 		if (!rs && S_ISDIR(st.st_mode)) {
707 			add_to_strn(&home_links, "/links");
708 	    	} else {
709 			fprintf(stderr, "CONFIG_DIR set to %s. But directory %s doesn't exist.\n\007", config_dir, home_links);
710 			sleep(3);
711 			mem_free(home_links);
712 			home_links = stracpy(home);
713 			add_to_strn(&home_links, ".links");
714 		}
715 	} else add_to_strn(&home_links, ".links");
716 	EINTRLOOP(rs, stat(home_links, &st));
717 	if (rs) {
718 #ifdef HAVE_MKDIR
719 		EINTRLOOP(rs, mkdir(home_links, 0700));
720 		if (!rs) goto home_creat;
721 #ifdef OPENVMS
722 		if (errno == EEXIST) goto home_ok;
723 #endif
724 #endif
725 		if (config_dir) goto failed;
726 		goto first_failed;
727 	}
728 	if (S_ISDIR(st.st_mode)) goto home_ok;
729 	/* This is a Cygwin hack! Cygwin reports stat for "links" if no
730 	   "links" exists and only "links.exe" does. So try to create directory
731 	   anyway. */
732 #ifdef HAVE_MKDIR
733 	EINTRLOOP(rs, mkdir(home_links, 0700));
734 	if (!rs) goto home_creat;
735 #endif
736 	first_failed:
737 	mem_free(home_links);
738 	home_links = stracpy(home);
739 #ifdef DOS
740 	add_to_strn(&home_links, cast_uchar "links.cfg");
741 #else
742 	add_to_strn(&home_links, cast_uchar "links");
743 #endif
744 	EINTRLOOP(rs, stat(home_links, &st));
745 	if (rs) {
746 #ifdef HAVE_MKDIR
747 		EINTRLOOP(rs, mkdir(home_links, 0700));
748 		if (!rs) goto home_creat;
749 #ifdef OPENVMS
750 		if (errno == EEXIST) goto home_ok;
751 #endif
752 #else
753 		mem_free(home_links);
754 		home_links = stracpy(home);
755 		goto home_ok;
756 #endif
757 		goto failed;
758 	}
759 	if (S_ISDIR(st.st_mode)) goto home_ok;
760 #ifdef HAVE_MKDIR
761 	EINTRLOOP(rs, mkdir(home_links, 0700));
762 	if (!rs) goto home_creat;
763 #endif
764 	failed:
765 	mem_free(home_links);
766 	mem_free(home);
767 	if (config_dir) mem_free(config_dir);
768 	return NULL;
769 
770 	home_ok:
771 	if (n) *n = 0;
772 	home_creat:
773 #ifdef HAVE_CHMOD
774 	EINTRLOOP(rs, chmod(home_links, 0700));
775 #endif
776 	add_to_strn(&home_links, "/");
777 	mem_free(home);
778 	if (config_dir) mem_free(config_dir);
779 	return home_links;
780 }
781 
init_home()782 void init_home()
783 {
784 	get_system_name();
785 	get_compiler_name();
786 	links_home = get_home(&first_use);
787 	if (!links_home) {
788 		fprintf(stderr, "Unable to find or create links config directory. Please check, that you have $HOME variable set correctly and that you have write permission to your home directory.\n");
789 		sleep(3);
790 		return;
791 	}
792 }
793 
load_config_file(unsigned char * prefix,unsigned char * name)794 void load_config_file(unsigned char *prefix, unsigned char *name)
795 {
796 	unsigned char *c, *config_file;
797 	config_file = stracpy(prefix);
798 	if (!config_file) return;
799 	add_to_strn(&config_file, name);
800 	if ((c = read_config_file(config_file))) goto ok;
801 	mem_free(config_file);
802 	config_file = stracpy(prefix);
803 	if (!config_file) return;
804 	add_to_strn(&config_file, ".");
805 	add_to_strn(&config_file, name);
806 	if ((c = read_config_file(config_file))) goto ok;
807 	mem_free(config_file);
808 	return;
809 	ok:
810 	parse_config_file(config_file, c, all_options);
811 	mem_free(c);
812 	mem_free(config_file);
813 }
814 
load_config()815 void load_config()
816 {
817 #ifdef SHARED_CONFIG_DIR
818 	load_config_file(SHARED_CONFIG_DIR, "links.cfg");
819 #endif
820 	load_config_file(links_home, "links.cfg");
821 	load_config_file(links_home, "html.cfg");
822 	load_config_file(links_home, "user.cfg");
823 }
824 
write_config_data(unsigned char * prefix,unsigned char * name,struct option * o,struct terminal * term)825 int write_config_data(unsigned char *prefix, unsigned char *name, struct option *o, struct terminal *term)
826 {
827 	int err;
828 	unsigned char *c, *config_file;
829 	if (!(c = create_config_string(o))) return -1;
830 	config_file = stracpy(prefix);
831 	if (!config_file) {
832 		mem_free(c);
833 		return -1;
834 	}
835 	add_to_strn(&config_file, name);
836 	if ((err = write_to_config_file(config_file, c))) {
837 		if (term) msg_box(term, NULL, TEXT_(T_CONFIG_ERROR), AL_CENTER | AL_EXTD_TEXT, TEXT_(T_UNABLE_TO_WRITE_TO_CONFIG_FILE), ": ", get_err_msg(err), NULL, NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
838 		mem_free(c);
839 		mem_free(config_file);
840 		return -1;
841 	}
842 	mem_free(c);
843 	mem_free(config_file);
844 	return 0;
845 }
846 
write_config(struct terminal * term)847 void write_config(struct terminal *term)
848 {
849 	write_config_data(links_home, "links.cfg", links_options, term);
850 }
851 
write_html_config(struct terminal * term)852 void write_html_config(struct terminal *term)
853 {
854 	write_config_data(links_home, "html.cfg", html_options, term);
855 }
856 
add_nm(struct option * o,unsigned char ** s,int * l)857 void add_nm(struct option *o, unsigned char **s, int *l)
858 {
859 	if (*l) add_to_str(s, l, NEWLINE);
860 	add_to_str(s, l, o->cfg_name);
861 	add_to_str(s, l, " ");
862 }
863 
add_quoted_to_str(unsigned char ** s,int * l,unsigned char * q)864 void add_quoted_to_str(unsigned char **s, int *l, unsigned char *q)
865 {
866 	add_chr_to_str(s, l, '"');
867 	while (*q) {
868 		if (*q == '"' || *q == '\\') add_chr_to_str(s, l, '\\');
869 		add_chr_to_str(s, l, *q);
870 		q++;
871 	}
872 	add_chr_to_str(s, l, '"');
873 }
874 
num_rd(struct option * o,unsigned char * c)875 unsigned char *num_rd(struct option *o, unsigned char *c)
876 {
877 	unsigned char *tok = get_token(&c);
878 	unsigned char *end;
879 	long l;
880 	if (!tok) return "Missing argument";
881 	l = strtolx(tok, &end);
882 	if (*end) {
883 		mem_free(tok);
884 		return "Number expected";
885 	}
886 	if (l < o->min || l > o->max) {
887 		mem_free(tok);
888 		return "Out of range";
889 	}
890 	*(int *)o->ptr = l;
891 	mem_free(tok);
892 	return NULL;
893 }
894 
num_wr(struct option * o,unsigned char ** s,int * l)895 void num_wr(struct option *o, unsigned char **s, int *l)
896 {
897 	add_nm(o, s, l);
898 	add_knum_to_str(s, l, *(int *)o->ptr);
899 }
900 
str_rd(struct option * o,unsigned char * c)901 unsigned char *str_rd(struct option *o, unsigned char *c)
902 {
903 	unsigned char *tok = get_token(&c);
904 	unsigned char *e = NULL;
905 	if (!tok) tok = stracpy(cast_uchar "");
906 	if (strlen(tok) + 1 > (size_t)o->max) e = "String too long";
907 	else strcpy(o->ptr, tok);
908 	mem_free(tok);
909 	return e;
910 }
911 
str_wr(struct option * o,unsigned char ** s,int * l)912 void str_wr(struct option *o, unsigned char **s, int *l)
913 {
914 	add_nm(o, s, l);
915 	if (strlen(o->ptr) + 1 > (size_t)o->max) {
916 		unsigned char *s1 = init_str();
917 		int l1 = 0;
918 		add_bytes_to_str(&s1, &l1, o->ptr, o->max - 1);
919 		add_quoted_to_str(s, l, s1);
920 		mem_free(s1);
921 	}
922 	else add_quoted_to_str(s, l, o->ptr);
923 }
924 
cp_rd(struct option * o,unsigned char * c)925 unsigned char *cp_rd(struct option *o, unsigned char *c)
926 {
927 	unsigned char *tok = get_token(&c);
928 	unsigned char *e = NULL;
929 	int i;
930 	if (!tok) return "Missing argument";
931 	if ((i = get_cp_index(tok)) == -1) e = "Unknown codepage";
932 	else if (o->min == 1 && is_cp_special(i)) e = "UTF-8 can't be here";
933 	else *(int *)o->ptr = i;
934 	mem_free(tok);
935 	return e;
936 }
937 
cp_wr(struct option * o,unsigned char ** s,int * l)938 void cp_wr(struct option *o, unsigned char **s, int *l)
939 {
940 	unsigned char *n = get_cp_mime_name(*(int *)o->ptr);
941 	add_nm(o, s, l);
942 	add_to_str(s, l, n);
943 }
944 
lang_rd(struct option * o,unsigned char * c)945 unsigned char *lang_rd(struct option *o, unsigned char *c)
946 {
947 	int i;
948 	unsigned char *tok = get_token(&c);
949 	if (!tok) return "Missing argument";
950 	for (i = 0; i < n_languages(); i++)
951 		if (!(strcasecmp(language_name(i), tok))) {
952 			set_language(i);
953 			mem_free(tok);
954 			return NULL;
955 		}
956 	mem_free(tok);
957 	return "Unknown language";
958 }
959 
lang_wr(struct option * o,unsigned char ** s,int * l)960 void lang_wr(struct option *o, unsigned char **s, int *l)
961 {
962 	add_nm(o, s, l);
963 	add_quoted_to_str(s, l, language_name(current_language));
964 }
965 
getnum(unsigned char * s,int * n,int r1,int r2)966 int getnum(unsigned char *s, int *n, int r1, int r2)
967 {
968 	unsigned char *e;
969 	long l = strtol(s, (char **)(void *)&e, 10);
970 	if (*e || !*s) return -1;
971 	if (l < r1 || l >= r2) return -1;
972 	*n = (int)l;
973 	return 0;
974 }
975 
type_rd(struct option * o,unsigned char * c)976 unsigned char *type_rd(struct option *o, unsigned char *c)
977 {
978 	unsigned char *err = "Error reading association specification";
979 	struct assoc neww;
980 	unsigned char *w;
981 	int n;
982 	memset(&neww, 0, sizeof(struct assoc));
983 	if (!(neww.label = get_token(&c))) goto err;
984 	if (!(neww.ct = get_token(&c))) goto err;
985 	if (!(neww.prog = get_token(&c))) goto err;
986 	if (!(w = get_token(&c))) goto err;
987 	if (getnum(w, &n, 0, 32)) goto err_f;
988 	mem_free(w);
989 	neww.cons = !!(n & 1);
990 	neww.xwin = !!(n & 2);
991 	neww.ask = !!(n & 4);
992 	if ((n & 8) || (n & 16)) neww.block = !!(n & 16);
993 	else neww.block = !neww.xwin || neww.cons;
994 	if (!(w = get_token(&c))) goto err;
995 	if (getnum(w, &neww.system, 0, 256)) goto err_f;
996 	mem_free(w);
997 	update_assoc(&neww);
998 	err = NULL;
999 	err:
1000 	if (neww.label) mem_free(neww.label);
1001 	if (neww.ct) mem_free(neww.ct);
1002 	if (neww.prog) mem_free(neww.prog);
1003 	return err;
1004 	err_f:
1005 	mem_free(w);
1006 	goto err;
1007 }
1008 
type_wr(struct option * o,unsigned char ** s,int * l)1009 void type_wr(struct option *o, unsigned char **s, int *l)
1010 {
1011 	struct assoc *a;
1012 	foreachback(a, assoc) {
1013 		add_nm(o, s, l);
1014 		add_quoted_to_str(s, l, a->label);
1015 		add_to_str(s, l, " ");
1016 		add_quoted_to_str(s, l, a->ct);
1017 		add_to_str(s, l, " ");
1018 		add_quoted_to_str(s, l, a->prog);
1019 		add_to_str(s, l, " ");
1020 		add_num_to_str(s, l, (!!a->cons) + (!!a->xwin) * 2 + (!!a->ask) * 4 + (!a->block) * 8 + (!!a->block) * 16);
1021 		add_to_str(s, l, " ");
1022 		add_num_to_str(s, l, a->system);
1023 	}
1024 }
1025 
ext_rd(struct option * o,unsigned char * c)1026 unsigned char *ext_rd(struct option *o, unsigned char *c)
1027 {
1028 	unsigned char *err = "Error reading extension specification";
1029 	struct extension neww;
1030 	memset(&neww, 0, sizeof(struct extension));
1031 	if (!(neww.ext = get_token(&c))) goto err;
1032 	if (!(neww.ct = get_token(&c))) goto err;
1033 	update_ext(&neww);
1034 	err = NULL;
1035 	err:
1036 	if (neww.ext) mem_free(neww.ext);
1037 	if (neww.ct) mem_free(neww.ct);
1038 	return err;
1039 }
1040 
ext_wr(struct option * o,unsigned char ** s,int * l)1041 void ext_wr(struct option *o, unsigned char **s, int *l)
1042 {
1043 	struct extension *a;
1044 	foreachback(a, extensions) {
1045 		add_nm(o, s, l);
1046 		add_quoted_to_str(s, l, a->ext);
1047 		add_to_str(s, l, " ");
1048 		add_quoted_to_str(s, l, a->ct);
1049 	}
1050 }
1051 
prog_rd(struct option * o,unsigned char * c)1052 unsigned char *prog_rd(struct option *o, unsigned char *c)
1053 {
1054 	unsigned char *err = "Error reading program specification";
1055 	unsigned char *prog, *w;
1056 	int n;
1057 	if (!(prog = get_token(&c))) goto err_1;
1058 	if (!(w = get_token(&c))) goto err_2;
1059 	if (getnum(w, &n, 0, 256)) goto err_3;
1060 	update_prog(o->ptr, prog, n);
1061 	err = NULL;
1062 	err_3:
1063 	mem_free(w);
1064 	err_2:
1065 	mem_free(prog);
1066 	err_1:
1067 	return err;
1068 }
1069 
prog_wr(struct option * o,unsigned char ** s,int * l)1070 void prog_wr(struct option *o, unsigned char **s, int *l)
1071 {
1072 	struct protocol_program *a;
1073 	foreachback(a, *(struct list_head *)o->ptr) {
1074 		if (!*a->prog) continue;
1075 		add_nm(o, s, l);
1076 		add_quoted_to_str(s, l, a->prog);
1077 		add_to_str(s, l, " ");
1078 		add_num_to_str(s, l, a->system);
1079 	}
1080 }
1081 
term_rd(struct option * o,unsigned char * c)1082 unsigned char *term_rd(struct option *o, unsigned char *c)
1083 {
1084 	struct term_spec *ts;
1085 	unsigned char *w;
1086 	int i;
1087 	if (!(w = get_token(&c))) goto err;
1088 	if (!(ts = new_term_spec(w))) {
1089 		mem_free(w);
1090 		goto end;
1091 	}
1092 	mem_free(w);
1093 	if (!(w = get_token(&c))) goto err;
1094 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '4') goto err_f;
1095 	ts->mode = w[0] - '0';
1096 	mem_free(w);
1097 	if (!(w = get_token(&c))) goto err;
1098 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
1099 	ts->m11_hack = w[0] - '0';
1100 	mem_free(w);
1101 	if (!(w = get_token(&c))) goto err;
1102 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '7') goto err_f;
1103 	ts->col = (w[0] - '0') & 1;
1104 	ts->restrict_852 = !!((w[0] - '0') & 2);
1105 	ts->block_cursor = !!((w[0] - '0') & 4);
1106 	mem_free(w);
1107 	if (!(w = get_token(&c))) goto err;
1108 	if ((i = get_cp_index(w)) == -1 || is_cp_special(i)) goto err_f;
1109 	ts->charset = i;
1110 	mem_free(w);
1111 	end:
1112 	return NULL;
1113 	err_f:
1114 	mem_free(w);
1115 	err:
1116 	return "Error reading terminal specification";
1117 }
1118 
term2_rd(struct option * o,unsigned char * c)1119 unsigned char *term2_rd(struct option *o, unsigned char *c)
1120 {
1121 	struct term_spec *ts;
1122 	unsigned char *w;
1123 	int i;
1124 	if (!(w = get_token(&c))) goto err;
1125 	if (!(ts = new_term_spec(w))) {
1126 		mem_free(w);
1127 		goto end;
1128 	}
1129 	mem_free(w);
1130 	if (!(w = get_token(&c))) goto err;
1131 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '4') goto err_f;
1132 	ts->mode = w[0] - '0';
1133 	mem_free(w);
1134 	if (!(w = get_token(&c))) goto err;
1135 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
1136 	ts->m11_hack = w[0] - '0';
1137 	mem_free(w);
1138 	if (!(w = get_token(&c))) goto err;
1139 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
1140 	ts->restrict_852 = w[0] - '0';
1141 	mem_free(w);
1142 	if (!(w = get_token(&c))) goto err;
1143 	if (strlen(w) != 1 || w[0] < '0' || w[0] > '1') goto err_f;
1144 	ts->col = w[0] - '0';
1145 	mem_free(w);
1146 	if (!(w = get_token(&c))) goto err;
1147 	if ((i = get_cp_index(w)) == -1 || is_cp_special(i)) goto err_f;
1148 	ts->charset = i;
1149 	mem_free(w);
1150 	end:
1151 	return NULL;
1152 	err_f:
1153 	mem_free(w);
1154 	err:
1155 	return "Error reading terminal specification";
1156 }
1157 
term_wr(struct option * o,unsigned char ** s,int * l)1158 void term_wr(struct option *o, unsigned char **s, int *l)
1159 {
1160 	struct term_spec *ts;
1161 	foreachback(ts, term_specs) {
1162 		add_nm(o, s, l);
1163 		add_quoted_to_str(s, l, ts->term);
1164 		add_to_str(s, l, " ");
1165 		add_num_to_str(s, l, ts->mode);
1166 		add_to_str(s, l, " ");
1167 		add_num_to_str(s, l, ts->m11_hack);
1168 		add_to_str(s, l, " ");
1169 		add_num_to_str(s, l, !!ts->col + !!ts->restrict_852 * 2 + !!ts->block_cursor * 4);
1170 		add_to_str(s, l, " ");
1171 		add_to_str(s, l, get_cp_mime_name(ts->charset));
1172 	}
1173 }
1174 
gen_cmd(struct option * o,unsigned char *** argv,int * argc)1175 unsigned char *gen_cmd(struct option *o, unsigned char ***argv, int *argc)
1176 {
1177 	unsigned char *e;
1178 	int l;
1179 	unsigned char *r;
1180 	if (!*argc) return "Parameter expected";
1181 	e = init_str();
1182 	l = 0;
1183 	add_quoted_to_str(&e, &l, **argv);
1184 	r = o->rd_cfg(o, e);
1185 	mem_free(e);
1186 	if (r) return r;
1187 	(*argv)++; (*argc)--;
1188 	return NULL;
1189 }
1190 
lookup_cmd(struct option * o,unsigned char *** argv,int * argc)1191 unsigned char *lookup_cmd(struct option *o, unsigned char ***argv, int *argc)
1192 {
1193 	ip addr;
1194 	unsigned char *p = (unsigned char *)&addr;
1195 	if (!*argc) return "Parameter expected";
1196 	if (*argc >= 2) return "Too many parameters";
1197 	(*argv)++; (*argc)--;
1198 	if (do_real_lookup(*(*argv - 1), &addr)) {
1199 #if defined(HAVE_GETHOSTBYNAME) && defined(HAVE_HERROR)
1200 		herror("error");
1201 #else
1202 		fprintf(stderr, "error: host not found\n");
1203 #endif
1204 		exit(RET_ERROR);
1205 		return "";
1206 	}
1207 	printf("%d.%d.%d.%d\n", (int)p[0], (int)p[1], (int)p[2], (int)p[3]);
1208 	fflush(stdout);
1209 	exit(RET_OK);
1210 	return "";
1211 }
1212 
version_cmd(struct option * o,unsigned char *** argv,int * argc)1213 unsigned char *version_cmd(struct option *o, unsigned char ***argv, int *argc)
1214 {
1215 	printf("Links " VERSION_STRING "\n");
1216 	fflush(stdout);
1217 	exit(RET_OK);
1218 	return "";
1219 }
1220 
no_connect_cmd(struct option * o,unsigned char *** argv,int * argc)1221 unsigned char *no_connect_cmd(struct option *o, unsigned char ***argv, int *argc)
1222 {
1223 	no_connect = 1;
1224 	return NULL;
1225 }
1226 
anonymous_cmd(struct option * o,unsigned char *** argv,int * argc)1227 unsigned char *anonymous_cmd(struct option *o, unsigned char ***argv, int *argc)
1228 {
1229 	anonymous = 1;
1230 	return NULL;
1231 }
1232 
force_html_cmd(struct option * o,unsigned char *** argv,int * argc)1233 unsigned char *force_html_cmd(struct option *o, unsigned char ***argv, int *argc)
1234 {
1235 	force_html = 1;
1236 	return NULL;
1237 }
1238 
dump_cmd(struct option * o,unsigned char *** argv,int * argc)1239 unsigned char *dump_cmd(struct option *o, unsigned char ***argv, int *argc)
1240 {
1241 	if (dmp != o->min && dmp) return "Can't use both -dump and -source";
1242 	dmp = o->min;
1243 	no_connect = 1;
1244 	return NULL;
1245 }
1246 
printhelp_cmd(struct option * o,unsigned char *** argv,int * argc)1247 unsigned char *printhelp_cmd(struct option *o, unsigned char ***argv, int *argc)
1248 {
1249 /* Changed and splited - translation is much easier.
1250  * Print to stdout instead stderr (,,links -help | more''
1251  * is much better than ,,links -help 2>&1 | more'').
1252  */
1253 fprintf(stdout, "%s%s%s%s%s%s\n",
1254 
1255 ("links [options] URL\n\
1256 Options are:\n\
1257 \n\
1258  -async-dns <0>/<1>\n\
1259   Asynchronous DNS resolver on(1)/off(0).\n\
1260 \n\
1261  -download-utime <0>/<1>\n\
1262   Set time of downloaded files to time from server.\n\
1263 \n\
1264  -max-connections <max>\n\
1265   Maximum number of concurrent connections.\n\
1266   (default: 10)\n\
1267 \n"),
1268 (" -max-connections-to-host <max>\n\
1269   Maximum number of concurrent connection to a given host.\n\
1270   (default: 2)\n\
1271 \n\
1272  -retries <retry>\n\
1273   Number of retries.\n\
1274   (default: 3)\n\
1275 \n\
1276  -receive-timeout <sec>\n\
1277   Timeout on receive.\n\
1278   (default: 120)\n\
1279 \n"),
1280 (" -unrestartable-receive-timeout <sec>\n\
1281   Timeout on non restartable connections.\n\
1282   (default: 600)\n\
1283 \n\
1284  -format-cache-size <num>\n\
1285   Number of formatted document pages cached.\n\
1286   (default: 5)\n\
1287 \n\
1288  -memory-cache-size <Kbytes>\n\
1289   Cache memory in Kilobytes.\n\
1290   (default: 1024)\n\
1291 \n"),
1292 (" -http-proxy <host:port>\n\
1293   Host and port number of the HTTP proxy, or blank.\n\
1294   (default: blank)\n\
1295 \n\
1296  -ftp-proxy <host:port>\n\
1297   Host and port number of the FTP proxy, or blank.\n\
1298   (default: blank)\n\
1299 \n\
1300  -download-dir <path>\n\
1301   Default download directory.\n\
1302   (default: actual dir)\n\
1303 \n\
1304  -http-bugs.http10 <0>/<1>\n\
1305   \"1\" forces using only HTTP/1.0 protocol.\n\
1306 \n\
1307  -http-bugs.allow-blacklist <0>/<1>\n\
1308   \"1\" defaults to using list of servers that have broken HTTP/1.1 support.\n\
1309   When links finds such server, it will retry the request with HTTP/1.0.\n\
1310 \n\
1311  -http-bugs.bug-302-redirect <0>/<1>\n\
1312   Process 302 redirect in a way that is incompatible with RFC1945 and RFC2068,\n\
1313   but the same as Netscape and MSIE. Many pages depend on it.\n\
1314 \n\
1315  -http-bugs.bug-post-no-keepalive <0>/<1>\n\
1316   No keepalive connection after post requests. For some buggy servers.\n\
1317 \n\
1318  -http-bugs.bug-no-accept-charset <0>/<1>\n\
1319   Do not send Accept-Charset field of HTTP header.\n\
1320 \n\
1321  -ftp.anonymous-password <string>\n\
1322   Use ftp PASV command to bypass firewalls.\n\
1323 \n\
1324  -ftp.fast <0>/<1>\n\
1325   Send more ftp commands simultaneously. Faster response when\n\
1326   browsing ftp directories, but it is incompatible with RFC\n\
1327   and some servers don't like it.\n\
1328 \n\
1329  -ftp.set-iptos <0>/<1>\n\
1330   Set IP Type-of-service to high throughput on ftp connections.\n\
1331 \n"),
1332 (" -html-assume-codepage <codepage>\n\
1333   Use the given codepage when the webpage did not specify\n\
1334   its codepage. (default: ISO 8859-1)\n\
1335 \n\
1336  -html-tables <0>/<1>\n\
1337   Render tables.\n\
1338 \n\
1339  -html-frames <0>/<1>\n\
1340   Render frames.\n\
1341 \n\
1342  -html-images <0>/<1>\n\
1343   Display links to images.\n\
1344 \n\
1345  -html-numbered-links <0>/<1>\n\
1346   Link numbering.\n\
1347 \n\
1348  -html-table-order <0>/<1>\n\
1349   Walk table by rows (0) or columns (1).\n\
1350 \n\
1351  -html-margin <margin>\n\
1352   Text margin.\n\
1353 \n\
1354  -language <language>\n\
1355   User interface language.\n\
1356 \n\
1357  -anonymous\n\
1358   Restrict links so that it can run on an anonymous account.\n\
1359   No local file browsing. No downloads. Executing of viewers\n\
1360   is allowed, but user can't add or modify entries in\n\
1361   association table.\n\
1362 \n\
1363  -force-html\n\
1364   Treat file as if it had an .html extension.\n\
1365 \n\
1366  -source\n\
1367   Write the given HTML document in source form to stdout.\n\
1368 \n\
1369  -dump\n\
1370   Write a plain-text version of the given HTML document to\n\
1371   stdout.\n\
1372 \n\
1373  -width <size>\n\
1374   Size of screen in characters, used in combination with -dump.\n\
1375 \n\
1376  -codepage <codepage>\n\
1377   Character set of output of -dump.\n\
1378 \n\
1379  -no-connect\n\
1380   Runs links as a separate instance - instead of connecting to\n\
1381   existing instance.\n\
1382 \n\
1383  -lookup <host>\n\
1384   Do lookup like \"host\" command.\n\
1385 \n\
1386  -version\n\
1387   Prints the links version number and exit.\n\
1388 \n\
1389  -help\n\
1390   Prints this help screen\n\
1391 \n\
1392 \n"),
1393 ("Keys:\n\
1394  	ESC	 display menu\n\
1395 	^C	 quit\n\
1396 	^P, ^N	 scroll up, down\n\
1397 	[, ]	 scroll left, right\n\
1398 	up, down select link\n\
1399 	->	 follow link\n\
1400 	<-	 go back\n\
1401 	g	 go to url\n\
1402 	G	 go to url based on current url\n\
1403 	^R	 reload\n\
1404 	/	 search\n\
1405 	?	 search back\n\
1406 	n	 find next\n\
1407 	N	 find previous\n\
1408 	=	 document info\n\
1409 	\\	 document source\n\
1410 	|	 HTTP header\n\
1411 	*	 toggle displaying of image links\n\
1412 	d	 download\n\
1413 	s	 bookmarks\n\
1414 	q	 quit\n"));
1415 
1416 	fflush(stdout);
1417 	exit(RET_OK);
1418 	return "";
1419 }
1420 
end_config()1421 void end_config()
1422 {
1423 	if (links_home) mem_free(links_home);
1424 }
1425 
1426 int anonymous = 0;
1427 
1428 unsigned char *links_home = NULL;
1429 int first_use = 0;
1430 int created_home = 0;
1431 
1432 int no_connect = 0;
1433 int base_session = 0;
1434 int dmp = 0;
1435 int force_html = 0;
1436 
1437 int async_lookup = 1;
1438 int download_utime = 0;
1439 #ifdef DOS
1440 /* DOS networking is slow with too many connections */
1441 int max_connections = 3;
1442 int max_connections_to_host = 2;
1443 #else
1444 int max_connections = 10;
1445 int max_connections_to_host = 2;
1446 #endif
1447 int max_tries = 3;
1448 int receive_timeout = 120;
1449 int unrestartable_receive_timeout = 600;
1450 
1451 int screen_width = 80;
1452 int dump_codepage = -1;
1453 
1454 int max_format_cache_entries = 5;
1455 int memory_cache_size = 1048576;
1456 
1457 int enable_html_tables = 1;
1458 int enable_html_frames = 1;
1459 int display_images = 1;
1460 
1461 struct document_setup dds = { 0, 0, 1, 1, 0, 3, 0, 0 };
1462 
1463 struct rgb default_fg = { 191, 191, 191, 0 };
1464 struct rgb default_bg = { 0, 0, 0, 0 };
1465 struct rgb default_link = { 255, 255, 255, 0 };
1466 struct rgb default_vlink = { 255, 255, 0, 0 };
1467 
1468 int default_left_margin = HTML_LEFT_MARGIN;
1469 
1470 unsigned char http_proxy[MAX_STR_LEN] = "";
1471 unsigned char ftp_proxy[MAX_STR_LEN] = "";
1472 
1473 unsigned char download_dir[MAX_STR_LEN] = "";
1474 
1475 struct ftp_options ftp_options = { "somebody@host.domain", 0, 0, 1 };
1476 
1477 /* These are workarounds for some CGI script bugs */
1478 struct http_bugs http_bugs = { 0, 1, 1, 0, 0 };
1479 /*int bug_302_redirect = 0;*/
1480 	/* When got 301 or 302 from POST request, change it to GET
1481 	   - this violates RFC2068, but some buggy message board scripts rely on it */
1482 /*int bug_post_no_keepalive = 0;*/
1483 	/* No keepalive connection after POST request. Some buggy PHP databases report bad
1484 	   results if GET wants to retreive data POSTed in the same connection */
1485 
1486 struct option links_options[] = {
1487 	{ 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "?" },
1488 	{ 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "h" },
1489 	{ 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "help" },
1490 	{ 1, printhelp_cmd, NULL, NULL, 0, 0, NULL, NULL, "-help" },
1491 	{ 1, lookup_cmd, NULL, NULL, 0, 0, NULL, NULL, "lookup" },
1492 	{ 1, version_cmd, NULL, NULL, 0, 0, NULL, NULL, "version" },
1493 	{ 1, no_connect_cmd, NULL, NULL, 0, 0, NULL, NULL, "no-connect" },
1494 	{ 1, anonymous_cmd, NULL, NULL, 0, 0, NULL, NULL, "anonymous" },
1495 	{ 1, gen_cmd, num_rd, NULL, 0, MAXINT, &base_session, NULL, "base-session" },
1496 	{ 1, force_html_cmd, NULL, NULL, 0, 0, NULL, NULL, "force-html" },
1497 	{ 1, dump_cmd, NULL, NULL, D_SOURCE, 0, NULL, NULL, "source" },
1498 	{ 1, dump_cmd, NULL, NULL, D_DUMP, 0, NULL, NULL, "dump" },
1499 	{ 1, gen_cmd, num_rd, NULL, 10, 512, &screen_width, "dump_width", "width" },
1500 	{ 1, gen_cmd, cp_rd, NULL, 1, 0, &dump_codepage, "dump_codepage", "codepage" },
1501 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &async_lookup, "async_dns", "async-dns" },
1502 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &download_utime, "download_utime", "download-utime" },
1503 	{ 1, gen_cmd, num_rd, num_wr, 1, 99, &max_connections, "max_connections", "max-connections" },
1504 	{ 1, gen_cmd, num_rd, num_wr, 1, 99, &max_connections_to_host, "max_connections_to_host", "max-connections-to-host" },
1505 	{ 1, gen_cmd, num_rd, num_wr, 0, 16, &max_tries, "retries", "retries" },
1506 	{ 1, gen_cmd, num_rd, num_wr, 1, 1800, &receive_timeout, "receive_timeout", "receive-timeout" },
1507 	{ 1, gen_cmd, num_rd, num_wr, 1, 1800, &unrestartable_receive_timeout, "unrestartable_receive_timeout", "unrestartable-receive-timeout" },
1508 	{ 1, gen_cmd, num_rd, num_wr, 0, 256, &max_format_cache_entries, "format_cache_size", "format-cache-size" },
1509 	{ 1, gen_cmd, num_rd, num_wr, 0, MAXINT, &memory_cache_size, "memory_cache_size", "memory-cache-size" },
1510 	{ 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, http_proxy, "http_proxy", "http-proxy" },
1511 	{ 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, ftp_proxy, "ftp_proxy", "ftp-proxy" },
1512 	{ 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, download_dir, "download_dir", "download-dir" },
1513 	{ 1, gen_cmd, lang_rd, lang_wr, 0, 0, &current_language, "language", "language" },
1514 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.http10, "http_bugs.http10", "http-bugs.http10" },
1515 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.allow_blacklist, "http_bugs.allow_blacklist", "http-bugs.allow-blacklist" },
1516 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.bug_302_redirect, "http_bugs.bug_302_redirect", "http-bugs.bug-302-redirect" },
1517 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.bug_post_no_keepalive, "http_bugs.bug_post_no_keepalive", "http-bugs.bug-post-no-keepalive" },
1518 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &http_bugs.no_accept_charset, "http_bugs.no_accept_charset", "http-bugs.bug-no-accept-charset" },
1519 	{ 1, gen_cmd, str_rd, str_wr, 0, MAX_STR_LEN, ftp_options.anon_pass, "ftp.anonymous_password", "ftp.anonymous-password" },
1520 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.passive_ftp, "ftp.use_passive", "ftp.use-passive" },
1521 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.fast_ftp, "ftp.fast", "ftp.fast" },
1522 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &ftp_options.set_tos, "ftp.set_iptos", "ftp.set-iptos" },
1523 	{ 1, gen_cmd, cp_rd, NULL, 0, 0, &dds.assume_cp, "assume_codepage", "assume-codepage" },
1524 	{ 1, NULL, term_rd, term_wr, 0, 0, NULL, "terminal", NULL },
1525 	{ 1, NULL, term2_rd, NULL, 0, 0, NULL, "terminal2", NULL },
1526 	{ 1, NULL, type_rd, type_wr, 0, 0, NULL, "association", NULL },
1527 	{ 1, NULL, ext_rd, ext_wr, 0, 0, NULL, "extension", NULL },
1528 	{ 1, NULL, prog_rd, prog_wr, 0, 0, &mailto_prog, "mailto", NULL },
1529 	{ 1, NULL, prog_rd, prog_wr, 0, 0, &telnet_prog, "telnet", NULL },
1530 	{ 1, NULL, prog_rd, prog_wr, 0, 0, &tn3270_prog, "tn3270", NULL },
1531 	{ 1, NULL, prog_rd, prog_wr, 0, 0, &mms_prog, "mms", NULL },
1532 	{ 1, NULL, bind_rd, NULL, 0, 0, NULL, "bind", NULL },
1533 	{ 1, NULL, unbind_rd, NULL, 0, 0, NULL, "unbind", NULL },
1534 	{ 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL },
1535 };
1536 
1537 struct option html_options[] = {
1538 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.hard_assume, "html_hard_assume", "html-hard-assume" },
1539 	{ 1, gen_cmd, cp_rd, cp_wr, 0, 0, &dds.assume_cp, "html_assume_codepage", "html-assume-codepage" },
1540 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.tables, "html_tables", "html-tables" },
1541 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.frames, "html_frames", "html-frames" },
1542 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.images, "html_images", "html-images" },
1543 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.num_links, "html_numbered_links", "html-numbered-links" },
1544 	{ 1, gen_cmd, num_rd, num_wr, 0, 1, &dds.table_order, "html_table_order", "html-table-order" },
1545 	{ 1, gen_cmd, num_rd, num_wr, 0, 9, &dds.margin, "html_margin", "html-margin" },
1546 	{ 0, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL },
1547 };
1548 
load_url_history(void)1549 void load_url_history(void)
1550 {
1551 	unsigned char *history_file, *hs;
1552 	unsigned char *hsp;
1553 
1554 	if (anonymous) return;
1555 	/* Must have been called after init_home */
1556 	if (!links_home) return;
1557 	history_file = stracpy(links_home);
1558 	add_to_strn(&history_file, "links.his");
1559 	hs = read_config_file(history_file);
1560 	mem_free(history_file);
1561 	if (!hs) return;
1562 	for (hsp = hs; *hsp; ) {
1563 		unsigned char *hsl, *hsc;
1564 		for (hsl = hsp; *hsl && *hsl != 10 && *hsl != 13; hsl++) ;
1565 		hsc = memacpy(hsp, hsl - hsp);
1566 		add_to_history(&goto_url_history, hsc, 0);
1567 		mem_free(hsc);
1568 		hsp = hsl;
1569 		while (*hsp == 10 || *hsp == 13) hsp++;
1570 	}
1571 	mem_free(hs);
1572 }
1573 
save_url_history(void)1574 void save_url_history(void)
1575 {
1576 	struct history_item *hi;
1577 	unsigned char *history_file;
1578 	unsigned char *hs;
1579 	int hsl = 0;
1580 	int i = 0;
1581 	if (anonymous) return;
1582 
1583 	/* Must have been called after init_home */
1584 	if (!links_home) return;
1585 	history_file = stracpy(links_home);
1586 	add_to_strn(&history_file, "links.his");
1587 	hs = init_str();
1588 	hsl = 0;
1589 	foreachback(hi, goto_url_history.items) {
1590 		if (!*hi->d || strchr(hi->d, 10) || strchr(hi->d, 13)) continue;
1591 		if (!url_not_saveable(hi->d) && i++ <= MAX_HISTORY_ITEMS) {
1592 			add_to_str(&hs, &hsl, hi->d);
1593 			add_to_str(&hs, &hsl, NEWLINE);
1594 		}
1595 	}
1596 	write_to_config_file(history_file, hs);
1597 	mem_free(history_file);
1598 	mem_free(hs);
1599 	return;
1600 }
1601 
1602