summaryrefslogtreecommitdiff
path: root/Build/source/libs/zziplib/zziplib-src/zzip/write.c
blob: cc09c5b9e81ed8a1698fd5f818fd1e060bfdc193 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488

/*
 * The write-support in zziplib is not a full-flegded interface to the
 * internals that zip file-header or zip archive an contain. It's
 * primary use goes for savegames or transfer `pack-n-go` archives
 * where time-stamps are rather unimportant. Here we can create an 
 * archive with filenames and their data portions, possibly obfuscated.
 *
 * DONT USE THIS
 *
 * The write support is supposed to be added directly into the main
 * zziplib but it has not been implemented so far. It does however
 * export the relevant call entries which will return EROFS (read-only
 * filesystem) in case they are being called. That allows later programs
 * to start up with earlier versions of zziplib that can only read ZIPs.
 *
 * Author: 
 *      Guido Draheim <guidod@gmx.de>
 *
 * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL)
 */

#define _ZZIP_WRITE_SOURCE

#if defined DDDD || defined DDDDD || defined DDDDDD || defined DDDDDDD
#define _ZZIP_ENABLE_WRITE
#else /* per default, we add support for passthrough to posix write */
#define _ZZIP_POSIX_WRITE
#endif

#include <zzip/write.h>         /* #includes <zzip/lib.h> */
#include <zzip/file.h>

#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>

#ifdef ZZIP_HAVE_DIRECT_H
#include <direct.h>
#endif

#include <zzip/format.h>
#include <zzip/plugin.h>
#include <zzip/__debug.h>

#define ___ {
#define ____ }

#ifndef EROFS
# ifdef ENOSYS
#define EROFS ENOSYS
# else
#define EROFS EPERM
#endif
#endif

/* try real zlib routines for writing ? very experimental, very very ex... */
#ifndef _ZZIP_ENABLE_WRITE
#define _ZZIP_TRY 0
#else
#define _ZZIP_TRY 1
#endif

/* btw, is there any system that did define those different ? get away.. */
#  ifndef S_IWGRP
#  define S_IWGRP 00020
#  endif
#  ifndef S_IRWXO
#  define S_IRWXO 00007
#  endif

#  ifdef ZZIP_HAVE_DIRECT_H
#  define _mkdir(a,b) mkdir(a)
#  else
#  define _mkdir      mkdir
#  endif

/** create a new zip archive for writing
 *
 * This function will create a new zip archive. The returned parameter 
 * is a new "zzip dir" handle that should be saved to a variable so it
 * can be used a base argument for => zzip_mkdir and => zzip_creat calls.
 * The returned handle represents a zip central directory that must be
 * saved to disk using => zzip_closedir.
 *
 * Returns null on error and sets errno. Remember, according to posix
 * the => creat(2) call is equivalent to 
   open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode)
 * so any previous zip-archive will be overwritten unconditionally and
 * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the
 * given subtree? like suggested by O_TRUNC? not done so far!)
 */
ZZIP_DIR *
zzip_dir_creat(zzip_char_t * name, int o_mode)
{
    return zzip_dir_creat_ext_io(name, o_mode, 0, 0);
}

/** => zzip_dir_creat
 *
 * If the third argument "ext" has another special meaning here, as it
 * is used to ensure that a given zip-file is created with the first entry 
 * of the ext-list appended as an extension unless the file-path already 
 * ends with a file-extension registered in the list. Therefore {"",0} 
 * matches all files and creates them as zip-archives under the given 
 * nonmodified name. (Some magic here? If the path ends in the path
 * separator then make a real directory even in the presence of ext-list?)
 *
 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
 * Write-support will extend => zzip_closedir with semantics to finalize the
 * zip-archive by writing the zip-trailer and closing the archive file.
 */
