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