1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id$ */
3 
4 /*  libticalcs - Ti Calculator library, a part of the TiLP project
5  *  Copyright (C) 1999-2005  Romain Liévin
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software Foundation,
19  *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /*
23 	Utility functions for directory list (tree management).
24 	All functions can be applied on both trees (vars & apps) because:
25 	- tree type is stored in the tree itself (TreeInfo),
26 	- vars & apps use the same tree format.
27 */
28 
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "ticalcs.h"
35 #include "internal.h"
36 #include "gettext.h"
37 #include "logging.h"
38 
39 /* Dirlist format:
40 
41 - Vars:
42 
43   top (data = TreeInfo)
44   |
45   + folder (data = NULL if TI8x, data = VarEntry if TI9x)
46 	  |
47 	  +- var1 (data = VarEntry)
48 	  +- var2 (data = VarEntry)
49 
50 - Apps:
51 
52   top (data = TreeInfo)
53   |
54   + no folder (data = NULL)
55 	|
56 	+- app1 (data = VarEntry)
57 	+- app2 (data = VarEntry)
58 
59 */
60 
free_varentry(GNode * node,gpointer data)61 static gboolean free_varentry(GNode* node, gpointer data)
62 {
63 	(void)data;
64 	if (node && node->data)
65 	{
66 		VarEntry* ve = node->data;
67 		tifiles_ve_delete(ve);
68 	}
69 
70 	return FALSE;
71 }
72 
73 /**
74  * ticalcs_dirlist_destroy:
75  * @tree: the tree to destroy.
76  *
77  * Destroy the whole tree create by #ticalcs_calc_get_dirlist.
78  *
79  * Return value: none.
80  **/
ticalcs_dirlist_destroy(GNode ** tree)81 TIEXPORT3 void TICALL ticalcs_dirlist_destroy(GNode** tree)
82 {
83 	if (tree != NULL && *tree != NULL)
84 	{
85 		TreeInfo *ti;
86 
87 		if ((*tree)->children != NULL)
88 		{
89 			g_node_traverse(*tree, G_IN_ORDER, G_TRAVERSE_LEAVES, -1, free_varentry, NULL);
90 		}
91 
92 		ti = (TreeInfo *)((*tree)->data);
93 		g_free(ti);
94 		g_node_destroy(*tree);
95 
96 		*tree = NULL;
97 	}
98 }
99 
display_node(TreeInfo * info,VarEntry * ve,unsigned char type)100 static void display_node(TreeInfo * info, VarEntry * ve, unsigned char type)
101 {
102 	char * utf8 = ticonv_varname_to_utf8(info->model, ve->name, type);
103 	int k;
104 
105 	printf("| ");
106 	for (k = 0; k < 8; k++)
107 	{
108 		printf("%02X", (uint8_t) (ve->name)[k]);
109 	}
110 	printf(" | ");
111 	printf("%8s", utf8);
112 	printf(" | ");
113 	printf("%2i", ve->attr);
114 	printf(" | ");
115 	printf("%02X", ve->type);
116 	printf(" | ");
117 	printf("%08X", ve->size);
118 	printf(" | ");
119 	printf("%8s", ve->folder);
120 	printf(" |");
121 	printf("\n");
122 
123 	ticonv_utf8_free(utf8);
124 }
125 
display_tree(TreeInfo * info,GNode * vars)126 static void display_tree(TreeInfo * info, GNode * vars)
127 {
128 	int i;
129 
130 	//ticalcs_info("vars has %d children", (int)g_node_n_children(vars));
131 	for (i = 0; i < (int)g_node_n_children(vars); i++) // parse children
132 	{
133 		GNode *parent = g_node_nth_child(vars, i);
134 		VarEntry *fe = (VarEntry *) (parent->data);
135 		int j;
136 
137 		if (fe != NULL)
138 		{
139 			display_node(info, fe, -1);
140 		}
141 
142 		//ticalcs_info("parent has %d children", (int)g_node_n_children(parent));
143 		if (info->model != CALC_NSPIRE)
144 		{
145 			for (j = 0; j < (int)g_node_n_children(parent); j++)
146 			{
147 				GNode *child = g_node_nth_child(parent, j);
148 				VarEntry *ve = (VarEntry *) (child->data);
149 
150 				display_node(info, ve, ve->type);
151 			}
152 		}
153 		else
154 		{
155 			// Recurse into sub-folder if necessary.
156 			if (fe != NULL && fe->type == NSP_DIR)
157 			{
158 				//ticalcs_info("Recurse");
159 				display_tree(info, parent);
160 			}
161 		}
162 		//ticalcs_info("finished enumerating %d children of parent", (int)g_node_n_children(parent));
163 	}
164 	//ticalcs_info("finished enumerating %d children of vars", (int)g_node_n_children(vars));
165 }
166 
167 /**
168  * ticalcs_dirlist_display:
169  * @tree: the tree to display.
170  *
171  * Display to stdout the tree content formatted in a tab.
172  *
173  * Return value: none.
174  **/
ticalcs_dirlist_display(GNode * tree)175 TIEXPORT3 void TICALL ticalcs_dirlist_display(GNode* tree)
176 {
177 	GNode *vars = tree;
178 	TreeInfo *info;
179 	int i;
180 
181 	if (tree == NULL)
182 	{
183 		ticalcs_critical("ticalcs_dirlist_display(NULL)");
184 		return;
185 	}
186 
187 	info = (TreeInfo *)(tree->data);
188 	if (info == NULL)
189 	{
190 		return;
191 	}
192 
193 	printf(  "+------------------+----------+----+----+----------+----------+\n");
194 	printf(_("| B. name          | T. name  |Attr|Type| Size     | Folder   |\n"));
195 	printf(  "+------------------+----------+----+----+----------+----------+\n");
196 
197 	i = (int)g_node_n_children(vars);
198 	//ticalcs_info("Root has %d children", i);
199 	display_tree(info, vars);
200 	if (!i)
201 	{
202 		if (!strcmp(info->type, VAR_NODE_NAME))
203 		{
204 			printf(_("| No variables     |\n"));
205 		}
206 		else if (!strcmp(info->type, APP_NODE_NAME))
207 		{
208 			printf(_("| No applications  |\n"));
209 		}
210 	}
211 
212 	printf(_("+------------------+----------+----+----+----------+----------+"));
213 	printf("\n");
214 }
215 
216 /**
217  * ticalcs_dirlist_ve_exist:
218  * @tree: the tree to parse.
219  * @s: the full name of the variable or application to search for.
220  *
221  * Parse the tree for the given varname & folder or appname.
222  *
223  * Return value: a pointer on the #VarEntry found or NULL if not found.
224  **/
ticalcs_dirlist_ve_exist(GNode * tree,VarEntry * s)225 TIEXPORT3 VarEntry *TICALL ticalcs_dirlist_ve_exist(GNode* tree, VarEntry *s)
226 {
227 	int i, j;
228 	GNode *vars = tree;
229 	TreeInfo *info;
230 
231 	if (tree == NULL || s == NULL)
232 	{
233 		ticalcs_critical("ticalcs_dirlist_ve_exist: an argument is NULL");
234 		return NULL;
235 	}
236 
237 	info = (TreeInfo *)(tree->data);
238 	if (info == NULL)
239 	{
240 		return NULL;
241 	}
242 
243 	if (strcmp(info->type, VAR_NODE_NAME) && strcmp(info->type, APP_NODE_NAME))
244 	{
245 		return NULL;
246 	}
247 
248 	for (i = 0; i < (int)g_node_n_children(vars); i++)	// parse folders
249 	{
250 		GNode *parent = g_node_nth_child(vars, i);
251 		VarEntry *fe = (VarEntry *) (parent->data);
252 
253 		if ((fe != NULL) && strcmp(fe->name, s->folder))
254 		{
255 			continue;
256 		}
257 
258 		for (j = 0; j < (int)g_node_n_children(parent); j++)	//parse variables
259 		{
260 			GNode *child = g_node_nth_child(parent, j);
261 			VarEntry *ve = (VarEntry *) (child->data);
262 
263 			if (   !strcmp(ve->name, s->name)
264 			    && (   !((info->model >= CALC_TI73 && info->model <= CALC_TI84P)
265 			        || info->model == CALC_TI84P_USB || info->model == CALC_TI83PCE_USB
266 			        || info->model == CALC_TI84PCE_USB || info->model == CALC_TI82A_USB)
267 			        || info->model == CALC_TI84PT_USB || (ve->type == s->type)))
268 			{
269 				return ve;
270 			}
271 		}
272 	}
273 
274 	return NULL;
275 }
276 
277 /**
278  * ticalcs_dirlist_ve_count:
279  * @tree: a tree (var or app).
280  *
281  * Count how many entries (vars or apps) are listed in the tree.
282  *
283  * Return value: the number of entries.
284  **/
ticalcs_dirlist_ve_count(GNode * tree)285 TIEXPORT3 unsigned int TICALL ticalcs_dirlist_ve_count(GNode* tree)
286 {
287 	unsigned int i, j;
288 	GNode *vars = tree;
289 	unsigned int nvars = 0;
290 	TreeInfo *info;
291 
292 	if (tree == NULL)
293 	{
294 		ticalcs_critical("ticalcs_dirlist_ve_count(NULL)");
295 		return 0;
296 	}
297 
298 	info = (TreeInfo *)(tree->data);
299 	if (info == NULL)
300 	{
301 		return 0;
302 	}
303 
304 	if (strcmp(info->type, VAR_NODE_NAME) && strcmp(info->type, APP_NODE_NAME))
305 	{
306 		return 0;
307 	}
308 
309 	for (i = 0; i < g_node_n_children(vars); i++)	// parse folders
310 	{
311 		GNode *parent = g_node_nth_child(vars, i);
312 
313 		for (j = 0; j < g_node_n_children(parent); j++)	//parse variables
314 		{
315 			nvars++;
316 		}
317 	}
318 
319 	return nvars;
320 }
321 
322 /**
323  * ticalcs_dirlist_ram_used:
324  * @tree: a tree (var only).
325  *
326  * Count how much memory is used by variables listed in the tree.
327  *
328  * Return value: size of all variables in bytes.
329  **/
ticalcs_dirlist_ram_used(GNode * tree)330 TIEXPORT3 int TICALL ticalcs_dirlist_ram_used(GNode* tree)
331 {
332 	int i, j;
333 	GNode *vars = tree;
334 	uint32_t mem = 0;
335 	TreeInfo *info;
336 
337 	if (tree == NULL)
338 	{
339 		ticalcs_critical("ticalcs_dirlist_ram_used(NULL)");
340 		return 0;
341 	}
342 
343 	info = (TreeInfo *)(tree->data);
344 	if (info == NULL)
345 	{
346 		return 0;
347 	}
348 
349 	if (strcmp(info->type, VAR_NODE_NAME))
350 	{
351 		return 0;
352 	}
353 
354 	for (i = 0; i < (int)g_node_n_children(vars); i++)	// parse folders
355 	{
356 		GNode *parent = g_node_nth_child(vars, i);
357 
358 		for (j = 0; j < (int)g_node_n_children(parent); j++)	//parse variables
359 		{
360 			GNode *child = g_node_nth_child(parent, j);
361 			VarEntry *ve = (VarEntry *) (child->data);
362 
363 			if (ve->attr != ATTRB_ARCHIVED)
364 			{
365 				mem += ve->size;
366 			}
367 		}
368 	}
369 
370 	return mem;
371 }
372 
373 /**
374  * ticalcs_dirlist_flash_used:
375  * @tree: a tree (app only).
376  *
377  * Count how much memory is used by archived variables and apps listed in the trees.
378  *
379  * Return value: size of all FLASH in bytes.
380  **/
ticalcs_dirlist_flash_used(GNode * vars,GNode * apps)381 TIEXPORT3 int TICALL ticalcs_dirlist_flash_used(GNode* vars, GNode* apps)
382 {
383 	int i, j;
384 	uint32_t mem = 0;
385 	TreeInfo *info1;
386 	TreeInfo *info2;
387 
388 	if (vars == NULL || apps == NULL)
389 	{
390 		ticalcs_critical("ticalcs_dirlist_flash_used: an argument is NULL");
391 		return 0;
392 	}
393 
394 	info1 = (TreeInfo *)(vars->data);
395 	info2 = (TreeInfo *)(apps->data);
396 	if (info1 == NULL ||  info2 == NULL)
397 	{
398 		return 0;
399 	}
400 
401 	if (!strcmp(info1->type, VAR_NODE_NAME))
402 	{
403 		for (i = 0; i < (int)g_node_n_children(vars); i++)	// parse folders
404 		{
405 			GNode *parent = g_node_nth_child(vars, i);
406 
407 			for (j = 0; j < (int)g_node_n_children(parent); j++)	//parse variables
408 			{
409 				GNode *child = g_node_nth_child(parent, j);
410 				VarEntry *ve = (VarEntry *) (child->data);
411 
412 				if (ve->attr == ATTRB_ARCHIVED)
413 				{
414 					mem += ve->size;
415 				}
416 			}
417 		}
418 	}
419 
420 	if (!strcmp(info2->type, APP_NODE_NAME))
421 	{
422 		for (i = 0; i < (int)g_node_n_children(apps); i++)
423 		{
424 			GNode *parent = g_node_nth_child(apps, i);
425 
426 			for (j = 0; j < (int)g_node_n_children(parent); j++)	//parse apps
427 			{
428 				GNode *child = g_node_nth_child(parent, i);
429 				VarEntry *ve = (VarEntry *) (child->data);
430 
431 				mem += ve->size;
432 			}
433 		}
434 	}
435 
436 	return mem;
437 }
438 
439 /**
440  * ticalcs_dirlist_ve_add:
441  * @tree: source tree.
442  * @entry: entry to add.
443  *
444  * Add an entry into the main tree (if it doesn't exist yet).
445  *
446  * Return value: none.
447  **/
ticalcs_dirlist_ve_add(GNode * tree,VarEntry * entry)448 TIEXPORT3 void TICALL ticalcs_dirlist_ve_add(GNode* tree, VarEntry *entry)
449 {
450 	TreeInfo *info;
451 	int i, j;
452 	int found = 0;
453 
454 	GNode *parent = NULL;
455 	VarEntry *fe = NULL;
456 
457 	GNode *child;
458 	VarEntry *ve;
459 
460 	const char *folder;
461 
462 	if (tree == NULL || entry == NULL)
463 	{
464 		ticalcs_critical("ticalcs_dirlist_ve_add: an argument is NULL");
465 		return;
466 	}
467 
468 	info = (TreeInfo *)(tree->data);
469 	if (info == NULL)
470 	{
471 		return;
472 	}
473 
474 	if (strcmp(info->type, VAR_NODE_NAME) && strcmp(info->type, APP_NODE_NAME))
475 	{
476 		return;
477 	}
478 
479 	if (!strcmp(entry->folder, "") && tifiles_has_folder(info->model))
480 	{
481 		folder = "main";
482 	}
483 	else
484 	{
485 		folder = entry->folder;
486 	}
487 
488 	// If TI8x tree is empty, create pseudo-folder (NULL)
489 	if (!g_node_n_children(tree) && !tifiles_has_folder(info->model))
490 	{
491 		parent = g_node_new(NULL);
492 		g_node_append(tree, parent);
493 	}
494 
495 	// Check for one folder at least...
496 	if (g_node_n_children(tree) > 0)
497 	{
498 		// Parse folders
499 		for (found = 0, i = 0; i < (int)g_node_n_children(tree); i++)
500 		{
501 			parent = g_node_nth_child(tree, i);
502 			fe = (VarEntry *) (parent->data);
503 
504 			if (fe == NULL)
505 			{
506 				break;
507 			}
508 
509 			if (!strcmp(fe->name, folder))
510 			{
511 				found = !0;
512 				break;
513 			}
514 		}
515 	}
516 
517 	// folder doesn't exist? => create!
518 	if ((!found && fe) ||
519 	    (!g_node_n_children(tree) && tifiles_has_folder(info->model)))
520 	{
521 		fe = tifiles_ve_create();
522 		if (fe != NULL)
523 		{
524 			ticalcs_strlcpy(fe->name, entry->folder, sizeof(fe->name));
525 			fe->type = TI89_DIR;
526 
527 			parent = g_node_new(fe);
528 			g_node_append(tree, parent);
529 		}
530 	}
531 
532 	if (!strcmp(entry->name, ""))
533 	{
534 		return;
535 	}
536 
537 	// next, add variables beneath this folder
538 	for (found = 0, j = 0; j < (int)g_node_n_children(parent); j++)
539 	{
540 		child = g_node_nth_child(parent, j);
541 		ve = (VarEntry *) (child->data);
542 
543 		if (!strcmp(ve->name, entry->name))
544 		{
545 			found = !0;
546 			break;
547 		}
548 	}
549 
550 	if (!found)
551 	{
552 		ve = tifiles_ve_dup(entry);
553 		if (ve != NULL)
554 		{
555 			child = g_node_new(ve);
556 			g_node_append(parent, child);
557 		}
558 	}
559 
560 	if (fe && found)
561 	{
562 		fe->size++;
563 	}
564 }
565 
566 
567 /**
568  * ticalcs_dirlist_ve_del:
569  * @tree: source tree.
570  * @entry: entry to remove.
571  *
572  * Remove an entry into the main tree (if it doesn't exist yet).
573  *
574  * Return value: none.
575  **/
ticalcs_dirlist_ve_del(GNode * tree,VarEntry * entry)576 TIEXPORT3 void TICALL ticalcs_dirlist_ve_del(GNode* tree, VarEntry *entry)
577 {
578 	TreeInfo *info;
579 	int i, j;
580 	int found = 0;
581 
582 	GNode *parent = NULL;
583 	VarEntry *fe = NULL;
584 
585 	GNode *child = NULL;
586 	VarEntry *ve;
587 
588 	const char *folder;
589 
590 	if (tree == NULL || entry == NULL)
591 	{
592 		ticalcs_critical("ticalcs_dirlist_ve_del: an argument is NULL");
593 		return;
594 	}
595 
596 	info = (TreeInfo *)(tree->data);
597 	if (info == NULL)
598 	{
599 		return;
600 	}
601 
602 	if (strcmp(info->type, VAR_NODE_NAME))
603 	{
604 		return;
605 	}
606 
607 	if (!strcmp(entry->folder, "") && tifiles_has_folder(info->model))
608 	{
609 		folder = "main";
610 	}
611 	else
612 	{
613 		folder = entry->folder;
614 	}
615 
616 	// Parse folders
617 	for (found = 0, i = 0; i < (int)g_node_n_children(tree); i++)
618 	{
619 		parent = g_node_nth_child(tree, i);
620 		fe = (VarEntry *) (parent->data);
621 
622 		if (fe == NULL)
623 		{
624 			break;
625 		}
626 
627 		if (!strcmp(fe->name, folder))
628 		{
629 			found = !0;
630 			break;
631 		}
632 	}
633 
634 	if (!found && fe)
635 	{
636 		return;
637 	}
638 
639 	// next, delete variables beneath this folder
640 	for (found = 0, j = 0; j < (int)g_node_n_children(parent); j++)
641 	{
642 		child = g_node_nth_child(parent, j);
643 		ve = (VarEntry *) (child->data);
644 
645 		if (!strcmp(ve->name, entry->name))
646 		{
647 			found = !0;
648 			break;
649 		}
650 	}
651 
652 	if (found)
653 	{
654 		tifiles_ve_delete(child->data);
655 		g_node_destroy(child);
656 	}
657 
658 	if (fe && found)
659 	{
660 		fe->size--;
661 	}
662 }
663