ZZIP_DIR *
zzip_dir_creat_ext_io(zzip_char_t * name, int o_mode,
                      zzip_strings_t * ext, zzip_plugin_io_t io)
{
    if (! io)
        io = zzip_get_default_io();

    if (io != zzip_get_default_io())
    {
        /* the current io-structure does not contain a "write" entry,
         * and therefore this parameter is useless. Anyone to expect
         * some behavior should be warned, so here we let the function
         * fail bluntly - and leaving the recovery to the application
         */
        errno = EINVAL;
        return 0;
    }


    if (! _ZZIP_TRY)
    {
        /* not implemented - however, we respect that a null argument to 
         * zzip_mkdir and zzip_creat works, so we silently still do the mkdir 
         */
        if (! _mkdir(name, o_mode) || errno == EEXIST)
            errno = EROFS;
        return 0;
    } else
    {
#       define MAX_EXT_LEN 10
        ZZIP_DIR *dir = zzip_dir_alloc(ext);
        int name_len = strlen(name);
        dir->realname = malloc(name_len + MAX_EXT_LEN);
        if (! dir->realname)
            goto error;

        memcpy(dir->realname, name, name_len + 1);
        ___ int fd =
            __zzip_try_open(dir->realname, O_EXCL | O_TRUNC | O_WRONLY, ext,
                            io);
        if (fd != -1)
            { dir->fd = fd; return dir; }

        ___ zzip_strings_t *exx = ext;
        int exx_len;
        for (; *exx; exx++)
        {
            if ((exx_len = strlen(*exx) + 1) <= name_len &&
                ! memcmp(dir->realname + (name_len - exx_len), *exx, exx_len))
                break;          /* keep unmodified */
            exx++;
            if (*exx)
                continue;

            if (! (exx_len = strlen(*exx)) || exx_len >= MAX_EXT_LEN)
                break;
            memcpy(dir->realname + name_len, exx, exx_len);     /* append! */
        }
        ____;
        fd = (io->fd.open)(dir->realname, O_CREAT | O_TRUNC | O_WRONLY, o_mode);
        dir->realname[name_len] = '\0'; /* keep ummodified */
        if (fd != -1)
            { dir->fd = fd; return dir; }
      error:
        zzip_dir_free(dir);
        return 0;
        ____;
    }
}

/** create a new archive area for writing
 *
 * This function will create a new archive area. This may either be a
 * a new zip archive or a new directory in the filesystem. The returned 
 * parameter is a new "zzip dir" handle that should be saved to a variable 
 * so it can be used a base argument for => zzip_file_mkdir and 
 * => zzip_file_creat calls.  The returned handle wraps both possibilities,
 * it can be representing a zip central directory that must be
 * saved to disk using => zzip_closedir or it is just a handle for the
 * name of the real directory that still must be run through 
 * => zzip_closedir to release the wrapper around the directory name.
 *
 * The magic is pushed through the o_mode argument. Using a mode that
 * has no group-write bit set (S_IWGRP = 0040) then the file is
 * created as a zip directory. Note that this is unabridged of any
 * umask value in the system where the argument to this function could
 * be 0775 but with an umask of 0755 it turns out as 0755 for a real
 * directory. Using 0755 directly would not create it as a real directory
 * but as a zip archive handle.
 *
 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
 * Write-support will extend => zzip_closedir with semantics to finalize the
 * zip-archive by writing the zip-trailer and closing the archive file.
 *
 * Returns null on error and sets errno. Remember, according to posix
 * the => creat(2) call is equivalent to 
   open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode)
 * so any previous zip-archive will be overwritten unconditionally and
 * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the
 * given subtree? like suggested by O_TRUNC? not done so far!)
 */
ZZIP_DIR *
zzip_createdir(zzip_char_t * name, int o_mode)
{
    if (o_mode & S_IWGRP)
    {
        if (-1 == _mkdir(name, o_mode) && errno != EEXIST)      /* fail */
            return 0;
        return zzip_opendir(name);
    } else
        return zzip_dir_creat(name, o_mode);
}

