1 /*
2  * Copyright (c) 1998,1999,2000 Ethan Fischer <allanon@crystaltokyo.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  */
19 
20 /****************************************************************************
21  *
22  * dirtree.c: converts a menu directory tree into a configuration file
23  *
24  ***************************************************************************/
25 
26 #define DIRTREE_C
27 #define LOCAL_DEBUG
28 #include <sys/stat.h>
29 
30 #include "../../configure.h"
31 #include "../../libAfterStep/asapp.h"
32 #include "../../libAfterStep/desktop_category.h"
33 #include "../../libAfterConf/afterconf.h"
34 #include "dirtree.h"
35 void init_func_data (FunctionData * data);
36 int txt2func (const char *text, FunctionData * fdata, int quiet);
37 int free_func_data (FunctionData * data);
38 
39 dirtree_compar_f dirtree_compar_list[] = {
40 	dirtree_compar_base_order,
41 	dirtree_compar_order,
42 	dirtree_compar_type,
43 	dirtree_compar_alpha,
44 	NULL
45 };
46 
dirtree_new(void)47 dirtree_t *dirtree_new (void)
48 {
49 	dirtree_t *tree = safecalloc (1, sizeof (dirtree_t));
50 
51 	init_func_data (&tree->command);
52 	tree->order = 10000;
53 
54 	return tree;
55 }
56 
57 #define ASSERT_TREE(t)  do{if((t)==NULL){show_debug(__FILE__, __FUNCTION__,__LINE__,"dirtree is NULL%s","");return;}}while(0)
58 #define ASSERT_TREE_INT(t,r)  do{if((t)==NULL){show_debug(__FILE__, __FUNCTION__,__LINE__,"dirtree is NULL%s","");return (r);}}while(0)
59 
dirtree_remove(dirtree_t * tree)60 void dirtree_remove (dirtree_t * tree)
61 {
62 	ASSERT_TREE (tree);
63 	if (tree->parent != NULL && tree->parent->child != NULL) {
64 		if (tree->parent->child == tree)
65 			tree->parent->child = tree->next;
66 		else {
67 			dirtree_t *t;
68 
69 			for (t = tree->parent->child; t->next != NULL; t = t->next)
70 				if (t->next == tree) {
71 					t->next = tree->next;
72 					break;
73 				}
74 		}
75 		tree->next = tree->parent = NULL;
76 	}
77 }
78 
dirtree_delete(dirtree_t * tree)79 void dirtree_delete (dirtree_t * tree)
80 {
81 	ASSERT_TREE (tree);
82 	/* find and remove ourself from our parent's list */
83 	dirtree_remove (tree);
84 
85 	/* kill our children */
86 	while (tree->child != NULL)
87 		dirtree_delete (tree->child);
88 
89 	/* free members */
90 	if (tree->stripped_name != NULL && tree->stripped_name != tree->name)
91 		free (tree->stripped_name);
92 	if (tree->name != NULL)
93 		free (tree->name);
94 	if (tree->path != NULL)
95 		free (tree->path);
96 	if (tree->icon != NULL)
97 		free (tree->icon);
98 	if (tree->extension != NULL)
99 		free (tree->extension);
100 	if (tree->minipixmap_extension != NULL)
101 		free (tree->minipixmap_extension);
102 	free_func_data (&tree->command);
103 	if (tree->de != NULL)
104 		unref_desktop_entry (tree->de);
105 	if (tree->Comment != NULL)
106 		free (tree->Comment);
107 	if (tree->FolderReference != NULL)
108 		free (tree->FolderReference);
109 
110 	free (tree);
111 }
112 
make_absolute(const char * path1,const char * path2)113 char *make_absolute (const char *path1, const char *path2)
114 {
115 	char *path;
116 
117 	if (path1 == NULL || path2 == NULL)
118 		return mystrdup ("./");
119 	if (*path2 == '/' || *path2 == '~' || *path2 == '$')
120 		path = copy_replace_envvar (path2);
121 	else
122 		/* relative path */
123 	{
124 		path =
125 				safemalloc (strlen ((char *)path1) + strlen ((char *)path2) + 2);
126 		sprintf (path, "%s/%s", path1, path2);
127 	}
128 	return path;
129 }
130 
strip_extension(char * name,char * ext)131 char *strip_extension (char *name, char *ext)
132 {
133 	if (name && ext) {
134 		int nlen = strlen (name);
135 		int elen = strlen (ext);
136 
137 		if (nlen >= elen) {
138 			if (!strcmp (name + nlen - elen, ext))
139 				return mystrndup (name, nlen - elen);
140 			else if (!strncmp (name, ext, elen))
141 				return mystrndup (name + elen, nlen - elen);
142 		}
143 	}
144 	return name;
145 }
146 
147 /* assumes that tree->name and tree->path are already filled in */
dirtree_fill_from_dir(dirtree_t * tree)148 void dirtree_fill_from_dir (dirtree_t * tree)
149 {
150 	struct direntry **list;
151 	int i, n;
152 
153 	ASSERT_TREE (tree);
154 	n = my_scandir (tree->path, &list, no_dots_except_include, NULL);
155 	for (i = 0; i < n; i++) {
156 		dirtree_t *t = dirtree_new ();
157 
158 		t->name = mystrdup (list[i]->d_name);
159 		t->path = make_absolute (tree->path, t->name);
160 		if (S_ISDIR (list[i]->d_mode))
161 			t->flags |= DIRTREE_DIR;
162 		t->mtime = list[i]->d_mtime;
163 
164 		dirtree_fill_from_dir (t);
165 		t->parent = tree;
166 		t->next = tree->child;
167 		tree->child = t;
168 
169 		free (list[i]);
170 	}
171 	if (n > 0) {
172 		tree->flags |= DIRTREE_DIR;
173 		free (list);
174 	}
175 }
176 
dirtree_new_from_dir(const char * dir)177 dirtree_t *dirtree_new_from_dir (const char *dir)
178 {
179 
180 	dirtree_t *tree = NULL;
181 
182 	if (dir) {
183 		char *p;
184 		int start_mark = 0, end_mark;
185 		register int i = 0;
186 		register char *ptr = (char *)dir;
187 		tree = dirtree_new ();
188 
189 		while (ptr[i])
190 			++i;
191 		end_mark = i;
192 
193 		p = tree->path = safemalloc (i + 1);
194 		do {
195 			p[i] = ptr[i];
196 		} while (--i >= 0);
197 		while (--i > 0)
198 			if (ptr[i] == '/') {
199 				end_mark = i;
200 				break;
201 			}
202 
203 		while (--i >= 0)
204 			if (ptr[i] == '/') {
205 				start_mark = i + 1;
206 				break;
207 			}
208 		tree->name = mystrndup (&(ptr[start_mark]), end_mark - start_mark);
209 		dirtree_fill_from_dir (tree);
210 	}
211 	return tree;
212 }
213 
214 /* move tree2's children to tree1 */
dirtree_move_children(dirtree_t * tree1,dirtree_t * tree2)215 void dirtree_move_children (dirtree_t * tree1, dirtree_t * tree2)
216 {
217 	ASSERT_TREE (tree1);
218 	ASSERT_TREE (tree2);
219 	if (tree2->child != NULL) {
220 		dirtree_t *t1, *t2 = NULL;
221 
222 		for (t1 = tree2->child; t1 != NULL; t2 = t1, t1 = t1->next)
223 			t1->parent = tree1;
224 		if (t2 != NULL) {
225 			t2->next = tree1->child;
226 			tree1->child = tree2->child;
227 		}
228 		tree2->child = NULL;
229 	}
230 }
231 
232 void
dirtree_set_command(dirtree_t * tree,struct FunctionData * command,int recurse)233 dirtree_set_command (dirtree_t * tree, struct FunctionData *command,
234 										 int recurse)
235 {
236 	dirtree_t *t;
237 	ASSERT_TREE (tree);
238 	if (command) {
239 		for (t = tree->child; t != NULL; t = t->next) {
240 			t->command = *command;
241 			if (t->command.text != NULL)
242 				t->command.text = mystrdup (t->command.text);
243 			if (recurse)
244 				dirtree_set_command (t, command, 1);
245 		}
246 	}
247 }
248 
249 void
dirtree_add_category(dirtree_t * tree,ASCategoryTree * ct,ASDesktopCategory * dc,Bool include_children,ASHashTable * exclusions,int depth)250 dirtree_add_category (dirtree_t * tree, ASCategoryTree * ct,
251 											ASDesktopCategory * dc, Bool include_children,
252 											ASHashTable * exclusions, int depth)
253 {
254 	dirtree_t *t = NULL;
255 	int i, valid_entries_num = 0;
256 	ASDesktopEntryInfo *entries;
257 
258 	if (++depth >= 50 )
259 		return;
260 
261 	ASSERT_TREE (tree);
262 	ASSERT_TREE (dc);
263 	ASSERT_TREE (ct);
264 
265 	LOCAL_DEBUG_OUT ("DesktopCategory \"%s\", has %d entries", dc->name,
266 									 PVECTOR_USED (dc->entries));
267 	entries =
268 			desktop_category_get_entries (ct, dc, include_children ? 1 : 0,
269 																		exclusions, &valid_entries_num);
270 
271 	if (entries) {
272 		for (i = 0; i < valid_entries_num; ++i) {
273 			ASDesktopEntry *de = entries[i].de;
274 			ASDesktopCategory *sub_dc = entries[i].dc == dc ? NULL : entries[i].dc;
275 
276 			if (de->type != ASDE_TypeDirectory && include_children) {
277 				if (desktop_entry_in_subcategory
278 						(ct, de, entries, valid_entries_num))
279 					continue;
280 			}
281 
282 			t = dirtree_new ();
283 
284 			if (dup_desktop_entry_Name (de, &(t->name)))
285 				set_flags (t->flags, DIRTREE_NAME_IS_UTF8);
286 
287 			if (dup_desktop_entry_Comment (de, &(t->Comment)))
288 				set_flags (t->flags, DIRTREE_COMMENT_IS_UTF8);
289 
290 			ref_desktop_entry (de);
291 			t->de = de;
292 
293 			t->parent = tree;
294 			t->next = tree->child;
295 			tree->child = t;
296 
297 			if (sub_dc) {
298 				t->flags |= DIRTREE_DIR;
299 				dirtree_add_category (t, ct, sub_dc, include_children, exclusions, depth);
300 			}
301 		}
302 		free (entries);
303 	}
304 }
305 
306 
307 void
dirtree_add_category_by_name(dirtree_t * tree,const char * cat_name,Bool include_children,ASHashTable * exclusions)308 dirtree_add_category_by_name (dirtree_t * tree, const char *cat_name,
309 															Bool include_children,
310 															ASHashTable * exclusions)
311 {
312 	ASCategoryTree *ct = CombinedCategories;
313 	ASDesktopCategory *dc = NULL;
314 
315 	ASSERT_TREE (tree);
316 
317 	dc = name2desktop_category (cat_name, &ct);
318 	dirtree_add_category (tree, ct, dc, include_children, exclusions, 0);
319 }
320 
dirtree_fill_from_reference(dirtree_t * tree,const char * reference)321 void dirtree_fill_from_reference (dirtree_t * tree, const char *reference)
322 {
323 	ASCategoryTree *ct = CombinedCategories;
324 	ASDesktopCategory *dc = NULL;
325 	ASDesktopEntry *de = NULL;
326 
327 	ASSERT_TREE (tree);
328 
329 	dc = name2desktop_category (reference, &ct);
330 	if (!dc)
331 		return;
332 	de = fetch_desktop_entry (ct,
333 														dc->index_name ? dc->index_name : dc->name);
334 
335 	if (dup_desktop_entry_Name (de, &(tree->name)))
336 		set_flags (tree->flags, DIRTREE_NAME_IS_UTF8);
337 
338 	if (dup_desktop_entry_Comment (de, &(tree->Comment)))
339 		set_flags (tree->flags, DIRTREE_COMMENT_IS_UTF8);
340 
341 	if (de->Icon) {
342 		set_string (&(tree->icon), mystrdup (de->Icon));
343 		set_flags (tree->flags, DIRTREE_ICON_IS_SMALL);
344 	}
345 }
346 
347 
dirtree_parse(dirtree_t * tree,const char * file)348 int dirtree_parse (dirtree_t * tree, const char *file)
349 {
350 	FILE *fp;
351 	char *str;
352 	ASHashTable *exclusions = NULL;
353 
354 	ASSERT_TREE_INT (tree, 1);
355 
356 	if (file == NULL)
357 		return 1;
358 
359 	if ((fp = fopen (file, "r")) == NULL)
360 		return 1;
361 
362 LOCAL_DEBUG_OUT ("Parsing \"%s\"", file);
363 	str = safemalloc (8192);
364 	while (fgets (str, 8192, fp) != NULL) {
365 		char *ptr;
366 		Bool do_include = False;
367 		int include_order = 0;
368 
369 		ptr = strip_whitespace (str);
370 		/* ignore comments and blank lines */
371 		if (*ptr == '#' || *ptr == '\0')
372 			continue;
373 		if (!mystrncasecmp (ptr, "exclude", 7)) {
374 			char *excl_name;
375 			if (exclusions == NULL)
376 				exclusions =
377 						create_ashash (0, casestring_hash_value, casestring_compare,
378 													 string_destroy);
379 			if (exclusions) {
380 				excl_name = stripcpy2 (ptr + 7, 0);
381 				LOCAL_DEBUG_OUT("Exclusion [%s] added ", excl_name);
382 				add_hash_item (exclusions, AS_HASHABLE (excl_name), NULL);
383 			}
384 			continue;
385 		}
386 
387 		if (!mystrncasecmp (ptr, "category", 8)) {
388 			char *cat_name;
389 			Bool include_children = False;
390 			ptr += 8;
391 			if (*ptr == '_')
392 				++ptr;
393 			if (!mystrncasecmp (ptr, "tree", 4)) {
394 				include_children = True;
395 				ptr += 4;
396 			}
397 			cat_name = stripcpy2 (ptr, 0);
398 			dirtree_add_category_by_name (tree, cat_name, include_children,
399 																		exclusions);
400 			free (cat_name);
401 			continue;
402 		}
403 
404 		if (!mystrncasecmp (ptr, "include", 7)) {
405 			do_include = True;
406 			ptr += 7;
407 			if (*ptr == '_')
408 				++ptr;
409 			if (!mystrncasecmp (ptr, "ordered", 7)) {
410 				for (ptr += 7; isspace (*ptr); ptr++) ;
411 				if (isdigit (*ptr)) {
412 					include_order = atoi (ptr);
413 					while (isdigit (*ptr))
414 						++ptr;
415 				}
416 			}
417 		}
418 
419 		if (do_include) {
420 			char *path;
421 			dirtree_t *t;
422 
423 			while (isspace (*ptr))
424 				ptr++;
425 			if (*ptr != '"')
426 				continue;
427 			path = ++ptr;
428 			for (; *ptr != '\0' && *ptr != '"'; ptr++) ;
429 			if (*ptr == '"')
430 				for (*ptr++ = '\0'; isspace (*ptr); ptr++) ;
431 			path = make_absolute (tree->path, path);
432 			t = dirtree_new_from_dir (path);
433 			free (path);
434 			if (t != NULL) {
435 				if (*ptr != '\0') {
436 					txt2func (ptr, &t->command, False);
437 					dirtree_set_command (t, &t->command, 1);
438 				}
439 
440 				/* included dir might have a .include */
441 				dirtree_parse_include (t);
442 				if (include_order != 0)
443 					dirtree_set_base_order (t, include_order);
444 
445 				dirtree_move_children (tree, t);
446 				dirtree_delete (t);
447 			}
448 		} else if (!mystrncasecmp (ptr, "keepname", 8))
449 			tree->flags |= DIRTREE_KEEPNAME;
450 		else if (!mystrncasecmp (ptr, "ShowUnavailable", 15))
451 			tree->flags |= DIRTREE_SHOW_UNAVAILABLE;
452 		else if (!mystrncasecmp (ptr, "extension", 9)) {
453 			char *tmp;
454 
455 			for (ptr += 9; isspace (*ptr); ptr++) ;
456 			for (tmp = ptr + strlen (ptr); tmp > ptr && isspace (*(tmp - 1));
457 					 tmp--) ;
458 			if (tmp != ptr) {
459 				if (tree->extension)
460 					free (tree->extension);
461 				tree->extension = mystrndup (ptr, tmp - ptr);
462 			}
463 		} else if (!mystrncasecmp (ptr, "miniextension", 13)) {
464 			char *tmp;
465 
466 			for (ptr += 13; isspace (*ptr); ptr++) ;
467 			for (tmp = ptr + strlen (ptr); tmp > ptr && isspace (*(tmp - 1));
468 					 tmp--) ;
469 			if (tmp != ptr)
470 				tree->minipixmap_extension = mystrndup (ptr, tmp - ptr);
471 		} else if (!mystrncasecmp (ptr, "minipixmap", 10)
472 							 || !mystrncasecmp (ptr, "smallminipixmap", 15)) {
473 			if (ptr[0] == 's' || ptr[0] == 'S') {
474 				set_flags (tree->flags, DIRTREE_ICON_IS_SMALL);
475 				ptr += 5;
476 			} else
477 				clear_flags (tree->flags, DIRTREE_ICON_IS_SMALL);
478 			set_string (&(tree->icon), stripcpy2 (ptr + 10, False));
479 
480 		} else if (!mystrncasecmp (ptr, "command", 7)) {
481 			for (ptr += 7; isspace (*ptr); ptr++) ;
482 			txt2func (ptr, &tree->command, False);
483 			dirtree_set_command (tree, &tree->command, 0);
484 		} else if (!mystrncasecmp (ptr, "order", 5)) {
485 			tree->order = strtol (ptr + 5, NULL, 10);
486 		} else if (!mystrncasecmp (ptr, "RecentSubmenuItems", 18)) {
487 			tree->recent_items = strtol (ptr + 18, NULL, 10);
488 			tree->flags |= DIRTREE_RECENT_ITEMS_SET;
489 		} else if (!mystrncasecmp (ptr, "name", 4)) {
490 			set_string (&(tree->name), stripcpy2 (ptr + 4, False));
491 			clear_flags (tree->flags, DIRTREE_NAME_IS_UTF8);
492 		} else if (!mystrncasecmp (ptr, "Comment", 7)) {
493 			set_string (&(tree->Comment), stripcpy2 (ptr + 7, False));
494 			clear_flags (tree->flags, DIRTREE_COMMENT_IS_UTF8);
495 		} else if (!mystrncasecmp (ptr, "FolderReference", 15)) {
496 			set_string (&(tree->FolderReference), stripcpy2 (ptr + 15, False));
497 			dirtree_fill_from_reference (tree, tree->FolderReference);
498 		}
499 	}
500 	free (str);
501 	fclose (fp);
502 	return 0;
503 }
504 
dirtree_parse_include(dirtree_t * tree)505 void dirtree_parse_include (dirtree_t * tree)
506 {
507 	dirtree_t *t;
508 	ASSERT_TREE (tree);
509 
510 	/* parse the first .include */
511 	for (t = tree->child; t != NULL; t = t->next)
512 		if (t->name[0] == '.') {
513 			dirtree_remove (t);
514 			dirtree_parse (tree, t->path);
515 			dirtree_delete (t);
516 			break;
517 		}
518 
519 	/* nuke any other .include's */
520 	for (t = tree->child; t != NULL; t = t->next)
521 		if (t->name[0] == '.')
522 			dirtree_delete (t);
523 
524 	for (t = tree->child; t != NULL; t = t->next)
525 		dirtree_parse_include (t);
526 }
527 
dirtree_set_base_order(dirtree_t * tree,int base_order)528 void dirtree_set_base_order (dirtree_t * tree, int base_order)
529 {
530 	dirtree_t *t;
531 	ASSERT_TREE (tree);
532 
533 	if (tree->base_order == 0)
534 		tree->base_order = base_order;
535 
536 	for (t = tree->child; t != NULL; t = t->next) {
537 		if (t->child == NULL) {
538 			if (t->base_order == 0)
539 				t->base_order = base_order;
540 		} else
541 			dirtree_set_base_order (t, base_order);
542 	}
543 }
544 
545 
dirtree_remove_order(dirtree_t * tree)546 void dirtree_remove_order (dirtree_t * tree)
547 {
548 	dirtree_t *t;
549 	char *ptr;
550 	int order = strtol (tree->name, &ptr, 10);
551 	ASSERT_TREE (tree);
552 
553 	if (ptr != tree->name && *ptr == '_') {
554 		tree->order = order;
555 		memmove (tree->name, ptr + 1, strlen (ptr + 1) + 1);
556 	}
557 	for (t = tree->child; t != NULL; t = t->next)
558 		dirtree_remove_order (t);
559 }
560 
dirtree_merge(dirtree_t * tree)561 void dirtree_merge (dirtree_t * tree)
562 {
563 	dirtree_t *t;
564 	ASSERT_TREE (tree);
565 
566 	/* PASS1: merge all the subdirs of the current dir */
567 	for (t = tree->child; t != NULL; t = t->next) {
568 		if (t->stripped_name == NULL) {
569 			t->stripped_name =
570 					tree->extension ? strip_extension (t->name,
571 																						 tree->extension) : t->name;
572 			if (t->stripped_name == t->name && tree->minipixmap_extension) {
573 				t->stripped_name =
574 						strip_extension (t->name, tree->minipixmap_extension);
575 				if (t->stripped_name != t->name)
576 					set_flags (t->flags, DIRTREE_MINIPIXMAP);
577 			}
578 		}
579 		if (t->flags & DIRTREE_DIR) {
580 			dirtree_t *t2;
581 
582 			for (t2 = t->next; t2 != NULL;) {
583 				if ((t2->flags & DIRTREE_DIR) && !strcmp (t->name, t2->name)) {
584 					if (t2->order != -1)
585 						t->order = t2->order;
586 					if (t2->base_order < t->base_order)
587 						t->base_order = t2->base_order;
588 					dirtree_remove (t2);
589 					dirtree_move_children (t, t2);
590 					dirtree_delete (t2);
591 					t2 = t->next;
592 				} else
593 					t2 = t2->next;
594 			}
595 		}
596 	}
597 	/* PASS2: attach all the minipixmaps : */
598 	for (t = tree->child; t != NULL; t = t->next)
599 		if (get_flags (t->flags, DIRTREE_MINIPIXMAP)) {	/* let us try and find matching filename */
600 			dirtree_t *t2;
601 
602 			for (t2 = tree->child; t2 != NULL; t2 = t2->next) {
603 				if (t2 != t && !strcmp (t2->stripped_name, t->stripped_name)) {
604 					if (t2->icon)
605 						free (t2->icon);
606 					t2->icon = mystrdup (t->path);
607 				}
608 			}
609 		}
610 
611 	/* PASS3: merge all the subdirs : */
612 	for (t = tree->child; t != NULL; t = t->next)
613 		dirtree_merge (t);
614 }
615 
616 /* sort entries based on the array dirtree_compar_list */
dirtree_compar(const dirtree_t ** d1,const dirtree_t ** d2)617 int dirtree_compar (const dirtree_t ** d1, const dirtree_t ** d2)
618 {
619 	int diff = 0;
620 	dirtree_compar_f *compar;
621 
622 	ASSERT_TREE_INT (*d1, 1);
623 	ASSERT_TREE_INT (*d2, -1);
624 
625 	for (compar = dirtree_compar_list; diff == 0 && *compar != NULL;
626 			 compar++)
627 		diff = (*compar) (d1, d2);
628 	return diff;
629 }
630 
631 /* sort entries based their base_order; 0 comes before 1000 */
632 int
dirtree_compar_base_order(const dirtree_t ** d1,const dirtree_t ** d2)633 dirtree_compar_base_order (const dirtree_t ** d1, const dirtree_t ** d2)
634 {
635 	return (**d1).base_order - (**d2).base_order;
636 }
637 
638 
639 /* sort entries based their order; 1 comes before 2 */
dirtree_compar_order(const dirtree_t ** d1,const dirtree_t ** d2)640 int dirtree_compar_order (const dirtree_t ** d1, const dirtree_t ** d2)
641 {
642 	return (**d1).order - (**d2).order;
643 }
644 
645 /* sort entries based on their type; directories come first */
dirtree_compar_type(const dirtree_t ** d1,const dirtree_t ** d2)646 int dirtree_compar_type (const dirtree_t ** d1, const dirtree_t ** d2)
647 {
648 	return ((**d2).flags & DIRTREE_DIR) - ((**d1).flags & DIRTREE_DIR);
649 }
650 
651 /* sort entries based on their names; A comes before Z */
dirtree_compar_alpha(const dirtree_t ** d1,const dirtree_t ** d2)652 int dirtree_compar_alpha (const dirtree_t ** d1, const dirtree_t ** d2)
653 {
654 	return strcmp ((**d1).name, (**d2).name);
655 }
656 
657 /* sort entries based on their mtimes; old entries before new entries */
dirtree_compar_mtime(const dirtree_t ** d1,const dirtree_t ** d2)658 int dirtree_compar_mtime (const dirtree_t ** d1, const dirtree_t ** d2)
659 {
660 	return (**d1).mtime - (**d2).mtime;
661 }
662 
dirtree_sort(dirtree_t * tree)663 void dirtree_sort (dirtree_t * tree)
664 {
665 	int i, n;
666 	dirtree_t *t;
667 	dirtree_t **list;
668 
669 	ASSERT_TREE (tree);
670 
671 	if (tree->child == NULL)
672 		return;
673 
674 	for (n = 0, t = tree->child; t != NULL; t = t->next, n++) ;
675 	list = (dirtree_t **) safemalloc (n * sizeof (dirtree_t *));
676 	for (n = 0, t = tree->child; t != NULL; t = t->next, n++)
677 		list[n] = t;
678 	qsort (list, n, sizeof (dirtree_t *), (int (*)())dirtree_compar);
679 	tree->child = list[0];
680 	for (i = 1; i < n; i++)
681 		list[i - 1]->next = list[i];
682 	list[n - 1]->next = NULL;
683 	free (list);
684 
685 	for (t = tree->child; t != NULL; t = t->next)
686 		dirtree_sort (t);
687 }
688 
dirtree_set_id(dirtree_t * tree,int id)689 int dirtree_set_id (dirtree_t * tree, int id)
690 {
691 	dirtree_t *t;
692 
693 	tree->flags = (tree->flags & ~DIRTREE_ID) | id;
694 	id++;
695 	for (t = tree->child; t != NULL; t = t->next)
696 		id = dirtree_set_id (t, id);
697 	return id;
698 }
699 
700 #if 0														/* no longer used */
701 void dirtree_output_tree (FILE * fp, dirtree_t * tree, int recurse)
702 {
703 	extern struct config func_config[];
704 	dirtree_t *t;
705 	char *buf;
706 
707 	/* first pass: print children */
708 	if (recurse)
709 		for (t = tree->child; t != NULL; t = t->next)
710 			if (t->flags & DIRTREE_DIR)
711 				dirtree_output_tree (fp, t, recurse);
712 
713 	/* second pass: print self */
714 	buf = safemalloc (8192);
715 	if (tree->flags & DIRTREE_KEEPNAME)
716 		fprintf (fp, "PopUp \"%s\"\n", tree->name);
717 	else
718 		fprintf (fp, "PopUp \"%d\"\n", tree->flags & DIRTREE_ID);
719 	fprintf (fp, "  Title \"%s\"\n", tree->name);
720 	fprintf (fp, "  MiniPixmap \"%s\"\n",
721 					 tree->icon != NULL ? tree->icon : "mini-menu.xpm");
722 	for (t = tree->child; t != NULL; t = t->next) {
723 		if (t->flags & DIRTREE_DIR) {
724 			if (t->flags & DIRTREE_KEEPNAME)
725 				fprintf (fp, "  PopUp \"%s\" %s\n", t->name, t->name);
726 			else
727 				fprintf (fp, "  PopUp \"%s\" %d\n", t->name,
728 								 t->flags & DIRTREE_ID);
729 
730 			if (t->icon != NULL)
731 				fprintf (fp, "  MiniPixmap \"%s\"\n", t->icon);
732 			else if (t->flags & DIRTREE_DIR)
733 				fprintf (fp, "  MiniPixmap \"mini-folder.xpm\"\n");
734 		} else if (t->command && t->command->keyword) {
735 			fprintf (fp, "  %s \"%s\" %s\n", t->command->keyword, t->name,
736 							 t->path);
737 			if (t->icon != NULL)
738 				fprintf (fp, "  MiniPixmap \"%s\"\n", t->icon);
739 		} else {
740 			FILE *fp2 = fopen (t->path, "r");
741 
742 			/* try to load a command */
743 			if (fp2 != NULL && fgets (buf, 8192, fp2) != NULL) {
744 				struct config *config = find_config (func_config, buf);
745 				char *ptr = strip_whitespace (buf);
746 
747 				if (config != NULL && !isspace (buf[strlen (config->keyword)]))
748 					config = NULL;
749 				if (config == NULL && 13 + strlen (t->name) + strlen (ptr) < 8192) {
750 					memmove (ptr + 13 + strlen (t->name), ptr, strlen (ptr) + 1);
751 					sprintf (ptr, "Exec \"%s\" exec", t->name);
752 					ptr[strlen (ptr)] = ' ';
753 				}
754 				if (config == NULL || !mystrcasecmp (config->keyword, "Exec")) {
755 #ifndef NO_AVAILABILITYCHECK
756 					char *tmp;
757 
758 					for (tmp = ptr + 4; isspace (*tmp); tmp++) ;
759 					if (*tmp == '"') {
760 						for (tmp++; *tmp != '\0' && *tmp != '"'; tmp++) ;
761 						if (*tmp == '"') {
762 							for (tmp++; isspace (*tmp); tmp++) ;
763 							if (!is_executable_in_path (tmp)) {
764 								if (config != NULL)
765 									memcpy (ptr, "Nop ", 4);
766 								else
767 									sprintf (ptr = buf, "Nop \"%s\"", t->name);
768 							}
769 						}
770 					}
771 #endif													/* NO_AVAILABILITYCHECK */
772 				}
773 				fprintf (fp, "  %s\n", ptr);
774 			} else
775 				fprintf (fp, "  Exec \"%s\" exec %s\n", t->name, t->name);
776 			/* check for a MiniPixmap */
777 			if (fp2 != NULL && fgets (buf, 8192, fp2) != NULL) {
778 				char *ptr = strip_whitespace (buf);
779 
780 				if (!mystrncasecmp (ptr, "MiniPixmap", 10))
781 					fprintf (fp, "  %s\n", ptr);
782 			}
783 			if (t->icon != NULL)
784 				fprintf (fp, "  MiniPixmap \"%s\"\n", t->icon);
785 			fclose (fp2);
786 		}
787 	}
788 	fprintf (fp, "EndPopUp\n\n");
789 	free (buf);
790 }
791 #endif
792 
793 /* debugging code */
dirtree_print_tree(dirtree_t * tree,int depth)794 void dirtree_print_tree (dirtree_t * tree, int depth)
795 {
796 	dirtree_t *t;
797 	ASSERT_TREE (tree);
798 	fprintf (stderr,
799 					 "%*s%s%s(%s: order = %d: base_order = %d: flags = %x)\n", depth,
800 					 "", tree->name, (tree->flags & DIRTREE_DIR) ? "/ " : " ",
801 					 tree->icon, tree->order, tree->base_order, tree->flags);
802 	for (t = tree->child; t != NULL; t = t->next)
803 		dirtree_print_tree (t, depth + 1);
804 }
805 
806 #if 0
807 void dirtree_print_tree_from_dir (const char *dir)
808 {
809 	dirtree_t *tree;
810 
811 	tree = dirtree_new_from_dir (dir);
812 	dirtree_parse_include (tree);
813 	dirtree_remove_order (tree);
814 	dirtree_merge (tree);
815 	dirtree_sort (tree);
816 	dirtree_set_id (tree, 0);
817 	dirtree_output_tree (stderr, tree, 1);
818 	dirtree_delete (tree);
819 }
820 
821 void dirtree_main (void)
822 {
823 	dirtree_print_tree_from_dir ("/root/GNUstep/Library/AfterStep/start");
824 }
825 #endif
826