1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26
27 #include "cctree-priv.h"
28 #include "copycat-priv.h"
29 #include <klib/printf.h>
30 #include <klib/rc.h>
31 #include <kapp/main.h>
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <assert.h>
37 #include <time.h>
38
39
40 /*--------------------------------------------------------------------------
41 * fowards
42 */
43 static bool CCNameDump ( BSTNode *n, void *data );
44
45
46 /*--------------------------------------------------------------------------
47 * CCDumper
48 */
49 typedef struct CCDumper CCDumper;
50 struct CCDumper
51 {
52 rc_t ( * flush ) ( void*, const void*, size_t );
53 void *out;
54
55 const char *sep;
56
57 #if ! STORE_ID_IN_NODE
58 uint32_t id;
59 #endif
60 uint32_t indent;
61 rc_t rc;
62
63 size_t total;
64 char buffer [ 4096 ];
65 };
66
67
68 /* Init
69 * sets up block
70 */
71 static
CCDumperInit(CCDumper * self,rc_t (* flush)(void * out,const void * buffer,size_t size),void * out)72 void CCDumperInit ( CCDumper *self,
73 rc_t ( * flush ) ( void *out, const void *buffer, size_t size ), void *out )
74 {
75 self -> flush = flush;
76 self -> out = out;
77 self -> sep = "";
78 #if ! STORE_ID_IN_NODE
79 self -> id = 0;
80 #endif
81 self -> indent = 0;
82 self -> rc = 0;
83 self -> total = 0;
84 }
85
86
87 /* Flush
88 */
89 static
CCDumperFlush(CCDumper * self)90 rc_t CCDumperFlush ( CCDumper *self )
91 {
92 rc_t rc = ( * self -> flush ) ( self -> out, self -> buffer, self -> total );
93 if ( rc == 0 )
94 self -> total = 0;
95 return rc;
96 }
97
98 static
CCDumperFlushLine(CCDumper * self)99 rc_t CCDumperFlushLine ( CCDumper *self )
100 {
101 #if ! _DEBUGGING
102 if ( self -> total < sizeof self -> buffer / 2 )
103 return 0;
104 #endif
105 return CCDumperFlush ( self );
106 }
107
108
109 /* Whack
110 * flushes buffer if necessary
111 */
112 static
CCDumperWhack(CCDumper * self)113 rc_t CCDumperWhack ( CCDumper *self )
114 {
115 if ( self -> rc == 0 && self -> total != 0 )
116 return CCDumperFlush ( self );
117 return 0;
118 }
119
120
121 /* Write
122 * writes data to buffer, flushes as necessary
123 */
124 static
CCDumperWrite(CCDumper * self,const char * buffer,size_t size)125 rc_t CCDumperWrite ( CCDumper *self, const char *buffer, size_t size )
126 {
127 rc_t rc;
128 size_t total, num_writ;
129
130 for ( rc = 0, total = 0; total < size; total += num_writ )
131 {
132 if ( self -> total == sizeof self -> buffer )
133 {
134 rc = CCDumperFlush ( self );
135 if ( rc != 0 )
136 break;
137 }
138
139 num_writ = size - total;
140 if ( num_writ > sizeof self -> buffer - self -> total )
141 num_writ = sizeof self -> buffer - self -> total;
142
143 memmove ( & self -> buffer [ self -> total ], & buffer [ total ], num_writ );
144 self -> total += num_writ;
145 }
146
147 return rc;
148 }
149
150 /* IndentLevel
151 * increase or decrease indentation level
152 */
153 static
CCDumperIncIndentLevel(CCDumper * self)154 void CCDumperIncIndentLevel ( CCDumper *self )
155 {
156 ++ self -> indent;
157 }
158
159 static
CCDumperDecIndentLevel(CCDumper * self)160 void CCDumperDecIndentLevel ( CCDumper *self )
161 {
162 if ( self -> indent > 0 )
163 -- self -> indent;
164 }
165
166
167 /* Indent
168 * writes indentation spacing
169 */
170 static
CCDumperIndent(CCDumper * self)171 rc_t CCDumperIndent ( CCDumper *self )
172 {
173 rc_t rc;
174 uint32_t total, num_writ;
175
176 /* use 2 spaces per tab */
177 const char *tabs = " ";
178
179 for ( rc = 0, total = 0; total < self -> indent; total += num_writ )
180 {
181 num_writ = ( ( self -> indent - total - 1 ) & 0xF ) + 1;
182 rc = CCDumperWrite ( self, tabs, num_writ + num_writ );
183 if ( rc != 0 )
184 break;
185 }
186
187 return rc;
188 }
189
190 /* Sep
191 * write separator string
192 */
193 static
CCDumperSep(CCDumper * self)194 rc_t CCDumperSep ( CCDumper *self )
195 {
196 if ( self -> sep == NULL )
197 return 0;
198
199 return CCDumperWrite ( self, self -> sep, strlen ( self -> sep ) );
200 }
201
202 /* Print
203 * \t - indent
204 * \n - end of line
205 * %d - integer
206 * %u - unsigned
207 * %ld - int64_t
208 * %lu - uint64_t
209 * %f - double
210 * %s - null-terminated C-string
211 * %p - separator
212 * %S - String*
213 * %I - unique id
214 * %T - timestamp
215 * %M - MD5 digest
216 * %C - CRC32
217 * %N - CCName*
218 * %F - full CCName*
219 */
220 static
StringPrint(const String * self,CCDumper * d)221 rc_t StringPrint ( const String *self, CCDumper *d )
222 {
223 /* BUFF_WARNING_TRACK has to be larger than all size differences
224 * between a special character and its encoding in XML
225 * 8 is big enough for all I know about when writing this with some
226 * safety margin
227 */
228 #define BUFF_WARNING_TRACK 8
229 #define BUFF_SIZE (256)
230 #define REPLACE_COPY(C,S) \
231 case C: \
232 sp++;\
233 count--;\
234 memmove (bp,(S),sizeof(S)-1);\
235 bp += sizeof(S)-1;\
236 break
237
238 char buff [256 + 5];
239 char * bp;
240 const char * sp;
241 size_t count;
242 rc_t rc = 0;
243
244 /* start at the beginnings of the string and buffer */
245 sp = self->addr;
246 bp = buff;
247
248 count = self->size;
249
250 while (count > 0)
251 {
252 if ((bp - buff) > (BUFF_SIZE - BUFF_WARNING_TRACK))
253 {
254 rc = CCDumperWrite (d, buff, bp-buff);
255 if (rc)
256 return rc;
257 bp = buff;
258 }
259 switch (*sp)
260 {
261 /* just copy most characters */
262 default:
263 *bp++ = *sp++;
264 count --;
265 break;
266
267 REPLACE_COPY('<',"<");
268 REPLACE_COPY('>',">");
269 REPLACE_COPY('&',"&");
270 REPLACE_COPY('"',""");
271 REPLACE_COPY('\'',"'");
272
273 }
274 }
275 if (bp > buff)
276 rc = CCDumperWrite (d, buff, bp-buff);
277 return rc;
278
279 }
280
281 static
CCNamePrint(const CCName * self,CCDumper * d)282 rc_t CCNamePrint ( const CCName *self, CCDumper *d )
283 {
284 return StringPrint ( & self -> name, d );
285 }
286
287 static
CCNamePrintFull(const CCName * self,CCDumper * d)288 rc_t CCNamePrintFull ( const CCName *self, CCDumper *d )
289 {
290 if ( self -> dad != NULL )
291 {
292 rc_t rc = CCNamePrintFull ( self -> dad, d );
293 if ( rc == 0 )
294 rc = CCDumperWrite ( d, "/", 1 );
295 if ( rc != 0 )
296 return rc;
297 }
298 return CCNamePrint ( self, d );
299 }
300
301 static
KTimePrint(KTime_t self,CCDumper * d)302 rc_t KTimePrint ( KTime_t self, CCDumper *d )
303 {
304 int len;
305 char buffer [ 64 ];
306 time_t t = ( time_t ) self;
307
308 struct tm gmt;
309 gmtime_r ( & t, & gmt );
310 len = sprintf ( buffer, "%04d-%02d-%02dT%02d:%02d:%02dZ"
311 , gmt . tm_year + 1900
312 , gmt . tm_mon + 1
313 , gmt . tm_mday
314 , gmt . tm_hour
315 , gmt . tm_min
316 , gmt . tm_sec
317 );
318
319 return CCDumperWrite ( d, buffer, len );
320 }
321
322 static
MD5Print(const uint8_t * digest,CCDumper * d)323 rc_t MD5Print ( const uint8_t *digest, CCDumper *d )
324 {
325 int i, len;
326 char buff [ 36 ];
327
328 for ( i = len = 0; i < 16; ++ i )
329 len += sprintf ( & buff [ len ], "%02x", digest [ i ] );
330
331 return CCDumperWrite ( d, buff, 32 );
332 }
333
334 static
CCDumperVPrint(CCDumper * self,const char * fmt,va_list args)335 rc_t CCDumperVPrint ( CCDumper *self, const char *fmt, va_list args )
336 {
337 rc_t rc;
338 const char *start, *end;
339
340 for ( rc = 0, start = end = fmt; * end != 0; ++ end )
341 {
342 int len;
343 size_t size;
344 char buffer [ 256 ];
345
346 switch ( * end )
347 {
348 case '\t':
349 if ( end > start )
350 rc = CCDumperWrite ( self, start, end - start );
351 if ( rc == 0 )
352 rc = CCDumperIndent ( self );
353 start = end + 1;
354 break;
355 case '\n':
356 rc = CCDumperWrite ( self, start, end - start + 1 );
357 if ( rc == 0 )
358 rc = CCDumperFlushLine ( self );
359 start = end + 1;
360 break;
361 case '%':
362 if ( end > start )
363 {
364 rc = CCDumperWrite ( self, start, end - start );
365 if ( rc != 0 )
366 break;
367 }
368 switch ( * ( ++ end ) )
369 {
370 case 'd':
371 len = sprintf ( buffer, "%d", va_arg ( args, int ) );
372 rc = CCDumperWrite ( self, buffer, len );
373 break;
374 case 'u':
375 len = sprintf ( buffer, "%u", va_arg ( args, unsigned int ) );
376 rc = CCDumperWrite ( self, buffer, len );
377 break;
378 case 'x':
379 len = sprintf ( buffer, "%x", va_arg ( args, unsigned int ) );
380 rc = CCDumperWrite ( self, buffer, len );
381 break;
382 case 'f':
383 len = sprintf ( buffer, "%f", va_arg ( args, double ) );
384 rc = CCDumperWrite ( self, buffer, len );
385 break;
386 case 'l':
387 switch ( * ( ++ end ) )
388 {
389 case 'd':
390 rc = string_printf ( buffer, sizeof buffer, & size, "%ld", va_arg ( args, int64_t ) );
391 if ( rc == 0 )
392 rc = CCDumperWrite ( self, buffer, size );
393 break;
394 case 'u':
395 rc = string_printf ( buffer, sizeof buffer, & size, "%lu", va_arg ( args, uint64_t ) );
396 if ( rc == 0 )
397 rc = CCDumperWrite ( self, buffer, size );
398 break;
399 }
400 break;
401 case 's':
402 len = sprintf ( buffer, "%s", va_arg ( args, const char* ) );
403 rc = CCDumperWrite ( self, buffer, len );
404 break;
405 case 'p':
406 rc = CCDumperSep ( self );
407 break;
408 case 'S':
409 rc = StringPrint ( va_arg ( args, const String* ), self );
410 break;
411 case 'I':
412 #if STORE_ID_IN_NODE
413 len = sprintf ( buffer, "%u", va_arg ( args, uint32_t ) );
414 #else
415 len = sprintf ( buffer, "%u", ++ self -> id );
416 #endif
417 rc = CCDumperWrite ( self, buffer, len );
418 break;
419 case 'T':
420 rc = KTimePrint ( va_arg ( args, KTime_t ), self );
421 break;
422 case 'M':
423 rc = MD5Print ( va_arg ( args, const uint8_t* ), self );
424 break;
425 case 'C':
426 len = sprintf ( buffer, "%08x", va_arg ( args, unsigned int ) );
427 rc = CCDumperWrite ( self, buffer, len );
428 break;
429 case 'N':
430 rc = CCNamePrint ( va_arg ( args, const CCName* ), self );
431 break;
432 case 'F':
433 rc = CCNamePrintFull ( va_arg ( args, const CCName* ), self );
434 break;
435 case '%':
436 rc = CCDumperWrite ( self, "%", 1 );
437 break;
438 }
439 start = end + 1;
440 break;
441 }
442
443 if ( rc != 0 )
444 break;
445 }
446
447 if ( rc == 0 && end > start )
448 {
449 rc = CCDumperWrite ( self, start, end - start );
450 if ( rc == 0 )
451 rc = CCDumperFlushLine ( self );
452 }
453
454 return rc;
455 }
456
457 static
CCDumperPrint(CCDumper * self,const char * fmt,...)458 rc_t CCDumperPrint ( CCDumper *self, const char *fmt, ... )
459 {
460 rc_t rc;
461 va_list args;
462
463 va_start ( args, fmt );
464 rc = CCDumperVPrint ( self, fmt, args );
465 va_end ( args );
466
467 return rc;
468 }
469
470
471 /*--------------------------------------------------------------------------
472 * CCFileNode
473 * a node with a size and modification timestamp
474 * has a file type determined by magic/etc.
475 * has an md5 checksum
476 *
477 * how would an access mode be used? access mode of a file is
478 * whatever the filesystem says it is, and within an archive,
479 * it's read-only based upon access mode of outer file...
480 */
481
482 /* Dump
483 */
484 static
CCFileNodeDumpCmn(const CCFileNode * self,const char * tag,const CCName * name,const String * cached,CCDumper * d)485 rc_t CCFileNodeDumpCmn ( const CCFileNode *self, const char *tag,
486 const CCName *name, const String *cached, CCDumper *d )
487 {
488 rc_t rc = CCDumperPrint ( d,
489 "\t<%s " /* node class */
490 "id=\"%I\" " /* unique id */
491 "path=\"%F\" " /* full path */
492 "name=\"%N\" " /* node name */
493 , tag
494 #if STORE_ID_IN_NODE
495 , self -> id
496 #endif
497 , name
498 , name );
499 if ( rc == 0 && cached != NULL )
500 rc = CCDumperPrint ( d,
501 "cached=\"%S\" " /* cached name */
502 , cached );
503 if ( rc == 0 )
504 rc = CCDumperPrint ( d,
505 "size=\"%lu\" "
506 , self->size );
507 if (( rc == 0 ) && ( self->size > 0 ) && ( self->lines != 0 ))
508 rc = CCDumperPrint ( d,
509 "lines=\"%lu\" "
510 , self->lines );
511 if ( rc == 0 )
512 rc = CCDumperPrint ( d,
513 "mtime=\"%T\" " /* mod time */
514 , name -> mtime );
515 if ( rc == 0 )
516 {
517 if ( self -> rc )
518 rc = CCDumperPrint ( d,
519 "filetype=\"Errored%s\" " /* file type */
520 , self -> ftype );
521 else
522 rc = CCDumperPrint ( d,
523 "filetype=\"%s\" " /* file type */
524 , self -> ftype );
525 }
526 if ( rc == 0 && ! no_md5 )
527 rc = CCDumperPrint ( d,
528 "md5=\"%M\" " /* md5 digest */
529 , self -> _md5 );
530
531 return rc;
532 }
533
534 typedef struct dump_log_data
535 {
536 rc_t rc;
537 CCDumper * d;
538 } dump_log_data;
539
540 static
CCNodeDumpLog(void * n,CCDumper * d)541 rc_t CCNodeDumpLog ( void * n, CCDumper * d )
542 {
543 String s;
544
545 StringInitCString (&s, n); /* cast after add gets past node */
546
547 return CCDumperPrint ( d, "\t<CCError>%S</CCError>\n", &s );
548 }
549
550
551 static
CCFileNodeDump(const CCFileNode * cself,const char * tag,const CCName * name,const String * cached,CCDumper * d,bool close)552 rc_t CCFileNodeDump ( const CCFileNode *cself, const char *tag,
553 const CCName *name, const String *cached, CCDumper *d, bool close )
554 {
555 rc_t rc;
556 bool trunc;
557 CCFileNode * self = (CCFileNode *)cself;
558
559 trunc = ((self->expected != SIZE_UNKNOWN) &&
560 (self->expected != self->size));
561
562 rc = CCFileNodeDumpCmn ( self, tag, name, cached, d );
563 if ( rc == 0 && self -> crc32 != 0 )
564 rc = CCDumperPrint ( d, " crc32=\"%C\"", self -> crc32 );
565 if ( rc == 0 )
566 {
567 if (self->err || trunc || (self->logs.head != NULL))
568 {
569 do
570 {
571 rc = CCDumperPrint (d, ">\n");
572 if (rc) break;
573
574 if (trunc)
575 {
576 rc = CCDumperPrint (d, "\t<CCErrSize expected=\"%lu\">"
577 "Error in file size expected %lu but got %lu"
578 "</CCErrSize>\n", self->expected,
579 self->expected, self->size);
580 if (rc) break;
581 }
582
583 if (self->err)
584 {
585 if (self->logs.head != NULL)
586 {
587 SLNode* log;
588
589 while ((log = SLListPopHead (&self->logs)) != NULL)
590 {
591 CCNodeDumpLog (log+1, d);
592 free (log);
593 }
594 }
595 else
596 rc = CCDumperPrint (d, "\t<CCErr>Not specified</CCErr>\n");
597 }
598 if (rc) break;
599
600 if (close)
601 rc = CCDumperPrint (d, "\t</%s>\n", tag);
602 } while (0);
603 }
604 else if (close)
605 rc = CCDumperPrint (d, "/>\n");
606 else
607 rc = CCDumperPrint (d, ">\n");
608 }
609 return rc;
610 }
611
612 /*--------------------------------------------------------------------------
613 * CCArcFileNode
614 * a file with an offset into another file
615 */
616
617 /* Dump
618 */
619 static
CCArcFileNodeDump(const CCArcFileNode * cself,const char * tag,const CCName * name,const String * cached,CCDumper * d,bool close)620 rc_t CCArcFileNodeDump ( const CCArcFileNode *cself, const char *tag,
621 const CCName *name, const String *cached, CCDumper *d, bool close )
622 {
623 rc_t rc;
624 bool trunc;
625 CCArcFileNode * self = (CCArcFileNode *)cself;
626
627 trunc = ((self->dad.expected != SIZE_UNKNOWN) &&
628 (self->dad.expected != self->dad.size));
629
630 rc = CCFileNodeDumpCmn ( & self -> dad, tag, name, cached, d );
631 if ( rc == 0 )
632 {
633 if (!xml_dir)
634 rc = CCDumperPrint ( d, " offset=\"%lu\"", self->offset);
635 if (rc == 0) do
636 {
637 if (self->dad.err || trunc || (self->dad.logs.head != NULL))
638 {
639 rc = CCDumperPrint (d, ">\n");
640 if (rc) break;
641
642 if (trunc)
643 {
644 rc = CCDumperPrint (d, "\t<CCErrSize expected=\"%lu\">"
645 "Error in file size expected %lu but got %lu"
646 "</CCErrSize>\n", self->dad.expected,
647 self->dad.expected, self->dad.size);
648 if (rc) break;
649 }
650
651 if (self->dad.err)
652 {
653 if (self->dad.logs.head != NULL)
654 {
655 SLNode* log;
656
657 while ((log = SLListPopHead (&self->dad.logs)) != NULL)
658 {
659 CCNodeDumpLog (log+1, d);
660 free (log);
661 }
662 }
663 else
664 rc = CCDumperPrint (d, "\t<CCErr>Not specified</CCErr>\n");
665 }
666 if (rc) break;
667
668 if (close)
669 rc = CCDumperPrint (d, "\t</%s>\n", tag);
670 }
671 else if (close)
672 rc = CCDumperPrint (d, "/>\n");
673 else
674 rc = CCDumperPrint (d, ">\n");
675 } while (0);
676 }
677 return rc;
678 }
679
680
681 /*--------------------------------------------------------------------------
682 * CChunkFileNode
683 * a file with one or more chunks (offset/size) into another file
684 */
685
686 /* Dump
687 */
688 static
CChunkDump(SLNode * n,void * data)689 bool CChunkDump ( SLNode *n, void *data )
690 {
691 CCDumper *d = data;
692 const CChunk *self = ( const CChunk* ) n;
693
694 d -> rc = CCDumperPrint ( d, "\t<chunk offset=\"%lu\" size=\"%lu\"/>\n",
695 self -> offset, self -> size );
696
697 return ( d -> rc != 0 ) ? true : false;
698 }
699
700 static
CChunkFileNodeDump(const CChunkFileNode * self,const char * tag,const CCName * name,const String * cached,CCDumper * d,bool close)701 rc_t CChunkFileNodeDump ( const CChunkFileNode *self, const char *tag,
702 const CCName *name, const String *cached, CCDumper *d, bool close )
703 {
704 rc_t rc = CCFileNodeDumpCmn ( & self -> dad, tag, name, cached, d );
705 if ( rc == 0 )
706 rc = CCDumperPrint ( d, " size=\"%lu\">\n", self -> dad . size );
707 CCDumperIncIndentLevel ( d );
708 if ( rc == 0 )
709 {
710 if ( SLListDoUntil ( & self -> chunks, CChunkDump, d ) )
711 rc = d -> rc;
712 }
713 CCDumperDecIndentLevel ( d );
714 if ( rc == 0 && close )
715 rc = CCDumperPrint ( d, "\t</%s>\n", tag );
716
717 return rc;
718 }
719
720
721 /*--------------------------------------------------------------------------
722 * CCCachedFileNode
723 * a file wrapper with cached file name
724 */
725
726 /* Dump
727 */
728 #if 0 /* why is this commented out... */
729 static
730 rc_t CCCachedFileNodeDump ( const CCCachedFileNode *self,
731 const CCName *name, CCDumper *d )
732 {
733 rc_t rc;
734 const void *entry = self -> entry;
735
736 switch ( self -> type )
737 {
738 case ccFile:
739 case ccContFile:
740 rc = CCFileNodeDump ( entry, "file", name, & self -> cached, d, true );
741 break;
742 case ccArcFile:
743 rc = CCArcFileNodeDump ( entry, "file", name, & self -> cached, d, true );
744 break;
745 case ccChunkFile:
746 rc = CChunkFileNodeDump ( entry, "file", name, & self -> cached, d, true );
747 break;
748 default:
749 rc = RC ( rcExe, rcTree, rcWriting, rcNode, rcUnrecognized );
750 }
751
752 return rc;
753 }
754 #endif
755
756 /*--------------------------------------------------------------------------
757 * CCContainerNode
758 * a container/archive file entry
759 * a file with sub-entries
760 */
761
762 /* Dump
763 */
764 static
CCContainerNodeDump(const CCContainerNode * self,const char * node,const CCName * name,CCDumper * d)765 rc_t CCContainerNodeDump ( const CCContainerNode *self, const char *node,
766 const CCName *name, CCDumper *d )
767 {
768 rc_t rc;
769 const void *entry = self -> entry;
770
771 switch ( self -> type )
772 {
773 case ccFile:
774 case ccContFile:
775 rc = CCFileNodeDump ( entry, node, name, NULL, d, false );
776 break;
777 case ccArcFile:
778 rc = CCArcFileNodeDump ( entry, node, name, NULL, d, false );
779 break;
780 case ccChunkFile:
781 rc = CChunkFileNodeDump ( entry, node, name, NULL, d, false );
782 break;
783 default:
784 rc = RC ( rcExe, rcTree, rcWriting, rcNode, rcUnrecognized );
785 }
786
787 if ( rc != 0 )
788 return rc;
789
790 CCDumperIncIndentLevel ( d );
791
792 if ( BSTreeDoUntil ( & self -> sub, false, CCNameDump, d ) )
793 rc = d -> rc;
794
795 CCDumperDecIndentLevel ( d );
796
797 if ( rc == 0 )
798 rc = CCDumperPrint ( d, "\t</%s>\n", node );
799
800 return rc;
801 }
802
803
804 /*--------------------------------------------------------------------------
805 * CCSymlinkNode
806 * a directory entry with a substitution path
807 */
808
809 /* Dump
810 */
811 static
CCSymlinkNodeDump(const CCSymlinkNode * self,const CCName * name,CCDumper * d,bool replaced)812 rc_t CCSymlinkNodeDump ( const CCSymlinkNode *self, const CCName *name, CCDumper *d, bool replaced )
813 {
814 const char * tag = replaced ? "replaced-symlink" : "symlink";
815 return CCDumperPrint ( d, "\t<%s name=\"%N\" mtime=\"%T\">%S</%s>\n",
816 tag, name, name -> mtime, & self -> path, tag );
817 }
818
819
820 /*--------------------------------------------------------------------------
821 * CCTreeNode
822 * doesn't actually exist, but is treated separately from tree
823 */
824
825 /* Dump
826 */
827 static
CCTreeNodeDump(const CCTree * self,const CCName * name,CCDumper * d,bool replaced)828 rc_t CCTreeNodeDump ( const CCTree *self, const CCName *name, CCDumper *d, bool replaced )
829 {
830 const char * tag = replaced ? "replaced-directory" : "directory";
831 rc_t rc = CCDumperPrint ( d, "\t<%s name=\"%N\" mtime=\"%T\">\n",
832 tag, name, name -> mtime );
833
834 CCDumperIncIndentLevel ( d );
835
836 if ( rc == 0 && BSTreeDoUntil ( self, false, CCNameDump, d ) )
837 rc = d -> rc;
838
839 CCDumperDecIndentLevel ( d );
840
841 if ( rc == 0 )
842 rc = CCDumperPrint ( d, "\t</%s>\n", tag );
843
844 return rc;
845 }
846
847
848 /*--------------------------------------------------------------------------
849 * CCName
850 * the main entrypoint
851 */
852
853 /* Dump
854 */
855 static
CCNameDump(BSTNode * n,void * data)856 bool CCNameDump ( BSTNode *n, void *data )
857 {
858 CCDumper * d = data;
859 const CCName * self = (const CCName*)n;
860 void * entry = self->entry;
861 uint32_t type = self->type;
862 bool replaced = false;
863
864 if (type == ccReplaced)
865 {
866 const CCReplacedNode * node = entry;
867 type = node->type;
868 entry = node->entry;
869 replaced = true;
870 }
871
872 if (type == ccCached)
873 {
874 const CCCachedFileNode * node = entry;
875 type = node->type;
876 entry = node->entry;
877 }
878
879 if ( type == ccHardlink )
880 {
881 do
882 {
883 /* if for some reason the link is broken */
884 if ( self -> entry == NULL )
885 return false;
886 self = self -> entry;
887 }
888 while ( self -> type == ccHardlink );
889
890 entry = self -> entry;
891 type = self -> type;
892 self = ( const CCName* ) n;
893 }
894
895 switch ( type )
896 {
897 case ccFile:
898 case ccContFile:
899 d -> rc = CCFileNodeDump ( entry, replaced ? "replaced-file" : "file", self, NULL, d, true );
900 break;
901 case ccArcFile:
902 d -> rc = CCArcFileNodeDump ( entry, replaced ? "replaced-file" : "file", self, NULL, d, true );
903 break;
904 case ccChunkFile:
905 d -> rc = CChunkFileNodeDump ( entry, replaced ? "replaced-file" : "file", self, NULL, d, true );
906 break;
907 case ccContainer:
908 d -> rc = CCContainerNodeDump ( entry, replaced ? "replaced-container" : "container", self, d );
909 break;
910 case ccArchive:
911 d -> rc = CCContainerNodeDump ( entry, replaced ? "replaced-archive" : "archive", self, d );
912 break;
913 case ccSymlink:
914 d -> rc = CCSymlinkNodeDump ( entry, self, d, replaced );
915 break;
916 case ccDirectory:
917 d -> rc = CCTreeNodeDump ( entry, self, d, replaced );
918 break;
919 case ccCached:
920 #if 0
921 d -> rc = CCCachedFileNodeDump ( entry, self, d );
922 #else
923 d -> rc = RC ( rcExe, rcTree, rcWriting, rcNode, rcCorrupt );
924 #endif
925 break;
926 default:
927 d -> rc = RC ( rcExe, rcTree, rcWriting, rcNode, rcUnrecognized );
928 }
929
930 return ( d -> rc != 0 ) ? true : false;
931 }
932
933
934
935 /*--------------------------------------------------------------------------
936 * CCTree
937 * a binary search tree with CCNodes
938 */
939
940 /* Dump
941 * dump tree using provided callback function
942 *
943 * "write" [ IN, NULL OKAY ] and "out" [ IN, OPAQUE ] - callback function
944 * for writing. if "write" is NULL, print to stdout.
945 */
946 static
CCTreeDumpInt2(const CCTree * self,CCDumper * d,SLList * logs)947 rc_t CCTreeDumpInt2 ( const CCTree *self, CCDumper *d, SLList * logs )
948 {
949 rc_t rc = 0;
950
951 /* print logs attached to this node */
952 if (logs->head != NULL)
953 {
954 SLNode * log;
955 while ((log = SLListPopHead (logs)) != NULL)
956 {
957 CCNodeDumpLog (log+1, d);
958 free (log);
959 }
960 }
961
962 CCDumperIncIndentLevel ( d );
963
964 if ( BSTreeDoUntil ( self, false, CCNameDump, d ) )
965 rc = d -> rc;
966
967 CCDumperDecIndentLevel ( d );
968
969 return rc;
970 }
971
972 /* print root node and call out to print what that contains */
973 static
CCTreeDumpInt(const CCTree * self,CCDumper * d,SLList * logs)974 rc_t CCTreeDumpInt ( const CCTree *self, CCDumper *d, SLList * logs )
975 {
976 ver_t v = KAppVersion ();
977 rc_t rc = CCDumperPrint ( d, "<ROOT version=\"%u.%u.%u\">\n",
978 VersionGetMajor(v),
979 VersionGetMinor(v),
980 VersionGetRelease(v));
981 if ( rc == 0 )
982 {
983 rc = CCTreeDumpInt2 ( self, d, logs );
984
985 if ( rc == 0 )
986 CCDumperPrint ( d, "</ROOT>\n" );
987 }
988 return rc;
989 }
990
991 static
write_FILE(void * out,const void * buffer,size_t bytes)992 rc_t write_FILE ( void *out, const void *buffer, size_t bytes )
993 {
994 size_t num_writ;
995
996 if ( bytes == 0 )
997 return 0;
998
999 num_writ = fwrite ( buffer, 1, bytes, out );
1000 if ( num_writ == bytes )
1001 return 0;
1002 if ( num_writ != 0 )
1003 return RC ( rcExe, rcFile, rcWriting, rcTransfer, rcIncomplete );
1004 if ( buffer == NULL )
1005 return RC ( rcExe, rcFile, rcWriting, rcParam, rcNull );
1006
1007 return RC ( rcExe, rcFile, rcWriting, rcNoObj, rcUnknown );
1008 }
1009
CCTreeDump(const CCTree * self,rc_t (* write)(void * out,const void * buffer,size_t bytes),void * out,SLList * logs)1010 rc_t CCTreeDump ( const CCTree *self,
1011 rc_t ( * write ) ( void *out, const void *buffer, size_t bytes ),
1012 void *out, SLList * logs )
1013 {
1014 rc_t rc, rc2;
1015 CCDumper d;
1016
1017 if ( write == NULL )
1018 {
1019 write = write_FILE;
1020 out = stdout;
1021 }
1022
1023 CCDumperInit ( & d, write, out );
1024
1025 rc = CCTreeDumpInt ( self, & d, logs );
1026
1027 rc2 = CCDumperWhack ( & d );
1028
1029 return rc ? rc : rc2;
1030 }
1031