/** => zzip_file_creat              also: mkdir(2), creat(2), zzip_dir_creat
 *
 * This function has an additional primary argument over the posix
 * mkdir(2) - if it is null then this function behaves just like
 * posix mkdir(2). The zzip_dir argument can be set to the result
 * of a => zzip_createdir which allows for some magic that the
 * given directory name is created as an entry in the zip archive.
 *
 * If the given dir name argument is not within the basepath of 
 * the zip central directory then a real directory is created.
 * Any EEXIST errors are not suppressed unlike with => zzip_createdir
 *
 * Standard usage accepts a global/threaded/modular ZZIP_DIR pointer
 * for all zip archive operations like in:
   ZZIP_DIR* zip = zzip_createdir (sysconfpath, 0755, zip);
   zzip_file_mkdir (zip, filepath[i], 0755);
   ZZIP_FILE* file = zzip_file_creat (zip, filename[i], 0644);
   zzip_write (file, buf, len);
   zzip_close (file); file = 0;
   zzip_closedir (zip); zip = 0;
 *
 * compare with => zzip_mkdir inline macro which allows to
 * collapse the examples script to
   #define zzip_savefile myproject_saveconfig
   #include <zzip/zzip.h>
   ZZIP_DIR* zzip_savefile = zzip_createdir (sysconfpath, 0755);
   zzip_mkdir (filepath[i], 0755);
   ZZIP_FILE* file = zzip_creat(filepath[i], 0644);
   zzip_write (file, buf, len);
   zzip_close (file); file = 0;
   zzip_closedir (zip_savefile);
 */
int
zzip_file_mkdir(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
{
    if (! dir)
        return _mkdir(name, o_mode);

    if (! _ZZIP_TRY)
    {                           /* not implemented */
        errno = EROFS;
        return -1;
    } else
    {
        errno = EROFS;
        return -1;
    }
}

/** start next file entry in a zip archive
 *
 * This function will create a new file within a zzip archive, the
 * one given as the primary argument and additionally to the posix
 * creat(2) - just like zzip_mkdir has an additional argument over
 * the posix mkdir(2) spec. For this function the primary parameter
 * can be null as well thereby creating a real file instead of a new
 * one inside the zip-archive otherwise given. If the primary parameter is
 * not null but wraps a real directory then all new files are also real.
 *
 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
 *
 * Returns NULL on an error setting errno, and opening a file _within_ 
 * a zip archive using O_RDONLY (and similar stuff) will surely lead to 
 * an error.
 */
ZZIP_FILE *
zzip_file_creat(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
{
    if (! dir)
        return zzip_open(name, o_mode);

    if (! _ZZIP_TRY)
    {                           /* not implemented */
        errno = EROFS;
        return 0;
    } else
    {
        errno = EROFS;
        return 0;
    }
}

/** write to zzip storage                     also: write(2), zlib(3)
 *
 * This function will write data to a file descriptor. If the file
 * descriptor represents a real file then it will be forwarded to
 * call posix => write(2) directly. If it is a descriptor for a
 * file within a zip directory then the data will be "deflated"
 * using => zlib(3) and appended to the zip archive file.
 */
zzip_ssize_t
zzip_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len)
{
    if (zzip_file_real(file))
        return write(zzip_realfd(file), ptr, len);
    else
        return zzip_file_write(file, ptr, len);
}

/** => zzip_write                            also: zzip_file_creat
 *
 * This function will write data to a file descriptor inside a zip
 * archive. The data will be "deflated" using => zlib(3) compression
 * and appended to the end of the zip archive file. Only one file
 * descriptor may be open per zzip_dir archive handle (fifo-like).
 *
 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
 * It returns immediately -1 and sets errno=EROFS for indication.
 */
zzip_ssize_t
zzip_file_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len)
{
    if (! _ZZIP_TRY)
    {                           /* not implemented */
        errno = EROFS;
        return -1;
    } else
    {
        /* add calls to zlib here... */
        errno = EROFS;
        return -1;
    }
}

/** => zzip_write
 * This function is the stdc variant for writing and the arguments
 * are forwarded to => zzip_write - the return value is floored to
 * null as for STDC spec but there is no zzip_ferror call so far
 * for the zziplib (later? is it actually needed?).
 *
 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
 * Write-support extends => zzip_close with semantics to write out a 
 * file-trailer to the zip-archive leaving a name/offset marker in
 * the (still-open) ZZIP_DIR handle.
 */
zzip_size_t
zzip_fwrite(const void *ptr, zzip_size_t len, zzip_size_t multiply,
            ZZIP_FILE * file)
{
    zzip_ssize_t value = zzip_write(file, ptr, len * multiply);
    if (value == -1)
        value = 0;
    return (zzip_size_t) value;
}

