1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id: grouped.c 1737 2006-01-23 12:54:47Z roms $ */
3
4 /* libtifiles - file format library, a part of the TiLP project
5 * Copyright (C) 1999-2006 Romain Lievin
6 * Copyright (C) 2006 Kevin Kofler
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 /*
24 TiGroup (*.tig) management
25 A TiGroup file is in fact a ZIP archive with no compression (stored).
26
27 Please note that I don't use USEWIN32IOAPI!
28 */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <archive.h>
40 #include <archive_entry.h>
41
42 #include <ticonv.h>
43 #include "tifiles.h"
44 #include "logging.h"
45 #include "error.h"
46 #include "rwfile.h"
47
48 // Whether to print detailed information about TigEntry, TigContent instances throughout their lifecycle.
49 //#define TRACE_CONTENT_INSTANCES
50
51 #define WRITEBUFFERSIZE (8192)
52
53 /**
54 * tifiles_te_create:
55 * @filename: internal filename in archive.
56 * @type: file type (regular or flash)
57 * @model: calculator model
58 *
59 * Allocates a TigEntry structure and allocates fields (aka call #tifiles_content_create_flash/regular for you).
60 *
61 * Return value: the allocated block.
62 **/
tifiles_te_create(const char * filename,FileClass type,CalcModel model)63 TIEXPORT2 TigEntry* TICALL tifiles_te_create(const char *filename, FileClass type, CalcModel model)
64 {
65 TigEntry *entry = NULL;
66
67 if (filename != NULL && strcmp(filename, ""))
68 {
69 entry = (TigEntry *)g_malloc0(sizeof(TigEntry));
70 if (entry != NULL)
71 {
72 entry->filename = g_path_get_basename(filename);
73 entry->type = type;
74
75 if (type == TIFILE_FLASH)
76 {
77 entry->content.flash = tifiles_content_create_flash(model);
78 }
79 else if (type & TIFILE_REGULAR)
80 {
81 entry->content.regular = tifiles_content_create_regular(model);
82 }
83 }
84 }
85 else
86 {
87 tifiles_critical("%s: invalid filename", __FUNCTION__);
88 }
89
90 #ifdef TRACE_CONTENT_INSTANCES
91 tifiles_info("tifiles_te_create: %p", entry);
92 tifiles_te_display(entry);
93 #endif
94
95 return entry;
96 }
97
98 /**
99 * tifiles_te_delete:
100 * @entry: a #TigEntry structure.
101 *
102 * Destroy a #TigEntry structure as well as fields.
103 *
104 * Return value: always 0.
105 **/
tifiles_te_delete(TigEntry * entry)106 TIEXPORT2 int TICALL tifiles_te_delete(TigEntry* entry)
107 {
108 #ifdef TRACE_CONTENT_INSTANCES
109 tifiles_info("tifiles_te_delete: %p", entry);
110 tifiles_te_display(entry);
111 #endif
112
113 if (entry != NULL)
114 {
115 g_free(entry->filename);
116
117 if (entry->type == TIFILE_FLASH)
118 {
119 tifiles_content_delete_flash(entry->content.flash);
120 }
121 else if (entry->type & TIFILE_REGULAR)
122 {
123 tifiles_content_delete_regular(entry->content.regular);
124 }
125
126 g_free(entry);
127 }
128 else
129 {
130 tifiles_critical("%s(NULL)", __FUNCTION__);
131 }
132
133 return 0;
134 }
135
136 /**
137 * tifiles_te_display:
138 * @entry: a #TigEntry structure pointer.
139 *
140 * Display a #TigEntry structure's contents.
141 *
142 * Return value: an error code, 0 otherwise.
143 **/
tifiles_te_display(TigEntry * entry)144 TIEXPORT2 int TICALL tifiles_te_display(TigEntry* entry)
145 {
146 if (entry == NULL)
147 {
148 tifiles_critical("%s(NULL)", __FUNCTION__);
149 return ERR_INVALID_FILE;
150 }
151
152 tifiles_info("Filename: %s", entry->filename);
153 tifiles_info("File class: %04X (%u)", entry->type, entry->type);
154
155 if (entry->type == TIFILE_FLASH)
156 {
157 tifiles_file_display_flash(entry->content.flash);
158 }
159 else if (entry->type & TIFILE_REGULAR)
160 {
161 tifiles_file_display_regular(entry->content.regular);
162 }
163 else
164 {
165 tifiles_info("Data: %p", entry->content.data);
166 }
167
168 return 0;
169 }
170
171 /**
172 * tifiles_te_create_array:
173 * @nelts: size of NULL-terminated array (number of #TigEntry structures).
174 *
175 * Allocate a NULL-terminated array of #TigEntry structures. You have to allocate
176 * each element of the array by yourself.
177 *
178 * Return value: the array or NULL if error.
179 **/
tifiles_te_create_array(unsigned int nelts)180 TIEXPORT2 TigEntry** TICALL tifiles_te_create_array(unsigned int nelts)
181 {
182 return g_malloc0((nelts + 1) * sizeof(TigEntry *));
183 }
184
185 /**
186 * tifiles_te_resize_array:
187 * @array: address of array
188 * @nelts: size of NULL-terminated array (number of #TigEntry structures).
189 *
190 * Re-allocate a NULL-terminated array of #TigEntry structures. You have to allocate
191 * each element of the array by yourself.
192 *
193 * Return value: the array or NULL if error.
194 **/
tifiles_te_resize_array(TigEntry ** array,unsigned int nelts)195 TIEXPORT2 TigEntry** TICALL tifiles_te_resize_array(TigEntry** array, unsigned int nelts)
196 {
197 TigEntry ** ptr = g_realloc(array, (nelts + 1) * sizeof(TigEntry *));
198 if (ptr != NULL)
199 {
200 ptr[nelts] = NULL;
201 }
202 return ptr;
203 }
204
205 /**
206 * tifiles_ve_delete_array:
207 * @array: an NULL-terminated array of TigEntry structures.
208 *
209 * Free the whole array (data buffer, TigEntry structure and array itself).
210 *
211 * Return value: none.
212 **/
tifiles_te_delete_array(TigEntry ** array)213 TIEXPORT2 void TICALL tifiles_te_delete_array(TigEntry** array)
214 {
215 TigEntry** ptr;
216
217 #ifdef TRACE_CONTENT_INSTANCES
218 tifiles_info("tifiles_te_delete_array: %p", array);
219 #endif
220
221 if (array != NULL)
222 {
223 for (ptr = array; *ptr; ptr++)
224 {
225 tifiles_te_delete(*ptr);
226 }
227 g_free(array);
228 }
229 else
230 {
231 tifiles_critical("%s(NULL)", __FUNCTION__);
232 }
233 }
234
235 /**
236 * tifiles_te_sizeof_array:
237 * @array: an NULL-terminated array of TigEntry structures.
238 * @r: number of FileContent entries
239 * @f: number of FlashContent entries
240 *
241 * Returns the size of a #TigEntry array.
242 *
243 * Return value: none.
244 **/
tifiles_te_sizeof_array(TigEntry ** array)245 TIEXPORT2 int TICALL tifiles_te_sizeof_array(TigEntry** array)
246 {
247 int i = 0;
248 TigEntry **p;
249
250 if (array != NULL)
251 {
252 for (p = array; *p; p++, i++);
253 }
254 else
255 {
256 tifiles_critical("%s(NULL)", __FUNCTION__);
257 }
258
259 return i;
260 }
261
262 // ---------------------------------------------------------------------------
263
264 /**
265 * tifiles_content_add_te:
266 * @content: a file content (TiGroup).
267 * @te: the entry to add
268 *
269 * Adds the entry to the file content and updates internal structures.
270 * Beware: the entry is not duplicated.
271 *
272 * Return value: the number of entries.
273 **/
tifiles_content_add_te(TigContent * content,TigEntry * te)274 TIEXPORT2 int TICALL tifiles_content_add_te(TigContent *content, TigEntry *te)
275 {
276 if (content == NULL || te == NULL)
277 {
278 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
279 return 0;
280 }
281
282 if (te->type == TIFILE_FLASH)
283 {
284 int n = content->n_apps;
285
286 content->app_entries = tifiles_te_resize_array(content->app_entries, n + 1);
287
288 content->app_entries[n++] = te;
289 content->app_entries[n] = NULL;
290 content->n_apps = n;
291
292 return n;
293 }
294 else if (te->type & TIFILE_REGULAR)
295 {
296 int n = content->n_vars;
297
298 content->var_entries = tifiles_te_resize_array(content->var_entries, n + 1);
299
300 content->var_entries[n++] = te;
301 content->var_entries[n] = NULL;
302 content->n_vars = n;
303
304 return n;
305 }
306
307 return 0;
308 }
309
310 /**
311 * tifiles_content_del_te:
312 * @content: a file content (TiGroup).
313 * @te: the entry to remove
314 *
315 * Search for entry name and remove it from file content.
316 *
317 * Return value: the number of entries or -1 if not found.
318 **/
tifiles_content_del_te(TigContent * content,TigEntry * te)319 TIEXPORT2 int TICALL tifiles_content_del_te(TigContent *content, TigEntry *te)
320 {
321 unsigned int i, j, k;
322
323 if (content == NULL || te == NULL)
324 {
325 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
326 return -1;
327 }
328
329 #ifdef TRACE_CONTENT_INSTANCES
330 tifiles_info("tifiles_content_del_te: %p %p", content, te);
331 tifiles_file_display_tigcontent(content);
332 tifiles_te_display(te);
333 #endif
334
335 // Search for entry
336 for (i = 0; i < content->n_vars && (te->type & TIFILE_REGULAR); i++)
337 {
338 TigEntry *s = content->var_entries[i];
339
340 if (!strcmp(s->filename, te->filename))
341 {
342 break;
343 }
344 }
345
346 for (j = 0; j < content->n_apps && (te->type & TIFILE_FLASH); j++)
347 {
348 TigEntry *s = content->app_entries[i];
349
350 if (!strcmp(s->filename, te->filename))
351 {
352 break;
353 }
354 }
355
356 // Not found ? Exit !
357 if ((i == content->n_vars) && (j == content->n_apps))
358 {
359 return -1;
360 }
361
362 // Release
363 if (i < content->n_vars)
364 {
365 // Delete
366 tifiles_te_delete(content->var_entries[i]);
367
368 // And shift
369 for (k = i; k < content->n_vars; k++)
370 {
371 content->var_entries[k] = content->var_entries[k+1];
372 }
373 content->var_entries[k] = NULL;
374
375 // And resize
376 content->var_entries = tifiles_te_resize_array(content->var_entries, content->n_vars - 1);
377 content->n_vars--;
378
379 return content->n_vars;
380 }
381
382 if (j < content->n_apps)
383 {
384 // Delete
385 tifiles_te_delete(content->app_entries[j]);
386
387 // And shift
388 for (k = j; k < content->n_apps; k++)
389 {
390 content->app_entries[k] = content->app_entries[k+1];
391 }
392 content->app_entries[k] = NULL;
393
394 // And resize
395 content->app_entries = tifiles_te_resize_array(content->app_entries, content->n_apps - 1);
396 content->n_apps--;
397
398 return content->n_apps;
399 }
400
401 return 0;
402 }
403
404 #ifndef __WIN32__
405 # define stricmp strcasecmp
406 #endif
407
408 /**
409 * tifiles_tigroup_add_file:
410 * @src_filename: the file to add to TiGroup file
411 * @dst_filename: the TiGroup file (must exist!)
412 *
413 * Add src_filename content to dst_filename content and write to dst_filename.
414 *
415 * Return value: 0 if successful, an error code otherwise.
416 **/
tifiles_tigroup_add_file(const char * src_filename,const char * dst_filename)417 TIEXPORT2 int TICALL tifiles_tigroup_add_file(const char *src_filename, const char *dst_filename)
418 {
419 CalcModel model;
420 FileClass type;
421 TigEntry *te;
422 TigContent *content = NULL;
423 int ret = 0;
424
425 if (src_filename == NULL || dst_filename == NULL)
426 {
427 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
428 return -1;
429 }
430
431 // group file is created if non existent
432 if (!stricmp(tifiles_fext_get(dst_filename), "tig"))
433 {
434 if (!g_file_test(dst_filename, G_FILE_TEST_EXISTS))
435 {
436 content = tifiles_content_create_tigroup(CALC_NONE, 0);
437 tifiles_file_write_tigroup(dst_filename, content);
438 tifiles_content_delete_tigroup(content);
439 content = NULL;
440 }
441 }
442
443 // src can't be a TiGroup file but dst should be
444 if (!(tifiles_file_is_ti(src_filename) && !tifiles_file_is_tigroup(src_filename) &&
445 tifiles_file_is_tigroup(dst_filename)))
446 {
447 return -1;
448 }
449
450 // load src file
451 model = tifiles_file_get_model(src_filename);
452 type = tifiles_file_get_class(src_filename);
453
454 te = tifiles_te_create(src_filename, type, model);
455 if (te == NULL)
456 {
457 ret = ERR_BAD_FILE;
458 goto ttaf;
459 }
460 if (type == TIFILE_FLASH)
461 {
462 ret = tifiles_file_read_flash(src_filename, te->content.flash);
463 if (ret)
464 {
465 goto ttaf;
466 }
467 }
468 else if (type & TIFILE_REGULAR)
469 {
470 ret = tifiles_file_read_regular(src_filename, te->content.regular);
471 if (ret)
472 {
473 goto ttaf;
474 }
475 }
476
477 // load dst file
478 content = tifiles_content_create_tigroup(CALC_NONE, 0);
479 ret = tifiles_file_read_tigroup(dst_filename, content);
480 if (ret)
481 {
482 goto ttaf;
483 }
484
485 tifiles_content_add_te(content, te);
486 ret = tifiles_file_write_tigroup(dst_filename, content);
487 if (ret)
488 {
489 goto ttaf;
490 }
491
492 tifiles_content_delete_tigroup(content);
493
494 return 0;
495
496 ttaf: // release on exit
497 tifiles_te_delete(te);
498 tifiles_content_delete_tigroup(content);
499 return ret;
500 }
501
502 /**
503 * tifiles_tigroup_del_file:
504 * @src_filename: the file to remove from TiGroup file
505 * @dst_filename: the TiGroup file
506 *
507 * Search for entry and remove it from file.
508 *
509 * Return value: 0 if successful, an error code otherwise.
510 **/
tifiles_tigroup_del_file(TigEntry * entry,const char * filename)511 TIEXPORT2 int TICALL tifiles_tigroup_del_file(TigEntry *entry, const char *filename)
512 {
513 TigContent* content = NULL;
514 int ret = 0;
515
516 if (entry == NULL || filename == NULL)
517 {
518 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
519 return -1;
520 }
521
522 #ifdef TRACE_CONTENT_INSTANCES
523 tifiles_info("tifiles_tigroup_del_file: %p", entry);
524 tifiles_te_display(entry);
525 #endif
526
527 content = tifiles_content_create_tigroup(CALC_NONE, 0);
528 ret = tifiles_file_read_tigroup(filename, content);
529 if (!ret)
530 {
531 (void)tifiles_content_del_te(content, entry);
532 ret = tifiles_file_write_tigroup(filename, content);
533 }
534
535 tifiles_content_delete_tigroup(content);
536 return ret;
537 }
538
539 // ---------------------------------------------------------------------------
540
541 /**
542 * tifiles_tigroup_contents:
543 * @src_contents1: a pointer on an array of #FileContent structures or NULL. The array must be NULL-terminated.
544 * @src_contents2: a pointer on an array of #FlashContent structures or NULL. The array must be NULL-terminated.
545 * @dst_content: the address of a pointer. This pointer will see the allocated TiGroup file.
546 *
547 * Group several #FileContent/#FlashContent structures into a single one.
548 * Must be freed when no longer used by a call to #tifiles_content_delete_tigroup.
549 *
550 * Return value: an error code if unsuccessful, 0 otherwise.
551 **/
tifiles_tigroup_contents(FileContent ** src_contents1,FlashContent ** src_contents2,TigContent ** dst_content)552 TIEXPORT2 int TICALL tifiles_tigroup_contents(FileContent **src_contents1, FlashContent **src_contents2, TigContent **dst_content)
553 {
554 TigContent *content;
555 int i, m=0, n=0;
556 CalcModel model = CALC_NONE;
557
558 if (src_contents1 == NULL && src_contents2 == NULL)
559 {
560 return -1;
561 }
562
563 if (dst_content == NULL)
564 {
565 tifiles_critical("%s: dst_content is NULL", __FUNCTION__);
566 return -1;
567 }
568
569 if (src_contents1)
570 {
571 for (m = 0; src_contents1[m] != NULL; m++);
572 }
573 if (src_contents2)
574 {
575 for (n = 0; src_contents2[n] != NULL; n++);
576 }
577
578 if (src_contents2)
579 {
580 if (*src_contents2)
581 {
582 model = src_contents2[0]->model;
583 }
584 }
585 if (src_contents1)
586 {
587 if (*src_contents1)
588 {
589 model = src_contents1[0]->model; // FileContent is more precise than FlashContent
590 }
591 }
592
593 content = tifiles_content_create_tigroup(model, m+n);
594
595 if (src_contents1)
596 {
597 for (i = 0; i < m; i++)
598 {
599 TigEntry *te = (TigEntry *)g_malloc0(sizeof(TigEntry));
600
601 te->filename = tifiles_build_filename(model, src_contents1[i]->entries[0]);
602 te->type = TIFILE_GROUP;
603 te->content.regular = tifiles_content_dup_regular(src_contents1[i]);
604 tifiles_content_add_te(content, te);
605 }
606 }
607
608 if (src_contents2)
609 {
610 for (i = 0; i < n; i++)
611 {
612 TigEntry *te;
613 VarEntry ve;
614 FlashContent *ptr;
615
616 for (ptr = src_contents2[i]; ptr; ptr = ptr->next)
617 {
618 if (ptr->data_type == tifiles_flash_type(model))
619 {
620 break;
621 }
622 }
623 if (ptr == NULL)
624 {
625 tifiles_critical("%s: ptr is NULL, skipping", __FUNCTION__);
626 continue;
627 }
628
629 te = (TigEntry *)g_malloc0(sizeof(TigEntry));
630 ve.folder[0] = 0;
631 strncpy(ve.name, ptr->name, sizeof(ve.name) - 1);
632 ve.name[sizeof(ve.name) - 1] = 0;
633 ve.type = ptr->data_type;
634 te->filename = tifiles_build_filename(model, &ve);
635 te->type = TIFILE_FLASH;
636 te->content.flash = tifiles_content_dup_flash(src_contents2[i]);
637 tifiles_content_add_te(content, te);
638 }
639 }
640
641 *dst_content = content;
642
643 #ifdef TRACE_CONTENT_INSTANCES
644 tifiles_info("tifiles_tigroup_contents: %p", content);
645 tifiles_file_display_tigcontent(content);
646 #endif
647
648 return 0;
649 }
650
651 /**
652 * tifiles_untigroup_content:
653 * @src_content: a pointer on the structure to unpack.
654 * @dst_contents1: the address of your pointer. This pointers will point on a
655 * @dst_contents2: the address of your pointer. This pointers will point on a
656 * dynamically allocated array of structures. The array is terminated by NULL.
657 *
658 * Ungroup a TiGroup file by exploding the structure into an array of structures.
659 * Must be freed when no longer used by a call to #tifiles_content_delete_tigroup.
660 *
661 * Return value: an error code if unsuccessful, 0 otherwise.
662 **/
tifiles_untigroup_content(TigContent * src_content,FileContent *** dst_contents1,FlashContent *** dst_contents2)663 TIEXPORT2 int TICALL tifiles_untigroup_content(TigContent *src_content, FileContent ***dst_contents1, FlashContent ***dst_contents2)
664 {
665 TigContent *src = src_content;
666 FileContent **dst1 = NULL;
667 FlashContent **dst2 = NULL;
668 unsigned int i, j;
669
670 if (src_content == NULL || dst_contents1 == NULL || dst_contents2 == NULL)
671 {
672 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
673 return -1;
674 }
675
676 #ifdef TRACE_CONTENT_INSTANCES
677 tifiles_info("tifiles_untigroup_content: %p", src_content);
678 tifiles_file_display_tigcontent(src_content);
679 #endif
680
681 // allocate an array of FileContent/FlashContent structures (NULL terminated)
682 dst1 = (FileContent **)g_malloc0((src->n_vars+1) * sizeof(FileContent *));
683 if (dst1 == NULL)
684 {
685 return ERR_MALLOC;
686 }
687 dst2 = (FlashContent **)g_malloc0((src->n_apps+1) * sizeof(FlashContent *));
688 if (dst2 == NULL)
689 {
690 g_free(dst1);
691 return ERR_MALLOC;
692 }
693
694 // parse each entry and duplicate it into a single content
695 for (i = 0; i < src->n_vars; i++)
696 {
697 TigEntry *te = src->var_entries[i];
698
699 dst1[i] = tifiles_content_dup_regular(te->content.regular);
700 }
701
702 for (j = 0; j < src->n_apps; j++)
703 {
704 TigEntry *te = src->app_entries[j];
705
706 dst2[j] = tifiles_content_dup_flash(te->content.flash);
707 }
708
709 *dst_contents1 = dst1;
710 *dst_contents2 = dst2;
711
712 return 0;
713 }
714
715 /**
716 * tifiles_group_files:
717 * @src_filenames: a NULL-terminated array of strings (list of files to group).
718 * @dst_filename: the filename where to store the TiGroup.
719 *
720 * Group several TI files (regular/flash) into a single one (TiGroup file).
721 *
722 * Return value: an error code if unsuccessful, 0 otherwise.
723 **/
tifiles_tigroup_files(char ** src_filenames,const char * dst_filename)724 TIEXPORT2 int TICALL tifiles_tigroup_files(char **src_filenames, const char *dst_filename)
725 {
726 FileContent **src1 = NULL;
727 FlashContent **src2 = NULL;
728 TigContent *dst = NULL;
729 CalcModel model;
730 int i, j, k, m, n;
731 int ret = 0;
732
733 if (src_filenames == NULL || dst_filename == NULL)
734 {
735 tifiles_critical("%s: an argument is NULL !", __FUNCTION__);
736 return -1;
737 }
738
739 // counts number of files to group and allocate space for that
740 for (k = m = n = 0; src_filenames[k]; k++)
741 {
742 if (tifiles_file_is_regular(src_filenames[k]))
743 {
744 m++;
745 }
746 else if (tifiles_file_is_flash(src_filenames[k]))
747 {
748 n++;
749 }
750 }
751 model = tifiles_file_get_model(src_filenames[0]);
752
753 // allocate space for that
754 src1 = (FileContent **)g_malloc0((m + 1) * sizeof(FileContent *));
755 if (src1 == NULL)
756 {
757 return ERR_MALLOC;
758 }
759
760 src2 = (FlashContent **)g_malloc0((n + 1) * sizeof(FlashContent *));
761 if (src2 == NULL)
762 {
763 g_free(src1);
764 return ERR_MALLOC;
765 }
766
767 for (i = j = k = 0; k < m+n; k++)
768 {
769 if (tifiles_file_is_regular(src_filenames[k]))
770 {
771 src1[i] = tifiles_content_create_regular(model);
772 ret = tifiles_file_read_regular(src_filenames[k], src1[i]);
773 if (ret)
774 {
775 goto tgf;
776 }
777 i++;
778 }
779 else if (tifiles_file_is_flash(src_filenames[k]))
780 {
781 src2[j] = tifiles_content_create_flash(model);
782 ret = tifiles_file_read_flash(src_filenames[k], src2[j]);
783 if (ret)
784 {
785 goto tgf;
786 }
787 j++;
788 }
789 }
790
791 ret = tifiles_tigroup_contents(src1, src2, &dst);
792 if (ret)
793 {
794 goto tgf;
795 }
796
797 ret = tifiles_file_write_tigroup(dst_filename, dst);
798
799 tgf:
800 for (i = 0; i < m; i++)
801 {
802 g_free(src1[i]);
803 }
804 g_free(src1);
805 for (i = 0; i < n; i++)
806 {
807 g_free(src2[i]);
808 }
809 g_free(src2);
810 tifiles_content_delete_tigroup(dst);
811
812 return ret;
813 }
814
815 /**
816 * tifiles_ungroup_file:
817 * @src_filename: full path of file to ungroup.
818 * @dst_filenames: NULL or the address of a pointer where to store a NULL-terminated
819 * array of strings which contain the list of ungrouped files (regular/flash).
820 *
821 * Ungroup a TiGroup file into several files. Resulting files have the
822 * same name as the variable stored within group file.
823 * Beware: there is no existence check; files may be overwritten !
824 *
825 * %dst_filenames must be freed when no longer used.
826 *
827 * Return value: an error code if unsuccessful, 0 otherwise.
828 **/
tifiles_untigroup_file(const char * src_filename,char *** dst_filenames)829 TIEXPORT2 int TICALL tifiles_untigroup_file(const char *src_filename, char ***dst_filenames)
830 {
831 TigContent *src = NULL;
832 FileContent **ptr1, **dst1 = NULL;
833 FlashContent **ptr2, **dst2 = NULL;
834 char *real_name;
835 unsigned int i, j;
836 int ret = 0;
837
838 if (src_filename == NULL)
839 {
840 tifiles_critical("%s: src_filename is NULL !", __FUNCTION__);
841 return -1;
842 }
843
844 // read TiGroup file
845 src = tifiles_content_create_tigroup(CALC_NONE, 0);
846 ret = tifiles_file_read_tigroup(src_filename, src);
847 if (ret)
848 {
849 goto tuf;
850 }
851
852 // ungroup structure
853 ret = tifiles_untigroup_content(src, &dst1, &dst2);
854 if (ret)
855 {
856 goto tuf;
857 }
858
859 // count number of structures and allocates array of strings
860 if (dst_filenames != NULL)
861 {
862 *dst_filenames = (char **)g_malloc((src->n_vars + src->n_apps + 1) * sizeof(char *));
863 }
864
865 // store each structure content to file
866 for (ptr1 = dst1, i = 0; *ptr1 != NULL || i < src->n_vars; ptr1++, i++)
867 {
868 ret = tifiles_file_write_regular(NULL, *ptr1, &real_name);
869 if (ret)
870 {
871 goto tuf;
872 }
873
874 if (dst_filenames != NULL)
875 {
876 *dst_filenames[i] = real_name;
877 }
878 else
879 {
880 g_free(real_name);
881 }
882 }
883
884 for (ptr2 = dst2, j = 0; *ptr2 != NULL || j < src->n_apps; ptr2++, j++)
885 {
886 ret = tifiles_file_write_flash2(NULL, *ptr2, &real_name);
887 if (ret)
888 {
889 goto tuf;
890 }
891
892 if (dst_filenames != NULL)
893 {
894 *dst_filenames[i+j] = real_name;
895 }
896 else
897 {
898 g_free(real_name);
899 }
900 }
901
902 // release allocated memory
903 tuf:
904 if (dst1)
905 {
906 for (ptr1 = dst1; *ptr1; ptr1++)
907 {
908 tifiles_content_delete_regular(*ptr1);
909 }
910 }
911 if (dst2)
912 {
913 for (ptr2 = dst2; *ptr2; ptr2++)
914 {
915 tifiles_content_delete_flash(*ptr2);
916 }
917 }
918 tifiles_content_delete_tigroup(src);
919
920 return ret;
921 }
922
923 // ---------------------------------------------------------------------------
924
925 /**
926 * tifiles_content_create_tigroup:
927 * @model: a calculator model or CALC_NONE.
928 * @n: number of #tigEntry entries
929 *
930 * Allocates a TigContent structure. Note: the calculator model is not required
931 * if the content is used for file reading but is compulsory for file writing.
932 *
933 * Return value: the allocated block.
934 **/
tifiles_content_create_tigroup(CalcModel model,unsigned int n)935 TIEXPORT2 TigContent* TICALL tifiles_content_create_tigroup(CalcModel model, unsigned int n)
936 {
937 TigContent* content = g_malloc0(sizeof(*content));
938 if (content != NULL)
939 {
940 content->model = content->model_dst = model;
941 content->comment = g_strdup(tifiles_comment_set_tigroup());
942 content->comp_level = 4;
943 content->var_entries = (TigEntry **)g_malloc0(sizeof(TigEntry *));
944 content->app_entries = (TigEntry **)g_malloc0(sizeof(TigEntry *));
945 }
946
947 #ifdef TRACE_CONTENT_INSTANCES
948 tifiles_info("tifiles_content_create_tigroup: %p", content);
949 tifiles_file_display_tigcontent(content);
950 #endif
951
952 return content;
953 }
954
955 /**
956 * tifiles_content_delete_tigroup:
957 *
958 * Free the whole content of a @TigContent structure and the content itself.
959 *
960 * Return value: none.
961 **/
tifiles_content_delete_tigroup(TigContent * content)962 TIEXPORT2 int TICALL tifiles_content_delete_tigroup(TigContent *content)
963 {
964 unsigned int i;
965
966 #ifdef TRACE_CONTENT_INSTANCES
967 tifiles_info("tifiles_content_delete_tigroup: %p", content);
968 tifiles_file_display_tigcontent(content);
969 #endif
970
971 if (content != NULL)
972 {
973 if (content->var_entries != NULL)
974 {
975 for (i = 0; i < content->n_vars; i++)
976 {
977 TigEntry* entry = content->var_entries[i];
978 tifiles_te_delete(entry);
979 }
980 g_free(content->var_entries);
981 }
982
983 if (content->app_entries != NULL)
984 {
985 for (i = 0; i < content->n_apps; i++)
986 {
987 TigEntry* entry = content->app_entries[i];
988 tifiles_te_delete(entry);
989 }
990 g_free(content->app_entries);
991 }
992
993 g_free(content->comment);
994
995 g_free(content);
996 }
997 else
998 {
999 tifiles_critical("%s(NULL)", __FUNCTION__);
1000 }
1001
1002 return 0;
1003 }
1004
1005 /* Open a temporary file */
open_temp_file(const char * orig_name,char ** temp_name)1006 static int open_temp_file(const char *orig_name, char **temp_name)
1007 {
1008 const char *suffix;
1009 char *template;
1010 int fd;
1011
1012 *temp_name = NULL;
1013 suffix = strrchr(orig_name, '.');
1014 if (suffix && (strchr(suffix, '/') || strchr(suffix, '\\')))
1015 {
1016 suffix = NULL;
1017 }
1018 template = g_strconcat("tigXXXXXX", suffix, NULL);
1019
1020 fd = g_file_open_tmp(template, temp_name, NULL);
1021
1022 g_free(template);
1023 if (fd == -1) {
1024 g_free(*temp_name);
1025 *temp_name = NULL;
1026 }
1027
1028 return fd;
1029 }
1030
1031 /**
1032 * tifiles_file_read_tigroup:
1033 * @filename: the name of file to load.
1034 * @content: where to store content (may be re-allocated).
1035 *
1036 * This function loads a TiGroup from \a filename and places its content into \a content.
1037 * If an error occurs, the structure content is *NOT* released for you.
1038 *
1039 * The temporary folder is used by this function to store temporary files.
1040 *
1041 * Return value: an error code if unsuccessful, 0 otherwise.
1042 **/
tifiles_file_read_tigroup(const char * filename,TigContent * content)1043 TIEXPORT2 int TICALL tifiles_file_read_tigroup(const char *filename, TigContent *content)
1044 {
1045 FILE *tigf;
1046 struct archive *arc;
1047 struct archive_entry *entry;
1048 const char *filename_inzip;
1049 int ret = 0;
1050
1051 if (filename == NULL || content == NULL)
1052 {
1053 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
1054 return -1;
1055 }
1056
1057 // Open ZIP archive
1058 tigf = g_fopen(filename, "rb");
1059 if (tigf == NULL)
1060 {
1061 return ERR_FILE_OPEN;
1062 }
1063
1064 if (!(arc = archive_read_new())
1065 || archive_read_support_format_zip(arc) != ARCHIVE_OK
1066 || archive_read_open_FILE(arc, tigf) != ARCHIVE_OK)
1067 {
1068 if (arc)
1069 {
1070 archive_read_free(arc);
1071 }
1072 fclose(tigf);
1073 return ERR_FILE_ZIP;
1074 }
1075
1076 g_free(content->var_entries);
1077 content->var_entries = (TigEntry **)g_malloc0(1 * sizeof(TigEntry *));
1078 content->n_vars = 0;
1079
1080 g_free(content->app_entries);
1081 content->app_entries = (TigEntry **)g_malloc0(1 * sizeof(TigEntry *));
1082 content->n_apps = 0;
1083
1084 // Get comment
1085 g_free(content->comment);
1086 // FIXME: any way to get this from libarchive?
1087 content->comment = g_strdup("");
1088
1089 // Parse archive for files
1090 while (archive_read_next_header(arc, &entry) == ARCHIVE_OK)
1091 {
1092 gchar *fname;
1093 int fd;
1094
1095 filename_inzip = archive_entry_pathname(entry);
1096 if (!filename_inzip)
1097 {
1098 tifiles_warning("archive contains a file with no name");
1099 archive_read_data_skip(arc);
1100 continue;
1101 }
1102
1103 // create a temporary file
1104 fd = open_temp_file(filename_inzip, &fname);
1105 if (fd == -1)
1106 {
1107 ret = ERR_FILE_IO;
1108 goto tfrt_exit;
1109 }
1110
1111 // extract data into temporary file
1112 if (archive_read_data_into_fd(arc, fd) != ARCHIVE_OK)
1113 {
1114 close(fd);
1115 g_unlink(fname);
1116 g_free(fname);
1117 ret = ERR_FILE_IO;
1118 goto tfrt_exit;
1119 }
1120 close(fd);
1121
1122 // add to TigContent
1123 {
1124 int model = tifiles_file_get_model(fname);
1125
1126 if (content->model == CALC_NONE)
1127 {
1128 content->model = model;
1129 }
1130
1131 if (tifiles_file_is_regular(fname))
1132 {
1133 TigEntry *tigentry = tifiles_te_create(filename_inzip, tifiles_file_get_class(fname), content->model);
1134
1135 if (tigentry != NULL)
1136 {
1137 ret = tifiles_file_read_regular(fname, tigentry->content.regular);
1138 if (ret)
1139 {
1140 g_free(tigentry);
1141 g_unlink(fname);
1142 g_free(fname);
1143 break;
1144 }
1145
1146 tifiles_content_add_te(content, tigentry);
1147 }
1148 }
1149 else if (tifiles_file_is_flash(fname))
1150 {
1151 TigEntry *tigentry = tifiles_te_create(filename_inzip, tifiles_file_get_class(fname), content->model);
1152
1153 if (tigentry != NULL)
1154 {
1155 ret = tifiles_file_read_flash(fname, tigentry->content.flash);
1156 if (ret)
1157 {
1158 g_free(tigentry);
1159 g_unlink(fname);
1160 g_free(fname);
1161 break;
1162 }
1163
1164 tifiles_content_add_te(content, tigentry);
1165 }
1166 }
1167 else
1168 {
1169 // skip
1170 }
1171 }
1172 g_unlink(fname);
1173 g_free(fname);
1174 }
1175
1176 #ifdef TRACE_CONTENT_INSTANCES
1177 tifiles_info("tifiles_file_read_tigroup: %p", content);
1178 tifiles_file_display_tigcontent(content);
1179 #endif
1180
1181 // Close
1182 tfrt_exit:
1183 archive_read_free(arc);
1184 fclose(tigf);
1185 return ret;
1186 }
1187
zip_write(struct archive * arc,CalcModel model,const char * origfname,const char * tempfname)1188 static int zip_write(struct archive *arc, CalcModel model, const char *origfname, const char *tempfname)
1189 {
1190 char *filenameinzip;
1191 struct archive_entry *entry;
1192 struct stat st;
1193 int err = 0;
1194 FILE *f = NULL;
1195 int size_read;
1196 void* buf=NULL;
1197
1198 if (arc == NULL)
1199 {
1200 tifiles_critical("zip_write: arc is NULL !");
1201 return ERR_FILE_ZIP;
1202 }
1203
1204 // Set metadata
1205 entry = archive_entry_new();
1206 if (entry == NULL)
1207 {
1208 tifiles_critical("zip_write: cannot allocate archive entry");
1209 return ERR_FILE_ZIP;
1210 }
1211
1212 if (g_stat(tempfname, &st))
1213 {
1214 tifiles_critical("zip_write: cannot stat temporary file");
1215 archive_entry_free(entry);
1216 return ERR_FILE_IO;
1217 }
1218 archive_entry_copy_stat(entry, &st);
1219
1220 // ZIP archives don't like greek chars
1221 filenameinzip = ticonv_gfe_to_zfe(model, origfname);
1222 archive_entry_set_pathname(entry, filenameinzip);
1223 g_free(filenameinzip);
1224
1225 // missing tmp file !
1226 f = g_fopen(tempfname, "rb");
1227 if (f == NULL)
1228 {
1229 tifiles_critical("zip_write: cannot read temporary file");
1230 archive_entry_free(entry);
1231 err = ERR_FILE_IO;
1232 goto end2;
1233 }
1234
1235 if (archive_write_header(arc, entry) != ARCHIVE_OK)
1236 {
1237 archive_entry_free(entry);
1238 err = ERR_FILE_IO;
1239 goto end;
1240 }
1241 archive_entry_free(entry);
1242
1243 // Allocate buffer
1244 buf = (void*)g_malloc(WRITEBUFFERSIZE);
1245
1246 do
1247 {
1248 // feed with our data
1249 size_read = fread(buf, 1, WRITEBUFFERSIZE, f);
1250
1251 if (size_read < WRITEBUFFERSIZE)
1252 {
1253 if (!feof(f))
1254 {
1255 tifiles_critical("error in reading %s", tempfname);
1256 err = ERR_FILE_IO;
1257 }
1258 }
1259 if (size_read > 0)
1260 {
1261 if (archive_write_data(arc, buf, size_read) != size_read)
1262 {
1263 tifiles_critical("error in writing %s in the zipfile\n", origfname);
1264 err = ERR_FILE_IO;
1265 }
1266 }
1267 } while (!err && (size_read>0));
1268
1269 g_free(buf);
1270 end:
1271 fclose(f);
1272 end2:
1273 return err;
1274 }
1275
1276 /**
1277 * tifiles_file_write_tigroup:
1278 * @filename: the name of file to load.
1279 * @content: where to store content.
1280 *
1281 * This function store TiGroup contents to file. Please note that contents
1282 * can contains no data. In this case, the file is void but created.
1283 *
1284 * The temporary folder is used by this function to store temporary files.
1285 *
1286 * Return value: an error code if unsuccessful, 0 otherwise.
1287 **/
tifiles_file_write_tigroup(const char * filename,TigContent * content)1288 TIEXPORT2 int TICALL tifiles_file_write_tigroup(const char *filename, TigContent *content)
1289 {
1290 FILE *tigf;
1291 struct archive *arc;
1292 int err = 0;
1293 TigEntry **ptr;
1294
1295 if (filename == NULL || content == NULL)
1296 {
1297 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
1298 return -1;
1299 }
1300
1301 #ifdef TRACE_CONTENT_INSTANCES
1302 tifiles_info("tifiles_file_write_tigroup: %p", content);
1303 tifiles_file_display_tigcontent(content);
1304 #endif
1305
1306 // Open ZIP archive
1307 tigf = g_fopen(filename, "wb");
1308 if (tigf == NULL)
1309 {
1310 return ERR_FILE_OPEN;
1311 }
1312
1313 if (!(arc = archive_write_new()) || archive_write_set_format_zip(arc) != ARCHIVE_OK)
1314 {
1315 if (arc)
1316 {
1317 archive_write_close(arc);
1318 archive_write_free(arc);
1319 }
1320 fclose(tigf);
1321 return ERR_FILE_OPEN;
1322 }
1323
1324 // tell libarchive not to pad output to 10240-byte blocks (why
1325 // this is not the default for zip format, I have no idea)
1326 archive_write_set_bytes_per_block(arc, 0);
1327
1328 if (content->comp_level > 0)
1329 {
1330 archive_write_set_options(arc, "compression=deflate");
1331 }
1332 else
1333 {
1334 archive_write_set_options(arc, "compression=store");
1335 }
1336
1337 if (archive_write_open_FILE(arc, tigf) != ARCHIVE_OK)
1338 {
1339 err = ERR_FILE_OPEN;
1340 }
1341
1342 // Parse entries and store
1343 for (ptr = content->var_entries; *ptr && !err; ptr++)
1344 {
1345 TigEntry* entry = *ptr;
1346 char *fname = NULL;
1347 int fd;
1348
1349 // write TI file into tmp folder
1350 fd = open_temp_file(entry->filename, &fname);
1351 if (fd == -1)
1352 {
1353 g_free(fname);
1354 err = ERR_FILE_OPEN;
1355 break;
1356 }
1357 close(fd);
1358
1359 err = tifiles_file_write_regular(fname, entry->content.regular, NULL);
1360 if (!err)
1361 {
1362 err = zip_write(arc, content->model, entry->filename, fname);
1363 }
1364
1365 g_unlink(fname);
1366 g_free(fname);
1367 }
1368
1369 for (ptr = content->app_entries; *ptr && !err; ptr++)
1370 {
1371 TigEntry* entry = *ptr;
1372 char *fname = NULL;
1373 int fd;
1374
1375 // write TI file into tmp folder
1376 fd = open_temp_file(entry->filename, &fname);
1377 if (fd == -1)
1378 {
1379 g_free(fname);
1380 err = ERR_FILE_OPEN;
1381 break;
1382 }
1383 close(fd);
1384
1385 err = tifiles_file_write_flash(fname, entry->content.flash);
1386 if (!err)
1387 {
1388 err = zip_write(arc, content->model, entry->filename, fname);
1389 }
1390
1391 g_unlink(fname);
1392 g_free(fname);
1393 }
1394
1395 // close archive
1396 if (archive_write_close(arc) != ARCHIVE_OK)
1397 {
1398 err = ERR_FILE_IO;
1399 }
1400 archive_write_free(arc);
1401 fclose(tigf);
1402 return err;
1403 }
1404
1405 /**
1406 * tifiles_file_display_tigroup:
1407 * @filename: the name of file to load.
1408 *
1409 * This function shows file contents (similar to "unzip -l filename").
1410 *
1411 * Return value: an error code if unsuccessful, 0 otherwise.
1412 **/
tifiles_file_display_tigroup(const char * filename)1413 TIEXPORT2 int TICALL tifiles_file_display_tigroup(const char *filename)
1414 {
1415 FILE *tigf;
1416 struct archive *arc;
1417 struct archive_entry *entry;
1418
1419 if (filename == NULL)
1420 {
1421 tifiles_critical("%s(NULL)", __FUNCTION__);
1422 return -1;
1423 }
1424
1425 tigf = g_fopen(filename, "rb");
1426 if (tigf == NULL)
1427 {
1428 return ERR_FILE_OPEN;
1429 }
1430
1431 if (!(arc = archive_read_new())
1432 || archive_read_support_format_zip(arc) != ARCHIVE_OK
1433 || archive_read_open_FILE(arc, tigf) != ARCHIVE_OK)
1434 {
1435 if (arc)
1436 {
1437 archive_read_free(arc);
1438 }
1439 fclose(tigf);
1440 return ERR_FILE_ZIP;
1441 }
1442
1443 tifiles_info("TIGroup file contents:");
1444 tifiles_info(" Size Name");
1445 tifiles_info(" ------ ------");
1446
1447 while (archive_read_next_header(arc, &entry) == ARCHIVE_OK)
1448 {
1449 const char *name = archive_entry_pathname(entry);
1450 char *dispname = g_filename_display_name(name);
1451 unsigned long size = (unsigned long) archive_entry_size(entry);
1452 tifiles_info(" %-7lu %s", size, dispname);
1453 archive_read_data_skip(arc);
1454 g_free(dispname);
1455 }
1456
1457 archive_read_free(arc);
1458 fclose(tigf);
1459 return 0;
1460 }
1461
1462 /**
1463 * tifiles_file_display_tigcontent:
1464 * @content: the tigroup content to show, TigContent pointer.
1465 *
1466 * Display tigroup information contained in a TigContent structure.
1467 *
1468 * Return value: an error code, 0 otherwise.
1469 **/
tifiles_file_display_tigcontent(TigContent * content)1470 TIEXPORT2 int TICALL tifiles_file_display_tigcontent(TigContent *content)
1471 {
1472 unsigned int i;
1473
1474 if (content == NULL)
1475 {
1476 tifiles_critical("%s(NULL)", __FUNCTION__);
1477 return ERR_INVALID_FILE;
1478 }
1479
1480 tifiles_info("Model: %02X (%u)", content->model, content->model);
1481 tifiles_info("Signature: %s", tifiles_calctype2signature(content->model));
1482 tifiles_info("model_dst: %02X (%u)", content->model_dst, content->model_dst);
1483 tifiles_info("Comment: %s", content->comment);
1484 tifiles_info("Compression level: %d", content->comp_level);
1485
1486 tifiles_info("Number of vars: %u", content->n_vars);
1487 tifiles_info("Var entries: %p", content->var_entries);
1488
1489 if (content->var_entries != NULL)
1490 {
1491 for (i = 0; i < content->n_vars; i++)
1492 {
1493 tifiles_te_display(content->var_entries[i]);
1494 }
1495 }
1496
1497 tifiles_info("Number of apps: %u", content->n_apps);
1498 tifiles_info("Apps entries: %p", content->app_entries);
1499
1500 if (content->app_entries != NULL)
1501 {
1502 for (i = 0; i < content->n_apps; i++)
1503 {
1504 tifiles_te_display(content->app_entries[i]);
1505 }
1506 }
1507
1508 return 0;
1509 }
1510