00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00068 #include <apr_md5.h>
00069 #include <apr_pools.h>
00070 #include <apr_user.h>
00071 #include <apr_file_io.h>
00072 #include <subversion-1/svn_delta.h>
00073 #include <subversion-1/svn_ra.h>
00074 #include <subversion-1/svn_error.h>
00075 #include <subversion-1/svn_string.h>
00076 #include <subversion-1/svn_time.h>
00077 
00078 #include <sys/types.h>
00079 #include <sys/mman.h>
00080 #include <unistd.h>
00081 #include <time.h>
00082 #include <fcntl.h>
00083 
00084 
00085 #include "global.h"
00086 #include "status.h"
00087 #include "checksum.h"
00088 #include "waa.h"
00089 #include "cache.h"
00090 #include "est_ops.h"
00091 #include "props.h"
00092 #include "options.h"
00093 #include "ignore.h"
00094 #include "cp_mv.h"
00095 #include "racallback.h"
00096 #include "url.h"
00097 #include "helper.h"
00098 
00099 
00100 
00102 typedef svn_error_t *(*change_any_prop_t) (void *baton,
00103         const char *name,
00104         const svn_string_t *value,
00105         apr_pool_t *pool);
00106 
00107 
00109 unsigned committed_entries;
00111 char *missing_path_utf8;
00113 int missing_path_utf8_len;
00114 
00115 
00118 int ci__set_revision(struct estat *this, svn_revnum_t rev)
00119 {
00120     int i;
00121 
00122     
00123 
00124     if (this->url == current_url)
00125         this->repos_rev=rev;
00126 
00127     if (S_ISDIR(this->st.mode))
00128         for(i=0; i<this->entry_count; i++)
00129             ci__set_revision(this->by_inode[i], rev);
00130 
00131     return 0;
00132 }
00133 
00134 
00143 svn_error_t * ci__callback (
00144         svn_revnum_t new_revision,
00145         const char *utf8_date,
00146         const char *utf8_author,
00147         void *baton)
00148 {
00149     struct estat *root UNUSED=baton;
00150     int status;
00151 
00152 
00153     status=0;
00154     if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
00155         printf("committed revision\t%ld on %s as %s\n",
00156                 new_revision, utf8_date, utf8_author);
00157 
00158     
00159 
00160     current_url->current_rev = new_revision;
00161     
00162 
00163     RETURN_SVNERR(status);
00164 }
00165 
00166 
00170 int ci__action(struct estat *sts)
00171 {
00172     int status;
00173     char *path;
00174 
00175 
00176     STOPIF( ops__build_path(&path, sts), NULL);
00177 
00178     STOPIF_CODE_ERR( sts->flags & RF_CONFLICT, EBUSY,
00179             "!The entry \"%s\" is still marked as conflict.", path);
00180 
00181     if (sts->entry_status ||
00182             (sts->flags & RF___COMMIT_MASK) )
00183         ops__mark_parent_cc(sts, entry_status);
00184 
00185     STOPIF( st__progress(sts), NULL);
00186 
00187 ex:
00188     return status;
00189 }
00190 
00191 
00198 void ci___unset_copyflags(struct estat *root)
00199 {
00200     struct estat **sts;
00201 
00202     
00203 
00204     root->flags &= ~(RF_ADD | RF_COPY_BASE | RF_COPY_SUB);
00205     
00206     root->url=current_url;
00207 
00208     if (ops__has_children(root))
00209     {
00210         sts=root->by_inode;
00211         while (*sts)
00212         {
00213             if (! ( (*sts)->flags & RF_COPY_BASE) )
00214             {
00215                 ci___unset_copyflags(*sts);
00216             }
00217 
00218             sts++;
00219         }
00220     }
00221 }
00222 
00223 
00235 int ci___send_user_props(void *baton, 
00236         struct estat *sts,
00237         change_any_prop_t function,
00238         int store_encoder,
00239         apr_pool_t *pool)
00240 {
00241     int status;
00242     svn_error_t *status_svn;
00243     datum key, value;
00244     hash_t db;
00245     svn_string_t *str;
00246 
00247 
00248     db=NULL;
00249 
00250     
00251 
00252     int send_a_prop(char *key, svn_string_t *value)
00253     {
00254         int status;
00255 
00256         status=0;
00257         
00258 
00259         if (store_encoder && strcmp(key, propval_commitpipe) == 0)
00260         {
00261             if (value)
00262                 STOPIF( hlp__strdup( &sts->decoder, value->data), NULL);
00263             else 
00264                 sts->decoder=NULL;
00265         }
00266 
00267         STOPIF_SVNERR( function, (baton, key, value, pool) );
00268 
00269 ex:
00270         return status;
00271     }
00272 
00273 
00274     
00275     STOPIF( ops__apply_group(sts, &db, pool), NULL);
00276 
00277     
00278 
00279     if (db)
00280     {
00281         status=prp__first(db, &key);
00282         while (status==0)
00283         {
00284             STOPIF( prp__fetch(db, key, &value), NULL);
00285 
00286             if (hlp__is_special_property_name(key.dptr))
00287             {
00288                 DEBUGP("ignoring %s - should not have been taken?", key.dptr);
00289             }
00290             else if (prp__prop_will_be_removed(value))
00291             {
00292                 DEBUGP("removing property %s", key.dptr);
00293 
00294                 STOPIF( send_a_prop(key.dptr, NULL), NULL);
00295                 STOPIF( hsh__register_delete(db, key), NULL);
00296             }
00297             else
00298             {
00299                 DEBUGP("sending property %s=(%d)%.*s", key.dptr, 
00300                         value.dsize, value.dsize, value.dptr);
00301 
00302                 str=svn_string_ncreate(value.dptr, value.dsize-1, pool);
00303                 STOPIF( send_a_prop(key.dptr, str), NULL);
00304             }
00305 
00306             status=prp__next( db, &key, &key);
00307         }
00308 
00309         
00310         if (status != ENOENT)
00311             STOPIF(status, NULL);
00312         status=0;
00313     }
00314 
00315     
00316     STOPIF( hsh__close(db, status), NULL);
00317 
00318 ex:
00319     return status;
00320 }
00321 
00322 
00332 svn_error_t *ci___set_props(void *baton, 
00333         struct estat *sts,
00334         change_any_prop_t function,
00335         apr_pool_t *pool)
00336 {
00337     const char *ccp;
00338     svn_string_t *str;
00339     int status;
00340     svn_error_t *status_svn;
00341 
00342 
00343     status=0;
00344     
00345 
00346     if (!S_ISLNK(sts->st.mode))
00347     {
00348         
00349         str=svn_string_createf (pool, "0%03o", (int)(sts->st.mode & 07777));
00350         STOPIF_SVNERR( function, (baton, propname_umode, str, pool) );
00351     }
00352 
00353     
00354     str=svn_string_createf (pool, "%lu %s", 
00355             (unsigned long)sts->st.uid, hlp__get_uname(sts->st.uid, "") );
00356     STOPIF_SVNERR( function, (baton, propname_owner, str, pool) );
00357 
00358     
00359     str=svn_string_createf (pool, "%lu %s", 
00360             (unsigned long)sts->st.gid, hlp__get_grname(sts->st.gid, "") );
00361     STOPIF_SVNERR( function, (baton, propname_group, str, pool) );
00362 
00363     
00364     ccp=(char *)svn_time_to_cstring (
00365             apr_time_make( sts->st.mtim.tv_sec, sts->st.mtim.tv_nsec/1000),
00366             pool);
00367     str=svn_string_create(ccp, pool);
00368     STOPIF_SVNERR( function, (baton, propname_mtime, str, pool) );
00369 
00370 ex:
00371     RETURN_SVNERR(status);
00372 }
00373 
00374 
00383 svn_error_t *ci__nondir(const svn_delta_editor_t *editor, 
00384         struct estat *sts, 
00385         void *baton, 
00386         apr_pool_t *pool)
00387 {
00388     svn_txdelta_window_handler_t delta_handler;
00389     void *delta_baton;
00390     svn_error_t *status_svn;
00391     svn_stream_t *s_stream;
00392     char *cp;
00393     char *filename;
00394     int status;
00395     svn_string_t *stg;
00396     apr_file_t *a_stream;
00397     svn_stringbuf_t *str;
00398     struct encoder_t *encoder;
00399     int transfer_text, has_manber;
00400 
00401 
00402     str=NULL;
00403     a_stream=NULL;
00404     s_stream=NULL;
00405     encoder=NULL;
00406 
00407     STOPIF( ops__build_path(&filename, sts), NULL);
00408 
00409 
00410     
00411 
00412 
00413 
00414 
00415     
00416 
00417     
00418 
00419     STOPIF( ci___send_user_props(baton, sts, 
00420                 editor->change_file_prop, 1, pool), NULL);
00421 
00422     STOPIF_SVNERR( ci___set_props, 
00423             (baton, sts, editor->change_file_prop, pool) ); 
00424 
00425     
00426     BUG_ON( sts->entry_status & FS_LIKELY );
00427 
00428     
00429 
00430 
00431 
00432     DEBUGP("%s: status %s; flags %s", sts->name,
00433             st__status_string(sts), 
00434             st__flags_string_fromint(sts->flags));
00435 
00436 
00437     transfer_text= sts->entry_status & (FS_CHANGED | FS_NEW | FS_REMOVED);
00438     
00439 
00440 
00441 
00442 
00443 
00444 
00445 
00446 
00447 
00448 
00449 
00450 
00451 
00452 
00453 
00454 
00455 
00456     if (!transfer_text && !(sts->flags & RF___IS_COPY))
00457     {
00458         DEBUGP("hasn't changed, and no copy.");
00459     }
00460     else
00461     {
00462         has_manber=0;
00463         switch (sts->st.mode & S_IFMT)
00464         {
00465             case S_IFLNK:
00466                 STOPIF( ops__link_to_string(sts, filename, &cp), NULL);
00467                 STOPIF( hlp__local2utf8(cp, &cp, -1), NULL);
00468                 
00469 
00470 
00471                 str=svn_stringbuf_create(cp, pool);
00472                 break;
00473             case S_IFBLK:
00474             case S_IFCHR:
00475                 
00476                 
00477                 str=svn_stringbuf_create(
00478                         ops__dev_to_filedata(sts), pool);
00479                 break;
00480             case S_IFREG:
00481                 STOPIF( apr_file_open(&a_stream, filename, APR_READ, 0, pool),
00482                         "open file \"%s\" for reading", filename);
00483 
00484                 s_stream=svn_stream_from_aprfile (a_stream, pool);
00485 
00486                 
00487 
00488                 has_manber= (sts->st.size >= CS__MIN_FILE_SIZE);
00489                 if (has_manber)
00490                     STOPIF( cs__new_manber_filter(sts, s_stream, &s_stream, pool), NULL );
00491 
00492                 
00493 
00494                 if (transfer_text && sts->decoder)
00495                 {
00496                     
00497 
00498 
00499                     STOPIF( hlp__encode_filter(s_stream, sts->decoder, 0,
00500                                 filename, &s_stream, &encoder, pool), NULL );
00501                     encoder->output_md5= &(sts->md5);
00502 
00503                     IF_FREE(sts->decoder);
00504                 }
00505                 break;
00506             default:
00507                 BUG("invalid/unknown file type 0%o", sts->st.mode);
00508         }
00509 
00510         
00511         if (str) s_stream=svn_stream_from_stringbuf (str, pool);
00512 
00513         BUG_ON(!s_stream);
00514 
00515         if (transfer_text)
00516         {
00517             DEBUGP("really sending ...");
00518             STOPIF_SVNERR( editor->apply_textdelta,
00519                     (baton, 
00520                      NULL, 
00521                      pool,
00522                      &delta_handler,
00523                      &delta_baton));
00524 
00525             
00526 
00527             STOPIF_SVNERR( svn_txdelta_send_stream,
00528                     (s_stream, delta_handler,
00529                      delta_baton,
00530                      sts->md5, pool) );
00531             DEBUGP("after sending encoder=%p", encoder);
00532         }
00533         else
00534         {
00535             DEBUGP("doing local MD5.");
00536             
00537 
00538 
00539 
00540             STOPIF( hlp__stream_md5(s_stream, 
00541                         has_manber ? NULL : sts->md5), NULL);
00542         }
00543 
00544         STOPIF_SVNERR( svn_stream_close, (s_stream) );
00545 
00546 
00547         
00548         if (str)
00549         {
00550             stg=svn_string_create(propval_special, pool);
00551             STOPIF_SVNERR( editor->change_file_prop,
00552                     (baton, propname_special, stg, pool) );
00553         }
00554 
00555         
00556         if (encoder)
00557         {
00558             cp=cs__md5tohex_buffered(sts->md5);
00559             DEBUGP("Sending original MD5 as %s", cp);
00560 
00561             stg=svn_string_create(cp, pool);
00562             STOPIF_SVNERR( editor->change_file_prop,
00563                     (baton, propname_origmd5, stg, pool) );
00564         }
00565 
00566     }
00567 
00568 
00569     STOPIF( cs__set_file_committed(sts), NULL);
00570 
00571 ex:
00572     if (a_stream)
00573     {
00574         
00575 
00576         apr_file_close(a_stream);
00577     }
00578 
00579     RETURN_SVNERR(status);
00580 }
00581 
00582 
00585 svn_error_t *ci__directory(const svn_delta_editor_t *editor, 
00586         struct estat *dir, 
00587         void *dir_baton, 
00588         apr_pool_t *pool)
00589 {
00590     void *baton;
00591     int status;
00592     struct estat *sts;
00593     apr_pool_t *subpool;
00594     int i, exists_now;
00595     char *filename;
00596     char *utf8_filename, *tmp;
00597     svn_error_t *status_svn;
00598     char *src_path;
00599     svn_revnum_t src_rev;
00600     struct sstat_t stat;
00601     struct cache_entry_t *utf8fn_plus_missing;
00602     int utf8fn_len;
00603 
00604 
00605     status=0;
00606     utf8fn_plus_missing=NULL;
00607     subpool=NULL;
00608     DEBUGP("commit_dir with baton %p", dir_baton);
00609     for(i=0; i<dir->entry_count; i++)
00610     {
00611         sts=dir->by_inode[i];
00612 
00613         
00614 
00615         if ( (sts->flags & RF___COMMIT_MASK) && sts->do_this_entry)
00616         {
00617             
00618 
00619             if (sts->flags & RF_PUSHPROPS)
00620                 sts->entry_status |= FS_PROPERTIES;
00621         }
00622         else if (sts->entry_status)
00623         {
00624             
00625 
00626             
00627 
00628         }
00629         else
00630             
00631             continue;
00632 
00633 
00634         
00635         if (subpool) apr_pool_destroy(subpool);
00636         
00637         STOPIF( apr_pool_create_ex(&subpool, pool, NULL, NULL), 
00638                 "no pool");
00639 
00640         STOPIF( ops__build_path(&filename, sts), NULL);
00641         
00642 
00643         STOPIF( hlp__local2utf8(filename+2, &utf8_filename, -1), NULL );
00644         if (missing_path_utf8)
00645         {
00646             utf8fn_len=strlen(utf8_filename);
00647             STOPIF( cch__entry_set(&utf8fn_plus_missing, 0, NULL, 
00648                     missing_path_utf8_len + 1 + utf8fn_len + 1, 
00649                     0, &tmp), NULL);
00650             strcpy(tmp, missing_path_utf8);
00651             tmp[missing_path_utf8_len]='/';
00652             strcpy(tmp + missing_path_utf8_len +1, utf8_filename);
00653             utf8_filename=tmp;
00654         }
00655 
00656         DEBUGP("%s: action %X, updated mode 0%o, flags %X, filter %d", 
00657                 filename, sts->entry_status, sts->st.mode, sts->flags,
00658                 ops__allowed_by_filter(sts));
00659 
00660         if (ops__allowed_by_filter(sts))
00661             STOPIF( st__status(sts), NULL);
00662 
00663         exists_now= !(sts->flags & RF_UNVERSION) && 
00664             ( (sts->entry_status & (FS_NEW | FS_CHANGED | FS_META_CHANGED)) ||
00665                 (sts->flags & (RF_ADD | RF_PUSHPROPS | RF_COPY_BASE)) 
00666             );
00667 
00668         if ( (sts->flags & RF_UNVERSION) ||
00669                 (sts->entry_status & FS_REMOVED) )
00670         {
00671             DEBUGP("deleting %s", sts->name);
00672             
00673             STOPIF_SVNERR( editor->delete_entry,
00674                     (utf8_filename, SVN_INVALID_REVNUM, dir_baton, subpool) );
00675 
00676             committed_entries++;
00677 
00678             if (!exists_now)
00679             {
00680                 DEBUGP("%s=%d doesn't exist anymore", sts->name, i);
00681                 
00682 
00683                 STOPIF( ops__delete_entry(dir, NULL, i, UNKNOWN_INDEX),
00684                         NULL);
00685                 STOPIF( waa__delete_byext(filename, WAA__FILE_MD5s_EXT, 1), NULL);
00686                 STOPIF( waa__delete_byext(filename, WAA__PROP_EXT, 1), NULL);
00687                 i--;
00688                 continue;
00689             }
00690         } 
00691 
00692 
00693         
00694 
00695         if (!exists_now && !(sts->entry_status & FS_CHILD_CHANGED))
00696             continue;
00697 
00698         
00699 
00700 
00701 
00702 
00703 
00704 
00705 
00706 
00707 
00708 
00709 
00710         
00711 
00712         
00713         if (hlp__lstat(filename, &stat))
00714         {
00715             
00716 
00717 
00718             STOPIF_CODE_ERR( sts->flags & RF_ADD, ENOENT,
00719                     "Entry %s should be added, but doesn't exist.",
00720                     filename);
00721 
00722             DEBUGP("%s doesn't exist, ignoring (%d)", filename, errno);
00723             continue;
00724         }
00725 
00726         
00727 
00728 
00729         if (sts->do_this_entry && ops__allowed_by_filter(sts))
00730         {
00731             sts->st=stat;
00732             DEBUGP("set st for %s", sts->name);
00733         }
00734 
00735 
00736         
00737         baton=NULL;
00738         
00739 
00740 
00741 
00742         if ((sts->flags & (RF_ADD | RF_COPY_BASE) ) || 
00743                 (sts->entry_status & FS_NEW) )
00744         {
00745             
00746         }
00747         else
00748         {
00749             status_svn=
00750                 (S_ISDIR(sts->st.mode) ?
00751                  editor->open_directory : editor->open_file)
00752                 ( utf8_filename, dir_baton, 
00753                     current_url->current_rev,
00754                     subpool, &baton);       
00755 
00756             if (status_svn)
00757             {
00758                 DEBUGP("got error %d %d", status_svn->apr_err, SVN_ERR_FS_PATH_SYNTAX);
00759                 
00760 
00761 
00762 
00763 
00764 
00765                 if (S_ISDIR(sts->st.mode) &&
00766                         (
00767                          status_svn->apr_err == SVN_ERR_FS_PATH_SYNTAX ||
00768                          status_svn->apr_err == SVN_ERR_FS_NOT_DIRECTORY ) &&
00769                         urllist_count>1)
00770                 {
00771                     DEBUGP("dir %s didn't exist, trying to create", filename);
00772                     
00773                     baton=NULL;
00774                     status_svn=NULL;
00775                 }
00776                 else
00777                     STOPIF_CODE_ERR(1, status_svn->apr_err,
00778                             S_ISDIR(sts->st.mode) ?
00779                             "open_directory" : "open_file");
00780             }
00781 
00782             DEBUGP("baton for mod %s %p (parent %p)", 
00783                     sts->name, baton, dir_baton);
00784         }
00785 
00786         if (!baton)
00787         {
00788             DEBUGP("new %s (parent %p)", 
00789                     sts->name, dir_baton);
00790 
00791             
00792 
00793 
00794 
00795 
00796             if (sts->flags & RF_COPY_BASE)
00797             {
00798                 status=cm__get_source(sts, filename, &src_path, &src_rev, 1);
00799                 BUG_ON(status == ENOENT, "copy but not copied?");
00800                 STOPIF(status, NULL);
00801             }
00802             else
00803             {
00804                 
00805                 src_path=NULL;
00806                 src_rev=SVN_INVALID_REVNUM;
00807             }
00808 
00809             
00810 
00811 
00812 
00813             DEBUGP("adding %s with %s:%ld",
00814                     filename, src_path, src_rev);
00816             STOPIF_SVNERR_TEXT( 
00817                     (S_ISDIR(sts->st.mode) ?
00818                      editor->add_directory : editor->add_file),
00819                     (utf8_filename, dir_baton, 
00820                      src_path, src_rev, 
00821                      subpool, &baton),
00822                     "%s(\"%s\", source=\"%s\"@%s)",
00823                     S_ISDIR(sts->st.mode) ? "add_directory" : "add_file",
00824                     filename, src_path, hlp__rev_to_string(src_rev));
00825             DEBUGP("baton for new %s %p (parent %p)", 
00826                     sts->name, baton, dir_baton);
00827 
00828 
00829             
00830             if (!(sts->flags & RF_COPY_BASE))
00831             {
00832                 sts->flags &= ~RF_ADD;
00833                 sts->entry_status |= FS_NEW | FS_META_CHANGED;
00834             }
00835         }
00836 
00837 
00838         committed_entries++;
00839         DEBUGP("doing changes, flags=%X", sts->flags);
00840         
00841         if (S_ISDIR(sts->st.mode))
00842         {
00843             STOPIF_SVNERR( ci__directory, (editor, sts, baton, subpool) );
00844             STOPIF_SVNERR( editor->close_directory, (baton, subpool) );
00845         }
00846         else
00847         {
00848             STOPIF_SVNERR( ci__nondir, (editor, sts, baton, subpool) );
00849             STOPIF_SVNERR( editor->close_file, (baton, NULL, subpool) );
00850         }
00851 
00852 
00853         
00854 
00855 
00856 
00857         if (sts->flags & RF_COPY_BASE)
00858             ci___unset_copyflags(sts);
00859 
00860 
00861         
00862         if (url__current_has_precedence(sts->url))
00863         {
00864             DEBUGP("setting URL of %s", filename);
00865             sts->url=current_url;
00866             sts->repos_rev = SET_REVNUM;
00867         }
00868     }
00869 
00870 
00871     
00872 
00873 
00874 
00875 
00876 
00877 
00878     if (! (dir->do_this_entry && ops__allowed_by_filter(dir)) )
00879         dir->flags |= RF_CHECK;
00880     else
00881         dir->flags &= ~RF_CHECK;
00882 
00883 
00884     
00885 
00886 
00887 
00888     
00889 
00890 
00891 
00892 
00893 
00894 
00895 
00896 
00897 
00898     
00899 
00900 
00901 
00902 
00903 
00904 
00905 
00906     if ((dir->do_this_entry && 
00907                 ops__allowed_by_filter(dir) && 
00908                 dir->parent &&
00909                 
00910                 (dir->entry_status & (FS_META_CHANGED | FS_PROPERTIES))) ||
00911             (dir->entry_status & FS_NEW))
00912     {
00913         STOPIF_SVNERR( ci___set_props, 
00914                 (dir_baton, dir, editor->change_dir_prop, pool) ); 
00915 
00916         STOPIF( ci___send_user_props(dir_baton, dir, 
00917                     editor->change_dir_prop, 0, pool), NULL);
00918     }
00919 
00920 
00921 ex:
00922     if (subpool) 
00923         apr_pool_destroy(subpool);
00924     RETURN_SVNERR(status);
00925 }
00926 
00927 
00931 int ci__getmsg(char **filename)
00932 {
00933     char *editor_cmd, *cp;
00934     int l,status;
00935     apr_file_t *af;
00936 
00937 
00938     status=0;
00939     STOPIF( waa__get_tmp_name( NULL, filename, &af, global_pool), NULL);
00940 
00941     
00942 
00943     STOPIF( apr_file_close(af), "close commit message file");
00944 
00945     editor_cmd=getenv("EDITOR");
00946     if (!editor_cmd) editor_cmd=getenv("VISUAL");
00947     if (!editor_cmd) editor_cmd="vi";
00948 
00949     l=strlen(editor_cmd) + 1 + strlen(opt_commitmsgfile) + 1;
00950 
00951     STOPIF( hlp__strmnalloc(l, &cp, 
00952                 editor_cmd, " ", opt_commitmsgfile, NULL), NULL);
00953 
00954     l=system(cp);
00955     STOPIF_CODE_ERR(l == -1, errno, "fork() failed");
00956 
00957     STOPIF_CODE_ERR(l, WEXITSTATUS(l),
00958             "spawned editor exited with %d, signal %d", 
00959             WEXITSTATUS(l),
00960             WIFSIGNALED(l) ? WTERMSIG(l) : 0);
00961     status=0;
00962 
00963 ex:
00964     return status;
00965 }
00966 
00967 
00978 svn_error_t *ci___base_dirs(char *current_missing,
00979         const svn_delta_editor_t *editor, 
00980         struct estat *root, 
00981         void *dir_baton)
00982 {
00983     int status;
00984     svn_error_t *status_svn;
00985     char *delim;
00986     void *child_baton;
00987 
00988 
00989     status=0;
00990     if (current_missing && *current_missing)
00991     {
00992         
00993         delim=strchr(current_missing, '/');
00994         if (delim) 
00995         {
00996             *delim=0;
00997             delim++;
00998             
00999             BUG_ON(!*delim || *delim=='/');
01000         }
01001 
01002         DEBUGP("adding %s", missing_path_utf8);
01003         STOPIF_SVNERR( editor->add_directory,
01004                 (missing_path_utf8, dir_baton, 
01005                  NULL, SVN_INVALID_REVNUM, 
01006                  current_url->pool, &child_baton));
01007 
01008         if (delim)
01009             delim[-1]='/';
01010 
01011         STOPIF_SVNERR( ci___base_dirs,
01012                 (delim, editor, root, child_baton));
01013 
01014         STOPIF_SVNERR( editor->close_directory,
01015                 (child_baton, current_url->pool));
01016     }
01017     else
01018         STOPIF_SVNERR( ci__directory,
01019                 (editor, root, dir_baton, current_url->pool));
01020 
01021 
01022 ex:
01023     RETURN_SVNERR(status);
01024 }
01025 
01026 
01027 
01028 
01038 int ci__work(struct estat *root, int argc, char *argv[])
01039 {
01040     int status;
01041     svn_error_t *status_svn;
01042     const svn_delta_editor_t *editor;
01043     void *edit_baton;
01044     void *root_baton;
01045     struct stat st;
01046     int commitmsg_fh,
01047         commitmsg_is_temp;
01048     char *utf8_commit_msg;
01049     char **normalized;
01050     const char *url_name;
01051     time_t delay_start;
01052     char *missing_dirs;
01053 
01054 
01055     status=0;
01056     status_svn=NULL;
01057     edit_baton=NULL;
01058     editor=NULL;
01059     
01060     commitmsg_fh=-1;
01061 
01062 
01063     opt__set_int(OPT__CHANGECHECK, 
01064             PRIO_MUSTHAVE,
01065             opt__get_int(OPT__CHANGECHECK) | CHCHECK_DIRS | CHCHECK_FILE);
01066 
01067 
01068     
01069     commitmsg_is_temp=!opt_commitmsg && !opt_commitmsgfile;
01070     if (commitmsg_is_temp)
01071         STOPIF( ci__getmsg(&opt_commitmsgfile), NULL);
01072 
01073 
01074 
01075     
01076 
01077 
01078 
01079 
01080     if (opt_commitmsgfile)
01081     {
01082         commitmsg_fh=open(opt_commitmsgfile, O_RDONLY);
01083         STOPIF_CODE_ERR( commitmsg_fh<0, errno, 
01084                 "cannot open file %s", opt_commitmsgfile);
01085     }
01086 
01087 
01088     STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
01089 
01090     
01091     STOPIF( url__load_nonempty_list(NULL, 0), NULL);
01092 
01093     if (urllist_count==1)
01094         current_url=urllist[0];
01095     else
01096     {
01097         url_name=opt__get_string(OPT__COMMIT_TO);
01098         STOPIF_CODE_ERR( !url_name || !*url_name, EINVAL,
01099                 "!Which URL would you like to commit to?\n"
01100                 "Please choose one (config option \"commit_to\").");
01101 
01102         STOPIF( url__find_by_name(url_name, ¤t_url), 
01103                 "!No URL named \"%s\" could be found.", url_name);
01104     }
01105 
01106     STOPIF_CODE_ERR( current_url->is_readonly, EROFS, 
01107             "!Cannot commit to \"%s\",\n"
01108             "because it is marked read-only.",
01109             current_url->url);
01110 
01111 
01112     STOPIF(ign__load_list(NULL), NULL);
01113 
01114 
01115     STOPIF( url__open_session(NULL, &missing_dirs), NULL);
01116     
01117     if (missing_dirs)
01118         STOPIF_CODE_ERR( opt__get_int(OPT__MKDIR_BASE) == OPT__NO, ENOENT,
01119                 "!The given URL \"%s\" does not exist (yet).\n"
01120                 "The missing directories \"%s\" could possibly be created, if\n"
01121                 "you enable the \"mkdir_base\" option (with \"-o mkdir_base=yes\").",
01122                 current_url->url, missing_dirs);     
01123 
01124 
01125 
01126     opt__set_int( OPT__CHANGECHECK, PRIO_MUSTHAVE, 
01127             opt__get_int(OPT__CHANGECHECK) | CHCHECK_DIRS | CHCHECK_FILE);
01128 
01129     
01130 
01131     STOPIF( waa__read_or_build_tree(root, argc, normalized, argv, 
01132                 NULL, 0), NULL);
01133 
01134 
01135     if (opt_commitmsgfile)
01136     {
01137         STOPIF_CODE_ERR( fstat(commitmsg_fh, &st) == -1, errno,
01138                 "cannot estimate size of %s", opt_commitmsgfile);
01139 
01140         if (st.st_size == 0)
01141         {
01142             
01143             DEBUGP("empty file");
01144             opt_commitmsg="";
01145         }
01146         else
01147         {
01148             DEBUGP("file is %llu bytes",  (t_ull)st.st_size);
01149 
01150             opt_commitmsg=mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
01151                     commitmsg_fh, 0);
01152             STOPIF_CODE_ERR(!opt_commitmsg, errno,
01153                     "mmap commit message (%s, %llu bytes)", 
01154                     opt_commitmsgfile, (t_ull)st.st_size);
01155         }
01156         close(commitmsg_fh);
01157     }
01158 
01159     if (!*opt_commitmsg)
01160     {
01161         STOPIF_CODE_ERR( opt__get_int(OPT__EMPTY_MESSAGE)==OPT__NO, EINVAL,
01162                 "!Empty commit messages are defined as invalid, "
01163                 "see \"empty_message\" option.");
01164     }
01165 
01166     STOPIF( hlp__local2utf8(opt_commitmsg, &utf8_commit_msg, -1),
01167             "Conversion of the commit message to utf8 failed");
01168 
01169     if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
01170         printf("Committing to %s\n", current_url->url);
01171 
01172 
01173     STOPIF_SVNERR( svn_ra_get_commit_editor,
01174             (current_url->session,
01175              &editor,
01176              &edit_baton,
01177              utf8_commit_msg,
01178              ci__callback,
01179              root,
01180              NULL, 
01181              FALSE, 
01182              global_pool) );
01183 
01184     if (opt_commitmsgfile && st.st_size != 0)
01185         STOPIF_CODE_ERR( munmap(opt_commitmsg, st.st_size) == -1, errno,
01186                 "munmap()");
01187     if (commitmsg_is_temp)
01188         STOPIF_CODE_ERR( unlink(opt_commitmsgfile) == -1, errno,
01189                 "Cannot remove temporary message file %s", opt_commitmsgfile);
01190 
01191 
01192     
01193     STOPIF_SVNERR( editor->open_root,
01194             (edit_baton, current_url->current_rev, global_pool, &root_baton) );
01195 
01196     
01197     if (ops__allowed_by_filter(root))
01198         STOPIF( hlp__lstat( root->name, &root->st), NULL);
01199 
01200 
01201     committed_entries=0;
01202     if (missing_dirs)
01203     {
01204         STOPIF( hlp__local2utf8( missing_dirs, &missing_dirs, -1), NULL);
01205         
01206 
01207         missing_path_utf8_len=strlen(missing_dirs);
01208         STOPIF( hlp__strnalloc(missing_path_utf8_len+1, 
01209                     &missing_path_utf8, missing_dirs), NULL);
01210     }
01211 
01212 
01213     
01214     STOPIF_SVNERR( ci___base_dirs,
01215             (missing_path_utf8, editor, root, root_baton));
01216 
01217 
01218     
01219     if (!status)
01220     {
01221         if (opt__get_int(OPT__EMPTY_COMMIT)==OPT__NO && 
01222                 committed_entries==0)
01223         {
01224             if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
01225                 printf("Avoiding empty commit as requested.\n");
01226             goto abort_commit;
01227         }
01228 
01229 
01230         STOPIF_SVNERR( editor->close_edit, (edit_baton, global_pool) );
01231         edit_baton=NULL;
01232 
01233         delay_start=time(NULL);
01234 
01235         
01236         if (!status)
01237         {
01238             
01239 
01240 
01241 
01242 
01243 
01244 
01245             STOPIF( waa__output_tree(root), NULL);
01246             STOPIF( url__output_list(), NULL);
01247         }
01248 
01249         
01250 
01251         STOPIF( hlp__delay(delay_start, DELAY_COMMIT), NULL);
01252     }
01253 
01254 ex:
01255     STOP_HANDLE_SVNERR(status_svn);
01256 
01257 ex2:
01258     if (status && edit_baton)
01259     {
01260 abort_commit:
01261         
01262 
01263         editor->abort_edit(edit_baton, global_pool);
01264     }
01265 
01266     return status;
01267 }
01268