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