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('<',"&lt;");
268             REPLACE_COPY('>',"&gt;");
269             REPLACE_COPY('&',"&amp;");
270             REPLACE_COPY('"',"&quot;");
271             REPLACE_COPY('\'',"&apos;");
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