#if 0                           /* pure documentation */

/** create a zipped file/directory            also: zzip_dir_creat, mkdir(2)
 *
 * This function creates a directory entry in the default zip-archive. 
 * If you did  not specify a "#define zzip_savefile somevar" 
 * then the default zip-archive is null and all directories are 
 * created as real directories in the filesystem. This function is 
 * really a preprocessor macro or preferably an inline function
 *  around => zzip_file_mkdir, there is no such symbol generated 
 * into the library. The prototype is modelled after the posix 
 * => mkdir(2) call.
 #ifndef zzip_savefile
 #define zzip_savefile 0
 #endif
 #define zzip_mkdir(name,mode) \ -
         zzip_file_mkdir(zzip_savefile,name,mode)
 *
 */
int inline
zzip_mkdir(zzip_char_t * name, int o_mode)
{
    return zzip_file_creat(zzip_savefile, name, mode);
}
#endif

#if 0                           /* pure documentation */

/** => zzip_mkdir                 also: creat(2), zzip_start
 *
 * This function creates a file in the default zip-archive. 
 * If you did not specify a "#define zzip_savefile somevar" 
 * then the default zip-archive is null and all files are created 
 * as real files. This function is really a preprocessor macro 
 * or preferably an inline function around => zzip_file_creat, 
 * there is no such symbol generated into the library. The prototype
 * is modelled after the posix => creat(2) call.
 #ifndef zzip_savefile
 #define zzip_savefile 0
 #endif
 #define zzip_creat(name,mode) \ -
         zzip_file_creat(zzip_savefile,name,mode)
 */
ZZIP_FILE *inline
zzip_creat(zzip_char_t * name, int o_mode)
{
    return zzip_file_creat(zzip_savefile, name, mode);
}
#endif


#if 0                           /* pure documentation */

/** start writing to the magic zzip_savefile   also: zzip_creat, zzip_write
 * 
 * open a zip archive for writing via the magic zzip_savefile macro
 * variable. The name and mode are given to => zzip_createdir and
 * the result is stored into => zzip_savefile - if the => zzip_savefile
 * did already have a zzip_dir handle then it is automatically 
 * finalized with => zzip_sync and the handle closed and the
 * zzip_savefile variable reused for the new zip archive just started
 * with this call. - This function is really a preprocessor macro 
 * or preferably an inline function around => zzip_dir_create, there 
 * is no such symbol generated into the library.
 #ifndef zzip_savefile
 #define zzip_savefile 0
 #endif
 #define zzip_start(name,mode,ext) \ -
       { if (zzip_savefile) zzip_closedir(zzip_savefile); \ -
          zzip_savefile = zzip_createdir(name,mode,ext); }
 * This function returns null on error or a zzip_dir handle on
 * success. It is perfectly okay to continue with a null in the
 * zzip_savefile variable since it makes subsequent calls to
 * => zzip_creat and => zzip_mkdir to run as => creat(2) / => mkdir(2) 
 * on the real filesystem.
 */
void inline
zzip_mkfifo(zzip_char_t * name, int o_mode)
{
    if (zzip_savefile)
        zzip_closedir(zzip_savefile);
    zzip_savefile = zzip_createdir(name, o_mode);
}
#endif

#if 0                           /* pure documentation */

/** => zzip_mkfifo                        also: zzip_closedir, sync(2)
 * 
 * finalize a zip archive thereby writing the central directory to
 * the end of the file. If it was a real directory then we do just
 * nothing - even that the prototype of the call itself is modelled 
 * to be similar to the posix => sync(2) call. This function is 
 * really a preprocessor macro or preferably an inline function
 * around => zzip_closedir, there is no such symbol generated 
 * into the library.
 #ifndef zzip_savefile
 #define zzip_savefile 0
 #endif
 #define zzip_sync(name,mode) \ -
       { zzip_closedir(zzip_savefile); zzip_savefile = 0; }
 *
 */
void inline
zzip_sync(void)
{
    zzip_closedir(zzip_savefile);
    zzip_savefile = 0;
}
#endif

/* 
 * Local variables:
 * c-file-style: "stroustrup"
 * End:
 */