diff options
Diffstat (limited to 'xdelta1/xdmain.c')
-rwxr-xr-x | xdelta1/xdmain.c | 1941 |
1 files changed, 1941 insertions, 0 deletions
diff --git a/xdelta1/xdmain.c b/xdelta1/xdmain.c new file mode 100755 index 0000000..f0a61c8 --- /dev/null +++ b/xdelta1/xdmain.c | |||
@@ -0,0 +1,1941 @@ | |||
1 | /* -*- Mode: C;-*- | ||
2 | * | ||
3 | * This file is part of XDelta - A binary delta generator. | ||
4 | * | ||
5 | * Copyright (C) 1997, 1998, 1999, 2001 Josh MacDonald | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | * | ||
21 | * Author: Josh MacDonald <jmacd@CS.Berkeley.EDU> | ||
22 | * | ||
23 | * $Id: xdmain.c 1.22.1.5 Fri, 29 Jun 2001 06:01:08 -0700 jmacd $ | ||
24 | */ | ||
25 | |||
26 | #include <stdio.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <string.h> | ||
29 | #include <errno.h> | ||
30 | #include <ctype.h> | ||
31 | |||
32 | #include <fcntl.h> | ||
33 | #include <sys/stat.h> | ||
34 | |||
35 | #if defined(_WIN32) || defined(__DJGPP__) | ||
36 | #define WINHACK | ||
37 | #endif | ||
38 | |||
39 | #ifndef WINHACK | ||
40 | #include <unistd.h> | ||
41 | #include <sys/mman.h> | ||
42 | #define O_BINARY 0 | ||
43 | |||
44 | #else /* WINHACK */ | ||
45 | |||
46 | #include <io.h> | ||
47 | #include <process.h> | ||
48 | #ifdef __DJGPP__ | ||
49 | #include <unistd.h> | ||
50 | #include <dpmi.h> | ||
51 | #endif | ||
52 | |||
53 | #define STDOUT_FILENO 1 | ||
54 | #define lstat stat | ||
55 | |||
56 | #ifndef __DJGPP__ | ||
57 | #define S_IFMT _S_IFMT | ||
58 | #define S_IFREG _S_IFREG | ||
59 | #endif /* !__DJGPP__ */ | ||
60 | |||
61 | #endif /* !WINHACK_ */ | ||
62 | |||
63 | #include <zlib.h> | ||
64 | |||
65 | #include "xdelta.h" | ||
66 | |||
67 | static HandleFuncTable xd_handle_table; | ||
68 | |||
69 | #define XD_PAGE_SIZE (1<<20) | ||
70 | |||
71 | #define XDELTA_110_PREFIX "%XDZ004%" | ||
72 | #define XDELTA_104_PREFIX "%XDZ003%" | ||
73 | #define XDELTA_100_PREFIX "%XDZ002%" | ||
74 | #define XDELTA_020_PREFIX "%XDZ001%" | ||
75 | #define XDELTA_018_PREFIX "%XDZ000%" | ||
76 | #define XDELTA_014_PREFIX "%XDELTA%" | ||
77 | #define XDELTA_PREFIX XDELTA_110_PREFIX | ||
78 | #define XDELTA_PREFIX_LEN 8 | ||
79 | |||
80 | #define HEADER_WORDS (6) | ||
81 | #define HEADER_SPACE (HEADER_WORDS*4) | ||
82 | /* The header is composed of 4-byte words (network byte order) as follows: | ||
83 | * word 1: flags | ||
84 | * word 2: (from name length) << 16 | (to name length) | ||
85 | * word 3: (reserved) | ||
86 | * word 4: (reserved) | ||
87 | * word 5: (reserved) | ||
88 | * word 6: (reserved) | ||
89 | * flags are: | ||
90 | */ | ||
91 | #define FLAG_NO_VERIFY 1 | ||
92 | #define FLAG_FROM_COMPRESSED 2 | ||
93 | #define FLAG_TO_COMPRESSED 4 | ||
94 | #define FLAG_PATCH_COMPRESSED 8 | ||
95 | /* and, the header is follwed by the from file name, then the to file | ||
96 | * name, then the data. */ | ||
97 | |||
98 | #ifdef WINHACK | ||
99 | #define FOPEN_READ_ARG "rb" | ||
100 | #define FOPEN_WRITE_ARG "wb" | ||
101 | #define FILE_SEPARATOR '\\' | ||
102 | #else | ||
103 | #define FOPEN_READ_ARG "r" | ||
104 | #define FOPEN_WRITE_ARG "w" | ||
105 | #define FILE_SEPARATOR '/' | ||
106 | #endif /* WINHACK */ | ||
107 | |||
108 | #include "getopt.h" | ||
109 | |||
110 | typedef struct _LRU LRU; | ||
111 | |||
112 | struct _LRU | ||
113 | { | ||
114 | LRU *next; | ||
115 | LRU *prev; | ||
116 | |||
117 | gint refs; | ||
118 | guint page; | ||
119 | guint8* buffer; | ||
120 | }; | ||
121 | |||
122 | typedef struct _XdFileHandle XdFileHandle; | ||
123 | |||
124 | typedef struct { | ||
125 | gboolean patch_is_compressed; | ||
126 | const gchar* patch_name; | ||
127 | guint patch_flags; | ||
128 | const gchar* patch_version; | ||
129 | gboolean has_trailer; | ||
130 | |||
131 | XdeltaSourceInfo* data_source; | ||
132 | XdeltaSourceInfo* from_source; | ||
133 | |||
134 | gchar *from_name; | ||
135 | gchar *to_name; | ||
136 | |||
137 | guint control_offset; | ||
138 | guint header_offset; | ||
139 | |||
140 | gint16 from_name_len; | ||
141 | gint16 to_name_len; | ||
142 | |||
143 | guint32 header_space[HEADER_WORDS]; | ||
144 | guint8 magic_buf[XDELTA_PREFIX_LEN]; | ||
145 | |||
146 | XdFileHandle *patch_in; | ||
147 | |||
148 | XdeltaControl *cont; | ||
149 | } XdeltaPatch; | ||
150 | |||
151 | struct _XdFileHandle | ||
152 | { | ||
153 | FileHandle fh; | ||
154 | |||
155 | guint length; | ||
156 | guint real_length; | ||
157 | gint type; | ||
158 | const char* name; | ||
159 | const char* cleanup; | ||
160 | |||
161 | guint8 md5[16]; | ||
162 | EdsioMD5Ctx ctx; | ||
163 | |||
164 | /* for write */ | ||
165 | int out_fd; | ||
166 | void* out; | ||
167 | gboolean (* out_write) (XdFileHandle* handle, const void* buf, gint nbyte); | ||
168 | gboolean (* out_close) (XdFileHandle* handle); | ||
169 | |||
170 | /* for read */ | ||
171 | GPtrArray *lru_table; | ||
172 | LRU *lru_head; /* most recently used. */ | ||
173 | LRU *lru_tail; /* least recently used. */ | ||
174 | GMemChunk *lru_chunk; | ||
175 | guint lru_count; | ||
176 | guint lru_outstanding_refs; | ||
177 | |||
178 | guint narrow_low; | ||
179 | guint narrow_high; | ||
180 | guint current_pos; | ||
181 | FILE* in; | ||
182 | gboolean (* in_read) (XdFileHandle* handle, void* buf, gint nbyte); | ||
183 | gboolean (* in_close) (XdFileHandle* handle); | ||
184 | gboolean in_compressed; | ||
185 | |||
186 | const guint8* copy_page; | ||
187 | guint copy_pgno; | ||
188 | gboolean md5_good; | ||
189 | gboolean reset_length_next_write; | ||
190 | |||
191 | gint md5_page; | ||
192 | gint fd; | ||
193 | }; | ||
194 | |||
195 | /* $Format: "static const char xdelta_version[] = \"$ReleaseVersion$\"; " $ */ | ||
196 | static const char xdelta_version[] = "1.1.3"; | ||
197 | |||
198 | typedef struct _Command Command; | ||
199 | |||
200 | struct _Command { | ||
201 | gchar* name; | ||
202 | gint (* func) (gint argc, gchar** argv); | ||
203 | gint nargs; | ||
204 | }; | ||
205 | |||
206 | static gint delta_command (gint argc, gchar** argv); | ||
207 | static gint patch_command (gint argc, gchar** argv); | ||
208 | static gint info_command (gint argc, gchar** argv); | ||
209 | |||
210 | static const Command commands[] = | ||
211 | { | ||
212 | { "delta", delta_command, -1 }, | ||
213 | { "patch", patch_command, -1 }, | ||
214 | { "info", info_command, 1 }, | ||
215 | { NULL, NULL, 0 } | ||
216 | }; | ||
217 | |||
218 | static struct option const long_options[] = | ||
219 | { | ||
220 | {"help", no_argument, 0, 'h'}, | ||
221 | {"version", no_argument, 0, 'v'}, | ||
222 | {"verbose", no_argument, 0, 'V'}, | ||
223 | {"noverify", no_argument, 0, 'n'}, | ||
224 | {"pristine", no_argument, 0, 'p'}, | ||
225 | {"quiet", no_argument, 0, 'q'}, | ||
226 | {"maxmem", required_argument, 0, 'm'}, | ||
227 | {"blocksize", required_argument, 0, 's'}, | ||
228 | {0,0,0,0} | ||
229 | }; | ||
230 | |||
231 | static const gchar* program_name; | ||
232 | static gint compress_level = Z_DEFAULT_COMPRESSION; | ||
233 | static gint no_verify = FALSE; | ||
234 | static gint pristine = FALSE; | ||
235 | static gint verbose = FALSE; | ||
236 | static gint max_mapped_pages = G_MAXINT; | ||
237 | static gint quiet = FALSE; | ||
238 | |||
239 | #define xd_error g_warning | ||
240 | |||
241 | static void | ||
242 | usage () | ||
243 | { | ||
244 | xd_error ("usage: %s COMMAND [OPTIONS] [ARG1 ...]\n", program_name); | ||
245 | xd_error ("use --help for more help\n"); | ||
246 | exit (2); | ||
247 | } | ||
248 | |||
249 | static void | ||
250 | help () | ||
251 | { | ||
252 | xd_error ("usage: %s COMMAND [OPTIONS] [ARG1 ...]\n", program_name); | ||
253 | xd_error ("COMMAND is one of:\n"); | ||
254 | xd_error (" delta Produce a delta from ARG1 to ARG2 producing ARG3\n"); | ||
255 | xd_error (" info List details about delta ARG1\n"); | ||
256 | xd_error (" patch Apply patch ARG1 using file ARG2 producing ARG3\n"); | ||
257 | xd_error ("OPTIONS are:\n"); | ||
258 | xd_error (" -v, --version Print version information\n"); | ||
259 | xd_error (" -V, --verbose Print verbose error messages\n"); | ||
260 | xd_error (" -h, --help Print this summary\n"); | ||
261 | xd_error (" -n, --noverify Disable automatic MD5 verification\n"); | ||
262 | xd_error (" -p, --pristine Disable automatic GZIP decompression\n"); | ||
263 | xd_error (" -m, --maxmem=SIZE Set the buffer size limit, e.g. 640K, 16M\n"); | ||
264 | xd_error (" -[0-9] ZLIB compress level: 0=none, 1=fast, 6=default, 9=best\n"); | ||
265 | xd_error (" -s=BLOCK_SIZE Sets block size (power of 2), minimum match length\n"); | ||
266 | xd_error (" In-core memory requirement is (FROM_LEN * 8) / BLOCK_SIZE\n"); | ||
267 | exit (2); | ||
268 | } | ||
269 | |||
270 | static void | ||
271 | version () | ||
272 | { | ||
273 | xd_error ("version %s\n", xdelta_version); | ||
274 | exit (2); | ||
275 | } | ||
276 | |||
277 | static FILE* xd_error_file = NULL; | ||
278 | |||
279 | static void | ||
280 | xd_error_func (const gchar *log_domain, | ||
281 | GLogLevelFlags log_level, | ||
282 | const gchar *message, | ||
283 | gpointer user_data) | ||
284 | { | ||
285 | if (! xd_error_file) | ||
286 | xd_error_file = stderr; | ||
287 | |||
288 | fprintf (xd_error_file, "%s: %s", program_name, message); | ||
289 | } | ||
290 | |||
291 | static gboolean | ||
292 | event_devel (void) | ||
293 | { | ||
294 | static gboolean once = FALSE; | ||
295 | static gboolean devel = FALSE; | ||
296 | |||
297 | if (! once) | ||
298 | { | ||
299 | devel = g_getenv ("EDSIO_DEVEL") != NULL; | ||
300 | once = TRUE; | ||
301 | } | ||
302 | |||
303 | return devel; | ||
304 | } | ||
305 | |||
306 | static gboolean | ||
307 | event_watch (GenericEvent* ev, GenericEventDef* def, const char* message) | ||
308 | { | ||
309 | if (quiet && def->level <= EL_Warning) | ||
310 | return TRUE; | ||
311 | |||
312 | if (event_devel ()) | ||
313 | fprintf (stderr, "%s:%d: %s\n", ev->srcfile, ev->srcline, message); | ||
314 | else | ||
315 | fprintf (stderr, "%s: %s\n", program_name, message); | ||
316 | |||
317 | return TRUE; | ||
318 | } | ||
319 | |||
320 | gint | ||
321 | main (gint argc, gchar** argv) | ||
322 | { | ||
323 | const Command *cmd = NULL; | ||
324 | gint c; | ||
325 | gint longind; | ||
326 | |||
327 | eventdelivery_event_watch_all (event_watch); | ||
328 | |||
329 | if (! xd_edsio_init ()) | ||
330 | return 2; | ||
331 | |||
332 | #ifdef __DJGPP__ | ||
333 | /* | ||
334 | * (richdawe@bigfoot.com): Limit maximum memory usage to 87.5% of available | ||
335 | * physical memory for DJGPP. Otherwise the replacement for mmap() will | ||
336 | * exhaust memory and XDelta will fail. | ||
337 | */ | ||
338 | { | ||
339 | unsigned long phys_free = _go32_dpmi_remaining_physical_memory(); | ||
340 | |||
341 | max_mapped_pages = phys_free / XD_PAGE_SIZE; | ||
342 | max_mapped_pages %= 8; | ||
343 | max_mapped_pages *= 7; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * (richdawe@bigfoot.com): Strip off the file extension 'exe' from | ||
348 | * program_name, since it makes the help messages look ugly. | ||
349 | */ | ||
350 | { | ||
351 | char strip_ext[PATH_MAX + 1]; | ||
352 | char *p; | ||
353 | |||
354 | strcpy (strip_ext, argv[0]); | ||
355 | p = strrchr (strip_ext, '.'); | ||
356 | if ((p != NULL) && (strncasecmp (p + 1, "exe", 3) == 0)) | ||
357 | *p = '\0'; | ||
358 | program_name = g_basename (strip_ext); | ||
359 | } | ||
360 | #else /* !__DJGPP__ */ | ||
361 | program_name = g_basename (argv[0]); | ||
362 | #endif /* __DJGPP__ */ | ||
363 | |||
364 | g_log_set_handler (G_LOG_DOMAIN, | ||
365 | G_LOG_LEVEL_WARNING, | ||
366 | xd_error_func, | ||
367 | NULL); | ||
368 | |||
369 | if (argc < 2) | ||
370 | usage (); | ||
371 | |||
372 | for (cmd = commands; cmd->name; cmd += 1) | ||
373 | if (strcmp (cmd->name, argv[1]) == 0) | ||
374 | break; | ||
375 | |||
376 | if (strcmp (argv[1], "-h") == 0 || | ||
377 | strcmp (argv[1], "--help") == 0) | ||
378 | help (); | ||
379 | |||
380 | if (strcmp (argv[1], "-v") == 0 || | ||
381 | strcmp (argv[1], "--version") == 0) | ||
382 | version (); | ||
383 | |||
384 | if (!cmd->name) | ||
385 | { | ||
386 | xd_error ("unrecognized command\n"); | ||
387 | help (); | ||
388 | } | ||
389 | |||
390 | argc -= 1; | ||
391 | argv += 1; | ||
392 | |||
393 | while ((c = getopt_long(argc, | ||
394 | argv, | ||
395 | "+nqphvVs:m:0123456789", | ||
396 | long_options, | ||
397 | &longind)) != EOF) | ||
398 | { | ||
399 | switch (c) | ||
400 | { | ||
401 | case 'q': quiet = TRUE; break; | ||
402 | case 'n': no_verify = TRUE; break; | ||
403 | case 'p': pristine = TRUE; break; | ||
404 | case 'V': verbose = TRUE; break; | ||
405 | case 's': | ||
406 | { | ||
407 | int ret; | ||
408 | int s = atoi (optarg); | ||
409 | |||
410 | if ((ret = xdp_set_query_size_pow (s)) && ! quiet) | ||
411 | { | ||
412 | xd_error ("illegal query size: %s\n", xdp_errno (ret)); | ||
413 | } | ||
414 | } | ||
415 | break; | ||
416 | case 'm': | ||
417 | { | ||
418 | gchar* end = NULL; | ||
419 | glong l = strtol (optarg, &end, 0); | ||
420 | |||
421 | if (end && g_strcasecmp (end, "M") == 0) | ||
422 | l <<= 20; | ||
423 | else if (end && g_strcasecmp (end, "K") == 0) | ||
424 | l <<= 10; | ||
425 | else if (end || l < 0) | ||
426 | { | ||
427 | xd_error ("illegal maxmem argument %s\n", optarg); | ||
428 | return 2; | ||
429 | } | ||
430 | |||
431 | #ifdef __DJGPP__ | ||
432 | /* | ||
433 | * (richdawe@bigfoot.com): Old MS-DOS systems may have a maximum | ||
434 | * of 8MB memory == XD_PAGE_SIZE * 8. Therefore, do what the user | ||
435 | * asks for on MS-DOS. | ||
436 | */ | ||
437 | #else /* !__DJGPP__ */ | ||
438 | l = MAX (l, XD_PAGE_SIZE * 8); | ||
439 | #endif /* __DJGPP__ */ | ||
440 | |||
441 | max_mapped_pages = l / XD_PAGE_SIZE; | ||
442 | } | ||
443 | break; | ||
444 | case 'h': help (); break; | ||
445 | case 'v': version (); break; | ||
446 | case '0': case '1': case '2': case '3': case '4': | ||
447 | case '5': case '6': case '7': case '8': case '9': | ||
448 | compress_level = c - '0'; | ||
449 | break; | ||
450 | case '?': | ||
451 | default: | ||
452 | xd_error ("illegal argument, use --help for help\n"); | ||
453 | return 2; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | if (verbose && max_mapped_pages < G_MAXINT) | ||
458 | xd_error ("using %d kilobytes of buffer space\n", (max_mapped_pages * XD_PAGE_SIZE) >> 10); | ||
459 | |||
460 | argc -= optind; | ||
461 | argv += optind; | ||
462 | |||
463 | if (cmd->nargs >= 0 && argc != cmd->nargs) | ||
464 | { | ||
465 | xd_error ("wrong number of arguments\n"); | ||
466 | help (); | ||
467 | return 2; | ||
468 | } | ||
469 | |||
470 | return (* cmd->func) (argc, argv); | ||
471 | } | ||
472 | |||
473 | /* Commands */ | ||
474 | |||
475 | #define READ_TYPE 1 | ||
476 | #define READ_NOSEEK_TYPE 1 | ||
477 | #define READ_SEEK_TYPE 3 | ||
478 | #define WRITE_TYPE 4 | ||
479 | |||
480 | /* Note to the casual reader: the filehandle implemented here is | ||
481 | * highly aware of the calling patterns of the Xdelta library. It | ||
482 | * also plays games with narrowing to implement several files in the | ||
483 | * same handle. So, if you try to modify or use this code, BEWARE. | ||
484 | * See the repository package for a complete implementation. | ||
485 | * In fact, its really quite a hack. */ | ||
486 | |||
487 | static gssize xd_handle_map_page (XdFileHandle *fh, guint pgno, const guint8** mem); | ||
488 | static gboolean xd_handle_unmap_page (XdFileHandle *fh, guint pgno, const guint8** mem); | ||
489 | |||
490 | static gboolean | ||
491 | xd_fwrite (XdFileHandle* fh, const void* buf, gint nbyte) | ||
492 | { | ||
493 | return fwrite (buf, nbyte, 1, fh->out) == 1; | ||
494 | } | ||
495 | |||
496 | static gboolean | ||
497 | xd_fread (XdFileHandle* fh, void* buf, gint nbyte) | ||
498 | { | ||
499 | return fread (buf, nbyte, 1, fh->in) == 1; | ||
500 | } | ||
501 | |||
502 | static gboolean | ||
503 | xd_fclose (XdFileHandle* fh) | ||
504 | { | ||
505 | return fclose (fh->out) == 0; | ||
506 | } | ||
507 | |||
508 | static gboolean | ||
509 | xd_frclose (XdFileHandle* fh) | ||
510 | { | ||
511 | return fclose (fh->in) == 0; | ||
512 | } | ||
513 | |||
514 | static gboolean | ||
515 | xd_gzwrite (XdFileHandle* fh, const void* buf, gint nbyte) | ||
516 | { | ||
517 | return gzwrite (fh->out, (void*) buf, nbyte) == nbyte; | ||
518 | } | ||
519 | |||
520 | static gboolean | ||
521 | xd_gzread (XdFileHandle* fh, void* buf, gint nbyte) | ||
522 | { | ||
523 | return gzread (fh->in, buf, nbyte) == nbyte; | ||
524 | } | ||
525 | |||
526 | static gboolean | ||
527 | xd_gzclose (XdFileHandle* fh) | ||
528 | { | ||
529 | return gzclose (fh->out) == Z_OK; | ||
530 | } | ||
531 | |||
532 | static gboolean | ||
533 | xd_gzrclose (XdFileHandle* fh) | ||
534 | { | ||
535 | return gzclose (fh->in) == Z_OK; | ||
536 | } | ||
537 | |||
538 | static void | ||
539 | init_table (XdFileHandle* fh) | ||
540 | { | ||
541 | fh->lru_table = g_ptr_array_new (); | ||
542 | fh->lru_chunk = g_mem_chunk_create(LRU, 1<<9, G_ALLOC_ONLY); | ||
543 | fh->lru_head = NULL; | ||
544 | fh->lru_tail = NULL; | ||
545 | } | ||
546 | |||
547 | static XdFileHandle* | ||
548 | open_common (const char* name, const char* real_name) | ||
549 | { | ||
550 | XdFileHandle* fh; | ||
551 | |||
552 | gint fd; | ||
553 | struct stat buf; | ||
554 | |||
555 | if ((fd = open (name, O_RDONLY | O_BINARY, 0)) < 0) | ||
556 | { | ||
557 | xd_error ("open %s failed: %s\n", name, g_strerror (errno)); | ||
558 | return NULL; | ||
559 | } | ||
560 | |||
561 | if (stat (name, &buf) < 0) | ||
562 | { | ||
563 | xd_error ("stat %s failed: %s\n", name, g_strerror (errno)); | ||
564 | return NULL; | ||
565 | } | ||
566 | |||
567 | /* S_ISREG() is not on Windows */ | ||
568 | if ((buf.st_mode & S_IFMT) != S_IFREG) | ||
569 | { | ||
570 | xd_error ("%s is not a regular file\n", name); | ||
571 | return NULL; | ||
572 | } | ||
573 | |||
574 | fh = g_new0 (XdFileHandle, 1); | ||
575 | |||
576 | fh->fh.table = & xd_handle_table; | ||
577 | fh->name = real_name; | ||
578 | fh->fd = fd; | ||
579 | fh->length = buf.st_size; | ||
580 | fh->narrow_high = buf.st_size; | ||
581 | |||
582 | return fh; | ||
583 | } | ||
584 | |||
585 | static gboolean | ||
586 | file_gzipped (const char* name, gboolean *is_compressed) | ||
587 | { | ||
588 | FILE* f = fopen (name, FOPEN_READ_ARG); | ||
589 | guint8 buf[2]; | ||
590 | |||
591 | (*is_compressed) = FALSE; | ||
592 | |||
593 | if (! f) | ||
594 | { | ||
595 | xd_error ("open %s failed: %s\n", name, g_strerror (errno)); | ||
596 | return FALSE; | ||
597 | } | ||
598 | |||
599 | if (fread (buf, 2, 1, f) != 1) | ||
600 | return TRUE; | ||
601 | |||
602 | #define GZIP_MAGIC1 037 | ||
603 | #define GZIP_MAGIC2 0213 | ||
604 | |||
605 | if (buf[0] == GZIP_MAGIC1 && buf[1] == GZIP_MAGIC2) | ||
606 | (* is_compressed) = TRUE; | ||
607 | |||
608 | return TRUE; | ||
609 | } | ||
610 | |||
611 | static const char* | ||
612 | xd_tmpname (void) | ||
613 | { | ||
614 | const char* tmpdir = g_get_tmp_dir (); | ||
615 | GString* s; | ||
616 | gint x = getpid (); | ||
617 | static gint seq = 0; | ||
618 | struct stat buf; | ||
619 | |||
620 | s = g_string_new (NULL); | ||
621 | |||
622 | do | ||
623 | { | ||
624 | /* | ||
625 | * (richdawe@bigfoot.com): Limit temporary filenames to | ||
626 | * the MS-DOS 8+3 convention for DJGPP. | ||
627 | */ | ||
628 | g_string_sprintf (s, "%s/xd-%05d.%03d", tmpdir, x, seq++); | ||
629 | } | ||
630 | while (lstat (s->str, &buf) == 0); | ||
631 | |||
632 | return s->str; | ||
633 | } | ||
634 | |||
635 | static const char* | ||
636 | file_gunzip (const char* name) | ||
637 | { | ||
638 | const char* new_name = xd_tmpname (); | ||
639 | FILE* out = fopen (new_name, FOPEN_WRITE_ARG); | ||
640 | gzFile in = gzopen (name, "rb"); | ||
641 | guint8 buf[1024]; | ||
642 | int nread; | ||
643 | |||
644 | while ((nread = gzread (in, buf, 1024)) > 0) | ||
645 | { | ||
646 | if (fwrite (buf, nread, 1, out) != 1) | ||
647 | { | ||
648 | xd_error ("write %s failed (during uncompression): %s\n", new_name, g_strerror (errno)); | ||
649 | return NULL; | ||
650 | } | ||
651 | } | ||
652 | |||
653 | if (nread < 0) | ||
654 | { | ||
655 | xd_error ("gzread %s failed\n", name); | ||
656 | return NULL; | ||
657 | } | ||
658 | |||
659 | gzclose (in); | ||
660 | |||
661 | if (fclose (out)) | ||
662 | { | ||
663 | xd_error ("close %s failed (during uncompression): %s\n", new_name, g_strerror (errno)); | ||
664 | return NULL; | ||
665 | } | ||
666 | |||
667 | return new_name; | ||
668 | } | ||
669 | |||
670 | static XdFileHandle* | ||
671 | open_read_noseek_handle (const char* name, gboolean* is_compressed, gboolean will_read, gboolean honor_pristine) | ||
672 | { | ||
673 | XdFileHandle* fh; | ||
674 | const char* name0 = name; | ||
675 | |||
676 | /* we _could_ stream-read this file if compressed, but it adds a | ||
677 | * lot of complexity. the library can handle it, just set the | ||
678 | * length to (XDELTA_MAX_FILE_LEN-1) and make sure that the end | ||
679 | * of file condition is set when on the last page. However, I | ||
680 | * don't feel like it. */ | ||
681 | if (honor_pristine && pristine) | ||
682 | *is_compressed = FALSE; | ||
683 | else | ||
684 | { | ||
685 | if (! file_gzipped (name, is_compressed)) | ||
686 | return NULL; | ||
687 | } | ||
688 | |||
689 | if ((* is_compressed) && ! (name = file_gunzip (name))) | ||
690 | return NULL; | ||
691 | |||
692 | if (! (fh = open_common (name, name0))) | ||
693 | return NULL; | ||
694 | |||
695 | fh->type = READ_NOSEEK_TYPE; | ||
696 | |||
697 | edsio_md5_init (&fh->ctx); | ||
698 | |||
699 | if (*is_compressed) | ||
700 | fh->cleanup = name; | ||
701 | |||
702 | if (will_read) | ||
703 | { | ||
704 | g_assert (fh->fd >= 0); | ||
705 | if (! (fh->in = fdopen (dup (fh->fd), FOPEN_READ_ARG))) | ||
706 | { | ||
707 | xd_error ("fdopen: %s\n", g_strerror (errno)); | ||
708 | return NULL; | ||
709 | } | ||
710 | fh->in_read = &xd_fread; | ||
711 | fh->in_close = &xd_frclose; | ||
712 | } | ||
713 | else | ||
714 | { | ||
715 | init_table (fh); | ||
716 | } | ||
717 | |||
718 | return fh; | ||
719 | } | ||
720 | |||
721 | static void | ||
722 | xd_read_close (XdFileHandle* fh) | ||
723 | { | ||
724 | /* | ||
725 | * (richdawe@bigfoot.com): On Unix you can unlink a file while it is | ||
726 | * still open. On MS-DOS this can lead to filesystem corruption. Close | ||
727 | * the file before unlinking it. | ||
728 | */ | ||
729 | close (fh->fd); | ||
730 | |||
731 | if (fh->cleanup) | ||
732 | unlink (fh->cleanup); | ||
733 | |||
734 | if (fh->in) | ||
735 | (*fh->in_close) (fh); | ||
736 | } | ||
737 | |||
738 | static XdFileHandle* | ||
739 | open_read_seek_handle (const char* name, gboolean* is_compressed, gboolean honor_pristine) | ||
740 | { | ||
741 | XdFileHandle* fh; | ||
742 | const char* name0 = name; | ||
743 | |||
744 | if (honor_pristine && pristine) | ||
745 | *is_compressed = FALSE; | ||
746 | else | ||
747 | { | ||
748 | if (! file_gzipped (name, is_compressed)) | ||
749 | return NULL; | ||
750 | } | ||
751 | |||
752 | if ((* is_compressed) && ! (name = file_gunzip (name))) | ||
753 | return NULL; | ||
754 | |||
755 | if (! (fh = open_common (name, name0))) | ||
756 | return NULL; | ||
757 | |||
758 | fh->type = READ_SEEK_TYPE; | ||
759 | |||
760 | if (*is_compressed) | ||
761 | fh->cleanup = name; | ||
762 | |||
763 | init_table (fh); | ||
764 | |||
765 | edsio_md5_init (&fh->ctx); | ||
766 | |||
767 | return fh; | ||
768 | } | ||
769 | |||
770 | static XdFileHandle* | ||
771 | open_write_handle (int fd, const char* name) | ||
772 | { | ||
773 | XdFileHandle* fh = g_new0 (XdFileHandle, 1); | ||
774 | int nfd; | ||
775 | |||
776 | fh->fh.table = & xd_handle_table; | ||
777 | fh->out_fd = fd; | ||
778 | fh->out_write = &xd_fwrite; | ||
779 | fh->out_close = &xd_fclose; | ||
780 | |||
781 | g_assert (fh->out_fd >= 0); | ||
782 | |||
783 | nfd = dup (fh->out_fd); | ||
784 | |||
785 | if (! (fh->out = fdopen (nfd, FOPEN_WRITE_ARG))) | ||
786 | { | ||
787 | xd_error ("fdopen %s failed: %s\n", name, g_strerror (errno)); | ||
788 | return NULL; | ||
789 | } | ||
790 | |||
791 | fh->type = WRITE_TYPE; | ||
792 | fh->name = name; | ||
793 | |||
794 | edsio_md5_init (&fh->ctx); | ||
795 | |||
796 | return fh; | ||
797 | } | ||
798 | |||
799 | static gint | ||
800 | xd_begin_compression (XdFileHandle* fh) | ||
801 | { | ||
802 | gint filepos, nfd; | ||
803 | |||
804 | if (compress_level == 0) | ||
805 | return fh->real_length; | ||
806 | |||
807 | if (! (fh->out_close) (fh)) | ||
808 | { | ||
809 | xd_error ("fclose failed: %s\n", g_strerror (errno)); | ||
810 | return -1; | ||
811 | } | ||
812 | |||
813 | filepos = lseek (fh->out_fd, 0, SEEK_END); | ||
814 | |||
815 | if (filepos < 0) | ||
816 | { | ||
817 | xd_error ("lseek failed: %s\n", g_strerror (errno)); | ||
818 | return -1; | ||
819 | } | ||
820 | |||
821 | g_assert (fh->out_fd >= 0); | ||
822 | |||
823 | nfd = dup (fh->out_fd); | ||
824 | |||
825 | fh->out = gzdopen (nfd, "wb"); | ||
826 | fh->out_write = &xd_gzwrite; | ||
827 | fh->out_close = &xd_gzclose; | ||
828 | |||
829 | if (! fh->out) | ||
830 | { | ||
831 | xd_error ("gzdopen failed: %s\n", g_strerror (errno)); | ||
832 | return -1; | ||
833 | } | ||
834 | |||
835 | if (gzsetparams(fh->out, compress_level, Z_DEFAULT_STRATEGY) != Z_OK) | ||
836 | { | ||
837 | int foo; | ||
838 | xd_error ("gzsetparams failed: %s\n", gzerror (fh->out, &foo)); | ||
839 | return -1; | ||
840 | } | ||
841 | |||
842 | return filepos; | ||
843 | } | ||
844 | |||
845 | static gboolean | ||
846 | xd_end_compression (XdFileHandle* fh) | ||
847 | { | ||
848 | if (compress_level == 0) | ||
849 | return TRUE; | ||
850 | |||
851 | if (! (fh->out_close) (fh)) | ||
852 | { | ||
853 | xd_error ("fdclose failed: %s\n", g_strerror (errno)); | ||
854 | return FALSE; | ||
855 | } | ||
856 | |||
857 | if (lseek (fh->out_fd, 0, SEEK_END) < 0) | ||
858 | { | ||
859 | xd_error ("lseek failed: %s\n", g_strerror (errno)); | ||
860 | return FALSE; | ||
861 | } | ||
862 | |||
863 | g_assert (fh->out_fd >= 0); | ||
864 | fh->out = fdopen (dup (fh->out_fd), FOPEN_WRITE_ARG); | ||
865 | fh->out_write = &xd_fwrite; | ||
866 | fh->out_close = &xd_fclose; | ||
867 | |||
868 | if (! fh->out) | ||
869 | { | ||
870 | xd_error ("fdopen failed: %s\n", g_strerror (errno)); | ||
871 | return FALSE; | ||
872 | } | ||
873 | |||
874 | return TRUE; | ||
875 | } | ||
876 | |||
877 | static gssize | ||
878 | xd_handle_length (XdFileHandle *fh) | ||
879 | { | ||
880 | if (fh->in_compressed) | ||
881 | return fh->current_pos; | ||
882 | else | ||
883 | return fh->narrow_high - fh->narrow_low; | ||
884 | } | ||
885 | |||
886 | static gssize | ||
887 | xd_handle_pages (XdFileHandle *fh) | ||
888 | { | ||
889 | g_assert (fh->type & READ_TYPE); | ||
890 | return xd_handle_length (fh) / XD_PAGE_SIZE; | ||
891 | } | ||
892 | |||
893 | static gssize | ||
894 | xd_handle_pagesize (XdFileHandle *fh) | ||
895 | { | ||
896 | g_assert (fh->type & READ_TYPE); | ||
897 | return XD_PAGE_SIZE; | ||
898 | } | ||
899 | |||
900 | static gint | ||
901 | on_page (XdFileHandle* fh, guint pgno) | ||
902 | { | ||
903 | if (pgno > xd_handle_pages (fh)) | ||
904 | return -1; | ||
905 | |||
906 | if (pgno == xd_handle_pages (fh)) | ||
907 | return xd_handle_length (fh) % XD_PAGE_SIZE; | ||
908 | |||
909 | return XD_PAGE_SIZE; | ||
910 | } | ||
911 | |||
912 | static gboolean | ||
913 | xd_handle_close (XdFileHandle *fh, gint ignore) | ||
914 | { | ||
915 | /* this is really a reset for writable files */ | ||
916 | |||
917 | if (fh->type == WRITE_TYPE) | ||
918 | { | ||
919 | if (fh->reset_length_next_write) | ||
920 | { | ||
921 | fh->reset_length_next_write = FALSE; | ||
922 | fh->length = 0; | ||
923 | fh->narrow_high = 0; | ||
924 | } | ||
925 | |||
926 | fh->reset_length_next_write = TRUE; | ||
927 | edsio_md5_final (fh->md5, &fh->ctx); | ||
928 | edsio_md5_init (&fh->ctx); | ||
929 | } | ||
930 | else if (fh->in) | ||
931 | { | ||
932 | edsio_md5_final (fh->md5, &fh->ctx); | ||
933 | edsio_md5_init (&fh->ctx); | ||
934 | fh->md5_good = FALSE; | ||
935 | } | ||
936 | |||
937 | return TRUE; | ||
938 | } | ||
939 | |||
940 | static const guint8* | ||
941 | xd_handle_checksum_md5 (XdFileHandle *fh) | ||
942 | { | ||
943 | if (fh->in && ! fh->md5_good) | ||
944 | { | ||
945 | edsio_md5_final (fh->md5, &fh->ctx); | ||
946 | fh->md5_good = TRUE; | ||
947 | } | ||
948 | else if (fh->type != WRITE_TYPE && !fh->in) | ||
949 | { | ||
950 | const guint8* page; | ||
951 | |||
952 | while (fh->md5_page <= xd_handle_pages (fh)) | ||
953 | { | ||
954 | gint pgno = fh->md5_page; | ||
955 | gint onpage; | ||
956 | |||
957 | if ((onpage = xd_handle_map_page (fh, pgno, &page)) < 0) | ||
958 | return NULL; | ||
959 | |||
960 | if (pgno == fh->md5_page) | ||
961 | { | ||
962 | fh->md5_page += 1; | ||
963 | edsio_md5_update (&fh->ctx, page, onpage); | ||
964 | |||
965 | if (fh->md5_page > xd_handle_pages (fh)) | ||
966 | edsio_md5_final (fh->md5, &fh->ctx); | ||
967 | } | ||
968 | |||
969 | if (! xd_handle_unmap_page (fh, pgno, &page)) | ||
970 | return NULL; | ||
971 | } | ||
972 | } | ||
973 | |||
974 | return g_memdup (fh->md5, 16); | ||
975 | } | ||
976 | |||
977 | static gboolean | ||
978 | xd_handle_set_pos (XdFileHandle *fh, guint pos) | ||
979 | { | ||
980 | if (fh->current_pos == pos + fh->narrow_low) | ||
981 | return TRUE; | ||
982 | |||
983 | if (pos + fh->narrow_low > fh->narrow_high) | ||
984 | { | ||
985 | xd_error ("unexpected EOF in %s\n", fh->name); | ||
986 | return FALSE; | ||
987 | } | ||
988 | |||
989 | fh->current_pos = pos + fh->narrow_low; | ||
990 | |||
991 | if (fseek (fh->in, fh->current_pos, SEEK_SET)) | ||
992 | { | ||
993 | xd_error ("fseek failed: %s\n", g_strerror (errno)); | ||
994 | return FALSE; | ||
995 | } | ||
996 | |||
997 | return TRUE; | ||
998 | } | ||
999 | |||
1000 | static gboolean | ||
1001 | xd_handle_narrow (XdFileHandle* fh, guint low, guint high, gboolean compressed) | ||
1002 | { | ||
1003 | if (high > fh->length) | ||
1004 | { | ||
1005 | xd_error ("%s: corrupt or truncated delta\n", fh->name); | ||
1006 | return FALSE; | ||
1007 | } | ||
1008 | |||
1009 | fh->narrow_low = low; | ||
1010 | fh->narrow_high = high; | ||
1011 | |||
1012 | edsio_md5_init (&fh->ctx); | ||
1013 | |||
1014 | if (compressed) | ||
1015 | { | ||
1016 | (* fh->in_close) (fh); | ||
1017 | |||
1018 | if (lseek (fh->fd, low, SEEK_SET) < 0) | ||
1019 | { | ||
1020 | xd_error ("%s: corrupt or truncated delta: cannot seek to %d: %s\n", fh->name, low, g_strerror (errno)); | ||
1021 | return FALSE; | ||
1022 | } | ||
1023 | |||
1024 | g_assert (fh->fd >= 0); | ||
1025 | fh->in = gzdopen (dup (fh->fd), "rb"); | ||
1026 | fh->in_read = &xd_gzread; | ||
1027 | fh->in_close = &xd_gzrclose; | ||
1028 | fh->in_compressed = TRUE; | ||
1029 | fh->current_pos = 0; | ||
1030 | |||
1031 | if (! fh->in) | ||
1032 | { | ||
1033 | xd_error ("gzdopen failed: %s\n", g_strerror (errno)); | ||
1034 | return -1; | ||
1035 | } | ||
1036 | } | ||
1037 | else | ||
1038 | { | ||
1039 | if (! xd_handle_set_pos (fh, 0)) | ||
1040 | return FALSE; | ||
1041 | } | ||
1042 | |||
1043 | return TRUE; | ||
1044 | } | ||
1045 | |||
1046 | static guint | ||
1047 | xd_handle_get_pos (XdFileHandle* fh) | ||
1048 | { | ||
1049 | return fh->current_pos - fh->narrow_low; | ||
1050 | } | ||
1051 | |||
1052 | static const gchar* | ||
1053 | xd_handle_name (XdFileHandle *fh) | ||
1054 | { | ||
1055 | return g_strdup (fh->name); | ||
1056 | } | ||
1057 | |||
1058 | static gssize | ||
1059 | xd_handle_read (XdFileHandle *fh, guint8 *buf, gsize nbyte) | ||
1060 | { | ||
1061 | if (nbyte == 0) | ||
1062 | return 0; | ||
1063 | |||
1064 | if (! (fh->in_read) (fh, buf, nbyte)) /* This is suspicious */ | ||
1065 | { | ||
1066 | xd_error ("read failed: %s\n", g_strerror (errno)); | ||
1067 | return -1; | ||
1068 | } | ||
1069 | |||
1070 | if (!no_verify) | ||
1071 | edsio_md5_update (&fh->ctx, buf, nbyte); | ||
1072 | |||
1073 | fh->current_pos += nbyte; | ||
1074 | |||
1075 | return nbyte; | ||
1076 | } | ||
1077 | |||
1078 | static gboolean | ||
1079 | xd_handle_write (XdFileHandle *fh, const guint8 *buf, gsize nbyte) | ||
1080 | { | ||
1081 | g_assert (fh->type == WRITE_TYPE); | ||
1082 | |||
1083 | if (fh->reset_length_next_write) | ||
1084 | { | ||
1085 | fh->reset_length_next_write = FALSE; | ||
1086 | fh->length = 0; | ||
1087 | fh->narrow_high = 0; | ||
1088 | } | ||
1089 | |||
1090 | if (! no_verify) | ||
1091 | edsio_md5_update (&fh->ctx, buf, nbyte); | ||
1092 | |||
1093 | if (! (*fh->out_write) (fh, buf, nbyte)) | ||
1094 | { | ||
1095 | xd_error ("write failed: %s\n", g_strerror (errno)); | ||
1096 | return FALSE; | ||
1097 | } | ||
1098 | |||
1099 | fh->length += nbyte; | ||
1100 | fh->real_length += nbyte; | ||
1101 | fh->narrow_high += nbyte; | ||
1102 | |||
1103 | return TRUE; | ||
1104 | } | ||
1105 | |||
1106 | static gboolean | ||
1107 | xd_handle_really_close (XdFileHandle *fh) | ||
1108 | { | ||
1109 | g_assert (fh->type == WRITE_TYPE); | ||
1110 | |||
1111 | if (! (* fh->out_close) (fh) || close (fh->out_fd) < 0) | ||
1112 | { | ||
1113 | xd_error ("write failed: %s\n", g_strerror (errno)); | ||
1114 | return FALSE; | ||
1115 | } | ||
1116 | |||
1117 | return TRUE; | ||
1118 | } | ||
1119 | |||
1120 | static LRU* | ||
1121 | pull_lru (XdFileHandle* fh, LRU* lru) | ||
1122 | { | ||
1123 | if (lru->next && lru->prev) | ||
1124 | { | ||
1125 | lru->next->prev = lru->prev; | ||
1126 | lru->prev->next = lru->next; | ||
1127 | } | ||
1128 | else if (lru->next) | ||
1129 | { | ||
1130 | fh->lru_tail = lru->next; | ||
1131 | lru->next->prev = NULL; | ||
1132 | } | ||
1133 | else if (lru->prev) | ||
1134 | { | ||
1135 | fh->lru_head = lru->prev; | ||
1136 | lru->prev->next = NULL; | ||
1137 | } | ||
1138 | else | ||
1139 | { | ||
1140 | fh->lru_head = NULL; | ||
1141 | fh->lru_tail = NULL; | ||
1142 | } | ||
1143 | |||
1144 | lru->next = NULL; | ||
1145 | lru->prev = NULL; | ||
1146 | |||
1147 | return lru; | ||
1148 | } | ||
1149 | |||
1150 | static gboolean | ||
1151 | really_free_one_page (XdFileHandle* fh) | ||
1152 | { | ||
1153 | LRU *lru = fh->lru_tail; | ||
1154 | |||
1155 | for (; lru; lru = lru->prev) | ||
1156 | { | ||
1157 | gint to_unmap; | ||
1158 | LRU *lru_dead; | ||
1159 | |||
1160 | if (lru->refs > 0) | ||
1161 | continue; | ||
1162 | |||
1163 | lru_dead = pull_lru (fh, lru); | ||
1164 | |||
1165 | g_assert (lru_dead->buffer); | ||
1166 | |||
1167 | to_unmap = on_page (fh, lru_dead->page); | ||
1168 | |||
1169 | fh->lru_count -= 1; | ||
1170 | |||
1171 | if (to_unmap > 0) | ||
1172 | { | ||
1173 | #ifdef WINHACK | ||
1174 | g_free (lru_dead->buffer); | ||
1175 | #else | ||
1176 | if (munmap (lru_dead->buffer, to_unmap)) | ||
1177 | { | ||
1178 | xd_error ("munmap failed: %s\n", g_strerror (errno)); | ||
1179 | return FALSE; | ||
1180 | } | ||
1181 | #endif /* WINHACK */ | ||
1182 | } | ||
1183 | |||
1184 | lru_dead->buffer = NULL; | ||
1185 | |||
1186 | return TRUE; | ||
1187 | } | ||
1188 | |||
1189 | return TRUE; | ||
1190 | } | ||
1191 | |||
1192 | #if 0 | ||
1193 | static void | ||
1194 | print_lru (XdFileHandle* fh) | ||
1195 | { | ||
1196 | LRU* lru = fh->lru_head; | ||
1197 | |||
1198 | for (; lru; lru = lru->prev) | ||
1199 | { | ||
1200 | g_print ("page %d buffer %p\n", lru->page, lru->buffer); | ||
1201 | |||
1202 | if (! lru->prev && lru != fh->lru_tail) | ||
1203 | g_print ("incorrect lru_tail\n"); | ||
1204 | } | ||
1205 | } | ||
1206 | #endif | ||
1207 | |||
1208 | static gboolean | ||
1209 | make_lru_room (XdFileHandle* fh) | ||
1210 | { | ||
1211 | if (fh->lru_count == max_mapped_pages) | ||
1212 | { | ||
1213 | if (! really_free_one_page (fh)) | ||
1214 | return FALSE; | ||
1215 | } | ||
1216 | |||
1217 | g_assert (fh->lru_count < max_mapped_pages); | ||
1218 | |||
1219 | return TRUE; | ||
1220 | } | ||
1221 | |||
1222 | /*#define DEBUG_MAP*/ | ||
1223 | |||
1224 | static gssize | ||
1225 | xd_handle_map_page (XdFileHandle *fh, guint pgno, const guint8** mem) | ||
1226 | { | ||
1227 | LRU* lru; | ||
1228 | guint to_map; | ||
1229 | |||
1230 | #ifdef DEBUG_MAP | ||
1231 | g_print ("map %p:%d\n", fh, pgno); | ||
1232 | #endif | ||
1233 | |||
1234 | g_assert (fh->type & READ_TYPE); | ||
1235 | |||
1236 | if (fh->lru_table->len < (pgno + 1)) | ||
1237 | { | ||
1238 | gint olen = fh->lru_table->len; | ||
1239 | |||
1240 | g_ptr_array_set_size (fh->lru_table, pgno + 1); | ||
1241 | |||
1242 | while (olen <= pgno) | ||
1243 | fh->lru_table->pdata[olen++] = NULL; | ||
1244 | } | ||
1245 | |||
1246 | lru = fh->lru_table->pdata[pgno]; | ||
1247 | |||
1248 | if (! lru) | ||
1249 | { | ||
1250 | lru = g_chunk_new0 (LRU, fh->lru_chunk); | ||
1251 | fh->lru_table->pdata[pgno] = lru; | ||
1252 | lru->page = pgno; | ||
1253 | } | ||
1254 | else if (lru->buffer) | ||
1255 | { | ||
1256 | pull_lru (fh, lru); | ||
1257 | } | ||
1258 | |||
1259 | lru->prev = fh->lru_head; | ||
1260 | lru->next = NULL; | ||
1261 | |||
1262 | fh->lru_head = lru; | ||
1263 | |||
1264 | if (lru->prev) | ||
1265 | lru->prev->next = lru; | ||
1266 | |||
1267 | if (! fh->lru_tail) | ||
1268 | fh->lru_tail = lru; | ||
1269 | |||
1270 | to_map = on_page (fh, pgno); | ||
1271 | |||
1272 | if (to_map < 0) | ||
1273 | { | ||
1274 | xd_error ("unexpected EOF in %s\n", fh->name); | ||
1275 | return -1; | ||
1276 | } | ||
1277 | |||
1278 | if (! lru->buffer) | ||
1279 | { | ||
1280 | if (! make_lru_room (fh)) | ||
1281 | return -1; | ||
1282 | |||
1283 | fh->lru_count += 1; | ||
1284 | |||
1285 | if (to_map > 0) | ||
1286 | { | ||
1287 | #ifdef WINHACK | ||
1288 | lru->buffer = g_malloc (to_map); | ||
1289 | |||
1290 | if (lseek (fh->fd, pgno * XD_PAGE_SIZE, SEEK_SET) < 0) | ||
1291 | { | ||
1292 | xd_error ("lseek failed: %s\n", g_strerror (errno)); | ||
1293 | return -1; | ||
1294 | } | ||
1295 | |||
1296 | if (read (fh->fd, lru->buffer, to_map) != to_map) | ||
1297 | { | ||
1298 | xd_error ("read failed: %s\n", g_strerror (errno)); | ||
1299 | return -1; | ||
1300 | } | ||
1301 | #else | ||
1302 | if (! (lru->buffer = mmap (NULL, to_map, PROT_READ, MAP_PRIVATE, fh->fd, pgno * XD_PAGE_SIZE))) | ||
1303 | { | ||
1304 | xd_error ("mmap failed: %s\n", g_strerror (errno)); | ||
1305 | return -1; | ||
1306 | } | ||
1307 | #endif | ||
1308 | } | ||
1309 | else | ||
1310 | { | ||
1311 | lru->buffer = (void*) -1; | ||
1312 | } | ||
1313 | |||
1314 | if (pgno == fh->md5_page) | ||
1315 | { | ||
1316 | if (! no_verify) | ||
1317 | edsio_md5_update (&fh->ctx, lru->buffer, to_map); | ||
1318 | fh->md5_page += 1; | ||
1319 | |||
1320 | if (fh->md5_page > xd_handle_pages (fh)) | ||
1321 | edsio_md5_final (fh->md5, &fh->ctx); | ||
1322 | } | ||
1323 | } | ||
1324 | |||
1325 | (*mem) = lru->buffer; | ||
1326 | |||
1327 | lru->refs += 1; | ||
1328 | fh->lru_outstanding_refs += 1; | ||
1329 | |||
1330 | return to_map; | ||
1331 | } | ||
1332 | |||
1333 | static gboolean | ||
1334 | xd_handle_unmap_page (XdFileHandle *fh, guint pgno, const guint8** mem) | ||
1335 | { | ||
1336 | LRU* lru; | ||
1337 | |||
1338 | #ifdef DEBUG_MAP | ||
1339 | g_print ("unmap %p:%d\n", fh, pgno); | ||
1340 | #endif | ||
1341 | |||
1342 | g_assert (fh->type & READ_TYPE); | ||
1343 | |||
1344 | g_assert (pgno < fh->lru_table->len); | ||
1345 | |||
1346 | lru = fh->lru_table->pdata[pgno]; | ||
1347 | |||
1348 | g_assert (lru && lru->refs > 0); | ||
1349 | |||
1350 | g_assert (lru->buffer == (*mem)); | ||
1351 | |||
1352 | (*mem) = NULL; | ||
1353 | |||
1354 | lru->refs -= 1; | ||
1355 | fh->lru_outstanding_refs += 1; | ||
1356 | |||
1357 | if (lru->refs == 0 && fh->type == READ_NOSEEK_TYPE) | ||
1358 | { | ||
1359 | pull_lru (fh, lru); | ||
1360 | |||
1361 | lru->next = fh->lru_tail; | ||
1362 | if (lru->next) lru->next->prev = lru; | ||
1363 | lru->prev = NULL; | ||
1364 | fh->lru_tail = lru; | ||
1365 | |||
1366 | if (! really_free_one_page (fh)) | ||
1367 | return FALSE; | ||
1368 | } | ||
1369 | |||
1370 | return TRUE; | ||
1371 | } | ||
1372 | |||
1373 | static gboolean | ||
1374 | xd_handle_copy (XdFileHandle *from, XdFileHandle *to, guint off, guint len) | ||
1375 | { | ||
1376 | if (from->in) | ||
1377 | { | ||
1378 | guint8 buf[1024]; | ||
1379 | |||
1380 | /*if (! xd_handle_set_pos (from, off)) | ||
1381 | return FALSE;*/ | ||
1382 | |||
1383 | while (len > 0) | ||
1384 | { | ||
1385 | guint r = MIN (1024, len); | ||
1386 | |||
1387 | if (xd_handle_read (from, buf, r) != r) | ||
1388 | return FALSE; | ||
1389 | |||
1390 | if (! xd_handle_write (to, buf, r)) | ||
1391 | return FALSE; | ||
1392 | |||
1393 | len -= r; | ||
1394 | } | ||
1395 | } | ||
1396 | else | ||
1397 | { | ||
1398 | while (len > 0) | ||
1399 | { | ||
1400 | guint off_page = off / XD_PAGE_SIZE; | ||
1401 | guint off_off = off % XD_PAGE_SIZE; | ||
1402 | |||
1403 | gint on = on_page (from, off_page); | ||
1404 | guint rem; | ||
1405 | guint copy; | ||
1406 | |||
1407 | if (on <= 0) | ||
1408 | { | ||
1409 | xd_error ("unexpected EOF in %s\n", from->name); | ||
1410 | return FALSE; | ||
1411 | } | ||
1412 | |||
1413 | rem = on - off_off; | ||
1414 | copy = MIN (len, rem); | ||
1415 | |||
1416 | if (from->copy_pgno != off_page && | ||
1417 | from->copy_page && | ||
1418 | ! xd_handle_unmap_page (from, from->copy_pgno, &from->copy_page)) | ||
1419 | return FALSE; | ||
1420 | |||
1421 | from->copy_pgno = off_page; | ||
1422 | |||
1423 | if (xd_handle_map_page (from, off_page, &from->copy_page) < 0) | ||
1424 | return FALSE; | ||
1425 | |||
1426 | if (! xd_handle_write (to, from->copy_page + off_off, copy)) | ||
1427 | return FALSE; | ||
1428 | |||
1429 | if (! xd_handle_unmap_page (from, off_page, &from->copy_page)) | ||
1430 | return FALSE; | ||
1431 | |||
1432 | len -= copy; | ||
1433 | off += copy; | ||
1434 | } | ||
1435 | } | ||
1436 | |||
1437 | return TRUE; | ||
1438 | } | ||
1439 | |||
1440 | static gboolean | ||
1441 | xd_handle_putui (XdFileHandle *fh, guint32 i) | ||
1442 | { | ||
1443 | guint32 hi = g_htonl (i); | ||
1444 | |||
1445 | return xd_handle_write (fh, (guint8*)&hi, 4); | ||
1446 | } | ||
1447 | |||
1448 | static gboolean | ||
1449 | xd_handle_getui (XdFileHandle *fh, guint32* i) | ||
1450 | { | ||
1451 | if (xd_handle_read (fh, (guint8*)i, 4) != 4) | ||
1452 | return FALSE; | ||
1453 | |||
1454 | *i = g_ntohl (*i); | ||
1455 | |||
1456 | return TRUE; | ||
1457 | } | ||
1458 | |||
1459 | static HandleFuncTable xd_handle_table = | ||
1460 | { | ||
1461 | (gssize (*) (FileHandle *fh)) xd_handle_length, | ||
1462 | (gssize (*) (FileHandle *fh)) xd_handle_pages, | ||
1463 | (gssize (*) (FileHandle *fh)) xd_handle_pagesize, | ||
1464 | (gssize (*) (FileHandle *fh, guint pgno, const guint8** mem)) xd_handle_map_page, | ||
1465 | (gboolean (*) (FileHandle *fh, guint pgno, const guint8** mem)) xd_handle_unmap_page, | ||
1466 | (const guint8* (*) (FileHandle *fh)) xd_handle_checksum_md5, | ||
1467 | |||
1468 | (gboolean (*) (FileHandle *fh, gint flags)) xd_handle_close, | ||
1469 | |||
1470 | (gboolean (*) (FileHandle *fh, const guint8 *buf, gsize nbyte)) xd_handle_write, | ||
1471 | (gboolean (*) (FileHandle *from, FileHandle *to, guint off, guint len)) xd_handle_copy, | ||
1472 | |||
1473 | (gboolean (*) (FileHandle *fh, guint32* i)) xd_handle_getui, | ||
1474 | (gboolean (*) (FileHandle *fh, guint32 i)) xd_handle_putui, | ||
1475 | (gssize (*) (FileHandle *fh, guint8 *buf, gsize nbyte)) xd_handle_read, | ||
1476 | (const gchar* (*) (FileHandle *fh)) xd_handle_name, | ||
1477 | }; | ||
1478 | |||
1479 | static void | ||
1480 | htonl_array (guint32* array, gint len) | ||
1481 | { | ||
1482 | gint i; | ||
1483 | |||
1484 | for (i = 0; i < len; i += 1) | ||
1485 | array[i] = g_htonl(array[i]); | ||
1486 | } | ||
1487 | |||
1488 | static void | ||
1489 | ntohl_array (guint32* array, gint len) | ||
1490 | { | ||
1491 | gint i; | ||
1492 | |||
1493 | for (i = 0; i < len; i += 1) | ||
1494 | array[i] = g_ntohl(array[i]); | ||
1495 | } | ||
1496 | |||
1497 | static gint | ||
1498 | delta_command (gint argc, gchar** argv) | ||
1499 | { | ||
1500 | gint patch_out_fd; | ||
1501 | const char* patch_out_name; | ||
1502 | XdFileHandle *from, *to, *out; | ||
1503 | XdeltaGenerator* gen; | ||
1504 | XdeltaSource* src; | ||
1505 | XdeltaControl* cont; | ||
1506 | gboolean from_is_compressed = FALSE, to_is_compressed = FALSE; | ||
1507 | guint32 control_offset, header_offset; | ||
1508 | const char* from_name, *to_name; | ||
1509 | guint32 header_space[HEADER_WORDS]; | ||
1510 | int fd; | ||
1511 | |||
1512 | memset (header_space, 0, sizeof (header_space)); | ||
1513 | |||
1514 | if (argc != 3) | ||
1515 | { | ||
1516 | xd_error ("usage: %s delta fromfile tofile patchfile\n", program_name); | ||
1517 | return 2; | ||
1518 | } | ||
1519 | |||
1520 | if (verbose) | ||
1521 | { | ||
1522 | xd_error ("using block size: %d bytes\n", xdp_blocksize ()); | ||
1523 | } | ||
1524 | |||
1525 | |||
1526 | if (! (from = open_read_seek_handle (argv[0], &from_is_compressed, TRUE))) | ||
1527 | return 2; | ||
1528 | |||
1529 | if (! (to = open_read_noseek_handle (argv[1], &to_is_compressed, FALSE, TRUE))) | ||
1530 | return 2; | ||
1531 | |||
1532 | // Note: I tried support to patches to stdout, but it broke when | ||
1533 | // compression was added. Sigh | ||
1534 | fd = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); | ||
1535 | |||
1536 | if (fd < 0) | ||
1537 | { | ||
1538 | xd_error ("open %s failed: %s\n", argv[2], g_strerror (errno)); | ||
1539 | return 2; | ||
1540 | } | ||
1541 | |||
1542 | patch_out_fd = fd; | ||
1543 | patch_out_name = argv[2]; | ||
1544 | |||
1545 | from_name = g_basename (argv[0]); | ||
1546 | to_name = g_basename (argv[1]); | ||
1547 | |||
1548 | if (! (out = open_write_handle (patch_out_fd, patch_out_name))) | ||
1549 | return 2; | ||
1550 | |||
1551 | if (! (gen = xdp_generator_new ())) | ||
1552 | return 2; | ||
1553 | |||
1554 | if (! (src = xdp_source_new (from_name, (FileHandle*) from, NULL, NULL))) | ||
1555 | return 2; | ||
1556 | |||
1557 | xdp_source_add (gen, src); | ||
1558 | |||
1559 | if (! xd_handle_write (out, XDELTA_PREFIX, XDELTA_PREFIX_LEN)) | ||
1560 | return 2; | ||
1561 | |||
1562 | /* compute the header */ | ||
1563 | header_space[0] = 0; | ||
1564 | |||
1565 | if (no_verify) header_space[0] |= FLAG_NO_VERIFY; | ||
1566 | if (from_is_compressed) header_space[0] |= FLAG_FROM_COMPRESSED; | ||
1567 | if (to_is_compressed) header_space[0] |= FLAG_TO_COMPRESSED; | ||
1568 | if (compress_level != 0) header_space[0] |= FLAG_PATCH_COMPRESSED; | ||
1569 | |||
1570 | header_space[1] = strlen (from_name) << 16 | strlen (to_name); | ||
1571 | /* end compute the header */ | ||
1572 | |||
1573 | htonl_array (header_space, HEADER_WORDS); | ||
1574 | |||
1575 | if (! xd_handle_write (out, (guint8*) header_space, HEADER_SPACE)) | ||
1576 | return 2; | ||
1577 | |||
1578 | if (! xd_handle_write (out, from_name, strlen (from_name))) | ||
1579 | return 2; | ||
1580 | |||
1581 | if (! xd_handle_write (out, to_name, strlen (to_name))) | ||
1582 | return 2; | ||
1583 | |||
1584 | if (! xd_handle_close (out, 0)) | ||
1585 | return 2; | ||
1586 | |||
1587 | if ((header_offset = xd_begin_compression (out)) < 0) | ||
1588 | return 2; | ||
1589 | |||
1590 | if (! (cont = xdp_generate_delta (gen, (FileHandle*) to, NULL, (FileHandle*) out))) | ||
1591 | return 2; | ||
1592 | |||
1593 | #if 0 | ||
1594 | serializeio_print_xdeltacontrol_obj (cont, 0); | ||
1595 | #endif | ||
1596 | |||
1597 | if (cont->has_data && cont->has_data == cont->source_info_len) | ||
1598 | { | ||
1599 | if (! quiet) | ||
1600 | xd_error ("warning: no matches found in from file, patch will apply without it\n"); | ||
1601 | } | ||
1602 | |||
1603 | if (! xd_handle_close (out, 0)) | ||
1604 | return 2; | ||
1605 | |||
1606 | if ((control_offset = xd_begin_compression (out)) < 0) | ||
1607 | return 2; | ||
1608 | |||
1609 | if (! xdp_control_write (cont, (FileHandle*) out)) | ||
1610 | return 2; | ||
1611 | |||
1612 | if (! xd_end_compression (out)) | ||
1613 | return 2; | ||
1614 | |||
1615 | if (! xd_handle_putui (out, control_offset)) | ||
1616 | return 2; | ||
1617 | |||
1618 | if (! xd_handle_write (out, XDELTA_PREFIX, XDELTA_PREFIX_LEN)) | ||
1619 | return 2; | ||
1620 | |||
1621 | xd_read_close (from); | ||
1622 | xd_read_close (to); | ||
1623 | |||
1624 | if (! xd_handle_really_close (out)) | ||
1625 | return 2; | ||
1626 | |||
1627 | xdp_generator_free (gen); | ||
1628 | |||
1629 | return control_offset != header_offset; | ||
1630 | } | ||
1631 | |||
1632 | static XdeltaPatch* | ||
1633 | process_patch (const char* name) | ||
1634 | { | ||
1635 | XdeltaPatch* patch; | ||
1636 | guint total_trailer; | ||
1637 | |||
1638 | patch = g_new0 (XdeltaPatch, 1); | ||
1639 | |||
1640 | patch->patch_name = name; | ||
1641 | |||
1642 | /* Strictly speaking, I'm violating the intended semantics of noseek here. | ||
1643 | * It will seek the file, which is not in fact checked in the map/unmap | ||
1644 | * logic above. This only means that it will not cache pages of this file | ||
1645 | * since it will be read piecewise sequentially. */ | ||
1646 | if (! (patch->patch_in = open_read_noseek_handle (name, &patch->patch_is_compressed, TRUE, TRUE))) | ||
1647 | return NULL; | ||
1648 | |||
1649 | if (xd_handle_read (patch->patch_in, patch->magic_buf, XDELTA_PREFIX_LEN) != XDELTA_PREFIX_LEN) | ||
1650 | return NULL; | ||
1651 | |||
1652 | if (xd_handle_read (patch->patch_in, (guint8*) patch->header_space, HEADER_SPACE) != HEADER_SPACE) | ||
1653 | return NULL; | ||
1654 | |||
1655 | ntohl_array (patch->header_space, HEADER_WORDS); | ||
1656 | |||
1657 | if (strncmp (patch->magic_buf, XDELTA_110_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1658 | { | ||
1659 | patch->has_trailer = TRUE; | ||
1660 | patch->patch_version = "1.1"; | ||
1661 | } | ||
1662 | else if (strncmp (patch->magic_buf, XDELTA_104_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1663 | { | ||
1664 | patch->has_trailer = TRUE; | ||
1665 | patch->patch_version = "1.0.4"; | ||
1666 | } | ||
1667 | else if (strncmp (patch->magic_buf, XDELTA_100_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1668 | { | ||
1669 | patch->patch_version = "1.0"; | ||
1670 | } | ||
1671 | else if (strncmp (patch->magic_buf, XDELTA_020_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1672 | goto nosupport; | ||
1673 | else if (strncmp (patch->magic_buf, XDELTA_018_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1674 | goto nosupport; | ||
1675 | else if (strncmp (patch->magic_buf, XDELTA_014_PREFIX, XDELTA_PREFIX_LEN) == 0) | ||
1676 | goto nosupport; | ||
1677 | else | ||
1678 | { | ||
1679 | xd_error ("%s: bad magic number: not a valid delta\n", name); | ||
1680 | return NULL; | ||
1681 | } | ||
1682 | |||
1683 | patch->patch_flags = patch->header_space[0]; | ||
1684 | |||
1685 | if (no_verify) | ||
1686 | xd_error ("--noverify is only accepted when creating a delta\n"); | ||
1687 | |||
1688 | if (patch->patch_flags & FLAG_NO_VERIFY) | ||
1689 | no_verify = TRUE; | ||
1690 | else | ||
1691 | no_verify = FALSE; | ||
1692 | |||
1693 | patch->from_name_len = patch->header_space[1] >> 16; | ||
1694 | patch->to_name_len = patch->header_space[1] & 0xffff; | ||
1695 | |||
1696 | patch->from_name = g_malloc (patch->from_name_len+1); | ||
1697 | patch->to_name = g_malloc (patch->to_name_len+1); | ||
1698 | |||
1699 | patch->from_name[patch->from_name_len] = 0; | ||
1700 | patch->to_name[patch->to_name_len] = 0; | ||
1701 | |||
1702 | if (xd_handle_read (patch->patch_in, patch->from_name, patch->from_name_len) != patch->from_name_len) | ||
1703 | return NULL; | ||
1704 | |||
1705 | if (xd_handle_read (patch->patch_in, patch->to_name, patch->to_name_len) != patch->to_name_len) | ||
1706 | return NULL; | ||
1707 | |||
1708 | patch->header_offset = xd_handle_get_pos (patch->patch_in); | ||
1709 | |||
1710 | total_trailer = 4 + (patch->has_trailer ? XDELTA_PREFIX_LEN : 0); | ||
1711 | |||
1712 | if (! xd_handle_set_pos (patch->patch_in, xd_handle_length (patch->patch_in) - total_trailer)) | ||
1713 | return NULL; | ||
1714 | |||
1715 | if (! xd_handle_getui (patch->patch_in, &patch->control_offset)) | ||
1716 | return NULL; | ||
1717 | |||
1718 | if (patch->has_trailer) | ||
1719 | { | ||
1720 | guint8 trailer_buf[XDELTA_PREFIX_LEN]; | ||
1721 | |||
1722 | if (xd_handle_read (patch->patch_in, trailer_buf, XDELTA_PREFIX_LEN) != XDELTA_PREFIX_LEN) | ||
1723 | return NULL; | ||
1724 | |||
1725 | if (strncmp (trailer_buf, patch->magic_buf, XDELTA_PREFIX_LEN) != 0) | ||
1726 | { | ||
1727 | xd_error ("%s: bad trailing magic number, delta is corrupt\n", name); | ||
1728 | return NULL; | ||
1729 | } | ||
1730 | } | ||
1731 | |||
1732 | if (! xd_handle_narrow (patch->patch_in, patch->control_offset, | ||
1733 | xd_handle_length (patch->patch_in) - total_trailer, | ||
1734 | patch->patch_flags & FLAG_PATCH_COMPRESSED)) | ||
1735 | return NULL; | ||
1736 | |||
1737 | if (! (patch->cont = xdp_control_read ((FileHandle*) patch->patch_in))) | ||
1738 | return NULL; | ||
1739 | |||
1740 | if (patch->cont->source_info_len > 0) | ||
1741 | { | ||
1742 | XdeltaSourceInfo* info = patch->cont->source_info[0]; | ||
1743 | |||
1744 | if (info->isdata) | ||
1745 | patch->data_source = info; | ||
1746 | else | ||
1747 | { | ||
1748 | patch->from_source = info; | ||
1749 | |||
1750 | if (patch->cont->source_info_len > 1) | ||
1751 | { | ||
1752 | xd_generate_void_event (EC_XdIncompatibleDelta); | ||
1753 | return NULL; | ||
1754 | } | ||
1755 | } | ||
1756 | } | ||
1757 | |||
1758 | if (patch->cont->source_info_len > 1) | ||
1759 | { | ||
1760 | patch->from_source = patch->cont->source_info[1]; | ||
1761 | } | ||
1762 | |||
1763 | if (patch->cont->source_info_len > 2) | ||
1764 | { | ||
1765 | xd_generate_void_event (EC_XdIncompatibleDelta); | ||
1766 | return NULL; | ||
1767 | } | ||
1768 | |||
1769 | if (! xd_handle_narrow (patch->patch_in, | ||
1770 | patch->header_offset, | ||
1771 | patch->control_offset, | ||
1772 | patch->patch_flags & FLAG_PATCH_COMPRESSED)) | ||
1773 | return NULL; | ||
1774 | |||
1775 | return patch; | ||
1776 | |||
1777 | nosupport: | ||
1778 | |||
1779 | xd_error ("delta format is unsupported (too old)\n"); | ||
1780 | return NULL; | ||
1781 | } | ||
1782 | |||
1783 | static gint | ||
1784 | info_command (gint argc, gchar** argv) | ||
1785 | { | ||
1786 | XdeltaPatch* patch; | ||
1787 | char buf[33]; | ||
1788 | int i; | ||
1789 | XdeltaSourceInfo* si; | ||
1790 | |||
1791 | if (! (patch = process_patch (argv[0]))) | ||
1792 | return 2; | ||
1793 | |||
1794 | xd_error_file = stdout; | ||
1795 | |||
1796 | xd_error ("version %s found patch version %s in %s%s\n", | ||
1797 | xdelta_version, | ||
1798 | patch->patch_version, | ||
1799 | patch->patch_name, | ||
1800 | patch->patch_flags & FLAG_PATCH_COMPRESSED ? " (compressed)" : ""); | ||
1801 | |||
1802 | if (patch->patch_flags & FLAG_NO_VERIFY) | ||
1803 | xd_error ("generated with --noverify\n"); | ||
1804 | |||
1805 | if (patch->patch_flags & FLAG_FROM_COMPRESSED) | ||
1806 | xd_error ("generated with a gzipped FROM file\n"); | ||
1807 | |||
1808 | if (patch->patch_flags & FLAG_TO_COMPRESSED) | ||
1809 | xd_error ("generated with a gzipped TO file\n"); | ||
1810 | |||
1811 | edsio_md5_to_string (patch->cont->to_md5, buf); | ||
1812 | |||
1813 | xd_error ("output name: %s\n", patch->to_name); | ||
1814 | xd_error ("output length: %d\n", patch->cont->to_len); | ||
1815 | xd_error ("output md5: %s\n", buf); | ||
1816 | |||
1817 | xd_error ("patch from segments: %d\n", patch->cont->source_info_len); | ||
1818 | |||
1819 | xd_error ("MD5\t\t\t\t\tLength\tCopies\tUsed\tSeq?\tName\n"); | ||
1820 | |||
1821 | for (i = 0; i < patch->cont->source_info_len; i += 1) | ||
1822 | { | ||
1823 | si = patch->cont->source_info[i]; | ||
1824 | |||
1825 | edsio_md5_to_string (si->md5, buf); | ||
1826 | |||
1827 | xd_error ("%s\t%d\t%d\t%d\t%s\t%s\n", | ||
1828 | buf, | ||
1829 | si->len, | ||
1830 | si->copies, | ||
1831 | si->copy_length, | ||
1832 | si->sequential ? "yes" : "no", | ||
1833 | si->name); | ||
1834 | } | ||
1835 | |||
1836 | return 0; | ||
1837 | } | ||
1838 | |||
1839 | static gint | ||
1840 | patch_command (gint argc, gchar** argv) | ||
1841 | { | ||
1842 | XdFileHandle* to_out; | ||
1843 | XdeltaPatch* patch; | ||
1844 | gint to_out_fd; | ||
1845 | int count = 1; | ||
1846 | int ret; | ||
1847 | |||
1848 | if (argc < 1 || argc > 3) | ||
1849 | { | ||
1850 | xd_error ("usage: %s patch patchfile [fromfile [tofile]]\n", program_name); | ||
1851 | return 2; | ||
1852 | } | ||
1853 | |||
1854 | if (! (patch = process_patch (argv[0]))) | ||
1855 | return 2; | ||
1856 | |||
1857 | if (argc > 1) | ||
1858 | patch->from_name = argv[1]; | ||
1859 | else if (verbose) | ||
1860 | xd_error ("using from file name: %s\n", patch->from_name); | ||
1861 | |||
1862 | if (argc > 2) | ||
1863 | patch->to_name = argv[2]; | ||
1864 | else | ||
1865 | { | ||
1866 | struct stat sbuf; | ||
1867 | gchar *defname = g_strdup (patch->to_name); | ||
1868 | |||
1869 | while ((ret = stat (patch->to_name, & sbuf)) == 0) | ||
1870 | { | ||
1871 | if (verbose) | ||
1872 | xd_error ("to file exists: %s\n", patch->to_name); | ||
1873 | patch->to_name = g_strdup_printf ("%s.xdp%d", defname, count++); | ||
1874 | } | ||
1875 | |||
1876 | if (verbose || strcmp (defname, patch->to_name) != 0) | ||
1877 | xd_error ("using to file name: %s\n", patch->to_name); | ||
1878 | } | ||
1879 | |||
1880 | if (strcmp (patch->to_name, "-") == 0) | ||
1881 | { | ||
1882 | to_out_fd = STDOUT_FILENO; | ||
1883 | patch->to_name = "standard output"; | ||
1884 | } | ||
1885 | else | ||
1886 | { | ||
1887 | to_out_fd = open (patch->to_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); | ||
1888 | |||
1889 | if (to_out_fd < 0) | ||
1890 | { | ||
1891 | xd_error ("open %s failed: %s\n", patch->to_name, g_strerror (errno)); | ||
1892 | return 2; | ||
1893 | } | ||
1894 | } | ||
1895 | |||
1896 | to_out = open_write_handle (to_out_fd, patch->to_name); | ||
1897 | |||
1898 | if ((patch->patch_flags & FLAG_TO_COMPRESSED) && (xd_begin_compression (to_out) < 0)) | ||
1899 | return 2; | ||
1900 | |||
1901 | if (patch->from_source) | ||
1902 | { | ||
1903 | XdFileHandle* from_in; | ||
1904 | gboolean from_is_compressed = FALSE; | ||
1905 | |||
1906 | if (! (from_in = open_read_seek_handle (patch->from_name, &from_is_compressed, TRUE))) | ||
1907 | return 2; | ||
1908 | |||
1909 | if (from_is_compressed != ((patch->patch_flags & FLAG_FROM_COMPRESSED) && 1)) | ||
1910 | xd_error ("warning: expected %scompressed from file (%s)\n", | ||
1911 | (patch->patch_flags & FLAG_FROM_COMPRESSED) ? "" : "un", | ||
1912 | patch->from_name); | ||
1913 | |||
1914 | if (xd_handle_length (from_in) != patch->from_source->len) | ||
1915 | { | ||
1916 | xd_error ("expected from file (%s) of %slength %d bytes\n", | ||
1917 | patch->from_name, | ||
1918 | from_is_compressed ? "uncompressed " : "", | ||
1919 | patch->from_source->len); | ||
1920 | return 2; | ||
1921 | } | ||
1922 | |||
1923 | patch->from_source->in = (XdeltaStream*) from_in; | ||
1924 | } | ||
1925 | |||
1926 | if (patch->data_source) | ||
1927 | patch->data_source->in = (XdeltaStream*) patch->patch_in; | ||
1928 | |||
1929 | if (! xdp_apply_delta (patch->cont, (FileHandle*) to_out)) | ||
1930 | return 2; | ||
1931 | |||
1932 | if (patch->from_source) | ||
1933 | xd_read_close ((XdFileHandle*) patch->from_source->in); | ||
1934 | |||
1935 | xd_read_close (patch->patch_in); | ||
1936 | |||
1937 | if (! xd_handle_really_close (to_out)) | ||
1938 | return 2; | ||
1939 | |||
1940 | return 0; | ||
1941 | } | ||