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