1 /*
2  * Copyright (c) 2000 Andrew Ferguson <andrew@owsla.cjb.net>
3  * Copyright (c) 1998 Sasha Vasko <sasha at aftercode.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20 
21 /*#define DO_CLOCKING      */
22 
23 #define LOCAL_DEBUG
24 #include "../configure.h"
25 #include "asapp.h"
26 #include "session.h"
27 #include "afterstep.h"
28 #include "screen.h"
29 #include "parser.h"
30 #include "functions.h"
31 #include "freestor.h"
32 #include "../libAfterImage/afterimage.h"
33 
34 
change_func_code(const char * func_name,FunctionCode new_code)35 FunctionCode change_func_code (const char *func_name,
36 															 FunctionCode new_code)
37 {
38 	TermDef *fterm;
39 	if (IsValidFunc (new_code)
40 			&& (fterm = txt2fterm (func_name, True)) != NULL)
41 		fterm->id = new_code;
42 }
43 
44 /*************************************************************************/
45 /* parsing code :
46  */
txt2fterm(const char * txt,int quiet)47 TermDef *txt2fterm (const char *txt, int quiet)
48 {
49 	TermDef *fterm;
50 
51 	if (pFuncSyntax->term_hash == NULL)
52 		PrepareSyntax ((SyntaxDef *) pFuncSyntax);
53 	if ((fterm =
54 			 FindStatementTerm ((char *)txt, (SyntaxDef *) pFuncSyntax)) == NULL
55 			&& !quiet)
56 		show_error ("unknown function name in function specification [%s].\n",
57 								txt);
58 
59 	return fterm;
60 }
61 
txt2func_code(const char * text)62 int txt2func_code (const char *text)
63 {
64 	TermDef *fterm;
65 
66 	for (; isspace (*text); text++) ;
67 	fterm = txt2fterm (text, True);
68 	return (fterm != NULL) ? fterm->id : F_FUNCTIONS_NUM;
69 }
70 
txt2func(const char * text,FunctionData * fdata,int quiet)71 int txt2func (const char *text, FunctionData * fdata, int quiet)
72 {
73 	TermDef *fterm;
74 
75 	for (; isspace (*text); text++) ;
76 	fterm = txt2fterm (text, quiet);
77 	if (fterm != NULL) {
78 		init_func_data (fdata);
79 		fdata->func = fterm->id;
80 		for (; !isspace (*text) && *text; text++) ;
81 		for (; isspace (*text); text++) ;
82 		if (*text) {
83 			const char *ptr = text + strlen ((char *)text);
84 
85 			for (; isspace (*(ptr - 1)); ptr--) ;
86 			fdata->text = mystrndup (text, ptr - text);
87 		}
88 	}
89 	return (fterm != NULL);
90 }
91 
parse_func(const char * text,FunctionData * data,int quiet)92 int parse_func (const char *text, FunctionData * data, int quiet)
93 {
94 	TermDef *fterm;
95 	char *ptr;
96 	int curr_arg = 0;
97 	int sign = 0;
98 
99 	init_func_data (data);
100 	for (ptr = (char *)text; isspace (*ptr); ptr++) ;
101 	if (*ptr == '\0') {
102 		if (!quiet)
103 			show_error ("empty function specification encountered.%s");
104 		return -1;
105 	}
106 
107 	if ((fterm = txt2fterm (ptr, quiet)) == NULL)
108 		return -2;
109 
110 	if (IsInternFunc (fterm->id))
111 		return 0;
112 
113 	while (!isspace (*ptr) && *ptr)
114 		ptr++;
115 	data->func = fterm->id;
116 	if (fterm->flags & TF_SYNTAX_TERMINATOR)
117 		return 0;
118 
119 	set_func_val (data, -1, default_func_val (data->func));
120 
121 	/* now let's do actual parsing */
122 	if (!(fterm->flags & NEED_CMD))
123 		ptr = stripcomments (ptr);
124 	else {												/* we still want to strip trailing whitespaces */
125 		char *tail = ptr + strlen (ptr) - 1;
126 
127 		for (; isspace (*tail) && tail > ptr; tail--) ;
128 		*(tail + 1) = '\0';
129 	}
130 	/* this function is very often called so we try to use as little
131 	   calls to other function as possible */
132 	for (; *ptr; ptr++) {
133 		if (!isspace (*ptr)) {
134 			int is_text = 0;
135 
136 			if (*ptr == '"') {
137 				char *tail = ptr;
138 				char *text;
139 
140 				while (*(tail + 1) && *(tail + 1) != '"')
141 					tail++;
142 				if (*(tail + 1) == '\0') {
143 					show_error ("impaired doublequotes encountered in [%s].", ptr);
144 					return -3;
145 				}
146 				text = mystrndup (ptr + 1, (tail - ptr));
147 				if (data->name == NULL)
148 					data->name = text;
149 				else if (data->text == NULL)
150 					data->text = text;
151 				ptr = tail + 1;
152 			} else if (isdigit (*ptr)) {
153 				int count;
154 				char unit = '\0';
155 				int val = 0;
156 
157 				for (count = 1; isdigit (*(ptr + count)); count++) ;
158 				if (*(ptr + count) != '\0' && !isspace (*(ptr + count)))
159 					is_text = (!isspace (*(ptr + count + 1))
160 										 && *(ptr + count + 1) != '\0') ? 1 : 0;
161 				if (is_text == 0)
162 					ptr = parse_func_args (ptr, &unit, &val) - 1;
163 				if (curr_arg < MAX_FUNC_ARGS) {
164 					data->func_val[curr_arg] = (sign != 0) ? val * sign : val;
165 					data->unit[curr_arg] = unit;
166 					curr_arg++;
167 				}
168 			} else if (*ptr == '-') {
169 				if (sign == 0) {
170 					sign--;
171 					continue;
172 				} else
173 					is_text = 1;
174 			} else if (*ptr == '+') {
175 				if (sign == 0) {
176 					sign++;
177 					continue;
178 				} else
179 					is_text = 1;
180 			} else
181 				is_text = 1;
182 
183 			if (is_text) {
184 				if (sign != 0)
185 					ptr--;
186 				if (data->text == NULL) {
187 					if (fterm->flags & NEED_CMD) {
188 						data->text = mystrdup (ptr);
189 						break;
190 					}
191 					ptr = parse_token (ptr, &(data->text)) - 1;
192 				} else
193 					while (*(ptr + 1) && !isspace (*(ptr + 1)))
194 						ptr++;
195 			}
196 			sign = 0;
197 		}
198 	}
199 
200 	decode_func_units (data);
201 	data->hotkey = scan_for_hotkey (data->name);
202 
203 	/* now let's check for valid number of arguments */
204 	if ((fterm->flags & NEED_NAME) && data->name == NULL) {
205 		show_error ("function specification requires \"name\" in [%s].", text);
206 		return FUNC_ERR_NO_NAME;
207 	}
208 	if (data->text == NULL) {
209 		if ((fterm->flags & NEED_WINDOW)
210 				|| ((fterm->flags & NEED_WINIFNAME) && data->name != NULL)) {
211 			show_error ("function specification requires window name in [%s].",
212 									text);
213 			return FUNC_ERR_NO_TEXT;
214 		}
215 		if (fterm->flags & NEED_CMD) {
216 			show_error
217 					("function specification requires shell command or full file name in [%s].",
218 					 text);
219 			return FUNC_ERR_NO_TEXT;
220 		}
221 	}
222 /*
223    if( data->func == F_SCROLL )
224    {
225    fprintf( stderr,"Function parsed: [%s] [%s] [%s] [%d] [%d] [%c]\n",fterm->keyword,data->name,data->text,data->func_val[0], data->func_val[1],data->hotkey );
226    fprintf( stderr,"from: [%s]\n", text );
227    }
228  */
229 	return 0;
230 }
231 
232 #if 0
233 FunctionData *String2Func (const char *string, FunctionData * p_fdata,
234 													 Bool quiet)
235 {
236 	if (p_fdata)
237 		free_func_data (p_fdata);
238 	else
239 		p_fdata = safecalloc (1, sizeof (FunctionData));
240 
241 	LOCAL_DEBUG_OUT ("parsing message \"%s\"", string);
242 	if (parse_func (string, p_fdata, quiet) < 0) {
243 		LOCAL_DEBUG_OUT ("parsing failed%s", "");
244 		free_func_data (p_fdata);
245 		free (p_fdata);
246 		p_fdata = NULL;
247 	} else
248 		LOCAL_DEBUG_OUT ("parsing success with func = %d", p_fdata->func);
249 	return p_fdata;
250 }
251 
252 #endif
253 /****************************************************************************/
254 /* FunctionData - related code 	                                            */
init_func_data(FunctionData * data)255 void init_func_data (FunctionData * data)
256 {
257 	int i;
258 
259 	if (data) {
260 		data->func = F_NOP;
261 		for (i = 0; i < MAX_FUNC_ARGS; i++) {
262 			data->func_val[i] = DEFAULT_OTHERS;
263 			data->unit_val[i] = 0;
264 			data->unit[i] = '\0';
265 		}
266 		data->hotkey = '\0';
267 		data->name = data->text = NULL;
268 		data->name_encoding = 0;
269 		data->popup = NULL;
270 	}
271 }
272 
copy_func_data(FunctionData * dst,FunctionData * src)273 void copy_func_data (FunctionData * dst, FunctionData * src)
274 {
275 	if (dst && src) {
276 		register int i;
277 
278 		dst->func = src->func;
279 		dst->name = src->name;
280 		dst->name_encoding = src->name_encoding;
281 		dst->text = src->text;
282 		for (i = 0; i < MAX_FUNC_ARGS; i++) {
283 			dst->func_val[i] = src->func_val[i];
284 			dst->unit[i] = src->unit[i];
285 			dst->unit_val[i] = src->unit_val[i];
286 		}
287 		dst->hotkey = src->hotkey;
288 		dst->popup = src->popup;
289 	}
290 }
291 
dup_func_data(FunctionData * dst,FunctionData * src)292 void dup_func_data (FunctionData * dst, FunctionData * src)
293 {
294 	if (dst && src) {
295 		register int i;
296 
297 		dst->func = src->func;
298 		dst->name = mystrdup (src->name);
299 		dst->text = mystrdup (src->text);
300 		for (i = 0; i < MAX_FUNC_ARGS; i++) {
301 			dst->func_val[i] = src->func_val[i];
302 			dst->unit[i] = src->unit[i];
303 			dst->unit_val[i] = src->unit_val[i];
304 		}
305 		dst->hotkey = src->hotkey;
306 		dst->popup = src->popup;
307 	}
308 }
309 
create_named_function(int func,char * name)310 inline FunctionData *create_named_function (int func, char *name)
311 {
312 	FunctionData *fdata = safecalloc (1, sizeof (FunctionData));
313 
314 	init_func_data (fdata);
315 	fdata->func = func;
316 	if (name)
317 		fdata->name = mystrdup (name);
318 	return fdata;
319 }
320 
321 
322 
set_func_val(FunctionData * data,int arg,int value)323 void set_func_val (FunctionData * data, int arg, int value)
324 {
325 	int i;
326 
327 	if (arg >= 0 && arg < MAX_FUNC_ARGS)
328 		data->func_val[arg] = value;
329 	else
330 		for (i = 0; i < MAX_FUNC_ARGS; i++)
331 			data->func_val[i] = value;
332 }
333 
free_func_data(FunctionData * data)334 int free_func_data (FunctionData * data)
335 {
336 	if (data) {
337 #ifdef DEBUG_ALLOCS
338 		LOCAL_DEBUG_OUT ("freeing func = \"%s\"",
339 										 data->name ? data->name : "(null)");
340 #endif
341 		if (data->name) {
342 			free (data->name);
343 			data->name = NULL;
344 		}
345 		if (data->text) {
346 #ifdef DEBUG_ALLOCS
347 			LOCAL_DEBUG_OUT ("func->text = \"%s\"", data->text);
348 #endif
349 			free (data->text);
350 			data->text = NULL;
351 		}
352 		return data->func;
353 	}
354 	return F_NOP;
355 }
356 
destroy_func_data(FunctionData ** pdata)357 void destroy_func_data (FunctionData ** pdata)
358 {
359 	if (pdata && *pdata) {
360 		free_func_data (*pdata);
361 		free (*pdata);
362 		*pdata = NULL;
363 	}
364 }
365 
default_func_val(FunctionCode func)366 long default_func_val (FunctionCode func)
367 {
368 	long val = 0;
369 
370 	switch (func) {
371 	case F_MAXIMIZE:
372 		val = DEFAULT_MAXIMIZE;
373 		break;
374 	case F_MOVE:
375 	case F_RESIZE:
376 		val = INVALID_POSITION;
377 		break;
378 	default:
379 		break;
380 	}
381 	return val;
382 }
383 
decode_func_units(FunctionData * data)384 void decode_func_units (FunctionData * data)
385 {
386 	register int i;
387 
388 #if 0
389 	int defaults[MAX_FUNC_ARGS];
390 
391 	defaults[0] = ASDefaultScrWidth;
392 	defaults[1] = ASDefaultScrHeight;
393 #endif
394 	for (i = 0; i < MAX_FUNC_ARGS; i++)
395 		switch (data->unit[i]) {
396 		case 'p':
397 		case 'P':
398 			data->unit_val[i] = 1;
399 			break;
400 		default:
401 			data->unit_val[i] = 0 /*defaults[i] */ ;
402 		}
403 }
404 
405 /**********************************************************************
406  * complex function management code :
407  **********************************************************************/
really_destroy_complex_func(ComplexFunction * cf)408 void really_destroy_complex_func (ComplexFunction * cf)
409 {
410 	if (cf) {
411 		if (cf->magic == MAGIC_COMPLEX_FUNC) {
412 			register int i;
413 
414 			cf->magic = 0;
415 			if (cf->name)
416 				free (cf->name);
417 			if (cf->items) {
418 				for (i = 0; i < cf->items_num; i++)
419 					free_func_data (&(cf->items[i]));
420 				free (cf->items);
421 			}
422 			free (cf);
423 		}
424 	}
425 }
426 
complex_function_destroy(ASHashableValue value,void * data)427 void complex_function_destroy (ASHashableValue value, void *data)
428 {
429 	ComplexFunction *cf = data;
430 
431 	if ((char *)value)
432 		free ((char *)value);
433 	if (cf && cf->magic == MAGIC_COMPLEX_FUNC) {
434 		if (cf->name == (char *)value)
435 			cf->name = NULL;
436 		really_destroy_complex_func (cf);
437 	} else if (data)
438 		free (data);
439 }
440 
init_list_of_funcs(struct ASHashTable ** list,Bool force)441 void init_list_of_funcs (struct ASHashTable **list, Bool force)
442 {
443 	if (list == NULL)
444 		return;
445 
446 	if (force && *list != NULL)
447 		destroy_ashash (list);
448 
449 	if (*list == NULL)
450 		*list =
451 				create_ashash (0, casestring_hash_value, casestring_compare,
452 											 complex_function_destroy);
453 }
454 
455 /* list could be NULL here : */
new_complex_func(struct ASHashTable * list,char * name)456 ComplexFunction *new_complex_func (struct ASHashTable *list, char *name)
457 {
458 	ComplexFunction *cf = NULL;
459 
460 	if (name == NULL)
461 		return NULL;
462 	/* enlisting complex function is optional */
463 	cf = (ComplexFunction *) safecalloc (1, sizeof (ComplexFunction));
464 	cf->name = mystrdup (name);
465 	cf->magic = MAGIC_COMPLEX_FUNC;
466 	if (list) {
467 		remove_hash_item (list, AS_HASHABLE (name), NULL, True);	/* we want only one copy */
468 		if (add_hash_item (list, AS_HASHABLE (cf->name), cf) != ASH_Success) {
469 			really_destroy_complex_func (cf);
470 			cf = NULL;
471 		}
472 	}
473 	return cf;
474 }
475 
find_complex_func(struct ASHashTable * list,char * name)476 ComplexFunction *find_complex_func (struct ASHashTable * list, char *name)
477 {
478 	ASHashData hdata = { 0 };
479 	if (name && list)
480 		if (get_hash_item (list, AS_HASHABLE (name), &hdata.vptr) !=
481 				ASH_Success)
482 			hdata.vptr = NULL;				/* we are being paranoid */
483 	return (ComplexFunction *) hdata.vptr;
484 }
485 
free_minipixmap_data(MinipixmapData * minipixmap)486 void free_minipixmap_data (MinipixmapData * minipixmap)
487 {
488 #ifdef DEBUG_ALLOCS
489 	LOCAL_DEBUG_OUT ("filename = \"%s\", image = %p", minipixmap->filename,
490 									 minipixmap->image);
491 #endif
492 	if (minipixmap->filename)
493 		free (minipixmap->filename);
494 	if (minipixmap->image) {
495 		safe_asimage_destroy (minipixmap->image);
496 		minipixmap->image = NULL;
497 	}
498 }
499 
500 /***************************************************************
501  * MenuData code :
502  ***************************************************************/
503 /****************************************************************************
504  * Implementing create-destroy functions :
505  ****************************************************************************/
menu_data_item_destroy(MenuDataItem * mdi)506 void menu_data_item_destroy (MenuDataItem * mdi)
507 {
508 #ifdef DEBUG_ALLOCS
509 	LOCAL_DEBUG_CALLER_OUT ("menu_data_item_destroy(\"%s\",%p)",
510 													((mdi
511 														&& mdi->fdata) ? (mdi->fdata->name) : "NULL"),
512 													mdi);
513 #endif
514 	if (mdi) {
515 		if (mdi->magic == MAGIC_MENU_DATA_ITEM) {
516 			int i;
517 
518 			mdi->magic = 0;
519 #ifdef DEBUG_ALLOCS
520 			LOCAL_DEBUG_OUT ("freeing func data %p", mdi->fdata);
521 #endif
522 			if (mdi->fdata) {
523 				free_func_data (mdi->fdata);
524 				free (mdi->fdata);
525 			}
526 			for (i = 0; i < MINIPIXMAP_TypesNum; ++i)
527 				free_minipixmap_data (&(mdi->minipixmap[i]));
528 
529 			if (mdi->item != NULL)
530 				free (mdi->item);
531 			if (mdi->item2 != NULL)
532 				free (mdi->item2);
533 			if (mdi->comment != NULL)
534 				free (mdi->comment);
535 		}
536 		free (mdi);
537 	}
538 }
539 
purge_menu_data_items(MenuData * md)540 void purge_menu_data_items (MenuData * md)
541 {
542 	if (md) {
543 		MenuDataItem *mdi;
544 
545 		while ((mdi = md->first) != NULL) {
546 			md->first = mdi->next;
547 			mdi->next = NULL;
548 			menu_data_item_destroy (mdi);
549 		}
550 	}
551 }
552 
destroy_menu_data(MenuData ** pmd)553 void destroy_menu_data (MenuData ** pmd)
554 {
555 	if (pmd && *pmd) {
556 		MenuData *md = *pmd;
557 
558 		if (md->magic == MAGIC_MENU_DATA) {
559 #ifdef DEBUG_ALLOCS
560 			LOCAL_DEBUG_CALLER_OUT ("menu_data_destroy(\"%s\", %p)",
561 															md->name ? md->name : "(null)", md);
562 #endif
563 			if (md->name)
564 				free (md->name);
565 			if (md->comment)
566 				free (md->comment);
567 
568 			purge_menu_data_items (md);
569 			md->magic = 0;
570 			free (md);
571 			*pmd = NULL;
572 		}
573 	}
574 }
575 
create_menu_data(char * name)576 MenuData *create_menu_data (char *name)
577 {
578 	MenuData *md = (MenuData *) safecalloc (1, sizeof (MenuData));
579 
580 	md->name = mystrdup (name);
581 	md->magic = MAGIC_MENU_DATA;
582 	return md;
583 }
584 
new_menu_data(ASHashTable * list,char * name)585 MenuData *new_menu_data (ASHashTable * list, char *name)
586 {
587 	ASHashData hdata = { 0 };
588 	MenuData *md = NULL;
589 
590 	if (name == NULL)
591 		return NULL;
592 	if (list == NULL)
593 		return NULL;
594 
595 	if (get_hash_item (list, AS_HASHABLE (name), &hdata.vptr) == ASH_Success)
596 		return (MenuData *) hdata.vptr;
597 
598 	md = create_menu_data (name);
599 
600 	if (add_hash_item (list, AS_HASHABLE (md->name), md) != ASH_Success)
601 		destroy_menu_data (&md);
602 	return md;
603 }
604 
find_menu_data(ASHashTable * list,char * name)605 MenuData *find_menu_data (ASHashTable * list, char *name)
606 {
607 	ASHashData hdata = { 0 };
608 
609 	if (name && list)
610 		if (get_hash_item (list, AS_HASHABLE (name), &hdata.vptr) !=
611 				ASH_Success)
612 			hdata.vptr = NULL;				/* we are being paranoid */
613 	return (MenuData *) hdata.vptr;
614 }
615 
616 
617 
new_menu_data_item(MenuData * menu)618 MenuDataItem *new_menu_data_item (MenuData * menu)
619 {
620 	MenuDataItem *mdi = NULL;
621 
622 	if (menu) {
623 		mdi = safecalloc (1, sizeof (MenuDataItem));
624 		mdi->magic = MAGIC_MENU_DATA_ITEM;
625 		mdi->prev = menu->last;
626 		if (menu->first == NULL)
627 			menu->first = mdi;
628 		else
629 			menu->last->next = mdi;
630 		menu->last = mdi;
631 		++(menu->items_num);
632 	}
633 	return mdi;
634 }
635 
assign_minipixmaps(MenuDataItem * mdi,MinipixmapData * minipixmaps)636 void assign_minipixmaps (MenuDataItem * mdi, MinipixmapData * minipixmaps)
637 {
638 	if (mdi && minipixmaps) {
639 		int i;
640 
641 		for (i = 0; i < MINIPIXMAP_TypesNum; ++i) {
642 			LOCAL_DEBUG_OUT ("type = %d, filename = \"%s\"", i,
643 											 minipixmaps[i].filename);
644 			if (minipixmaps[i].filename) {
645 				free_minipixmap_data (&(mdi->minipixmap[i]));
646 				mdi->minipixmap[i].filename = mystrdup (minipixmaps[i].filename);
647 				mdi->minipixmap[i].image = minipixmaps[i].image;
648 			}
649 		}
650 	}
651 }
652 
is_web_background(FunctionData * fdata)653 Bool is_web_background (FunctionData * fdata)
654 {
655 	return (fdata->func == F_CHANGE_BACKGROUND_FOREIGN
656 					&& is_url (fdata->text));
657 }
658 
check_fdata_availability(FunctionData * fdata)659 Bool check_fdata_availability (FunctionData * fdata)
660 {
661 	if (fdata == NULL)
662 		return False;
663 	if (fdata->func < F_ExecToolStart || fdata->func > F_ExecToolEnd) {
664 		if (IsSwallowFunc (fdata->func) || IsExecFunc (fdata->func)) {
665 			LOCAL_DEBUG_OUT ("now really checking availability for \"%s\"",
666 											 fdata->name ? fdata->name : "nameless");
667 			if (!is_executable_in_path (fdata->text)) {
668 				LOCAL_DEBUG_OUT ("unavailable :  \"%s\"",
669 												 fdata->name ? fdata->name : "nameless");
670 				return False;
671 			}
672 		} else if (fdata->func == F_CHANGE_BACKGROUND_FOREIGN
673 							 && !is_web_background (fdata)) {
674 			ASImageFileTypes type = check_asimage_file_type (fdata->text);
675 
676 			LOCAL_DEBUG_OUT ("foreign back image \"%s\" has type %d",
677 											 fdata->text, type);
678 			if (type > ASIT_Supported) {
679 				LOCAL_DEBUG_OUT ("foreign back image of unknown type :  \"%s\"",
680 												 fdata->text);
681 				return False;
682 			}
683 		}
684 	} else if (fdata->func == F_ExecInTerm) {
685 		char *target = strstr (fdata->text, "-e ");
686 
687 		target = target ? target + 3 : fdata->text;
688 		if (!is_executable_in_path (target)) {
689 			LOCAL_DEBUG_OUT ("unavailable :  \"%s\", target = \"%s\"",
690 											 fdata->name ? fdata->name : "nameless", target);
691 			return False;
692 		}
693 	}
694 
695 	return True;
696 }
697 
check_availability(MenuDataItem * mdi)698 static void check_availability (MenuDataItem * mdi)
699 {
700 	clear_flags (mdi->flags, MD_Disabled);
701 #ifndef NO_AVAILABILITYCHECK
702 	LOCAL_DEBUG_OUT ("checking availability for \"%s\"",
703 									 mdi->fdata->name ? mdi->fdata->name : "nameless");
704 	check_fdata_availability (mdi->fdata);
705 #endif													/* NO_AVAILABILITYCHECK */
706 }
707 
add_menu_data_item(MenuData * menu,int func,char * name,MinipixmapData * minipixmaps)708 MenuDataItem *add_menu_data_item (MenuData * menu, int func, char *name,
709 																	MinipixmapData * minipixmaps)
710 {
711 	MenuDataItem *mdi = new_menu_data_item (menu);
712 
713 	if (mdi) {
714 		mdi->fdata->func = func;
715 		mdi->fdata->name = mystrdup (name);
716 		check_availability (mdi);
717 		assign_minipixmaps (mdi, minipixmaps);
718 	}
719 	return mdi;
720 }
721 
add_menu_fdata_item(MenuData * menu,FunctionData * fdata,MinipixmapData * minipixmaps)722 MenuDataItem *add_menu_fdata_item (MenuData * menu, FunctionData * fdata,
723 																	 MinipixmapData * minipixmaps)
724 {
725 	MenuDataItem *mdi = NULL;
726 
727 	if (fdata)
728 		if ((mdi = new_menu_data_item (menu)) != NULL) {
729 			mdi->fdata = safecalloc (1, sizeof (FunctionData));
730 			copy_func_data (mdi->fdata, fdata);
731 			memset (fdata, 0x00, sizeof (FunctionData));
732 			parse_menu_item_name (mdi, &(mdi->fdata->name));
733 			check_availability (mdi);
734 			assign_minipixmaps (mdi, minipixmaps);
735 
736 			LOCAL_DEBUG_OUT ("mdi_fdata_encoding = %d, fdata_encoding = %d",
737 											 mdi->fdata->name_encoding, fdata->name_encoding);
738 		}
739 	return mdi;
740 }
741 
check_scale_menu_pmap(ASImage * im,ASFlagType flags)742 ASImage *check_scale_menu_pmap (ASImage * im, ASFlagType flags)
743 {
744 	if (im) {
745 		int w = im->width;
746 		int h = im->height;
747 
748 		if (w > h) {
749 			if (w > MAX_MENU_ITEM_HEIGHT) {
750 				w = MAX_MENU_ITEM_HEIGHT;
751 				h = (h * w) / im->width;
752 				if (h == 0)
753 					h = 1;
754 			}
755 		} else if (h > MAX_MENU_ITEM_HEIGHT) {
756 			h = MAX_MENU_ITEM_HEIGHT;
757 			w = (w * h) / im->height;
758 			if (w == 0)
759 				w = 1;
760 		}
761 		if (get_flags (flags, MD_ScaleMinipixmapDown)) {
762 			int tmp_h = asxml_var_get (ASXMLVAR_MenuFontSize) + 8;
763 			int tmp_w = (w * tmp_h) / h;
764 
765 			if (w > tmp_w || h > tmp_h) {
766 				w = tmp_w;
767 				h = tmp_h;
768 			}
769 		} else if (get_flags (flags, MD_ScaleMinipixmapUp)) {
770 			int tmp_w = asxml_var_get (ASXMLVAR_MinipixmapWidth);
771 			int tmp_h = asxml_var_get (ASXMLVAR_MinipixmapHeight);
772 
773 			if (w > tmp_w || h > tmp_h) {
774 				w = tmp_w;
775 				h = tmp_h;
776 			}
777 		}
778 
779 		if (w != im->width || h != im->height) {
780 			return scale_asimage (ASDefaultVisual, im, w, h, ASA_ASImage, 100,
781 														ASIMAGE_QUALITY_DEFAULT);
782 		}
783 	}
784 	return im;
785 }
786 
free_menu_pmaps(MenuData * menu)787 void free_menu_pmaps (MenuData * menu)
788 {
789 	MenuDataItem *curr;
790 
791 	LOCAL_DEBUG_OUT ("menu = %p, image_manager = %p", menu,
792 									 ASDefaultScr->image_manager);
793 
794 	for (curr = menu ? menu->first : NULL; curr != NULL; curr = curr->next) {
795 		int i;
796 
797 		for (i = 0; i < MINIPIXMAP_TypesNum; ++i)
798 			if (curr->minipixmap[i].image) {
799 				safe_asimage_destroy (curr->minipixmap[i].image);
800 				curr->minipixmap[i].image = NULL;
801 			}
802 	}
803 }
804 
805 void
load_menuitem_pmap(MenuDataItem * mdi,MinipixmapTypes type,Bool force)806 load_menuitem_pmap (MenuDataItem * mdi, MinipixmapTypes type, Bool force)
807 {
808 	MinipixmapData *minipixmap = &(mdi->minipixmap[type]);
809 	int func = mdi->fdata->func;
810 	char *filename = minipixmap->filename;
811 
812 	if (minipixmap->image != NULL)
813 		return;
814 
815 	if (filename == NULL) {
816 		if (type == MINIPIXMAP_Icon) {
817 			if (func == F_CHANGE_BACKGROUND_FOREIGN
818 					&& !is_web_background (mdi->fdata))
819 				filename = mdi->fdata->text;
820 		} else if (type == MINIPIXMAP_Preview) {
821 			if (func == F_CHANGE_BACKGROUND_FOREIGN
822 					|| func == F_CHANGE_BACKGROUND)
823 				filename = mdi->fdata->text;
824 		}
825 	}
826 
827 	LOCAL_DEBUG_OUT ("type = %d, filename = \"%s\", fdata->text = \"%s\"",
828 									 type, filename, mdi->fdata->text);
829 	if (filename) {
830 		char *cachedFileName = NULL;
831 		int h = MAX_MENU_ITEM_HEIGHT;
832 
833 		if (type == MINIPIXMAP_Preview)
834 			h = ASDefaultScr->MyDisplayHeight / 5;
835 		else if (get_flags (mdi->flags, MD_ScaleMinipixmapDown))
836 			h = asxml_var_get (ASXMLVAR_MenuFontSize) + 8;
837 		else if (get_flags (mdi->flags, MD_ScaleMinipixmapUp))
838 			h = asxml_var_get (ASXMLVAR_MinipixmapHeight);
839 		LOCAL_DEBUG_OUT ("minipixmap target height = %d for \"%s\"", h,
840 										 minipixmap->filename);
841 
842 		if (func == F_CHANGE_BACKGROUND || func == F_CHANGE_BACKGROUND_FOREIGN) {
843 			Bool use_thumbnail = True;
844 
845 			if (is_web_background (mdi->fdata) && is_url (filename)) {
846 				int s1 = 0, s2 = 0;
847 
848 				cachedFileName = make_session_webcache_file (Session, filename);
849 				use_thumbnail =
850 						check_download_complete (0, cachedFileName, &s1, &s2);
851 				use_thumbnail = use_thumbnail && ((s1 > 0 || s2 == s1));
852 				if (use_thumbnail)
853 					filename = cachedFileName;
854 			}
855 
856 			if (type == MINIPIXMAP_Preview) {
857 				if (is_web_background (mdi->fdata) && is_url (filename)) {
858 					if (!use_thumbnail && force && minipixmap->loadCount == 0) {
859 						spawn_download (filename, cachedFileName);
860 						minipixmap->loadCount++;
861 					}
862 				} else if (filename != minipixmap->filename) {
863 					use_thumbnail = force;
864 				}
865 			}
866 
867 			if (use_thumbnail)
868 				minipixmap->image =
869 						get_thumbnail_asimage (ASDefaultScr->image_manager, filename,
870 																	 0, h,
871 																	 AS_THUMBNAIL_PROPORTIONAL |
872 																	 AS_THUMBNAIL_DONT_ENLARGE);
873 			destroy_string (&cachedFileName);
874 		} else {
875 			minipixmap->image = load_environment_icon_any (filename, h);
876 		}
877 		LOCAL_DEBUG_OUT ("minipixmap = \"%s\", minipixmap_image = %p",
878 										 filename, minipixmap->image);
879 	}
880 }
881 
882 
883 void
reload_menuitem_pmap(MenuDataItem * mdi,MinipixmapTypes type,Bool force)884 reload_menuitem_pmap (MenuDataItem * mdi, MinipixmapTypes type, Bool force)
885 {
886 	MinipixmapData *minipixmap = &(mdi->minipixmap[type]);
887 
888 	LOCAL_DEBUG_OUT ("type = %d, fdata->text = \"%s\"", type,
889 									 mdi->fdata->text);
890 
891 	if (minipixmap->image) {
892 		if (minipixmap->filename != NULL && !force) {
893 			if (get_asimage_file_type
894 					(ASDefaultScr->image_manager,
895 					 minipixmap->filename) != ASIT_XMLScript) {
896 				ASImage *im = minipixmap->image;
897 
898 				if (im->imageman != NULL
899 						&& im->imageman != ASDefaultScr->image_manager) {
900 					/* need to engage in trickery : */
901 					relocate_asimage (ASDefaultScr->image_manager, im);
902 				}
903 				/* fprintf( stderr, "minipixmap \"%s\" is not an XML script - skipping\n", minipixmap ); */
904 				return;
905 			}
906 		}
907 		safe_asimage_destroy (minipixmap->image);
908 		minipixmap->image = NULL;
909 	}
910 
911 	load_menuitem_pmap (mdi, type, False);
912 }
913 
reload_menu_pmaps(MenuData * menu,Bool force)914 void reload_menu_pmaps (MenuData * menu, Bool force)
915 {
916 	MenuDataItem *curr;
917 
918 	LOCAL_DEBUG_OUT ("menu = %p, image_manager = %p", menu,
919 									 ASDefaultScr->image_manager);
920 	if (menu && ASDefaultScr->image_manager)
921 		for (curr = menu->first; curr != NULL; curr = curr->next) {
922 			int i;
923 
924 			for (i = 0; i < MINIPIXMAP_TypesNum; ++i)
925 				reload_menuitem_pmap (curr, i, force);
926 		}
927 }
928 
929 
930 /* this is very often used function so we optimize it as good as we can */
parse_menu_item_name(MenuDataItem * item,char ** name)931 int parse_menu_item_name (MenuDataItem * item, char **name)
932 {
933 	register int i;
934 	register char *ptr = *name;
935 
936 	if (ptr == NULL || item == NULL)
937 		return -1;
938 
939 	for (i = 0; ptr[i] && ptr[i] != '\t'; i++) ;
940 
941 	if (ptr[i] == '\0') {
942 		item->item = *name;
943 		*name = NULL;								/* that will prevent us from memory deallocation */
944 		item->item2 = NULL;
945 	} else {
946 		item->item = mystrndup (*name, i);
947 		item->item2 = mystrdup (&(ptr[i + 1]));
948 	}
949 
950 	if ((ptr = item->item) != NULL)
951 		for (i = 0; ptr[i]; i++)
952 			if (ptr[i] == '_')
953 				ptr[i] = ' ';
954 
955 	if ((ptr = item->item2) != NULL)
956 		for (i = 0; ptr[i]; i++)
957 			if (ptr[i] == '_')
958 				ptr[i] = ' ';
959 
960 	return 0;
961 }
962 
menu_data_item_from_func(MenuData * menu,FunctionData * fdata,Bool do_check_availability)963 MenuDataItem *menu_data_item_from_func (MenuData * menu,
964 																				FunctionData * fdata,
965 																				Bool do_check_availability)
966 {
967 	MenuDataItem *item = NULL;
968 
969 	if (fdata != NULL) {
970 		MinipixmapTypes mtype = GetMinipixmapType (fdata->func);
971 
972 		if (mtype < MINIPIXMAP_TypesNum) {
973 			if (menu->last)
974 				item = menu->last;
975 			else {
976 				item = new_menu_data_item (menu);
977 				item->fdata = fdata;
978 			}
979 			if (fdata->func == F_SMALL_MINIPIXMAP)
980 				set_flags (item->flags, MD_ScaleMinipixmapDown);
981 			else if (fdata->func == F_LARGE_MINIPIXMAP)
982 				set_flags (item->flags, MD_ScaleMinipixmapUp);
983 
984 			LOCAL_DEBUG_OUT ("type = %d, filename = \"%s\"", mtype, fdata->name);
985 			set_string (&(item->minipixmap[mtype].filename),
986 									mystrdup (fdata->name));
987 		} else {
988 			item = new_menu_data_item (menu);
989 			if (parse_menu_item_name (item, &(fdata->name)) >= 0)
990 				item->fdata = fdata;
991 			if (do_check_availability)
992 				check_availability (item);
993 		}
994 		if (item == NULL || item->fdata != fdata) {
995 			free_func_data (fdata);		/* insurance measure */
996 			free (fdata);
997 		}
998 	}
999 	return item;
1000 }
1001 
compare_func_data_name(const void * a,const void * b)1002 int compare_func_data_name (const void *a, const void *b)
1003 {
1004 	FunctionData *fda = *(FunctionData **) a;
1005 	FunctionData *fdb = *(FunctionData **) b;
1006 
1007 	return strcmp (fda->name ? fda->name : "", fdb->name ? fdb->name : "");
1008 }
1009 
1010 
1011 
1012 void
print_func_data(const char * file,const char * func,int line,FunctionData * data)1013 print_func_data (const char *file, const char *func, int line,
1014 								 FunctionData * data)
1015 {
1016 	fprintf (stderr, "%s:%s:%s:%d>!!FUNC ", get_application_name (), file,
1017 					 func, line);
1018 	if (data == NULL)
1019 		fprintf (stderr, "NULL Function\n");
1020 	else {
1021 		TermDef *term = func2fterm (data->func, True);
1022 
1023 		if (term == NULL)
1024 			fprintf (stderr, "Invalid Function %ld\n", (long)data->func);
1025 		else {
1026 			fprintf (stderr, "%s \"%s\" text[%s] ", term->keyword,
1027 							 data->name ? data->name : "", data->text ? data->text : "");
1028 			fprintf (stderr, "val0[%ld%c(%ld)] ", (long)data->func_val[0],
1029 							 (data->unit[0] == '\0') ? ' ' : data->unit[0],
1030 							 (long)data->unit_val[0]);
1031 			fprintf (stderr, "val1[%ld%c(%ld)] ", (long)data->func_val[1],
1032 							 (data->unit[1] == '\0') ? ' ' : data->unit[1],
1033 							 (long)data->unit_val[1]);
1034 			fprintf (stderr, "(popup=%p)\n", data->popup);
1035 		}
1036 	}
1037 }
1038 
1039 
1040 
format_FunctionData(FunctionData * data)1041 char *format_FunctionData (FunctionData * data)
1042 {
1043 	char *buffer = NULL;
1044 
1045 	if (data != NULL) {
1046 		int len = 0;
1047 		int pos = 0;
1048 
1049 		if (data->name)
1050 			len += 1 + strlen (data->name) + 1;
1051 		if (data->text)
1052 			len += 1 + strlen (data->text) + 1;
1053 		else
1054 			len += 64;
1055 		buffer = safemalloc (len);
1056 
1057 		if (data->name) {
1058 			int i = 0;
1059 
1060 			buffer[pos++] = '"';
1061 			while (data->name[i])
1062 				buffer[pos++] = data->name[i++];
1063 			buffer[pos++] = '"';
1064 		}
1065 		if (data->text) {
1066 			int i = 0;
1067 
1068 			if (pos)
1069 				buffer[pos++] = ' ';
1070 			while (data->text[i])
1071 				buffer[pos++] = data->text[i++];
1072 		} else {
1073 #define FORMAT_FUNC_VALUE(val,unit) \
1074 		do{ int i = unsigned_int2buffer_end (&buffer[pos], len-pos, ((val) < 0)? -(val) : (val)); \
1075 			if (val < 0) buffer[pos++] = '-'; \
1076 			while (buffer[i]) buffer[pos++] = buffer[i++]; \
1077 			if (unit)buffer[pos++] = unit; }while(0)
1078 
1079 			if (pos)
1080 				buffer[pos++] = ' ';
1081 			FORMAT_FUNC_VALUE (data->func_val[0], data->unit[0]);
1082 			buffer[pos++] = ' ';
1083 			FORMAT_FUNC_VALUE (data->func_val[1], data->unit[1]);
1084 
1085 #undef FORMAT_FUNC_VALUE
1086 		}
1087 		buffer[pos++] = '\0';
1088 	}
1089 
1090 	return buffer;
1091 }
1092