00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 #include <stdio.h>
00010 #include <unistd.h>
00011 #include <time.h>
00012 
00013 #include "global.h"
00014 #include "actions.h"
00015 #include "status.h"
00016 #include "helper.h"
00017 #include "direnum.h"
00018 #include "cache.h"
00019 #include "url.h"
00020 #include "cp_mv.h"
00021 #include "ignore.h"
00022 #include "options.h"
00023 #include "est_ops.h"
00024 #include "waa.h"
00025 #include "checksum.h"
00026 #include "url.h"
00027 
00028 
00102 static FILE *progress_output=NULL;
00103 static int max_progress_len=0;
00104 
00105 
00109 char * st___visible_file_size(struct estat *sts)
00110 {
00111   static char buffer[20];
00112 
00113   switch ( (sts->st.mode ? sts->st.mode : sts->st.mode) & S_IFMT)
00114   {
00115         case S_IFBLK:
00116     case S_IFCHR:
00117       return "dev";
00118     case S_IFDIR:
00119       return "dir";
00120     default:
00121       
00122 
00123     case S_IFREG:
00124     case S_IFLNK:
00125       sprintf(buffer, "%llu", (t_ull) sts->st.size);
00126       break;
00127   }
00128 
00129   return buffer;
00130 }
00131 
00132 
00134 inline char * st___meta_string(int status_bits, int flags)
00135 {
00136   static char buffer[4];
00137     int prop;
00138 
00139     prop=(status_bits & FS_PROPERTIES) | (flags & RF_PUSHPROPS);
00140 
00141   if (opt__is_verbose() > 0)
00142   {
00143     buffer[0] = status_bits & FS_META_MTIME ?  't' : '.';
00144     buffer[1] = status_bits & 
00145       (FS_META_OWNER | FS_META_GROUP | FS_META_UMODE) ?  'p' : '.';
00146     buffer[2] = prop ?  'P' : '.';
00147     buffer[3] = 0;
00148   }
00149   else
00150   {
00151     buffer[0] = status_bits & FS_META_CHANGED ? 'm' : 
00152             prop ? 'P' : '.';
00153     buffer[1]=0;
00154   }
00155 
00156   return buffer;
00157 }
00158 
00159 
00161 char *st___color(int status_bits)
00162 {
00163   if ((status_bits & FS_REPLACED) == FS_REMOVED)
00164     return ANSI__RED;
00165   if (status_bits & FS_NEW)
00166     return ANSI__GREEN;
00167   if (status_bits & FS_CHANGED)
00168     return ANSI__BLUE;
00169   return "";
00170 }
00171 
00172 
00175 int st__print_status(char *path, int status_bits, int flags, char* size,
00176     struct estat *sts)
00177 {
00178   int status;
00179     char *copyfrom, *url;
00180     int copy_inherited;
00181     FILE* output=stdout;
00182 
00183 
00184     DEBUGP("VERBOSITY=%d", opt__get_int(OPT__VERBOSE));
00185   status=0;
00186   
00187   if (opt__get_int(OPT__VERBOSE)<=VERBOSITY_QUIET) goto ex;
00188 
00189 
00190     
00191 
00192 
00193   if ((status_bits & (FS_NEW | FS_REMOVED)) ||
00194       (flags & (RF_ADD | RF_UNVERSION)))
00195     status_bits &= ~(FS_META_CHANGED | FS_LIKELY | FS_CHANGED);
00196 
00197 
00198   
00199 
00200   if (opt__is_verbose() > 0 || 
00201       (status_bits & FS__CHANGE_MASK) ||
00202       (flags & ~RF_CHECK))
00203     {
00204         copyfrom=NULL;
00205         copy_inherited=0;
00206 
00207         
00208 
00209         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_COPYFROM)
00210         {
00211             copy_inherited= (flags & RF_COPY_SUB);
00212 
00213             if (flags & RF_COPY_BASE)
00214             {
00215                 status=cm__get_source(sts, NULL, ©from, NULL, 0);
00216                 BUG_ON(status == ENOENT, "Marked as copied, but no info?");
00217                 STOPIF(status, NULL);
00218             }
00219         }
00220 
00221 
00222         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_TOP_URL)
00223             STOPIF( url__full_url(sts, &url), NULL);
00224         else
00225             url=NULL;
00226 
00227 
00228         
00229 
00230         STOPIF( hlp__format_path(sts, path, &path), NULL);
00231 
00232 
00233         
00234 
00235 
00236 
00237         if (opt__get_int(OPT__STATUS_COLOR))
00238             STOPIF_CODE_EPIPE( fputs(st___color(status_bits), output), NULL);
00239 
00240         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_SHOWCHG)
00241             STOPIF_CODE_EPIPE( fprintf(output, "%c%s%c%c  ",
00242                         flags & RF_ADD ? 'n' : 
00243                         flags & RF_UNVERSION ? 'd' : 
00244                         (status_bits & FS_REPLACED) == FS_REPLACED ? 'R' : 
00245                         status_bits & FS_NEW ? 'N' : 
00246                         status_bits & FS_REMOVED ? 'D' : '.',
00247 
00248                         st___meta_string(status_bits, flags),
00249 
00250                         flags & RF_CONFLICT ? 'x' : 
00251                         status_bits & FS_CHANGED ? 'C' : '.',
00252 
00253                         flags & RF___IS_COPY ? '+' : 
00254                         status_bits & FS_LIKELY ? '?' : 
00255                         
00256 
00257                         ( ( status_bits & FS_REMOVED ) &&
00258                             ( flags & (RF_UNVERSION | RF_ADD) ) ) ? '!' : '.'
00259                         ), NULL);
00260 
00261 
00262         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_SHOWSIZE)
00263             STOPIF_CODE_EPIPE( fprintf(output, "%8s  ", size), NULL);
00264 
00265         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_GROUP)
00266             STOPIF_CODE_EPIPE( fprintf(output, "%-*s", 
00267                         ign__max_group_name_len+2,
00268                         sts->match_pattern ? sts->match_pattern->group_name :
00269                         "(none)"), NULL);
00270 
00271         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_SHOWNAME)
00272             STOPIF_CODE_EPIPE( fputs(path, output), NULL);
00273 
00274         if (opt__get_int(OPT__STATUS_COLOR))
00275             STOPIF_CODE_EPIPE( fputs(ANSI__NORMAL, output), NULL);
00276 
00277         
00278 
00279         if (copyfrom || copy_inherited)
00280             STOPIF_CODE_EPIPE( fprintf(output,
00281                         copy_inherited ? "  (inherited)" : "  (copied from %s)",
00282                         copyfrom), NULL);
00283 
00284         if (url)
00285             STOPIF_CODE_EPIPE( fprintf(output, "  %s", url), NULL);
00286 
00287 
00288         STOPIF_CODE_EPIPE( fputs("\n", output), NULL);
00289     }
00290 
00291 
00292 ex:
00293   return status;
00294 }
00295 
00296 
00299 int st__status(struct estat *sts)
00300 {
00301     int status;
00302     int e_stat, flags;
00303     char *path;
00304     int would_be_ignored;
00305 
00306 
00307     status=0;
00308     STOPIF( ops__build_path(&path, sts), NULL);
00309 
00310     
00311     BUG_ON(sts->was_output, "%s was already output ...", path);
00312     sts->was_output=1;
00313 
00314 
00315     e_stat=sts->entry_status;
00316     flags=sts->flags;
00317     
00318 
00319 
00320     if (flags & RF_ISNEW)
00321     {
00322         e_stat = ( e_stat & ~FS_REPLACED) | FS_NEW;
00323         flags &= ~RF_ADD;
00324         DEBUGP("Re-create the NEW status.");
00325 
00326         if (opt__get_int(OPT__VERBOSE) & VERBOSITY_GROUP)
00327             STOPIF( ign__is_ignore(sts, &would_be_ignored), NULL);
00328     }
00329 
00330     STOPIF( st__print_status(path, 
00331                 e_stat, flags,
00332                 st___visible_file_size(sts),
00333                 sts), NULL);
00334 
00335 ex:
00336     return status;
00337 }
00338 
00339 
00342 int st__action(struct estat *sts)
00343 {
00344     int status;
00345 
00346     if (opt__get_int(OPT__STOP_ON_CHANGE) &&
00347             sts->entry_status)
00348         
00349         exit(1);
00350 
00351     STOPIF( st__status(sts), NULL);
00352 
00353 ex:
00354     return status;
00355 }
00356 
00357 
00360 int st__rm_status(struct estat *sts)
00361 {
00362     int status;
00363     char *path;
00364 
00365 
00366     status=0;
00367     STOPIF( ops__build_path(&path, sts), NULL);
00368 
00369     STOPIF( st__print_status(path, 
00370                 sts->remote_status, 0,
00371                 st___visible_file_size(sts),
00372                 sts), NULL);
00373 
00374 ex:
00375     return status;
00376 }
00377 
00378 
00381 int st__work(struct estat *root, int argc, char *argv[])
00382 {
00383     int status;
00384     char **normalized;
00385 
00386 
00387     
00388 
00389     
00390 
00391 
00392     STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
00393 
00394 
00395     status=url__load_list(NULL, 0);
00396     
00397     if (status != ENOENT) STOPIF(status, NULL);
00398 
00399     STOPIF( ign__load_list(NULL), NULL);
00400 
00401     if (opt__get_int(OPT__DIR_SORT) && 
00402             !opt__get_int(OPT__STOP_ON_CHANGE))
00403     {
00404         action->local_callback=st__progress;
00405         action->local_uninit=st__progress_uninit;
00406     }
00407 
00408     STOPIF( waa__read_or_build_tree(root, argc, normalized, argv, NULL, 0), 
00409             "No working copy data could be found.");
00410 
00411 
00412     if (opt__get_int(OPT__DIR_SORT))
00413     {
00414         action->local_callback=st__status;
00415         STOPIF( waa__do_sorted_tree(root, ac__dispatch), NULL);
00416     }
00417 
00418     if (opt__get_int(OPT__GROUP_STATS))
00419         STOPIF( ign__print_group_stats(stdout), NULL);
00420 
00421 ex:
00422     return status;
00423 }
00424 
00425 
00435 int st__progress(struct estat *sts)
00436 {
00437     static unsigned int counter=0;
00438     static int is_tty=0;
00439     static int last_outp;
00440     static time_t last;
00441     static time_t too_many_new=0;
00442     int status;
00443     time_t now;
00444     int print;
00445     const int bar_chart_width=20;
00446     static const char bar_chart[bar_chart_width+1]="###################>";
00447     float pct;
00448 
00449 
00450     status=0;
00451     now=time(NULL);
00452 
00453     
00454     if (!progress_output) progress_output=stderr;
00455 
00456     if (is_tty == 0)
00457     {
00458         is_tty= isatty(fileno(progress_output)) ? +1 : -1;
00459         DEBUGP("we're on a tty");
00460     }
00461 
00462     if (is_tty == +1)
00463     {
00464         
00465         counter++;
00466 
00467         
00468 
00469 
00470 
00471 
00472 
00473 
00474         
00475         print= (counter & 0xfff) == 0 ? 1 : 0;
00476         if (!print && ((counter & 0x3f) == 0))
00477         {
00478             now=time(NULL);
00479             
00480 
00481             if (now != last) print=1;
00482         }
00483 
00484         if (print)
00485         {
00486             
00487 
00488             if (counter <= approx_entry_count && now<too_many_new)
00489             {
00490                 pct=(float)counter/approx_entry_count;
00491                 print = (int)((float)bar_chart_width*pct +0.5);
00492                 
00493 
00494 
00495 
00496 
00497                 print=fprintf(progress_output, 
00498                         "\r%8d of %8d done (%5.1f%%); [%s%*s]",
00499                         counter, approx_entry_count,
00500                         pct*100.0,
00501                         bar_chart+bar_chart_width-print,
00502                         bar_chart_width-print, "");
00503 
00504                 if (pct > 0.96 && !too_many_new) 
00505                     too_many_new=now+5;
00506             }
00507             else
00508             {
00509                 
00510 
00511 
00512                 print=fprintf(progress_output, "\r%8d entries done",
00513                         counter);
00514             }
00515 
00516             STOPIF_CODE_ERR(print < 0, errno,
00517                     "Progress status could not be written");
00518 
00519             
00520 
00521 
00522             if (print < last_outp)
00523                 fprintf(progress_output, "%*s ", last_outp-print, "");
00524             last_outp=print;
00525 
00526             
00527 
00528 
00529 
00530             if (print > max_progress_len) max_progress_len=print;
00531 
00532             
00533 
00534 
00535 
00536             time(&last);
00537         }
00538         
00539     }
00540 
00541 ex:
00542     return status;
00543 }
00544 
00545 
00549 int st__progress_uninit(void)
00550 {
00551     static const char err[]="Clearing the progress space";
00552     int status;
00553     char buff[max_progress_len+3];
00554 
00555     status=0;
00556     if (max_progress_len>0)
00557     {
00558         
00559 
00560 
00561         buff[0]='\r';
00562         memset(buff+1, ' ', max_progress_len);
00563         buff[1+max_progress_len]='\r';
00564         buff[2+max_progress_len]=0;
00565         
00566         STOPIF_CODE_ERR( fputs(buff, progress_output) == EOF, errno, err);
00567         fflush(progress_output);
00568     }
00569 
00570 ex:
00571     return status;
00572 }
00573 
00574 
00575 struct st___bit_info
00576 {
00577     int val;
00578     char *string;
00579     int str_len;
00580 };
00581 #define BIT_INFO(v, s) { .val=v, .string=s, .str_len=strlen(s) }
00582 
00583 
00587 #define st___string_from_bits(v, a, t) _st___string_from_bits(v, a, sizeof(a)/sizeof(a[0]), t)
00588 volatile char *_st___string_from_bits(int value, 
00589         const struct st___bit_info data[], int max,
00590         char *text_for_none)
00591 {
00592     int status;
00593     static struct cache_t *cache=NULL;
00594     static const char sep[]=", ";
00595     char *string;
00596     int i;
00597     int last_len, new_len;
00598     struct cache_entry_t **cc;
00599 
00600 
00601     STOPIF( cch__new_cache(&cache, 4), NULL);
00602     STOPIF( cch__add(cache, 0, NULL, 128, &string), NULL);
00603     cc=cache->entries + cache->lru;
00604 
00605     last_len=0;
00606     if (string) *string=0;
00607     for(i=0; i<max; i++)
00608     {
00609         if (value & data[i].val)
00610         {
00611             new_len = last_len + data[i].str_len + 
00612                 (last_len ? strlen(sep) : 0);
00613 
00614             if (new_len + 8 > (*cc)->len)
00615             {
00616                 STOPIF( cch__entry_set(cc, 0, NULL, new_len+64, 1, &string), NULL);
00617                 string[last_len]=0;
00618             }
00619 
00620             if (last_len)
00621             {
00622                 strcpy(string + last_len, sep);
00623                 last_len += strlen(sep);
00624             }
00625 
00626             strcpy(string + last_len, data[i].string);
00627             last_len=new_len;
00628 #if 0
00629             
00630             DEBUGP("match bit 0x%X on 0x%X: %s", 
00631                     data[i].val, value, 
00632                     data[i].string);
00633 #endif
00634         }
00635     }
00636 
00637 ex:
00638     
00639   if (status) return NULL;
00640     
00641     return string && *string ? string : text_for_none;
00642 }
00643 
00644 
00645 inline volatile char* st__flags_string_fromint(int mask)
00646 {
00647     const struct st___bit_info flags[]={
00648         BIT_INFO( RF_ADD,               "add"),
00649         BIT_INFO( RF_UNVERSION, "unversion"),
00650         
00651 
00652         
00653         BIT_INFO( RF_CHECK,         "check"),
00654         BIT_INFO( RF_COPY_BASE, "copy_base"),
00655         BIT_INFO( RF_COPY_SUB,  "copy_sub"),
00656         BIT_INFO( RF_CONFLICT,  "conflict"),
00657         BIT_INFO( RF_PUSHPROPS, "push_props"),
00658     };  
00659 
00660     return st___string_from_bits(mask, flags, "none");
00661 }
00662 
00663 
00664 inline volatile char* st__status_string_fromint(int mask)
00665 {
00666     const struct st___bit_info statii[]={
00667         BIT_INFO( FS_NEW,                           "new"),
00668         BIT_INFO( FS_REMOVED,                   "removed"),
00669         BIT_INFO( FS_CHANGED,               "changed"),
00670         BIT_INFO( FS_META_OWNER,            "owner"),
00671         BIT_INFO( FS_META_GROUP,            "group"),
00672         BIT_INFO( FS_META_MTIME,            "mtime"),
00673         BIT_INFO( FS_META_UMODE,            "umode"),
00674         BIT_INFO( FS_PROPERTIES,            "props"),
00675         BIT_INFO( FS_CHILD_CHANGED,     "child"),
00676         BIT_INFO( FS_LIKELY,                    "likely"), 
00677     };      
00678     return st___string_from_bits(mask, statii, "unmodified");
00679 }
00680 
00681 
00682 char *st__type_string(mode_t mode)
00683 {
00684     switch (mode & S_IFMT)
00685     {
00686         case S_IFDIR:     return "directory";
00687         case S_IFBLK:     return "block-dev";
00688         case S_IFCHR:     return "char-dev";
00689         case S_IFREG:     return "file";
00690         case S_IFLNK:     return "symlink";
00691         case S_IFSOCK:    return "any-special";
00692         case S_IFGARBAGE: return "garbage";
00693     }
00694 
00695     return "invalid";
00696 }
00697 
00698 
00699 inline volatile char* st__status_string(const struct estat * const sts)
00700 {
00701     return st__status_string_fromint(sts->entry_status);
00702 }
00703 
00704 
00705 int st__print_entry_info(struct estat *sts)
00706 {
00707     int status;
00708     char *path, *waa_path, *url, *copyfrom;
00709     svn_revnum_t copy_rev;
00710 
00711 
00712     status=errno=0;
00713     STOPIF( ops__build_path(&path, sts), NULL);
00714     STOPIF( url__full_url(sts, &url), NULL);
00715 
00716     copyfrom=NULL;
00717     if ((opt__get_int(OPT__VERBOSE) & VERBOSITY_COPYFROM) && 
00718             (sts->flags & RF___IS_COPY))
00719     {
00720         STOPIF( cm__get_source(sts, path, ©from, ©_rev, 0), NULL);
00721     }
00722 
00723     STOPIF_CODE_EPIPE( printf("   Type:   \t%s\n", 
00724                 st__type_string(sts->st.mode)), NULL);
00725     if (S_ISDIR(sts->st.mode))
00726         STOPIF_CODE_EPIPE( printf( "   ChildCount:\t%u\n", 
00727                     sts->entry_count), NULL);
00728     STOPIF_CODE_EPIPE( printf("   URL:   \t%s\n", url), NULL);
00729     STOPIF_CODE_EPIPE( printf("   Status:\t0x%X (%s)\n", 
00730                 sts->entry_status, st__status_string(sts)), NULL);
00731     STOPIF_CODE_EPIPE( printf("   Flags:\t0x%X (%s)\n", 
00732                 sts->flags & ~RF_PRINT,
00733                 st__flags_string_fromint(sts->flags)), NULL);
00734 
00735     if (copyfrom)
00736     {
00737         STOPIF_CODE_EPIPE( printf("   Copyfrom:\trev. %llu of %s\n", 
00738                     (t_ull)copy_rev, copyfrom), NULL);
00739     }
00740 
00741     STOPIF_CODE_EPIPE( printf("   Dev:  \t%llu\n", 
00742                 (t_ull)sts->st.dev), NULL);
00743     STOPIF_CODE_EPIPE( printf("   Inode:  \t%llu\n", 
00744                 (t_ull)sts->st.ino), NULL);
00745     STOPIF_CODE_EPIPE( printf("   Mode:  \t0%4o\n", 
00746                 sts->st.mode), NULL);
00747     STOPIF_CODE_EPIPE( printf("   UID/GID:\t%u (%s)/%u (%s)\n", 
00748                 sts->st.uid, hlp__get_uname(sts->st.uid, "undefined"), 
00749                 sts->st.gid, hlp__get_grname(sts->st.gid, "undefined") ), NULL);
00750     
00751     STOPIF_CODE_EPIPE( printf("   MTime:  \t%.24s\n", 
00752                 ctime( &(sts->st.mtim.tv_sec) )), NULL);
00753     STOPIF_CODE_EPIPE( printf("   CTime:  \t%.24s\n", 
00754                 ctime( &(sts->st.ctim.tv_sec) )), NULL);
00755 
00756     STOPIF( waa__get_waa_directory(path, &waa_path, NULL, NULL,
00757                 GWD_WAA), NULL);
00758     STOPIF_CODE_EPIPE( printf("   WAA-Path:\t%s\n", 
00759                 waa_path), NULL);
00760 
00761     if (!sts->parent)
00762     {
00763         STOPIF( waa__get_waa_directory(path, &waa_path, NULL, NULL,
00764                     GWD_CONF), NULL);
00765         STOPIF_CODE_EPIPE( printf("   Conf-Path:\t%s\n", 
00766                     waa_path), NULL);
00767     }
00768 
00769     
00770 
00771     STOPIF_CODE_EPIPE( printf("   Revision:\t%li\n", 
00772                 sts->parent ? sts->repos_rev : urllist[0]->current_rev), NULL);
00773 
00774     if (S_ISREG(sts->st.mode))
00775         STOPIF_CODE_EPIPE( printf("   Repos-MD5:\t%s\n", 
00776                     cs__md5tohex_buffered(sts->md5)), NULL);
00777 
00778     if (S_ISBLK(sts->st.mode) || S_ISCHR(sts->st.mode))
00779     {
00780 #ifdef DEVICE_NODES_DISABLED
00781         DEVICE_NODES_DISABLED();
00782 #else
00783         STOPIF_CODE_EPIPE( printf("   Device nr.:\t%llu:%llu\n", 
00784                     (t_ull)MAJOR(sts->st.rdev),
00785                     (t_ull)MINOR(sts->st.rdev)), NULL);
00786     }
00787 #endif
00788     else
00789         STOPIF_CODE_EPIPE( printf("   Size:  \t%llu\n", 
00790                     (t_ull)sts->st.size), NULL);
00791 
00792     
00793     STOPIF_CODE_EPIPE( printf("\n"), NULL);
00794 
00795 ex:
00796     return status;
00797 }
00798 
00799