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