1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; version 2.1
8 * of the License only.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA or on the web at http://www.gnu.org.
19 */
20 /**
21 * SECTION: IpatchFile
22 * @short_description: File abstraction object
23 * @see_also:
24 * @stability: Stable
25 *
26 * Provides an abstraction of file data sources and file type identification.
27 */
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31
32 /* for stat and fstat */
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 #include <glib-object.h>
39 #include "IpatchFile.h"
40 #include "ipatch_priv.h"
41 #include "util.h"
42
43 // Count of new files in file pool hash before garbage collection cleanup is run
44 #define IPATCH_FILE_POOL_CREATE_COUNT_CLEANUP 100
45
46 enum
47 {
48 PROP_0,
49 PROP_FILE_NAME
50 };
51
52 #define IPATCH_FILE_FREE_IOFUNCS(file) \
53 (ipatch_item_get_flags(file) & IPATCH_FILE_FLAG_FREE_IOFUNCS)
54
55 static void ipatch_file_class_init(IpatchFileClass *klass);
56 static void ipatch_file_init(IpatchFile *file);
57 static void ipatch_file_finalize(GObject *gobject);
58 static void ipatch_file_set_property(GObject *object,
59 guint property_id,
60 const GValue *value,
61 GParamSpec *pspec);
62 static void ipatch_file_get_property(GObject *object,
63 guint property_id,
64 GValue *value,
65 GParamSpec *pspec);
66 static gboolean ipatch_file_real_set_name(IpatchFile *file,
67 const char *file_name);
68 static GType ipatch_file_real_identify(IpatchFile *file, gboolean byext,
69 GError **err);
70 static GType *type_all_children(GType type, GArray *pass_array);
71 static gint sort_type_by_identify_order(gconstpointer a, gconstpointer b);
72
73 static gboolean ipatch_file_null_open_method(IpatchFileHandle *handle,
74 const char *mode, GError **err);
75 static GIOStatus ipatch_file_null_read_method(IpatchFileHandle *handle, gpointer buf,
76 guint size, guint *bytes_read,
77 GError **err);
78 static GIOStatus ipatch_file_null_write_method(IpatchFileHandle *handle,
79 gconstpointer buf,
80 guint size, GError **err);
81 static GIOStatus ipatch_file_null_seek_method(IpatchFileHandle *handle, int offset,
82 GSeekType type, GError **err);
83
84 /* default methods GIOChannel based methods */
85 static IpatchFileIOFuncs default_iofuncs =
86 {
87 ipatch_file_default_open_method,
88 ipatch_file_default_close_method,
89 ipatch_file_default_read_method,
90 ipatch_file_default_write_method,
91 ipatch_file_default_seek_method,
92 ipatch_file_default_getfd_method,
93 ipatch_file_default_get_size_method
94 };
95
96 /* null methods (/dev/null like iofuncs) */
97 static IpatchFileIOFuncs null_iofuncs =
98 {
99 ipatch_file_null_open_method,
100 NULL, /* close method */
101 ipatch_file_null_read_method,
102 ipatch_file_null_write_method,
103 ipatch_file_null_seek_method,
104 NULL, /* get fd method */
105 NULL /* get_size method */
106 };
107
108 G_DEFINE_TYPE(IpatchFile, ipatch_file, IPATCH_TYPE_ITEM)
109
110 /* Lock and hash for file pool */
111 G_LOCK_DEFINE_STATIC(ipatch_file_pool);
112 static GHashTable *ipatch_file_pool = NULL; // Hash of fileNames -> GWeakRef(IpatchFile)
113
114 /* Initialise static hash and create IpatchFile class */
115 /* ----- Initialization/deinitialization of ipatch_file pool ---------------*/
116 /* Initialise static hash */
_ipatch_file_init(void)117 void _ipatch_file_init(void)
118 {
119 /* create file pool has table */
120 ipatch_file_pool = g_hash_table_new_full (g_str_hash, g_str_equal,
121 g_free, ipatch_util_weakref_destroy);
122 }
123
124 /* Free static hash */
_ipatch_file_deinit(void)125 void _ipatch_file_deinit(void)
126 {
127 g_hash_table_destroy(ipatch_file_pool);
128 }
129
130 /* ----- IpatchFileHandle object functions ---------------------------------*/
131
132 static IpatchFileHandle *
ipatch_file_handle_duplicate(IpatchFileHandle * handle)133 ipatch_file_handle_duplicate(IpatchFileHandle *handle)
134 {
135 IpatchFileHandle *newhandle;
136
137 g_return_val_if_fail(handle != NULL, NULL);
138 g_return_val_if_fail(IPATCH_IS_FILE(handle->file), NULL);
139
140 newhandle = g_slice_new0(IpatchFileHandle); /* ++ alloc handle */
141 newhandle->file = g_object_ref(handle->file); /* ++ ref file */
142
143 return (newhandle);
144 }
145
146 static void
ipatch_file_handle_free(IpatchFileHandle * handle)147 ipatch_file_handle_free(IpatchFileHandle *handle)
148 {
149 g_return_if_fail(handle != NULL);
150 g_return_if_fail(IPATCH_IS_FILE(handle->file));
151
152 g_object_unref(handle->file); /* -- unref file */
153 g_slice_free(IpatchFileHandle, handle);
154 }
155
156 /**
157 * ipatch_file_handle_get_type:
158 *
159 * Get boxed type for #IpatchFileHandle
160 *
161 * Returns: Boxed type for file handle.
162 *
163 * Since: 1.1.0
164 */
165 GType
ipatch_file_handle_get_type(void)166 ipatch_file_handle_get_type(void)
167 {
168 static GType type = 0;
169
170 if(!type)
171 type = g_boxed_type_register_static("IpatchFileHandle",
172 (GBoxedCopyFunc)ipatch_file_handle_duplicate,
173 (GBoxedFreeFunc)ipatch_file_handle_free);
174
175 return (type);
176 }
177
178 static void
ipatch_file_class_init(IpatchFileClass * klass)179 ipatch_file_class_init(IpatchFileClass *klass)
180 {
181 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
182 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
183
184 obj_class->finalize = ipatch_file_finalize;
185 obj_class->get_property = ipatch_file_get_property;
186
187 item_class->item_set_property = ipatch_file_set_property;
188
189 klass->identify = NULL;
190
191 g_object_class_install_property(obj_class, PROP_FILE_NAME,
192 g_param_spec_string("file-name", "File Name",
193 "File Name",
194 NULL,
195 G_PARAM_READWRITE));
196 }
197
198 static void
ipatch_file_init(IpatchFile * file)199 ipatch_file_init(IpatchFile *file)
200 {
201 file->iofuncs = &default_iofuncs;
202 ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS);
203
204 if(G_BYTE_ORDER != G_LITTLE_ENDIAN)
205 {
206 ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP);
207 }
208
209 file->ref_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
210 NULL, ipatch_util_weakref_destroy);
211 }
212
213 static void
ipatch_file_finalize(GObject * gobject)214 ipatch_file_finalize(GObject *gobject)
215 {
216 IpatchFile *file = IPATCH_FILE(gobject);
217
218 IPATCH_ITEM_WLOCK(file);
219
220 /* No handles will be open, since they hold refs on the file */
221
222 /* free iofuncs structure if needed */
223 if(file->iofuncs && ipatch_item_get_flags(file)
224 & IPATCH_FILE_FLAG_FREE_IOFUNCS)
225 {
226 g_slice_free(IpatchFileIOFuncs, file->iofuncs);
227 file->iofuncs = NULL;
228 }
229
230 g_free(file->file_name);
231 file->file_name = NULL;
232
233 if(file->iochan)
234 {
235 g_io_channel_unref(file->iochan);
236 }
237
238 g_hash_table_destroy(file->ref_hash);
239
240 IPATCH_ITEM_WUNLOCK(file);
241
242 if(G_OBJECT_CLASS(ipatch_file_parent_class)->finalize)
243 {
244 G_OBJECT_CLASS(ipatch_file_parent_class)->finalize(gobject);
245 }
246 }
247
248 static void
ipatch_file_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)249 ipatch_file_set_property(GObject *object, guint property_id,
250 const GValue *value, GParamSpec *pspec)
251 {
252 IpatchFile *file = IPATCH_FILE(object);
253
254 switch(property_id)
255 {
256 case PROP_FILE_NAME:
257 ipatch_file_real_set_name(file, g_value_get_string(value));
258 break;
259
260 default:
261 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
262 return;
263 }
264 }
265
266 static void
ipatch_file_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)267 ipatch_file_get_property(GObject *object, guint property_id,
268 GValue *value, GParamSpec *pspec)
269 {
270 IpatchFile *file = IPATCH_FILE(object);
271
272 switch(property_id)
273 {
274 case PROP_FILE_NAME:
275 g_value_take_string(value, ipatch_file_get_name(file));
276 break;
277
278 default:
279 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
280 break;
281 }
282 }
283
284 /**
285 * ipatch_file_new:
286 *
287 * Create a new file object
288 *
289 * Returns: The new file object
290 */
291 IpatchFile *
ipatch_file_new(void)292 ipatch_file_new(void)
293 {
294 return (IPATCH_FILE(g_object_new(IPATCH_TYPE_FILE, NULL)));
295 }
296
297 /**
298 * ipatch_file_pool_new:
299 * @file_name: File name (converted to an absolute file name if it isn't already)
300 * @created: (out) (optional): Location to store %TRUE if file object was
301 * newly created, %FALSE if not (%NULL to ignore)
302 *
303 * Lookup existing file object from file pool by file name or create a new one if not open.
304 *
305 * Returns: File object with the assigned @file_name and an added reference which the caller owns
306 *
307 * Since: 1.1.0
308 */
309 IpatchFile *
ipatch_file_pool_new(const char * file_name,gboolean * created)310 ipatch_file_pool_new(const char *file_name, gboolean *created)
311 {
312 IpatchFile *file, *lookup_file = NULL;
313 char *abs_filename;
314 GWeakRef *weakref, *lookup;
315 static int createCount = 0; // Counter for garbage collection (destroyed file objects)
316
317 if(created)
318 {
319 *created = FALSE; // Initialize in case of bail out..
320 }
321
322 g_return_val_if_fail(file_name != NULL, NULL);
323
324 file = ipatch_file_new(); // ++ ref
325 weakref = g_slice_new(GWeakRef); // ++ allocate weak reference
326 g_weak_ref_init(weakref, file);
327 abs_filename = ipatch_util_abs_filename(file_name); // ++ allocate absolute filename
328
329 G_LOCK(ipatch_file_pool);
330
331 lookup = g_hash_table_lookup(ipatch_file_pool, abs_filename);
332
333 if(lookup)
334 {
335 lookup_file = g_weak_ref_get(lookup); // ++ ref object
336
337 if(!lookup_file)
338 {
339 g_weak_ref_set(lookup, file); // !! Re-use weak reference (it was NULL)
340 }
341 }
342 else
343 {
344 g_hash_table_insert(ipatch_file_pool, abs_filename, weakref); // !! hash takes over filename and weakref
345 }
346
347 if(!lookup_file)
348 {
349 if(++createCount >= IPATCH_FILE_POOL_CREATE_COUNT_CLEANUP) // Garbage collection for destroyed file objects
350 {
351 GHashTableIter iter;
352 gpointer key, value;
353 IpatchFile *value_file;
354
355 createCount = 0;
356
357 g_hash_table_iter_init(&iter, ipatch_file_pool);
358
359 while(g_hash_table_iter_next(&iter, &key, &value))
360 {
361 value_file = g_weak_ref_get((GWeakRef *)value); // ++ ref
362
363 if(value_file)
364 {
365 g_object_unref(value_file); // -- unref file value
366 }
367 else
368 {
369 g_hash_table_iter_remove(&iter); // Weak reference empty (file object destroyed) - remove
370 }
371 }
372 }
373 }
374
375 G_UNLOCK(ipatch_file_pool);
376
377 if(lookup_file)
378 {
379 g_free(abs_filename); // -- free absolute filename
380 g_weak_ref_clear(weakref); // -- clear weak reference
381 g_slice_free(GWeakRef, weakref); // -- free weak reference
382 g_object_unref(file); // -- unref
383 return (lookup_file); // !! caller takes over ref
384 }
385
386 if(created)
387 {
388 *created = TRUE;
389 }
390
391 if(lookup)
392 {
393 g_free(abs_filename); // -- free absolute filename
394 g_weak_ref_clear(weakref); // -- clear weak reference
395 g_slice_free(GWeakRef, weakref); // -- free weak reference
396 }
397
398 return (file); // !! caller takes over ref
399 }
400
401 /**
402 * ipatch_file_pool_lookup:
403 * @file_name: File name to lookup existing file object for
404 *
405 * Lookup an existing file object in the file pool, by file name. Does not
406 * create a new object, if not found, like ipatch_file_pool_new() does.
407 *
408 * Returns: (transfer full): Matching file object with a reference that the caller owns
409 * or %NULL if not found
410 *
411 * Since: 1.1.0
412 */
413 IpatchFile *
ipatch_file_pool_lookup(const char * file_name)414 ipatch_file_pool_lookup(const char *file_name)
415 {
416 IpatchFile *lookup_file = NULL;
417 char *abs_filename;
418 GWeakRef *lookup;
419
420 g_return_val_if_fail(file_name != NULL, NULL);
421
422 abs_filename = ipatch_util_abs_filename(file_name); // ++ allocate absolute filename
423
424 G_LOCK(ipatch_file_pool);
425 lookup = g_hash_table_lookup(ipatch_file_pool, abs_filename);
426
427 if(lookup)
428 {
429 lookup_file = g_weak_ref_get(lookup); // ++ ref object
430 }
431
432 G_UNLOCK(ipatch_file_pool);
433
434 g_free(abs_filename); // -- free absolute filename
435
436 return (lookup_file); // !! caller takes over ref
437 }
438
439 /**
440 * ipatch_file_ref_from_object:
441 * @file: File object to add a reference to (g_object_ref() called)
442 * @object: Object which is referencing the @file
443 *
444 * Reference a file object from another object and keep track of the
445 * external reference (using a #GWeakRef). References can be obtained with
446 * ipatch_file_get_refs(). Use ipatch_file_unref_from_object() to remove
447 * the reference, although the registration will be removed regardless at some
448 * point if @object gets destroyed and ipatch_file_get_refs() or
449 * ipatch_file_get_refs_by_type() is called.
450 *
451 * Since: 1.1.0
452 */
453 void
ipatch_file_ref_from_object(IpatchFile * file,GObject * object)454 ipatch_file_ref_from_object(IpatchFile *file, GObject *object)
455 {
456 GWeakRef *weakref;
457
458 g_return_if_fail(IPATCH_IS_FILE(file));
459 g_return_if_fail(G_IS_OBJECT(object));
460
461 weakref = g_slice_new(GWeakRef); // ++ allocate weak reference
462 g_weak_ref_init(weakref, object); // ++ initialize the weak reference with object
463
464 IPATCH_ITEM_WLOCK(file);
465 g_hash_table_insert(file->ref_hash, object, weakref); // !! list takes over weak reference
466 IPATCH_ITEM_WUNLOCK(file);
467
468 g_object_ref(file); // ++ ref file object for object
469 }
470
471 /**
472 * ipatch_file_unref_from_object:
473 * @file: File object to remove a reference from (g_object_unref() called)
474 * @object: Object which is unreferencing the @file
475 *
476 * Remove a reference previously registered with ipatch_file_ref_from_object().
477 * This will get done eventually if @object gets destroyed and ipatch_file_get_refs()
478 * or ipatch_file_get_refs_by_type() is called, however.
479 *
480 * Since: 1.1.0
481 */
482 void
ipatch_file_unref_from_object(IpatchFile * file,GObject * object)483 ipatch_file_unref_from_object(IpatchFile *file, GObject *object)
484 {
485 g_return_if_fail(IPATCH_IS_FILE(file));
486 g_return_if_fail(object != NULL); // We only need the pointer value really
487
488 IPATCH_ITEM_WLOCK(file);
489 g_hash_table_remove(file->ref_hash, object);
490 IPATCH_ITEM_WUNLOCK(file);
491
492 g_object_unref(file); // -- ref file object for object
493 }
494
495 /**
496 * ipatch_file_test_ref_object:
497 * @file: File object to test reference to
498 * @object: Object to test for reference to @file
499 *
500 * Check if a given @object is referencing @file. Must have been
501 * referenced with ipatch_file_ref_from_object().
502 *
503 * Returns: %TRUE if @object references @file, %FALSE otherwise
504 *
505 * Since: 1.1.0
506 */
507 gboolean
ipatch_file_test_ref_object(IpatchFile * file,GObject * object)508 ipatch_file_test_ref_object(IpatchFile *file, GObject *object)
509 {
510 gboolean retval;
511
512 g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE);
513 g_return_val_if_fail(object != NULL, FALSE); // We only need the pointer value really
514
515 IPATCH_ITEM_WLOCK(file);
516 retval = g_hash_table_lookup(file->ref_hash, object) != NULL;
517 IPATCH_ITEM_WUNLOCK(file);
518
519 return (retval);
520 }
521
522 /**
523 * ipatch_file_get_refs:
524 * @file: File object to get external references of
525 *
526 * Get list of objects referencing a file object.
527 * NOTE: A side effect of calling this function is that any references from
528 * destroyed objects are removed (if ipatch_file_unref_from_object() was not used).
529 *
530 * Returns: (transfer full): New object list which caller owns a reference to,
531 * unreference when finished using it.
532 *
533 * Since: 1.1.0
534 */
535 IpatchList *
ipatch_file_get_refs(IpatchFile * file)536 ipatch_file_get_refs(IpatchFile *file)
537 {
538 return (ipatch_file_get_refs_by_type(file, G_TYPE_NONE));
539 }
540
541 /**
542 * ipatch_file_get_refs_by_type:
543 * @file: File object to get external references of
544 * @type: Object type to match (or a descendant thereof) or #G_TYPE_NONE
545 * to match any type
546 *
547 * Like ipatch_file_get_refs() but only returns objects matching a given type
548 * or a descendant thereof.
549 *
550 * Returns: (transfer full): New object list which caller owns a reference to,
551 * unreference when finished using it.
552 *
553 * Since: 1.1.0
554 */
555 IpatchList *
ipatch_file_get_refs_by_type(IpatchFile * file,GType type)556 ipatch_file_get_refs_by_type(IpatchFile *file, GType type)
557 {
558 GHashTableIter iter;
559 gpointer key;
560 GObject *refobj;
561 GWeakRef *weakref;
562 IpatchList *list;
563
564 g_return_val_if_fail(IPATCH_IS_FILE(file), NULL);
565
566 if(type == G_TYPE_OBJECT)
567 {
568 type = G_TYPE_NONE; // G_TYPE_OBJECT is equivalent to G_TYPE_NONE (i.e., all objects)
569 }
570
571 g_return_val_if_fail(type == G_TYPE_NONE || g_type_is_a(type, G_TYPE_OBJECT), NULL);
572
573 list = ipatch_list_new(); // ++ ref object list
574
575 IPATCH_ITEM_WLOCK(file);
576
577 g_hash_table_iter_init(&iter, file->ref_hash);
578
579 while(g_hash_table_iter_next(&iter, &key, (gpointer *)&weakref))
580 {
581 refobj = g_weak_ref_get(weakref); // ++ ref object
582
583 if(refobj) // Object still alive?
584 {
585 // type not specified or object matches type?
586 if(type == G_TYPE_NONE || g_type_is_a(G_OBJECT_TYPE(refobj), type))
587 {
588 list->items = g_list_prepend(list->items, refobj); // Prepend object to list
589 }
590 else
591 {
592 g_object_unref(refobj); // -- unref object (did not match type criteria)
593 }
594 }
595 else
596 {
597 g_hash_table_iter_remove(&iter); // Object destroyed - remove from hash
598 }
599 }
600
601 IPATCH_ITEM_WUNLOCK(file);
602
603 return (list); // !! caller takes over list reference
604 }
605
606 /**
607 * ipatch_file_set_name:
608 * @file: File object to assign file name to
609 * @file_name: File name or %NULL to unset the file name
610 *
611 * Sets the file name of a file object. Assigning the file name of an #IpatchFile
612 * object is optional, since a file descriptor could be assigned instead,
613 * but some subsystems depend on it.
614 */
615 void
ipatch_file_set_name(IpatchFile * file,const char * file_name)616 ipatch_file_set_name(IpatchFile *file, const char *file_name)
617 {
618 if(ipatch_file_real_set_name(file, file_name))
619 {
620 g_object_notify(G_OBJECT(file), "file-name");
621 }
622 }
623
624 static gboolean
ipatch_file_real_set_name(IpatchFile * file,const char * file_name)625 ipatch_file_real_set_name(IpatchFile *file, const char *file_name)
626 {
627 char *new_filename, *old_filename;
628
629 g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE);
630
631 new_filename = g_strdup(file_name); // ++ alloc file name for file object
632
633 IPATCH_ITEM_WLOCK(file);
634 old_filename = file->file_name;
635 file->file_name = new_filename; // !! takes over allocation
636 IPATCH_ITEM_WUNLOCK(file);
637
638 g_free(old_filename); // -- free old file name
639
640 return (TRUE);
641 }
642
643 /**
644 * ipatch_file_get_name:
645 * @file: File object to get file name from
646 *
647 * Gets the assigned file name from a file object.
648 *
649 * Returns: The file name of the file object or %NULL if not set. String
650 * should be freed when finished with it.
651 */
652 char *
ipatch_file_get_name(IpatchFile * file)653 ipatch_file_get_name(IpatchFile *file)
654 {
655 char *file_name = NULL;
656
657 g_return_val_if_fail(IPATCH_IS_FILE(file), NULL);
658
659 IPATCH_ITEM_RLOCK(file);
660
661 if(file->file_name)
662 {
663 file_name = g_strdup(file->file_name);
664 }
665
666 IPATCH_ITEM_RUNLOCK(file);
667
668 return (file_name);
669 }
670
671 /**
672 * ipatch_file_rename:
673 * @file: File object to rename
674 * @new_name: New file name (can be a full path to move the file)
675 * @err: Location to store error info or %NULL to ignore
676 *
677 * Physically rename the file referenced by a @file object. The given file
678 * object must have a file name assigned and no file descriptor or I/O channel.
679 * On Windows, the file must also not have any open handles. If a file with
680 * @new_name already exists, it will be replaced and should not be referenced by
681 * any file object.
682 *
683 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set)
684 *
685 * Since: 1.1.0
686 */
687 gboolean
ipatch_file_rename(IpatchFile * file,const char * new_name,GError ** err)688 ipatch_file_rename(IpatchFile *file, const char *new_name, GError **err)
689 {
690 char *dup_newname, *old_filename;
691 IpatchFile *new_name_file;
692
693 g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE);
694 g_return_val_if_fail(new_name != NULL, FALSE);
695 g_return_val_if_fail(!err || !*err, FALSE);
696
697 // Check if new file name is already referenced by a file object
698 new_name_file = ipatch_file_pool_lookup(new_name); // ++ ref file object
699
700 if(new_name_file)
701 {
702 g_object_unref(new_name_file); // -- unref file object (only need pointer value)
703
704 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY,
705 "New file name '%s' is already claimed", new_name);
706 return (FALSE);
707 }
708
709 #ifdef G_OS_WIN32
710
711 if(g_file_test(new_name, G_FILE_TEST_EXISTS))
712 {
713 g_unlink(new_name); // Just blindly unlink the file
714 }
715
716 #endif
717
718 dup_newname = g_strdup(new_name); // ++ allocate for use by file object
719
720 IPATCH_ITEM_WLOCK(file);
721
722 if(log_if_fail(file->iochan == NULL))
723 {
724 goto error;
725 }
726
727 if(log_if_fail(file->file_name != NULL))
728 {
729 goto error;
730 }
731
732 // Don't even try renaming the file on Windows if it is open, should be fine on Unix for most purposes
733 #ifdef G_OS_WIN32
734
735 if(file->open_count > 0)
736 {
737 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY,
738 "File '%s' has open handles", file->file_name);
739 goto error;
740 }
741
742 #endif
743
744 if(g_rename(file->file_name, dup_newname) != 0)
745 {
746 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO,
747 "I/O error renaming file '%s' to '%s': %s", file->file_name,
748 dup_newname, g_strerror(errno));
749 goto error;
750 }
751
752 old_filename = file->file_name;
753 file->file_name = dup_newname; // !! takes over allocation
754
755 IPATCH_ITEM_WUNLOCK(file);
756
757 g_free(old_filename); // -- free old file name
758
759 return (TRUE);
760
761 error:
762 IPATCH_ITEM_WUNLOCK(file);
763 g_free(dup_newname); // -- free duplicate copy of original new_name
764 return (FALSE);
765 }
766
767 /**
768 * ipatch_file_unlink:
769 * @file: File object to rename
770 * @err: Location to store error info or %NULL to ignore
771 *
772 * Physically delete the file referenced by a @file object. The given file
773 * object must have a file name assigned and no file descriptor or I/O channel.
774 * On Windows, the file must also not have any open handles.
775 * The file object will remain alive, but the underlying file will be unlinked.
776 *
777 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set)
778 *
779 * Since: 1.1.0
780 */
781 gboolean
ipatch_file_unlink(IpatchFile * file,GError ** err)782 ipatch_file_unlink(IpatchFile *file, GError **err)
783 {
784 g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE);
785 g_return_val_if_fail(!err || !*err, FALSE);
786
787 IPATCH_ITEM_WLOCK(file);
788
789 if(log_if_fail(file->iochan == NULL))
790 {
791 goto error;
792 }
793
794 if(log_if_fail(file->file_name != NULL))
795 {
796 goto error;
797 }
798
799 // Don't even try deleting the file on Windows if it is open, should be fine on Unix for most purposes
800 #ifdef G_OS_WIN32
801
802 if(file->open_count > 0)
803 {
804 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY,
805 "File '%s' has open handles", file->file_name);
806 goto error;
807 }
808
809 #endif
810
811 if(g_unlink(file->file_name) != 0)
812 {
813 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO,
814 "I/O error unlinking file '%s': %s", file->file_name,
815 g_strerror(errno));
816 goto error;
817 }
818
819 IPATCH_ITEM_WUNLOCK(file);
820
821 return (TRUE);
822
823 error:
824 IPATCH_ITEM_WUNLOCK(file);
825 return (FALSE);
826 }
827
828 /**
829 * ipatch_file_replace:
830 * @newfile: New file to replace the @oldfile with (must have an assigned #IpatchFile::file-name property)
831 * @oldfile: The old file to replace (must have an assigned #IpatchFile::file-name property)
832 * @err: Location to store error info or %NULL to ignore
833 *
834 * Replace one file object with another. After successful execution of this function
835 * @oldfile will have an unset file name, @newfile will be assigned what was the oldfile name,
836 * and the file data of the old file on the filesystem will have been replaced by new file.
837 *
838 * NOTE: On Windows both files must not have any open file handles.
839 *
840 * NOTE: In the event an error occurs, recovery will be attempted, but may also fail, resulting in
841 * loss of @oldfile data.
842 *
843 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set)
844 *
845 * Since: 1.1.0
846 */
847 gboolean
ipatch_file_replace(IpatchFile * newfile,IpatchFile * oldfile,GError ** err)848 ipatch_file_replace(IpatchFile *newfile, IpatchFile *oldfile, GError **err)
849 {
850 char *filename, *free_filename;
851
852 g_return_val_if_fail(IPATCH_IS_FILE(newfile), FALSE);
853 g_return_val_if_fail(IPATCH_IS_FILE(oldfile), FALSE);
854 g_return_val_if_fail(!err || !*err, FALSE);
855
856 // Sanity check of files, prior to doing any funny business
857
858 IPATCH_ITEM_RLOCK(oldfile);
859
860 if(log_if_fail(oldfile->iochan == NULL)
861 || log_if_fail(oldfile->file_name != NULL))
862 {
863 IPATCH_ITEM_RUNLOCK(oldfile);
864 return (FALSE);
865 }
866
867 // Don't even try replacing the oldfile on Windows if open, should be fine on Unix for most purposes
868 #ifdef G_OS_WIN32
869
870 if(oldfile->open_count > 0)
871 {
872 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY,
873 "Old file '%s' has open handles", oldfile->file_name);
874 IPATCH_ITEM_RUNLOCK(oldfile);
875 return (FALSE);
876 }
877
878 #endif
879
880 IPATCH_ITEM_RUNLOCK(oldfile);
881
882 IPATCH_ITEM_RLOCK(newfile);
883
884 if(log_if_fail(newfile->iochan == NULL)
885 || log_if_fail(newfile->file_name != NULL))
886 {
887 IPATCH_ITEM_RUNLOCK(newfile);
888 return (FALSE);
889 }
890
891 // Don't even try renaming the newfile on Windows if open, should be fine on Unix for most purposes
892 #ifdef G_OS_WIN32
893
894 if(newfile->open_count > 0)
895 {
896 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY,
897 "New file '%s' has open handles", newfile->file_name);
898 IPATCH_ITEM_RUNLOCK(newfile);
899 return (FALSE);
900 }
901
902 #endif
903
904 IPATCH_ITEM_RUNLOCK(newfile);
905
906
907 // Steal filename from oldfile and delete file (on Windows)
908 IPATCH_ITEM_WLOCK(oldfile);
909
910 // filename must be valid before calling de g_unlink().
911 filename = oldfile->file_name; // ++ filename takes over allocation
912
913 #ifdef G_OS_WIN32
914 // Just blindly unlink the file
915 g_unlink(filename);
916 #endif
917
918 oldfile->file_name = NULL;
919
920 IPATCH_ITEM_WUNLOCK(oldfile);
921
922
923 // Rename newfile to oldfile name and assign the file name to it
924 IPATCH_ITEM_WLOCK(newfile);
925
926 if(g_rename(newfile->file_name, filename) != 0)
927 {
928 // WARNING - On windows, if rename fails, oldfile is lost.. Unlikely to happen though.
929 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO,
930 "I/O error renaming file '%s' to '%s': %s", newfile->file_name,
931 filename, g_strerror(errno));
932 IPATCH_ITEM_WUNLOCK(newfile);
933
934 #ifdef G_OS_WIN32
935 g_free(filename); // -- free the oldfile filename (on Windows oldfile has been deleted)
936 #else
937 // Restore oldfile file name on Unix
938 IPATCH_ITEM_WLOCK(oldfile);
939 free_filename = oldfile->file_name; // ++ take over file name which may have been assigned by another thread (highly unlikely..)
940 oldfile->file_name = filename;
941 IPATCH_ITEM_WUNLOCK(oldfile);
942
943 g_free(free_filename); // -- free possibly newly assigned file name
944 #endif
945
946 return (FALSE);
947 }
948
949 free_filename = newfile->file_name; // ++ free_filename takes over allocation
950 newfile->file_name = filename; // !! newfile takes over allocation
951
952 IPATCH_ITEM_WUNLOCK(newfile);
953
954 g_free(free_filename); // -- free the previous newfile file name
955
956 return (TRUE);
957 }
958
959 /**
960 * ipatch_file_open:
961 * @file: File object to open from a file name.
962 * @file_name: (nullable): Name of file to open or %NULL to use the file
963 object's assigned file name (in which case it should not be %NULL).
964 * @mode: File open mode ("r" for read or "w" for write)
965 * @err: Error information
966 *
967 * Opens a handle to a file object. If a I/O channel or file descriptor is
968 * already assigned (with ipatch_file_assign_fd() or
969 * ipatch_file_assign_io_channel()) then it is used instead of opening a file
970 * using @file_name or the already assigned #IpatchFile:file-name property.
971 *
972 * Returns: New file handle or %NULL (in which case @err may be set). The
973 * returned file handle is not multi-thread safe, but the file can be opened
974 * multiple times for this purpose.
975 */
976 IpatchFileHandle *
ipatch_file_open(IpatchFile * file,const char * file_name,const char * mode,GError ** err)977 ipatch_file_open(IpatchFile *file, const char *file_name, const char *mode,
978 GError **err)
979 {
980 IpatchFileHandle *handle;
981 GIOChannel *iochan = NULL;
982 char *old_file_name = NULL;
983 char *dup_file_name;
984 int retval = FALSE;
985
986 g_return_val_if_fail(IPATCH_IS_FILE(file), NULL);
987 g_return_val_if_fail(file->iofuncs != NULL, NULL);
988
989 dup_file_name = g_strdup(file_name); /* ++ dup file name */
990
991 handle = g_slice_new0(IpatchFileHandle); /* ++ alloc handle */
992 handle->file = file;
993
994 IPATCH_ITEM_WLOCK(file);
995
996 if(log_if_fail(file->iofuncs->open != NULL))
997 {
998 IPATCH_ITEM_WUNLOCK(file);
999 g_slice_free(IpatchFileHandle, handle); /* -- free handle */
1000 g_free(dup_file_name); /* -- free dup file name */
1001 return (NULL);
1002 }
1003
1004 if(dup_file_name) /* set file name if supplied */
1005 {
1006 old_file_name = file->file_name;
1007 file->file_name = dup_file_name; /* !! takes over allocation of dup file name */
1008 }
1009
1010 if(file->iochan)
1011 {
1012 iochan = g_io_channel_ref(file->iochan); /* ++ ref io channel */
1013 handle->iochan = iochan;
1014 }
1015
1016 retval = file->iofuncs->open(handle, mode, err);
1017
1018 if(retval)
1019 {
1020 file->open_count++;
1021 }
1022
1023 IPATCH_ITEM_WUNLOCK(file);
1024
1025 g_free(old_file_name); /* -- free old file name */
1026
1027 if(!retval)
1028 {
1029 g_slice_free(IpatchFileHandle, handle); /* -- free handle */
1030
1031 if(iochan)
1032 {
1033 g_io_channel_unref(iochan); /* -- unref iochan */
1034 }
1035
1036 return (NULL);
1037 }
1038
1039 g_object_ref(file); /* ++ ref file for handle */
1040 handle->buf = g_byte_array_new();
1041
1042 return (handle); /* !! caller takes over handle */
1043 }
1044
1045 /**
1046 * ipatch_file_assign_fd:
1047 * @file: File object
1048 * @fd: File descriptor to assign to file object, or -1 to clear it
1049 * @close_on_finalize: %TRUE if the descriptor should be closed when @file is
1050 * finalized, %FALSE to leave it open
1051 *
1052 * Assigns a file descriptor to a file, which gets used for calls to
1053 * ipatch_file_open(). Note that this means multiple opens will use the same
1054 * file descriptor and will therefore conflict, so it should only be used in the
1055 * case where the file object is used by a single exclusive handle.
1056 */
1057 void
ipatch_file_assign_fd(IpatchFile * file,int fd,gboolean close_on_finalize)1058 ipatch_file_assign_fd(IpatchFile *file, int fd, gboolean close_on_finalize)
1059 {
1060 GIOChannel *iochan;
1061
1062 g_return_if_fail(IPATCH_IS_FILE(file));
1063
1064 if(fd == -1)
1065 {
1066 ipatch_file_assign_io_channel(file, NULL);
1067 return;
1068 }
1069
1070 iochan = g_io_channel_unix_new(fd); /* ++ ref new io channel */
1071 g_io_channel_set_close_on_unref(iochan, close_on_finalize);
1072 g_io_channel_set_encoding(iochan, NULL, NULL);
1073 ipatch_file_assign_io_channel(file, iochan);
1074 g_io_channel_unref(iochan); /* -- unref creator's ref */
1075 }
1076
1077 /**
1078 * ipatch_file_assign_io_channel:
1079 * @file: File object
1080 * @iochan: IO channel to assign to @file or %NULL to clear it
1081 *
1082 * Assigns an I/O channel to a file, which gets used for calls to
1083 * ipatch_file_open(). Note that this means multiple opens will use the same
1084 * file descriptor and will therefore conflict, so it should only be used in the
1085 * case where the file object is used by a single exclusive handle.
1086 */
1087 void
ipatch_file_assign_io_channel(IpatchFile * file,GIOChannel * iochan)1088 ipatch_file_assign_io_channel(IpatchFile *file, GIOChannel *iochan)
1089 {
1090 GIOChannel *old_iochan;
1091 g_return_if_fail(IPATCH_IS_FILE(file));
1092
1093 if(iochan)
1094 {
1095 g_io_channel_ref(iochan); /* ++ ref for file */
1096 }
1097
1098 IPATCH_ITEM_WLOCK(file);
1099 old_iochan = file->iochan;
1100 file->iochan = iochan;
1101 IPATCH_ITEM_WUNLOCK(file);
1102
1103 if(old_iochan)
1104 {
1105 g_io_channel_unref(old_iochan);
1106 }
1107 }
1108
1109 /**
1110 * ipatch_file_get_io_channel:
1111 * @handle: File handle
1112 *
1113 * Get the glib IO channel object from a file handle. The caller owns a
1114 * reference to the returned io channel, and it should be unreferenced with
1115 * g_io_channel_unref() when finished with it.
1116 *
1117 * Returns: GIOChannel assigned to the @handle or %NULL if none (some
1118 * derived #IpatchFile types might not use io channels). Remember to unref it
1119 * with g_io_channel_unref() when finished.
1120 */
1121 GIOChannel *
ipatch_file_get_io_channel(IpatchFileHandle * handle)1122 ipatch_file_get_io_channel(IpatchFileHandle *handle)
1123 {
1124 GIOChannel *iochan;
1125
1126 g_return_val_if_fail(handle != NULL, NULL);
1127
1128 if((iochan = handle->iochan))
1129 {
1130 g_io_channel_ref(iochan);
1131 }
1132
1133 return (iochan);
1134 }
1135
1136 /**
1137 * ipatch_file_get_fd:
1138 * @handle: File handle
1139 *
1140 * Get the unix file descriptor associated with a file handle. Not all file
1141 * handles have a real OS file descriptor.
1142 *
1143 * Returns: File descriptor or -1 if not open or failed to get descriptor.
1144 */
1145 int
ipatch_file_get_fd(IpatchFileHandle * handle)1146 ipatch_file_get_fd(IpatchFileHandle *handle)
1147 {
1148 int fd = -1;
1149
1150 g_return_val_if_fail(handle != NULL, -1);
1151 g_return_val_if_fail(IPATCH_IS_FILE(handle->file), -1);
1152
1153 if(handle->file->iofuncs && handle->file->iofuncs->getfd)
1154 {
1155 fd = handle->file->iofuncs->getfd(handle);
1156 }
1157
1158 return (fd);
1159 }
1160
1161 /**
1162 * ipatch_file_close:
1163 * @handle: File handle
1164 *
1165 * Close a file handle and free it.
1166 */
1167 void
ipatch_file_close(IpatchFileHandle * handle)1168 ipatch_file_close(IpatchFileHandle *handle)
1169 {
1170 g_return_if_fail(handle != NULL);
1171 g_return_if_fail(IPATCH_IS_FILE(handle->file));
1172
1173 IPATCH_ITEM_WLOCK(handle->file);
1174
1175 if(handle->file->iofuncs && handle->file->iofuncs->close)
1176 {
1177 handle->file->iofuncs->close(handle);
1178 }
1179
1180 handle->file->open_count--;
1181
1182 IPATCH_ITEM_WUNLOCK(handle->file);
1183
1184 g_object_unref(handle->file);
1185
1186 if(handle->buf)
1187 {
1188 g_byte_array_free(handle->buf, TRUE);
1189 }
1190
1191 if(handle->iochan)
1192 {
1193 g_io_channel_unref(handle->iochan);
1194 }
1195
1196 g_slice_free(IpatchFileHandle, handle);
1197 }
1198
1199 /**
1200 * ipatch_file_get_position:
1201 * @handle: File handle
1202 *
1203 * Gets the current position in a file handle. Note that this
1204 * might not be the actual position in the file if the file handle was
1205 * attached to an already open file or if ipatch_file_update_position() is
1206 * used to set virtual positions.
1207 *
1208 * Returns: Position in file handle.
1209 */
1210 guint
ipatch_file_get_position(IpatchFileHandle * handle)1211 ipatch_file_get_position(IpatchFileHandle *handle)
1212 {
1213 g_return_val_if_fail(handle != NULL, 0);
1214 return (handle->position);
1215 }
1216
1217 /**
1218 * ipatch_file_update_position:
1219 * @handle: File handle
1220 * @offset: Offset to add to the position counter (can be negative)
1221 *
1222 * Adds an offset value to the position counter in a file handle. This can
1223 * be used if one is operating directly on the underlying file descriptor (i.e.,
1224 * not using the #IpatchFile functions) or to add virtual space to the counter.
1225 * Adding virtual space is useful when a system uses the position counter to
1226 * write data (such as the RIFF parser) to act as a place holder for data that
1227 * isn't actually written (sample data for example).
1228 */
1229 void
ipatch_file_update_position(IpatchFileHandle * handle,guint offset)1230 ipatch_file_update_position(IpatchFileHandle *handle, guint offset)
1231 {
1232 g_return_if_fail(handle != NULL);
1233 handle->position += offset;
1234 }
1235
1236 /**
1237 * ipatch_file_read:
1238 * @handle: File handle
1239 * @buf: (out) (array length=size) (element-type guint8): Buffer to read data into
1240 * @size: Amount of data to read, in bytes.
1241 * @err: A location to return an error of type GIOChannelError or %NULL.
1242 *
1243 * Reads data from a file handle. An end of file encountered while
1244 * trying to read the specified @size of data is treated as an error.
1245 * If this is undesirable use ipatch_file_read_eof() instead.
1246 *
1247 * Returns: %TRUE on success (the requested @size of data was read), %FALSE
1248 * otherwise
1249 */
1250 gboolean
ipatch_file_read(IpatchFileHandle * handle,gpointer buf,guint size,GError ** err)1251 ipatch_file_read(IpatchFileHandle *handle, gpointer buf, guint size, GError **err)
1252 {
1253 GIOStatus status;
1254
1255 /* we let ipatch_file_read_eof do checks for us */
1256 status = ipatch_file_read_eof(handle, buf, size, NULL, err);
1257
1258 if(status == G_IO_STATUS_EOF)
1259 {
1260 if(err && !*err)
1261 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNEXPECTED_EOF,
1262 _("Unexpected end of file"));
1263
1264 return (FALSE);
1265 }
1266
1267 return (status == G_IO_STATUS_NORMAL);
1268 }
1269
1270 /**
1271 * ipatch_file_read_eof:
1272 * @handle: File handle
1273 * @buf: (out) (array length=size) (element-type guint8): Buffer to read data into
1274 * @size: Amount of data to read, in bytes.
1275 * @bytes_read: (out) (optional): Pointer to store number of bytes actually read or %NULL.
1276 * @err: A location to return an error of type GIOChannelError or %NULL.
1277 *
1278 * Reads data from a file handle. This function does not treat end of file
1279 * as an error and will return #G_IO_STATUS_EOF with the number of bytes
1280 * actually read in @bytes_read. Use ipatch_file_read() for convenience to
1281 * ensure actual number of requested bytes is read.
1282 *
1283 * Returns: The status of the operation
1284 */
1285 GIOStatus
ipatch_file_read_eof(IpatchFileHandle * handle,gpointer buf,guint size,guint * bytes_read,GError ** err)1286 ipatch_file_read_eof(IpatchFileHandle *handle, gpointer buf, guint size,
1287 guint *bytes_read, GError **err)
1288 {
1289 GIOStatus status;
1290 guint _bytes_read = 0;
1291
1292 if(bytes_read)
1293 {
1294 *bytes_read = 0;
1295 }
1296
1297 g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR);
1298 g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR);
1299 g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR);
1300 g_return_val_if_fail(handle->file->iofuncs->read != NULL, G_IO_STATUS_ERROR);
1301 g_return_val_if_fail(buf != NULL, G_IO_STATUS_ERROR);
1302 g_return_val_if_fail(size > 0, FALSE);
1303 g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR);
1304
1305 status = handle->file->iofuncs->read(handle, buf, size, &_bytes_read, err);
1306
1307 if(bytes_read)
1308 {
1309 *bytes_read = _bytes_read;
1310 }
1311
1312 handle->position += _bytes_read;
1313
1314 return (status);
1315 }
1316
1317 /**
1318 * _ipatch_file_read_no_pos_update: (skip)
1319 *
1320 * Used internally by IpatchFileBuf. Like ipatch_file_read() but does not
1321 * update handle->position, since buffered functions do this themselves.
1322 */
1323 gboolean
_ipatch_file_read_no_pos_update(IpatchFileHandle * handle,gpointer buf,guint size,GError ** err)1324 _ipatch_file_read_no_pos_update(IpatchFileHandle *handle, gpointer buf,
1325 guint size, GError **err)
1326 {
1327 guint _bytes_read = 0;
1328
1329 return (handle->file->iofuncs->read(handle, buf, size, &_bytes_read, err)
1330 == G_IO_STATUS_NORMAL);
1331 }
1332
1333 /**
1334 * ipatch_file_write:
1335 * @handle: File handle
1336 * @buf: (array length=size) (element-type guint8): Buffer of data to write
1337 * @size: Amount of data to write, in bytes.
1338 * @err: A location to return an error of type GIOChannelError or %NULL.
1339 *
1340 * Writes data to a file object.
1341 *
1342 * Returns: TRUE on success, FALSE otherwise
1343 */
1344 gboolean
ipatch_file_write(IpatchFileHandle * handle,gconstpointer buf,guint size,GError ** err)1345 ipatch_file_write(IpatchFileHandle *handle, gconstpointer buf, guint size,
1346 GError **err)
1347 {
1348 GIOStatus status;
1349
1350 g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR);
1351 g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR);
1352 g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR);
1353 g_return_val_if_fail(handle->file->iofuncs->write != NULL, G_IO_STATUS_ERROR);
1354 g_return_val_if_fail(buf != NULL, G_IO_STATUS_ERROR);
1355 g_return_val_if_fail(size > 0, FALSE);
1356 g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR);
1357
1358 status = handle->file->iofuncs->write(handle, buf, size, err);
1359
1360 if(status == G_IO_STATUS_NORMAL)
1361 {
1362 handle->position += size;
1363 }
1364
1365 return (status == G_IO_STATUS_NORMAL);
1366 }
1367
1368 /**
1369 * _ipatch_file_write_no_pos_update: (skip)
1370 *
1371 * Used internally by IpatchFileBuf. Like ipatch_file_write() but does not
1372 * update handle->position, since buffered functions do this themselves.
1373 */
1374 gboolean
_ipatch_file_write_no_pos_update(IpatchFileHandle * handle,gconstpointer buf,guint size,GError ** err)1375 _ipatch_file_write_no_pos_update(IpatchFileHandle *handle, gconstpointer buf,
1376 guint size, GError **err)
1377 {
1378 return (handle->file->iofuncs->write(handle, buf, size, err) == G_IO_STATUS_NORMAL);
1379 }
1380
1381 /**
1382 * ipatch_file_seek:
1383 * @handle: File handle
1384 * @offset: Offset in bytes to seek from the position specified by @type
1385 * @type: Position in file to seek from (see g_io_channel_seek_position
1386 * for more details, only #G_SEEK_CUR and #G_SEEK_SET allowed).
1387 * @err: A location to return error info or %NULL.
1388 *
1389 * Seek in a file handle. An end of file condition is treated as an error, use
1390 * ipatch_file_seek_eof() if this is undesirable.
1391 *
1392 * Returns: %TRUE on success, %FALSE otherwise
1393 */
1394 gboolean
ipatch_file_seek(IpatchFileHandle * handle,int offset,GSeekType type,GError ** err)1395 ipatch_file_seek(IpatchFileHandle *handle, int offset, GSeekType type, GError **err)
1396 {
1397 GIOStatus status;
1398
1399 /* we let ipatch_file_seek_eof do checks for us */
1400 status = ipatch_file_seek_eof(handle, offset, type, err);
1401
1402 if(status == G_IO_STATUS_EOF)
1403 {
1404 if(err && !*err)
1405 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNEXPECTED_EOF,
1406 _("Unexpected end of file"));
1407
1408 return (FALSE);
1409 }
1410
1411 return (status == G_IO_STATUS_NORMAL);
1412 }
1413
1414 /**
1415 * ipatch_file_seek_eof:
1416 * @handle: File handle
1417 * @offset: Offset in bytes to seek from the position specified by @type
1418 * @type: Position in file to seek from (see g_io_channel_seek_position
1419 * for more details, only G_SEEK_CUR and G_SEEK_SET allowed).
1420 * @err: A location to return error info or %NULL.
1421 *
1422 * Seek in a file object. Does not treat end of file as an error, use
1423 * ipatch_file_seek() for convenience if this is desirable.
1424 *
1425 * Returns: The status of the operation
1426 */
1427 GIOStatus
ipatch_file_seek_eof(IpatchFileHandle * handle,int offset,GSeekType type,GError ** err)1428 ipatch_file_seek_eof(IpatchFileHandle *handle, int offset, GSeekType type,
1429 GError **err)
1430 {
1431 GIOStatus status;
1432
1433 g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR);
1434 g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR);
1435 g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR);
1436 g_return_val_if_fail(handle->file->iofuncs->seek != NULL, G_IO_STATUS_ERROR);
1437 g_return_val_if_fail(type == G_SEEK_CUR || type == G_SEEK_SET,
1438 G_IO_STATUS_ERROR);
1439 g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR);
1440
1441 status = handle->file->iofuncs->seek(handle, offset, type, err);
1442
1443 if(status == G_IO_STATUS_NORMAL)
1444 {
1445 if(type == G_SEEK_SET)
1446 {
1447 handle->position = offset;
1448 }
1449 else
1450 {
1451 handle->position += offset;
1452 }
1453 }
1454
1455 return (status);
1456 }
1457
1458 /**
1459 * ipatch_file_get_size:
1460 * @file: File object to get size of
1461 * @err: Location to store error information
1462 *
1463 * Get the size of a file object.
1464 *
1465 * Returns: File size or -1 on error or if operation unsupported by this file
1466 * object (in which case @err may be set)
1467 */
1468 int
ipatch_file_get_size(IpatchFile * file,GError ** err)1469 ipatch_file_get_size(IpatchFile *file, GError **err)
1470 {
1471 int size = -1;
1472
1473 g_return_val_if_fail(IPATCH_IS_FILE(file), -1);
1474 g_return_val_if_fail(file->iofuncs != NULL, -1);
1475 g_return_val_if_fail(!err || !*err, -1);
1476
1477 if(file->iofuncs->get_size) /* has get_size IO function? */
1478 {
1479 size = file->iofuncs->get_size(file, err);
1480 }
1481
1482 return (size);
1483 }
1484
1485 /**
1486 * ipatch_file_identify:
1487 * @file: File object to identify type of
1488 * @err: Location to store error information
1489 *
1490 * Attempts to identify the type of a file using the "identify" method of
1491 * registered types derived from #IpatchFile. The #IpatchFile:file-name property
1492 * should already be assigned.
1493 *
1494 * Returns: The first #IpatchFile derived type that had an "identify" method
1495 * which returned %TRUE, or 0 if unknown file type or error, in which
1496 * case error information will be stored in @err provided its not %NULL.
1497 */
1498 GType
ipatch_file_identify(IpatchFile * file,GError ** err)1499 ipatch_file_identify(IpatchFile *file, GError **err)
1500 {
1501 return (ipatch_file_real_identify(file, FALSE, err));
1502 }
1503
1504 /**
1505 * ipatch_file_identify_name:
1506 * @filename: Name of file to identify type of
1507 * @err: Location to store error information
1508 *
1509 * Like ipatch_file_identify() but uses a file name for convenience.
1510 *
1511 * Returns: The first #IpatchFile derived type that had an "identify" method
1512 * which returned %TRUE, or 0 if unknown file type or error, in which
1513 * case error information will be stored in @err provided its not %NULL.
1514 *
1515 * Since: 1.1.0
1516 */
1517 GType
ipatch_file_identify_name(const char * filename,GError ** err)1518 ipatch_file_identify_name(const char *filename, GError **err)
1519 {
1520 IpatchFile *file;
1521 GType type;
1522
1523 g_return_val_if_fail(filename != NULL, 0);
1524
1525 file = ipatch_file_new(); // ++ ref file
1526 ipatch_file_set_name(file, filename);
1527 type = ipatch_file_identify(file, err);
1528 g_object_unref(file); // -- unref file
1529
1530 return (type);
1531 }
1532
1533 /**
1534 * ipatch_file_identify_by_ext:
1535 * @file: File object to identify type of
1536 *
1537 * Attempts to identify the type of a file using the "identify" method of
1538 * registered types derived from #IpatchFile. The #IpatchFile:file-name property
1539 * should already be assigned. Like ipatch_file_identify() but identifies a
1540 * file by its file name extension only.
1541 *
1542 * Returns: The first #IpatchFile derived type that had an "identify" method
1543 * which returned %TRUE, or 0 if unknown file type or error, in which
1544 * case error information will be stored in @err provided its not %NULL.
1545 */
1546 GType
ipatch_file_identify_by_ext(IpatchFile * file)1547 ipatch_file_identify_by_ext(IpatchFile *file)
1548 {
1549 return (ipatch_file_real_identify(file, TRUE, NULL));
1550 }
1551
1552 static GType
ipatch_file_real_identify(IpatchFile * file,gboolean byext,GError ** err)1553 ipatch_file_real_identify(IpatchFile *file, gboolean byext, GError **err)
1554 {
1555 IpatchFileHandle *handle = NULL;
1556 IpatchFileClass *file_class;
1557 GType *children, *p;
1558 GType found = 0;
1559 GError *local_err = NULL;
1560
1561 g_return_val_if_fail(IPATCH_IS_FILE(file), 0);
1562 g_return_val_if_fail(file->file_name != NULL, 0);
1563
1564 if(!byext)
1565 {
1566 /* Open a handle to the file */
1567 handle = ipatch_file_open(file, NULL, "r", err);
1568
1569 if(!handle)
1570 {
1571 return (0);
1572 }
1573 }
1574
1575 children = type_all_children(IPATCH_TYPE_FILE, NULL);
1576
1577 for(p = children; p && *p; p++)
1578 {
1579 file_class = g_type_class_ref(*p);
1580
1581 if(!file_class)
1582 {
1583 continue;
1584 }
1585
1586 if(file_class->identify)
1587 {
1588 if(!file_class->identify(file, handle, &local_err))
1589 {
1590 if(local_err)
1591 {
1592 g_propagate_error(err, local_err);
1593 g_type_class_unref(file_class);
1594
1595 if(handle)
1596 {
1597 ipatch_file_close(handle);
1598 }
1599
1600 return (0);
1601 }
1602 }
1603 else
1604 {
1605 found = *p;
1606 }
1607
1608 if(handle)
1609 {
1610 ipatch_file_seek(handle, 0, G_SEEK_SET, NULL);
1611 }
1612 }
1613
1614 g_type_class_unref(file_class);
1615
1616 if(found != 0)
1617 {
1618 break;
1619 }
1620 }
1621
1622 if(handle)
1623 {
1624 ipatch_file_close(handle);
1625 }
1626
1627 return (found);
1628 }
1629
1630 static GType *
type_all_children(GType type,GArray * pass_array)1631 type_all_children(GType type, GArray *pass_array)
1632 {
1633 static GType *types = NULL;
1634 GArray *array = pass_array;
1635 GType *children;
1636 int i;
1637
1638 if(types)
1639 {
1640 return (types);
1641 }
1642
1643 if(!array)
1644 {
1645 array = g_array_new(TRUE, FALSE, sizeof(GType)); /* Zero terminated */
1646 }
1647
1648 children = g_type_children(type, NULL);
1649
1650 if(children)
1651 {
1652 for(i = 0; children[i]; i++)
1653 {
1654 type_all_children(children[i], array);
1655 g_array_append_val(array, children[i]);
1656 }
1657
1658 g_free(children);
1659 }
1660
1661 if(!pass_array) /* last iteration? */
1662 {
1663 types = (array->len > 0) ? (GType *)(array->data) : NULL;
1664 g_array_sort(array, sort_type_by_identify_order);
1665 g_array_free(array, FALSE);
1666 return (types);
1667 }
1668 else
1669 {
1670 return (NULL);
1671 }
1672 }
1673
1674 /* Sort IpatchFile types by their identify_order class field (largest value first order) */
1675 static gint
sort_type_by_identify_order(gconstpointer a,gconstpointer b)1676 sort_type_by_identify_order(gconstpointer a, gconstpointer b)
1677 {
1678 const GType *typea = a, *typeb = b;
1679 IpatchFileClass *klassa, *klassb;
1680 gint retval;
1681
1682 klassa = g_type_class_ref(*typea); /* ++ ref class */
1683 klassb = g_type_class_ref(*typeb); /* ++ ref class */
1684
1685 retval = klassb->identify_order - klassa->identify_order;
1686
1687 g_type_class_unref(klassa); /* -- unref class */
1688 g_type_class_unref(klassb); /* -- unref class */
1689
1690 return (retval);
1691 }
1692
1693 /**
1694 * ipatch_file_identify_open:
1695 * @file_name: File name to identify and open
1696 * @err: Location to store error of type GIOChannelError
1697 *
1698 * A convenience function which calls ipatch_file_identify() to determine the
1699 * file type of @file_name. If the type is identified a new file object, of the
1700 * identified type, is created and the file is opened with
1701 * ipatch_file_open() in read mode.
1702 *
1703 * Returns: The new opened handle of the identified type or %NULL if unable to
1704 * identify. Caller should free the handle with ipatch_file_close() when done
1705 * using it, at which point the parent #IpatchFile will be destroyed if no other
1706 * reference is held.
1707 */
1708 IpatchFileHandle *
ipatch_file_identify_open(const char * file_name,GError ** err)1709 ipatch_file_identify_open(const char *file_name, GError **err)
1710 {
1711 IpatchFileHandle *handle;
1712 IpatchFile *file;
1713 GType file_type;
1714
1715 g_return_val_if_fail(file_name != NULL, NULL);
1716 g_return_val_if_fail(!err || !*err, NULL);
1717
1718 file = ipatch_file_new(); /* ++ ref new file */
1719 ipatch_file_set_name(file, file_name);
1720 file_type = ipatch_file_identify(file, err);
1721 g_object_unref(file); /* -- unref file */
1722
1723 if(file_type == 0)
1724 {
1725 return (NULL);
1726 }
1727
1728 file = g_object_new(file_type, NULL); /* ++ ref file */
1729 handle = ipatch_file_open(file, file_name, "r", err); /* ++ alloc handle */
1730 g_object_unref(file); /* -- unref file (handle holds a ref) */
1731
1732 return (handle); /* !! caller takes over handle */
1733 }
1734
1735 /**
1736 * ipatch_file_identify_new:
1737 * @file_name: File name to identify and create file object for
1738 * @err: Location to store error of type GIOChannelError
1739 *
1740 * A convenience function which calls ipatch_file_identify() to determine the
1741 * file type of @file_name. If the type is identified a new file object, of the
1742 * identified type, is created and returned.
1743 *
1744 * Returns: The new file of the identified type or %NULL if unable to
1745 * identify. Caller owns a reference and should remove it when done using it.
1746 *
1747 * Since: 1.1.0
1748 */
1749 IpatchFile *
ipatch_file_identify_new(const char * file_name,GError ** err)1750 ipatch_file_identify_new(const char *file_name, GError **err)
1751 {
1752 IpatchFileHandle *handle;
1753 IpatchFile *file;
1754
1755 handle = ipatch_file_identify_open(file_name, err);
1756
1757 if(!handle)
1758 {
1759 return (NULL);
1760 }
1761
1762 file = handle->file;
1763 g_object_ref(file); // ++ ref for caller
1764 ipatch_file_close(handle);
1765
1766 return (file); // !! caller takes over reference
1767 }
1768
1769 /**
1770 * ipatch_file_set_little_endian:
1771 * @file: File object
1772 *
1773 * Sets the file object to little endian mode (the default mode).
1774 * If the system is big endian, byte swapping will be enabled
1775 * (see IPATCH_FILE_SWAPxx macros). The endian mode affects buffered
1776 * read and write functions that operate on multi-byte integers.
1777 */
1778 void
ipatch_file_set_little_endian(IpatchFile * file)1779 ipatch_file_set_little_endian(IpatchFile *file)
1780 {
1781 g_return_if_fail(IPATCH_IS_FILE(file));
1782
1783 IPATCH_ITEM_WLOCK(file);
1784
1785 ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_BIG_ENDIAN);
1786
1787 if(G_BYTE_ORDER != G_LITTLE_ENDIAN)
1788 {
1789 ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP);
1790 }
1791
1792 IPATCH_ITEM_WUNLOCK(file);
1793 }
1794
1795 /**
1796 * ipatch_file_set_big_endian:
1797 * @file: File object
1798 *
1799 * Sets the file object to big endian mode (the default is little endian).
1800 * If the system is little endian, byte swapping will be enabled
1801 * (see IPATCH_FILE_SWAPxx macros). The endian mode affects buffered
1802 * read and write functions that operate on multi-byte integers.
1803 */
1804 void
ipatch_file_set_big_endian(IpatchFile * file)1805 ipatch_file_set_big_endian(IpatchFile *file)
1806 {
1807 g_return_if_fail(IPATCH_IS_FILE(file));
1808
1809 IPATCH_ITEM_WLOCK(file);
1810
1811 ipatch_item_set_flags(file, IPATCH_FILE_FLAG_BIG_ENDIAN);
1812
1813 if(G_BYTE_ORDER != G_BIG_ENDIAN)
1814 {
1815 ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP);
1816 }
1817
1818 IPATCH_ITEM_WUNLOCK(file);
1819 }
1820
1821 /**
1822 * ipatch_file_set_iofuncs_static:
1823 * @file: File object
1824 * @funcs: Static IO functions structure or %NULL to set to defaults
1825 *
1826 * Sets the input/output functions of a file object using a statically
1827 * allocated (guaranteed to exist for lifetime of @file) functions structure.
1828 * Setting these functions allows one to write custom data sources or hook
1829 * into other file functions.
1830 */
1831 void
ipatch_file_set_iofuncs_static(IpatchFile * file,IpatchFileIOFuncs * funcs)1832 ipatch_file_set_iofuncs_static(IpatchFile *file, IpatchFileIOFuncs *funcs)
1833 {
1834 g_return_if_fail(IPATCH_IS_FILE(file));
1835
1836 IPATCH_ITEM_WLOCK(file);
1837
1838 if(IPATCH_FILE_FREE_IOFUNCS(file))
1839 {
1840 g_slice_free(IpatchFileIOFuncs, file->iofuncs);
1841 }
1842
1843 file->iofuncs = funcs ? funcs : &default_iofuncs;
1844 ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS);
1845
1846 IPATCH_ITEM_WUNLOCK(file);
1847 }
1848
1849 /**
1850 * ipatch_file_set_iofuncs:
1851 * @file: File object
1852 * @funcs: IO functions structure or %NULL to set to defaults
1853 *
1854 * Sets the input/output functions of a file object. The @funcs
1855 * structure is duplicated so as not to use the original, see
1856 * ipatch_file_set_iofuncs_static() for using a static structure.
1857 * Setting these functions allows one to write custom data sources or
1858 * hook into other file functions.
1859 */
1860 void
ipatch_file_set_iofuncs(IpatchFile * file,const IpatchFileIOFuncs * funcs)1861 ipatch_file_set_iofuncs(IpatchFile *file, const IpatchFileIOFuncs *funcs)
1862 {
1863 IpatchFileIOFuncs *dupfuncs = NULL;
1864
1865 g_return_if_fail(IPATCH_IS_FILE(file));
1866
1867 if(funcs) /* duplicate functions structure */
1868 {
1869 dupfuncs = g_slice_new(IpatchFileIOFuncs);
1870 *dupfuncs = *funcs;
1871 }
1872
1873 IPATCH_ITEM_WLOCK(file);
1874
1875 if(IPATCH_FILE_FREE_IOFUNCS(file))
1876 {
1877 g_slice_free(IpatchFileIOFuncs, file->iofuncs);
1878 }
1879
1880 file->iofuncs = dupfuncs ? dupfuncs : &default_iofuncs;
1881
1882 if(dupfuncs)
1883 {
1884 ipatch_item_set_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS);
1885 }
1886 else
1887 {
1888 ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS);
1889 }
1890
1891 IPATCH_ITEM_WUNLOCK(file);
1892 }
1893
1894 /**
1895 * ipatch_file_get_iofuncs:
1896 * @file: File object
1897 * @out_funcs: Location to store current IO functions to
1898 *
1899 * Get the current IO functions from a file object. The function pointers
1900 * are stored in a user supplied structure pointed to by @out_funcs.
1901 */
1902 void
ipatch_file_get_iofuncs(IpatchFile * file,IpatchFileIOFuncs * out_funcs)1903 ipatch_file_get_iofuncs(IpatchFile *file, IpatchFileIOFuncs *out_funcs)
1904 {
1905 g_return_if_fail(IPATCH_IS_FILE(file));
1906 g_return_if_fail(out_funcs != NULL);
1907
1908 IPATCH_ITEM_RLOCK(file);
1909 *out_funcs = *file->iofuncs;
1910 IPATCH_ITEM_RUNLOCK(file);
1911 }
1912
1913 /**
1914 * ipatch_file_set_iofuncs_null:
1915 * @file: File object
1916 *
1917 * Sets the I/O functions of a file object to /dev/null like methods.
1918 * Reading from the file will return 0s, writing/seeking will do nothing.
1919 */
1920 void
ipatch_file_set_iofuncs_null(IpatchFile * file)1921 ipatch_file_set_iofuncs_null(IpatchFile *file)
1922 {
1923 g_return_if_fail(IPATCH_IS_FILE(file));
1924 ipatch_file_set_iofuncs_static(file, &null_iofuncs);
1925 }
1926
1927
1928 /**
1929 * ipatch_file_default_open_method: (skip)
1930 * @handle: File handle
1931 * @mode: File open mode
1932 * @err: Error info
1933 *
1934 * Default "open" method for #IpatchFileIOFuncs. Useful when overriding only
1935 * some I/O functions.
1936 *
1937 * Returns: %TRUE on success, %FALSE otherwise
1938 */
1939 gboolean
ipatch_file_default_open_method(IpatchFileHandle * handle,const char * mode,GError ** err)1940 ipatch_file_default_open_method(IpatchFileHandle *handle, const char *mode,
1941 GError **err)
1942 {
1943 if(handle->iochan) /* io channel has been explicitly set? */
1944 {
1945 g_io_channel_set_encoding(handle->iochan, NULL, NULL);
1946 return (TRUE);
1947 }
1948
1949 g_return_val_if_fail(mode != NULL, FALSE);
1950 g_return_val_if_fail(handle->file->file_name != NULL, FALSE);
1951
1952 handle->iochan = g_io_channel_new_file(handle->file->file_name, mode, err);
1953
1954 if(handle->iochan)
1955 {
1956 g_io_channel_set_encoding(handle->iochan, NULL, NULL);
1957 return (TRUE);
1958 }
1959 else
1960 {
1961 return (FALSE);
1962 }
1963 }
1964
1965 /**
1966 * ipatch_file_default_close_method: (skip)
1967 * @handle: File handle
1968 *
1969 * Default "close" method for #IpatchFileIOFuncs. Useful when overriding only
1970 * some I/O functions.
1971 */
1972 void
ipatch_file_default_close_method(IpatchFileHandle * handle)1973 ipatch_file_default_close_method(IpatchFileHandle *handle)
1974 {
1975 g_return_if_fail(handle->iochan != NULL);
1976
1977 g_io_channel_shutdown(handle->iochan, TRUE, NULL);
1978 g_io_channel_unref(handle->iochan);
1979
1980 handle->iochan = NULL;
1981 }
1982
1983 /**
1984 * ipatch_file_default_read_method: (skip)
1985 * @handle: File handle
1986 * @buf: Buffer to store data to
1987 * @size: Size of data
1988 * @bytes_read: Number of bytes actually read
1989 * @err: Error info
1990 *
1991 * Default "read" method for #IpatchFileIOFuncs. Useful when overriding only
1992 * some I/O functions.
1993 *
1994 * Returns: The status of the operation.
1995 */
1996 GIOStatus
ipatch_file_default_read_method(IpatchFileHandle * handle,gpointer buf,guint size,guint * bytes_read,GError ** err)1997 ipatch_file_default_read_method(IpatchFileHandle *handle, gpointer buf,
1998 guint size, guint *bytes_read, GError **err)
1999 {
2000 gsize _bytes_read = 0;
2001 GIOStatus status;
2002
2003 g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR);
2004
2005 status = g_io_channel_read_chars(handle->iochan, buf, size,
2006 &_bytes_read, err);
2007 *bytes_read = _bytes_read;
2008
2009 return (status);
2010 }
2011
2012 /**
2013 * ipatch_file_default_write_method: (skip)
2014 * @handle: File handle
2015 * @buf: Buffer to read data from
2016 * @size: Size of data
2017 * @err: Error info
2018 *
2019 * Default "write" method for #IpatchFileIOFuncs. Useful when overriding only
2020 * some I/O functions.
2021 *
2022 * Returns: The status of the operation.
2023 */
2024 GIOStatus
ipatch_file_default_write_method(IpatchFileHandle * handle,gconstpointer buf,guint size,GError ** err)2025 ipatch_file_default_write_method(IpatchFileHandle *handle, gconstpointer buf,
2026 guint size, GError **err)
2027 {
2028 GIOStatus status;
2029
2030 g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR);
2031
2032 status = g_io_channel_write_chars(handle->iochan, buf, size, NULL, err);
2033
2034 return (status);
2035 }
2036
2037 /**
2038 * ipatch_file_default_seek_method: (skip)
2039 * @handle: File handle
2040 * @offset: Offset (depends on seek @type)
2041 * @type: Seek type
2042 * @err: Error info
2043 *
2044 * Default "seek" method for #IpatchFileIOFuncs. Useful when overriding only
2045 * some I/O functions.
2046 *
2047 * Returns: The status of the operation.
2048 */
2049 GIOStatus
ipatch_file_default_seek_method(IpatchFileHandle * handle,int offset,GSeekType type,GError ** err)2050 ipatch_file_default_seek_method(IpatchFileHandle *handle, int offset,
2051 GSeekType type, GError **err)
2052 {
2053 g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR);
2054
2055 return (g_io_channel_seek_position(handle->iochan, offset, type, err));
2056 }
2057
2058 /**
2059 * ipatch_file_default_getfd_method: (skip)
2060 * @handle: File handle
2061 *
2062 * Default "getfd" method for #IpatchFileIOFuncs. Useful when overriding only
2063 * some I/O functions. This method gets a unix file descriptor for the given
2064 * file object, it is an optional method.
2065 *
2066 * Returns: Unix file descriptor or -1 if no file descriptor or error.
2067 */
2068 int
ipatch_file_default_getfd_method(IpatchFileHandle * handle)2069 ipatch_file_default_getfd_method(IpatchFileHandle *handle)
2070 {
2071 return (handle->iochan ? g_io_channel_unix_get_fd(handle->iochan) : -1);
2072 }
2073
2074 /**
2075 * ipatch_file_default_get_size_method: (skip)
2076 * @file: File object
2077 * @err: Error info
2078 *
2079 * Default get file size method, which is optional.
2080 *
2081 * Returns: File size or -1 on error (in which case @err may be set).
2082 */
2083 int
ipatch_file_default_get_size_method(IpatchFile * file,GError ** err)2084 ipatch_file_default_get_size_method(IpatchFile *file, GError **err)
2085 {
2086 GStatBuf info;
2087
2088 if(file->file_name)
2089 {
2090 if(g_stat(file->file_name, &info) != 0)
2091 {
2092 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO,
2093 _("Error during call to stat(\"%s\"): %s"),
2094 file->file_name, g_strerror(errno));
2095 return (-1);
2096 }
2097
2098 return (info.st_size);
2099 }
2100 else
2101 {
2102 return (-1);
2103 }
2104 }
2105
2106 /* ---------------------------------------------------------------
2107 * NULL file iofunc methods (like /dev/null)
2108 * --------------------------------------------------------------- */
2109
2110 static gboolean
ipatch_file_null_open_method(IpatchFileHandle * handle,const char * mode,GError ** err)2111 ipatch_file_null_open_method(IpatchFileHandle *handle, const char *mode,
2112 GError **err)
2113 {
2114 return (TRUE);
2115 }
2116
2117 static GIOStatus
ipatch_file_null_read_method(IpatchFileHandle * handle,gpointer buf,guint size,guint * bytes_read,GError ** err)2118 ipatch_file_null_read_method(IpatchFileHandle *handle, gpointer buf, guint size,
2119 guint *bytes_read, GError **err)
2120 {
2121 memset(buf, 0, size);
2122 *bytes_read = size;
2123 return (G_IO_STATUS_NORMAL);
2124 }
2125
2126 static GIOStatus
ipatch_file_null_write_method(IpatchFileHandle * handle,gconstpointer buf,guint size,GError ** err)2127 ipatch_file_null_write_method(IpatchFileHandle *handle, gconstpointer buf,
2128 guint size, GError **err)
2129 {
2130 return (G_IO_STATUS_NORMAL);
2131 }
2132
2133 static GIOStatus
ipatch_file_null_seek_method(IpatchFileHandle * handle,int offset,GSeekType type,GError ** err)2134 ipatch_file_null_seek_method(IpatchFileHandle *handle, int offset, GSeekType type,
2135 GError **err)
2136 {
2137 return (G_IO_STATUS_NORMAL);
2138 }
2139