1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Damien
3 	CALISTE, laboratoire L_Sim, (2017)
4 
5 	Adresses mèl :
6 	CALISTE, damien P caliste AT cea P fr.
7 
8 	Ce logiciel est un programme informatique servant à visualiser des
9 	structures atomiques dans un rendu pseudo-3D.
10 
11 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
12 	respectant les principes de diffusion des logiciels libres. Vous pouvez
13 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
14 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
15 	sur le site "http://www.cecill.info".
16 
17 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
18 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
19 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
20 */
21 
22 /*   LICENCE SUM UP
23 	Copyright CEA, contributors : Damien
24 	CALISTE, laboratoire L_Sim, (2017)
25 
26 	E-mail addresses :
27 	CALISTE, damien P caliste AT cea P fr.
28 
29 	This software is a computer program whose purpose is to visualize atomic
30 	configurations in 3D.
31 
32 	This software is governed by the CeCILL  license under French law and
33 	abiding by the rules of distribution of free software.  You can  use,
34 	modify and/ or redistribute the software under the terms of the CeCILL
35 	license as circulated by CEA, CNRS and INRIA at the following URL
36 	"http://www.cecill.info".
37 
38 	The fact that you are presently reading this means that you have had
39 	knowledge of the CeCILL license and that you accept its terms. You can
40 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
41 */
42 
43 #include <coreTools/toolFiles.h>
44 
45 #ifdef HAVE_LIB_ARCHIVE
46 #include <archive.h>
47 #endif
48 #include <string.h>
49 
50 /**
51  * SECTION:toolFiles
52  * @short_description: abstract reading of compressed or not text files.
53  *
54  * <para>This class defines objects to read text files with the same
55  * API than with #GIOChannel. The addition is that these files can be
56  * transparently compressed or not.</para>
57  */
58 
59 struct _ToolFilesPrivate
60 {
61   gboolean dispose_has_run;
62 
63   gchar *filename;
64 
65 #ifdef HAVE_LIB_ARCHIVE
66   struct archive *archive;
67 #define BUFF_SIZE 1024
68   char buff[BUFF_SIZE + 1];
69   char *cur;
70   gssize buffSize;
71 #endif
72 
73   GIOChannel *direct;
74   GIOStatus latest;
75 
76   gchar *data;
77   gchar *at;
78 };
79 
80 static void tool_files_dispose(GObject* obj);
81 static void tool_files_finalize(GObject* obj);
82 
G_DEFINE_TYPE_WITH_CODE(ToolFiles,tool_files,VISU_TYPE_OBJECT,G_ADD_PRIVATE (ToolFiles))83 G_DEFINE_TYPE_WITH_CODE(ToolFiles, tool_files, VISU_TYPE_OBJECT,
84                         G_ADD_PRIVATE(ToolFiles))
85 
86 static void tool_files_class_init(ToolFilesClass *klass)
87 {
88   DBG_fprintf(stderr, "ToolFiles: creating the class of the object.\n");
89 
90   /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */
91 
92   /* Connect freeing methods. */
93   G_OBJECT_CLASS(klass)->dispose  = tool_files_dispose;
94   G_OBJECT_CLASS(klass)->finalize = tool_files_finalize;
95 }
96 
tool_files_init(ToolFiles * obj)97 static void tool_files_init(ToolFiles *obj)
98 {
99   DBG_fprintf(stderr, "ToolFiles: creating a new file reader (%p).\n", (gpointer)obj);
100 
101   obj->priv = tool_files_get_instance_private(obj);
102 
103   obj->priv->dispose_has_run = FALSE;
104   obj->priv->filename = (gchar*)0;
105 #ifdef HAVE_LIB_ARCHIVE
106   obj->priv->archive = (struct archive*)0;
107   obj->priv->buff[0] = '\0';
108   obj->priv->cur = (char*)0;
109   obj->priv->buffSize = -1;
110 #endif
111   obj->priv->direct = (GIOChannel*)0;
112   obj->priv->data   = (gchar*)0;
113   obj->priv->at     = (gchar*)0;
114 }
tool_files_dispose(GObject * obj)115 static void tool_files_dispose(GObject* obj)
116 {
117   ToolFiles *self = TOOL_FILES(obj);
118   DBG_fprintf(stderr, "ToolFiles: dispose object %p.\n", (gpointer)obj);
119 
120   if (self->priv->dispose_has_run)
121     return;
122   self->priv->dispose_has_run = TRUE;
123 
124   if (self->priv->direct)
125     g_io_channel_unref(self->priv->direct);
126 
127   /* Chain up to the parent class */
128   G_OBJECT_CLASS(tool_files_parent_class)->dispose(obj);
129 }
tool_files_finalize(GObject * obj)130 static void tool_files_finalize(GObject* obj)
131 {
132   ToolFiles *self = TOOL_FILES(obj);
133   DBG_fprintf(stderr, "ToolFiles: finalize object %p.\n", (gpointer)obj);
134 
135   g_free(self->priv->filename);
136 #ifdef HAVE_LIB_ARCHIVE
137   if (self->priv->archive)
138     archive_read_free(self->priv->archive);
139 #endif
140   g_free(self->priv->data);
141 
142   /* Chain up to the parent class */
143   G_OBJECT_CLASS(tool_files_parent_class)->finalize(obj);
144 }
145 
146 /**
147  * tool_files_new:
148  *
149  * Creates a new #ToolFiles object.
150  *
151  * Since: 3.8
152  *
153  * Returns: (transfer full): a newly created #ToolFiles object.
154  */
tool_files_new()155 ToolFiles* tool_files_new()
156 {
157   return TOOL_FILES(g_object_new(TOOL_TYPE_FILES, NULL));
158 }
159 
160 /**
161  * tool_files_open:
162  * @file: a #ToolFiles object.
163  * @filename: a filename.
164  * @error: a location for an error.
165  *
166  * Open @filename for read access. The file can be compressed or not.
167  *
168  * Since: 3.8
169  *
170  * Returns: TRUE if no error occured when opening the file.
171  **/
tool_files_open(ToolFiles * file,const gchar * filename,GError ** error)172 gboolean tool_files_open(ToolFiles *file, const gchar *filename, GError **error)
173 {
174 #ifdef HAVE_LIB_ARCHIVE
175   int r;
176   struct archive_entry *ae;
177 #else
178   char *pt;
179 #endif
180 
181   g_return_val_if_fail(TOOL_IS_FILES(file), FALSE);
182   g_return_val_if_fail(!error || *error == (GError*)0, FALSE);
183 
184   file->priv->filename = g_strdup(filename);
185 #ifdef HAVE_LIB_ARCHIVE
186   file->priv->archive = archive_read_new();
187   archive_read_support_filter_all(file->priv->archive);
188   archive_read_support_format_raw(file->priv->archive);
189 
190   r = archive_read_open_filename(file->priv->archive, filename, 16384);
191   if (r == ARCHIVE_OK)
192     {
193       r = archive_read_next_header(file->priv->archive, &ae);
194       if (r != ARCHIVE_OK)
195         {
196           g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
197                       _("cannot read next archive entry.\n"));
198           return FALSE;
199         }
200       return TRUE;
201     }
202   else
203     {
204       archive_read_free(file->priv->archive);
205       file->priv->archive = (struct archive*)0;
206     }
207 #else
208   pt = strrchr(filename, '.');
209   if (pt && (!strcmp(pt, ".gz") || !strcmp(pt, ".bz2")))
210     {
211       g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
212                   _("Format not supported."));
213       return FALSE;
214     }
215 #endif
216 
217   file->priv->direct = g_io_channel_new_file(filename, "r", error);
218   if (!file->priv->direct)
219     return FALSE;
220 
221   return TRUE;
222 }
223 /**
224  * tool_files_setEncoding:
225  * @file: a #ToolFiles object.
226  * @encoding: an encoding.
227  *
228  * Set-up the encoding of @file.
229  *
230  * Since: 3.8
231  **/
tool_files_setEncoding(ToolFiles * file,const gchar * encoding)232 void tool_files_setEncoding(ToolFiles *file, const gchar *encoding)
233 {
234   g_return_if_fail(TOOL_IS_FILES(file));
235 
236   if (file->priv->direct)
237     g_io_channel_set_encoding(file->priv->direct, encoding, NULL);
238 }
239 /**
240  * tool_files_fromMemory:
241  * @file: a #ToolFiles object.
242  * @data: some data.
243  *
244  * Create a #ToolFiles object from @data.
245  *
246  * Since: 3.8
247  **/
tool_files_fromMemory(ToolFiles * file,const gchar * data)248 void tool_files_fromMemory(ToolFiles *file, const gchar *data)
249 {
250   g_return_if_fail(TOOL_IS_FILES(file));
251 
252   file->priv->data = g_strdup(data);
253   file->priv->at = file->priv->data;
254 }
255 
256 #ifdef HAVE_LIB_ARCHIVE
_archiveReadChunk(ToolFiles * file,GError ** error)257 static GIOStatus _archiveReadChunk(ToolFiles *file, GError **error)
258 {
259   gboolean empty = file->priv->cur == (char*)0;
260 
261   file->priv->buff[0] = '\0';
262   file->priv->cur = (char*)0;
263   file->priv->buffSize = archive_read_data(file->priv->archive,
264                                            file->priv->buff, BUFF_SIZE);
265   if (file->priv->buffSize < 0)
266     {
267       g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_IO,
268                   _("read error from archive.\n"));
269       return G_IO_STATUS_ERROR;
270     }
271   if (file->priv->buffSize == 0)
272     return empty ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL;
273   file->priv->buff[file->priv->buffSize] = '\0';
274   file->priv->cur = file->priv->buff;
275   return G_IO_STATUS_NORMAL;
276 }
277 #endif
278 
279 /**
280  * tool_files_read:
281  * @file: a #ToolFiles object
282  * @buffer: (array) (element-type char): a buffer
283  * @count: the size of allocated @buffer in bytes.
284  * @error: an error location or NULL
285  *
286  * Read @count bytes from @file and store them into @buffer.
287  *
288  * Since: 3.8
289  *
290  * Returns: a IO status.
291  **/
tool_files_read(ToolFiles * file,void * buffer,gsize count,GError ** error)292 GIOStatus tool_files_read(ToolFiles *file,
293                           void *buffer, gsize count, GError **error)
294 {
295   g_return_val_if_fail(TOOL_IS_FILES(file), G_IO_STATUS_ERROR);
296   g_return_val_if_fail(!error || *error == (GError*)0, G_IO_STATUS_ERROR);
297 
298 #ifdef HAVE_LIB_ARCHIVE
299   if (file->priv->archive)
300     {
301       gchar *cbuff = (gchar*)buffer;
302       while (TRUE)
303         {
304           GIOStatus status;
305 
306           if (file->priv->cur)
307             {
308               gsize buffSize = file->priv->buffSize - (file->priv->cur - file->priv->buff);
309               if (count <= buffSize)
310                 {
311                   memcpy(cbuff, file->priv->cur, sizeof(char) * count);
312                   file->priv->cur += count;
313 
314                   return G_IO_STATUS_NORMAL;
315                 }
316 
317               memcpy(cbuff, file->priv->cur, sizeof(char) * buffSize);
318               cbuff += buffSize;
319               count -= buffSize;
320             }
321 
322           status = _archiveReadChunk(file, error);
323           if (status != G_IO_STATUS_NORMAL)
324             return status;
325         }
326     }
327 #endif
328   if (file->priv->direct)
329     {
330       file->priv->latest = g_io_channel_read_chars(file->priv->direct, buffer,
331                                                    count, NULL, error);
332       return file->priv->latest;
333     }
334   else if (file->priv->data)
335     {
336       gsize len = strlen(file->priv->at);
337       gchar *cbuff = (gchar*)buffer;
338       cbuff[count] = '\0';
339       if (len >= count)
340         memcpy(cbuff, file->priv->at, count);
341       else
342         cbuff[0] = '\0';
343       file->priv->at += MIN(len, count);
344       return *file->priv->at ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
345     }
346   g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("file not opened.\n"));
347   return G_IO_STATUS_ERROR;
348 }
349 /**
350  * tool_files_skip:
351  * @file: a #ToolFiles object.
352  * @count: a number of bytes.
353  * @error: an error location.
354  *
355  * Read @count bytes from @file but don't store them.
356  *
357  * Since: 3.8
358  *
359  * Returns: a status.
360  **/
tool_files_skip(ToolFiles * file,gsize count,GError ** error)361 GIOStatus tool_files_skip(ToolFiles *file, gsize count, GError **error)
362 {
363   g_return_val_if_fail(TOOL_IS_FILES(file), G_IO_STATUS_ERROR);
364   g_return_val_if_fail(!error || *error == (GError*)0, G_IO_STATUS_ERROR);
365 
366 #ifdef HAVE_LIB_ARCHIVE
367   if (file->priv->archive)
368     {
369       while (TRUE)
370         {
371           GIOStatus status;
372 
373           if (file->priv->cur)
374             {
375               gsize buffSize = file->priv->buffSize - (file->priv->cur - file->priv->buff);
376               if (count <= buffSize)
377                 {
378                   file->priv->cur += count;
379 
380                   return G_IO_STATUS_NORMAL;
381                 }
382 
383               count -= buffSize;
384             }
385 
386           status = _archiveReadChunk(file, error);
387           if (status != G_IO_STATUS_NORMAL)
388             return status;
389         }
390     }
391 #endif
392   if (file->priv->direct)
393     {
394       file->priv->latest = g_io_channel_seek_position(file->priv->direct,
395                                                       count, G_SEEK_CUR, error);
396       return file->priv->latest;
397     }
398   else if (file->priv->data)
399     {
400       file->priv->at += MIN( strlen(file->priv->at), count);
401       return *file->priv->at ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
402     }
403   g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("file not opened.\n"));
404   return G_IO_STATUS_ERROR;
405 }
406 /**
407  * tool_files_read_line_string:
408  * @file: a #ToolFiles object.
409  * @buffer: (out caller-allocates): an allocated string.
410  * @terminator_pos: (out caller-allocates): position of the terminator
411  * in @buffer.
412  * @error: an error location.
413  *
414  * Read a new line from @file and put it into @buffer. This works like
415  * g_io_channel_read_line_string() but is transparent for compressed files.
416  *
417  * Since: 3.8
418  *
419  * Returns: a status.
420  **/
tool_files_read_line_string(ToolFiles * file,GString * buffer,gsize * terminator_pos,GError ** error)421 GIOStatus tool_files_read_line_string(ToolFiles *file, GString *buffer,
422                                       gsize *terminator_pos, GError **error)
423 {
424   g_return_val_if_fail(TOOL_IS_FILES(file), G_IO_STATUS_ERROR);
425   g_return_val_if_fail(!error || *error == (GError*)0, G_IO_STATUS_ERROR);
426 
427 #ifdef HAVE_LIB_ARCHIVE
428   if (file->priv->archive)
429     {
430       if (buffer->str)
431         g_string_set_size(buffer, 0);
432       while (TRUE)
433         {
434           GIOStatus status;
435 
436           if (file->priv->cur)
437             {
438               char *eol = strchr(file->priv->cur, '\n');
439               if (eol)
440                 {
441                   char at = eol[1];
442                   eol += 1;
443                   *eol = '\0';
444                   g_string_append(buffer, file->priv->cur);
445                   *eol = at;
446                   file->priv->cur = eol;
447 
448                   return G_IO_STATUS_NORMAL;
449                 }
450               else if (*file->priv->cur)
451                 g_string_append(buffer, file->priv->cur);
452             }
453 
454           status = _archiveReadChunk(file, error);
455           if (status != G_IO_STATUS_NORMAL)
456             return status;
457         }
458     }
459 #endif
460   if (file->priv->direct)
461     {
462       file->priv->latest = g_io_channel_read_line_string(file->priv->direct, buffer,
463                                                          terminator_pos, error);
464       return file->priv->latest;
465     }
466   else if (file->priv->data)
467     {
468       char *eol = strchr(file->priv->at, '\n');
469       if (buffer->str)
470         g_string_set_size(buffer, 0);
471       if (eol)
472         {
473           char at = eol[1];
474           eol += 1;
475           *eol = '\0';
476           g_string_append(buffer, file->priv->at);
477           *eol = at;
478           file->priv->at = eol;
479 
480           return G_IO_STATUS_NORMAL;
481         }
482       else if (*file->priv->at)
483         {
484           g_string_append(buffer, file->priv->at);
485           for (; file->priv->at; file->priv->at++);
486 
487           return G_IO_STATUS_EOF;
488         }
489       else
490         return G_IO_STATUS_EOF;
491     }
492   g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("file not opened.\n"));
493   return G_IO_STATUS_ERROR;
494 }
495 
496 /**
497  * tool_files_rewind:
498  * @file: a #ToolFiles object.
499  * @error: an error location.
500  *
501  * Transparently rewind @file at the beginning for compressed files or not.
502  *
503  * Since: 3.8
504  *
505  * Returns: a status.
506  **/
tool_files_rewind(ToolFiles * file,GError ** error)507 GIOStatus tool_files_rewind(ToolFiles *file, GError **error)
508 {
509 #ifdef HAVE_LIB_ARCHIVE
510   if (file->priv->archive)
511     {
512       /* Close and reopen. */
513       archive_read_free(file->priv->archive);
514       return tool_files_open(file, file->priv->filename, error);
515     }
516 #endif
517   if (file->priv->direct)
518     {
519       return g_io_channel_seek_position(file->priv->direct, 0, G_SEEK_SET, error);
520     }
521   else if (file->priv->data)
522     {
523       file->priv->at = file->priv->data;
524       return G_IO_STATUS_NORMAL;
525     }
526   g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
527               _("file not opened.\n"));
528   return G_IO_STATUS_ERROR;
529 }
530 
531 /**
532  * tool_files_atEnd:
533  * @file: a #ToolFiles object.
534  *
535  * Inquires if @file is at end of file.
536  *
537  * Since: 3.8
538  *
539  * Returns: TRUE on success.
540  **/
tool_files_atEnd(ToolFiles * file)541 gboolean tool_files_atEnd(ToolFiles *file)
542 {
543   g_return_val_if_fail(TOOL_IS_FILES(file), TRUE);
544 
545 #ifdef HAVE_LIB_ARCHIVE
546   if (file->priv->archive)
547     {
548       if (file->priv->buffSize < 0)
549         _archiveReadChunk(file, NULL);
550       return file->priv->buffSize == 0;
551     }
552 #endif
553   if (file->priv->direct)
554     return file->priv->latest == G_IO_STATUS_EOF;
555   else if (file->priv->data)
556     return !*file->priv->at;
557 
558   return TRUE;
559 }
560