1 //
2 // ====================================================================
3 // Copyright (c) 2003-2009 Barry A Scott. All rights reserved.
4 //
5 // This software is licensed as described in the file LICENSE.txt,
6 // which you should have received as part of this distribution.
7 //
8 // ====================================================================
9 //
10
11 #if defined( _MSC_VER )
12 // disable warning C4786: symbol greater than 255 character,
13 // nessesary to ignore as <map> causes lots of warning
14 #pragma warning(disable: 4786)
15 #endif
16
17 #include "pysvn.hpp"
18 #include "svn_config.h"
19 #include "svn_time.h"
20
21 #include "pysvn_static_strings.hpp"
22
23 static const std::string str_URL( "URL" );
24 static const std::string str_action( "action" );
25 static const std::string str_author( "author" );
26 static const std::string str_base_abspath( "base_abspath" );
27 static const std::string str_changed_author( "changed_author" );
28 static const std::string str_changed_date( "changed_date" );
29 static const std::string str_changed_revision( "changed_revision" );
30 static const std::string str_changelist( "changelist" );
31 static const std::string str_checksum( "checksum" );
32 static const std::string str_comment( "comment" );
33 static const std::string str_commit_author( "commit_author" );
34 static const std::string str_commit_revision( "commit_revision" );
35 static const std::string str_commit_time( "commit_time" );
36 static const std::string str_conflict_new( "conflict_new" );
37 static const std::string str_conflict_old( "conflict_old" );
38 static const std::string str_conflict_work( "conflict_work" );
39 static const std::string str_conflicts( "conflicts" );
40 static const std::string str_copy_from_revision( "copy_from_revision" );
41 static const std::string str_copy_from_url( "copy_from_url" );
42 static const std::string str_copyfrom_rev( "copyfrom_rev" );
43 static const std::string str_copyfrom_url( "copyfrom_url" );
44 static const std::string str_creation_date( "creation_date" );
45 static const std::string str_date( "date" );
46 static const std::string str_depth( "depth" );
47 static const std::string str_entry( "entry" );
48 static const std::string str_expiration_date( "expiration_date" );
49 static const std::string str_filesize( "filesize" );
50 static const std::string str_is_absent( "is_absent" );
51 static const std::string str_is_binary( "is_binary" );
52 static const std::string str_is_conflicted( "is_copied" );
53 static const std::string str_is_copied( "is_copied" );
54 static const std::string str_is_dav_comment( "is_dav_comment" );
55 static const std::string str_is_deleted( "is_deleted" );
56 static const std::string str_is_file_external( "is_file_external" );
57 static const std::string str_is_locked( "is_locked" );
58 static const std::string str_is_switched( "is_switched" );
59 static const std::string str_is_versioned( "is_versioned" );
60 static const std::string str_kind( "kind" );
61 static const std::string str_last_changed_author( "last_changed_author" );
62 static const std::string str_last_changed_date( "last_changed_date" );
63 static const std::string str_last_changed_rev( "last_changed_rev" );
64 static const std::string str_local_abspath( "local_abspath" );
65 static const std::string str_lock( "lock" );
66 static const std::string str_lock_comment( "lock_comment" );
67 static const std::string str_lock_creation_date( "lock_creation_date" );
68 static const std::string str_lock_owner( "lock_owner" );
69 static const std::string str_lock_token( "lock_token" );
70 static const std::string str_merged_file( "merged_file" );
71 static const std::string str_mime_type( "mime_type" );
72 static const std::string str_moved_from_abspath( "moved_from_abspath" );
73 static const std::string str_moved_to_abspath( "moved_to_abspath" );
74 static const std::string str_my_abspath( "my_abspath" );
75 static const std::string str_name( "name" );
76 static const std::string str_node_kind( "node_kind" );
77 static const std::string str_node_status( "node_status" );
78 static const std::string str_ood_changed_author( "ood_changed_author" );
79 static const std::string str_ood_changed_date( "ood_changed_date" );
80 static const std::string str_ood_changed_rev( "ood_changed_rev" );
81 static const std::string str_ood_kind( "ood_kind" );
82 static const std::string str_operation( "operation" );
83 static const std::string str_owner( "owner" );
84 static const std::string str_path( "path" );
85 static const std::string str_path_in_repos( "path_in_repos" );
86 static const std::string str_peg_rev( "peg_rev" );
87 static const std::string str_post_commit_err( "post_commit_err" );
88 static const std::string str_prejfile( "prejfile" );
89 static const std::string str_prop_status( "prop_status" );
90 static const std::string str_prop_time( "prop_time" );
91 static const std::string str_properties_time( "properties_time" );
92 static const std::string str_property_name( "property_name" );
93 static const std::string str_property_reject_file( "property_reject_file" );
94 static const std::string str_reason( "reason" );
95 static const std::string str_recorded_size( "recorded_size" );
96 static const std::string str_recorded_time( "recorded_time" );
97 static const std::string str_repos( "repos" );
98 static const std::string str_repos_UUID( "repos_UUID" );
99 static const std::string str_repos_lock( "repos_lock" );
100 static const std::string str_repos_node_status( "repos_node_status" );
101 static const std::string str_repos_prop_status( "repos_prop_status" );
102 static const std::string str_repos_relpath( "repos_relpath" );
103 static const std::string str_repos_root_URL( "repos_root_URL" );
104 static const std::string str_repos_text_status( "repos_text_status" );
105 static const std::string str_repos_url( "repos_url" );
106 static const std::string str_rev( "rev" );
107 static const std::string str_revision( "revision" );
108 static const std::string str_schedule( "schedule" );
109 static const std::string str_size( "size" );
110 static const std::string str_src_left_version( "src_left_version" );
111 static const std::string str_src_right_version( "src_right_version" );
112 static const std::string str_text_status( "text_status" );
113 static const std::string str_text_time( "text_time" );
114 static const std::string str_their_abspath( "their_abspath" );
115 static const std::string str_token( "token" );
116 static const std::string str_url( "url" );
117 static const std::string str_uuid( "uuid" );
118 static const std::string str_wc_info( "wc_info" );
119 static const std::string str_wc_is_locked( "wc_is_locked" );
120 static const std::string str_wcroot_abspath( "wcroot_abspath" );
121 static const std::string str_working_size( "working_size" );
122
123 #include <iostream>
124
125 //------------------------------------------------------------
126 //
127 // DictWrapper
128 //
129 //------------------------------------------------------------
DictWrapper(Py::Dict result_wrappers,const std::string & wrapper_name)130 DictWrapper::DictWrapper( Py::Dict result_wrappers, const std::string &wrapper_name )
131 : m_wrapper_name( wrapper_name )
132 , m_have_wrapper( false )
133 , m_wrapper()
134 {
135 if( result_wrappers.hasKey( wrapper_name ) )
136 {
137 m_wrapper = result_wrappers[ wrapper_name ];
138 m_have_wrapper = true;
139 }
140 }
141
~DictWrapper()142 DictWrapper::~DictWrapper()
143 {
144 }
145
wrapDict(Py::Dict result) const146 Py::Object DictWrapper::wrapDict( Py::Dict result ) const
147 {
148 if( m_have_wrapper )
149 {
150 Py::Tuple args( 1 );
151 args[0] = result;
152
153 try
154 {
155 return m_wrapper.apply( args );
156 }
157 catch( Py::Exception &e )
158 {
159 std::cerr << "pysvn: unhandled exception calling " << m_wrapper_name << std::endl;
160 PyErr_Print();
161 e.clear();
162
163 return Py::None();
164 }
165 }
166 else
167 {
168 return result;
169 }
170 }
171
172 //------------------------------------------------------------
173 //
174 // Converters
175 //
176 //------------------------------------------------------------
utf8_string_or_none(const char * str)177 Py::Object utf8_string_or_none( const char *str )
178 {
179 if( str == NULL )
180 return Py::None();
181 else
182 return Py::String( str, name_utf8 );
183 }
184
utf8_string_or_none(const std::string & str)185 Py::Object utf8_string_or_none( const std::string &str )
186 {
187 if( str.empty() )
188 return Py::None();
189 else
190 return Py::String( str, name_utf8 );
191 }
192
path_string_or_none(const char * str,SvnPool & pool)193 Py::Object path_string_or_none( const char *str, SvnPool &pool )
194 {
195 if( str == NULL )
196 return Py::None();
197 else
198 return Py::String( osNormalisedPath( str, pool ), name_utf8 );
199 }
200
path_string_or_none(const std::string & str,SvnPool & pool)201 Py::Object path_string_or_none( const std::string &str, SvnPool &pool )
202 {
203 if( str.empty() )
204 return Py::None();
205 else
206 return Py::String( osNormalisedPath( str, pool ), name_utf8 );
207 }
208
convertStringToTime(const std::string & text,apr_time_t now,SvnPool & pool)209 apr_time_t convertStringToTime( const std::string &text, apr_time_t now, SvnPool &pool )
210 {
211 svn_boolean_t matched = 0;
212 apr_time_t result = 0;
213
214 svn_error_t *error = svn_parse_date
215 (
216 &matched,
217 &result,
218 text.c_str(),
219 now,
220 pool
221 );
222 if( error != NULL || !matched )
223 {
224 return 0;
225 }
226
227 return result;
228 }
229
toSvnRevNum(svn_revnum_t rev)230 Py::Object toSvnRevNum( svn_revnum_t rev )
231 {
232 return Py::asObject( new pysvn_revision( svn_opt_revision_number, 0, rev ) );
233 }
234
toObject(apr_time_t t)235 Py::Object toObject( apr_time_t t )
236 {
237 return Py::Float( double( t )/1000000 );
238 }
239
240 #if defined( PYSVN_HAS_SVN_COMMIT_INFO_T )
toObject(pysvn_commit_info_t * commit_info,int commit_style)241 Py::Object toObject( pysvn_commit_info_t *commit_info, int commit_style )
242 {
243 if( commit_info == NULL )
244 return Py::None();
245
246 if( commit_style == 0 )
247 {
248 if( !SVN_IS_VALID_REVNUM( commit_info->revision ) )
249 return Py::None();
250
251 return toSvnRevNum( commit_info->revision );
252 }
253 else
254 if( commit_style == 1 )
255 {
256 Py::Dict commit_info_dict;
257
258 commit_info_dict[ str_date ] = utf8_string_or_none( commit_info->date );
259 commit_info_dict[ str_author ] = utf8_string_or_none( commit_info->author );
260 commit_info_dict[ str_post_commit_err ] = utf8_string_or_none( commit_info->post_commit_err );
261 if( SVN_IS_VALID_REVNUM( commit_info->revision ) )
262 {
263 commit_info_dict[ str_revision ] = toSvnRevNum( commit_info->revision );
264 }
265 else
266 {
267 commit_info_dict[ str_revision ] = Py::None();
268 }
269
270 return commit_info_dict;
271 }
272 else
273 {
274 throw Py::RuntimeError( "commit_style value invalid" );
275 }
276 }
277 #else
toObject(pysvn_commit_info_t * commit_info)278 Py::Object toObject( pysvn_commit_info_t *commit_info )
279 {
280 if( commit_info == NULL || !SVN_IS_VALID_REVNUM( commit_info->revision ) )
281 return Py::None();
282
283 return toSvnRevNum( commit_info->revision );
284 }
285 #endif
286
toFilesize(svn_filesize_t filesize)287 Py::Object toFilesize( svn_filesize_t filesize )
288 {
289 if( filesize == SVN_INVALID_FILESIZE )
290 {
291 return Py::None();
292 }
293 else
294 {
295 #ifdef HAVE_LONG_LONG
296 return Py::LongLong( static_cast<PY_LONG_LONG>( filesize ) );
297 #else
298 return Py::Int( static_cast<int>( filesize ) );
299 #endif
300 }
301 }
302
303
304 #if defined( PYSVN_HAS_CLIENT_STATUS_T )
toObject(Py::String path,svn_client_status_t & svn_status,SvnPool & pool,const DictWrapper & wrapper_status2,const DictWrapper & wrapper_lock)305 Py::Object toObject
306 (
307 Py::String path,
308 svn_client_status_t &svn_status,
309 SvnPool &pool,
310 const DictWrapper &wrapper_status2,
311 const DictWrapper &wrapper_lock
312 )
313 {
314 Py::Dict status;
315
316 status[ str_path ] = path;
317 status[ str_local_abspath ] = path_string_or_none( svn_status.local_abspath, pool );
318 status[ str_kind ] = toEnumValue( svn_status.kind );
319 status[ str_filesize ] = toFilesize( svn_status.filesize );
320 status[ str_is_versioned] = Py::Boolean( svn_status.versioned );
321 status[ str_is_conflicted ] = Py::Boolean( svn_status.conflicted );
322 status[ str_node_status ] = toEnumValue( svn_status.node_status );
323 status[ str_text_status ] = toEnumValue( svn_status.text_status );
324 status[ str_prop_status ] = toEnumValue( svn_status.prop_status );
325 status[ str_wc_is_locked ] = Py::Boolean( svn_status.wc_is_locked );
326 status[ str_is_copied ] = Py::Boolean( svn_status.copied );
327 status[ str_repos_root_URL ] = utf8_string_or_none( svn_status.repos_root_url );
328 status[ str_repos_UUID ] = utf8_string_or_none( svn_status.repos_uuid );
329 status[ str_repos_relpath ] = utf8_string_or_none( svn_status.repos_relpath );
330 status[ str_revision ] = toSvnRevNum( svn_status.revision );
331 status[ str_changed_revision ] = toSvnRevNum( svn_status.changed_rev );
332 status[ str_changed_date ] = toObject( svn_status.changed_date );
333 status[ str_changed_author ] = utf8_string_or_none( svn_status.changed_author );
334 status[ str_is_switched ] = Py::Boolean( svn_status.switched );
335 status[ str_is_file_external ] = Py::Boolean( svn_status.file_external );
336 if( svn_status.lock == NULL )
337 {
338 status[ str_lock ] = Py::None();
339 }
340 else
341 {
342 status[ str_lock ] = toObject( *svn_status.lock, wrapper_lock );
343 }
344 status[ str_changelist ] = utf8_string_or_none( svn_status.changelist );
345 status[ str_depth ] = toEnumValue( svn_status.depth );
346 status[ str_ood_kind ] = toEnumValue( svn_status.ood_kind );
347 status[ str_repos_node_status ] = toEnumValue( svn_status.repos_node_status );
348 status[ str_repos_text_status ] = toEnumValue( svn_status.repos_text_status );
349 status[ str_repos_prop_status ] = toEnumValue( svn_status.repos_prop_status );
350 if( svn_status.repos_lock == NULL )
351 {
352 status[ str_repos_lock ] = Py::None();
353 }
354 else
355 {
356 status[ str_repos_lock ] = toObject( *svn_status.repos_lock, wrapper_lock );
357 }
358 status[ str_ood_changed_rev ] = toSvnRevNum( svn_status.ood_changed_rev );
359 status[ str_ood_changed_date ] = toObject( svn_status.ood_changed_date );
360 status[ str_ood_changed_author ] = utf8_string_or_none( svn_status.ood_changed_author );
361 #if defined(PYSVN_HAS_SVN_1_9)
362 status[ str_moved_from_abspath ] = utf8_string_or_none( svn_status.moved_from_abspath );
363 status[ str_moved_to_abspath ] = utf8_string_or_none( svn_status.moved_to_abspath );
364 #endif
365
366 return wrapper_status2.wrapDict( status );
367 }
368 #endif
369
toObject(Py::String path,pysvn_wc_status_t & svn_status,SvnPool & pool,const DictWrapper & wrapper_status,const DictWrapper & wrapper_entry,const DictWrapper & wrapper_lock)370 Py::Object toObject
371 (
372 Py::String path,
373 pysvn_wc_status_t &svn_status,
374 SvnPool &pool,
375 const DictWrapper &wrapper_status,
376 const DictWrapper &wrapper_entry,
377 const DictWrapper &wrapper_lock
378 )
379 {
380 Py::Dict status;
381
382 status[ str_path ] = path;
383 if( svn_status.entry == NULL )
384 {
385 status[ str_entry ] = Py::None();
386 }
387 else
388 {
389 status[ str_entry ] = toObject( *svn_status.entry, pool, wrapper_entry );
390 }
391 if( svn_status.repos_lock == NULL )
392 {
393 status[ str_repos_lock ] = Py::None();
394 }
395 #if defined( PYSVN_HAS_CLIENT_STATUS2 )
396 else
397 {
398 status[ str_repos_lock ] = toObject( *svn_status.repos_lock, wrapper_lock );
399 }
400 #endif
401
402 long is_versioned;
403 switch( svn_status.text_status )
404 {
405 // exists, but uninteresting
406 case svn_wc_status_normal:
407 // is scheduled for addition
408 case svn_wc_status_added:
409 // under v.c., but is missing
410 case svn_wc_status_missing:
411 // scheduled for deletion
412 case svn_wc_status_deleted:
413 // was deleted and then re-added
414 case svn_wc_status_replaced:
415 // text or props have been modified
416 case svn_wc_status_modified:
417 // local mods received repos mods (### unused)
418 case svn_wc_status_merged:
419 // local mods received conflicting repos mods
420 case svn_wc_status_conflicted:
421 is_versioned = 1;
422 break;
423
424 // an unversioned resource is in the way of the versioned resource
425 case svn_wc_status_obstructed:
426 // does not exist
427 case svn_wc_status_none:
428 // is not a versioned thing in this wc
429 case svn_wc_status_unversioned:
430 // is unversioned but configured to be ignored
431 case svn_wc_status_ignored:
432 // an unversioned directory path populated by an svn:externals
433 // property; this status is not used for file externals
434 case svn_wc_status_external:
435 // a directory doesn't contain a complete entries list
436 case svn_wc_status_incomplete:
437 // assume any new status not versioned
438 default:
439 is_versioned = 0;
440 }
441
442 status[ str_is_versioned ] = Py::Int( is_versioned );
443 status[ str_is_locked ] = Py::Int( svn_status.locked );
444 status[ str_is_copied ] = Py::Int( svn_status.copied );
445 status[ str_is_switched ] = Py::Int( svn_status.switched );
446 status[ str_prop_status ] = toEnumValue( svn_status.prop_status );
447 status[ str_text_status ] = toEnumValue( svn_status.text_status );
448 status[ str_repos_prop_status ] = toEnumValue( svn_status.repos_prop_status );
449 status[ str_repos_text_status ] = toEnumValue( svn_status.repos_text_status );
450
451 return wrapper_status.wrapDict( status );
452 }
453
toObject(const svn_wc_entry_t & svn_entry,SvnPool & pool,const DictWrapper & wrapper_entry)454 Py::Object toObject
455 (
456 const svn_wc_entry_t &svn_entry,
457 SvnPool &pool,
458 const DictWrapper &wrapper_entry
459 )
460 {
461 Py::Dict entry;
462
463 entry[ str_checksum ] = utf8_string_or_none( svn_entry.checksum );
464 entry[ str_commit_author ] = utf8_string_or_none( svn_entry.cmt_author );
465 entry[ str_commit_revision ] = toSvnRevNum( svn_entry.cmt_rev );
466 entry[ str_commit_time ] = toObject( svn_entry.cmt_date );
467 entry[ str_conflict_new ] = path_string_or_none( svn_entry.conflict_new, pool );
468 entry[ str_conflict_old ] = path_string_or_none( svn_entry.conflict_old, pool );
469 entry[ str_conflict_work ] = path_string_or_none( svn_entry.conflict_wrk, pool );
470 entry[ str_copy_from_revision ] = toSvnRevNum( svn_entry.copyfrom_rev );
471 entry[ str_copy_from_url ] = utf8_string_or_none( svn_entry.copyfrom_url );
472 entry[ str_is_absent ] = Py::Int( svn_entry.absent );
473 entry[ str_is_copied ] = Py::Int( svn_entry.copied );
474 entry[ str_is_deleted ] = Py::Int( svn_entry.deleted );
475 entry[ str_kind ] = toEnumValue( svn_entry.kind );
476 entry[ str_name ] = path_string_or_none( svn_entry.name, pool );
477 entry[ str_properties_time ] = toObject( svn_entry.prop_time );
478 entry[ str_property_reject_file ] = path_string_or_none( svn_entry.prejfile, pool );
479 entry[ str_repos ] = utf8_string_or_none( svn_entry.repos );
480 entry[ str_revision ] = toSvnRevNum( svn_entry.revision );
481 entry[ str_schedule ] = toEnumValue( svn_entry.schedule );
482 entry[ str_text_time ] = toObject( svn_entry.text_time );
483 entry[ str_url ] = utf8_string_or_none( svn_entry.url );
484 entry[ str_uuid ] = utf8_string_or_none( svn_entry.uuid );
485 #if defined( PYSVN_HAS_CLIENT_STATUS2 )
486 entry[ str_lock_token ] = utf8_string_or_none( svn_entry.lock_token );
487 entry[ str_lock_owner ] = utf8_string_or_none( svn_entry.lock_owner );
488 entry[ str_lock_comment ] = utf8_string_or_none( svn_entry.lock_comment );
489 entry[ str_lock_creation_date ] = toObject( svn_entry.lock_creation_date );
490 #endif
491
492 return wrapper_entry.wrapDict( entry );
493 }
494
495 #if defined( PYSVN_HAS_CLIENT_INFO )
toObject(const svn_info_t & info,const DictWrapper & wrapper_info,const DictWrapper & wrapper_lock,const DictWrapper & wrapper_wc_info)496 Py::Object toObject
497 (
498 const svn_info_t &info,
499 const DictWrapper &wrapper_info,
500 const DictWrapper &wrapper_lock,
501 const DictWrapper &wrapper_wc_info
502 )
503 {
504 Py::Dict py_info;
505
506 // Where the item lives in the repository.
507 py_info[str_URL] = utf8_string_or_none( info.URL );
508
509 // The revision of the object. If path_or_url is a working-copy
510 // path, then this is its current working revnum. If path_or_url
511 // is a URL, then this is the repos revision that path_or_url lives in.
512 py_info[str_rev] = toSvnRevNum( info.rev );
513
514 // The node's kind.
515 py_info[str_kind] = toEnumValue( info.kind );
516
517 // The root URL of the repository.
518 py_info[str_repos_root_URL] = utf8_string_or_none( info.repos_root_URL );
519
520 // The repository's UUID.
521 py_info[str_repos_UUID] = utf8_string_or_none( info.repos_UUID );
522
523 // The last revision in which this object changed.
524 py_info[str_last_changed_rev] = toSvnRevNum( info.last_changed_rev );
525
526 // The date of the last_changed_rev.
527 py_info[str_last_changed_date] = toObject( info.last_changed_date );
528
529 // The author of the last_changed_rev.
530 py_info[str_last_changed_author] = utf8_string_or_none( info.last_changed_author );
531
532 #if defined( PYSVN_HAS_CLIENT_LOCK )
533 // An exclusive lock, if present. Could be either local or remote.
534 if( info.lock == NULL )
535 {
536 py_info[str_lock] = Py::None();
537 }
538 else
539 {
540 py_info[str_lock] = toObject( *info.lock, wrapper_lock );
541 }
542 #endif
543
544 // Whether or not to ignore the next 10 wc-specific fields.
545 if( info.has_wc_info == 0 )
546 {
547 py_info[str_wc_info] = Py::None();
548 }
549 else
550 {
551 Py::Dict py_wc_info;
552
553 py_wc_info[str_schedule] = toEnumValue( info.schedule );
554 py_wc_info[str_copyfrom_url] = utf8_string_or_none( info.copyfrom_url );
555 py_wc_info[str_copyfrom_rev] = toSvnRevNum( info.copyfrom_rev );
556 py_wc_info[str_text_time] = toObject( info.text_time );
557 py_wc_info[str_prop_time] = toObject( info.prop_time );
558 py_wc_info[str_checksum] = utf8_string_or_none( info.checksum );
559 py_wc_info[str_conflict_old] = utf8_string_or_none( info.conflict_old );
560 py_wc_info[str_conflict_new] = utf8_string_or_none( info.conflict_new );
561 py_wc_info[str_conflict_work] = utf8_string_or_none( info.conflict_wrk );
562 py_wc_info[str_prejfile] = utf8_string_or_none( info.prejfile );
563 #ifdef PYSVN_HAS_SVN_INFO_T__CHANGELIST
564 py_wc_info[str_changelist] = utf8_string_or_none( info.changelist );
565 py_wc_info[str_depth] = toEnumValue( info.depth );
566 #endif
567 #ifdef PYSVN_HAS_SVN_INFO_T__SIZES
568 #ifdef HAVE_LONG_LONG
569 if( info.working_size == SVN_INFO_SIZE_UNKNOWN )
570 py_wc_info[str_working_size] = Py::None();
571 else
572 py_wc_info[str_working_size] = Py::LongLong( static_cast<PY_LONG_LONG>( info.working_size ) );
573 if( info.size == SVN_INFO_SIZE_UNKNOWN )
574 py_wc_info[str_size] = Py::None();
575 else
576 py_wc_info[str_size] = Py::LongLong( static_cast<PY_LONG_LONG>( info.size ) );
577 #else
578 if( info.working_size == SVN_INFO_SIZE_UNKNOWN )
579 py_wc_info[str_working_size] = Py::None();
580 else
581 py_wc_info[str_working_size] = Py::Int( static_cast<int>( info.working_size ) );
582 if( info.size == SVN_INFO_SIZE_UNKNOWN )
583 py_wc_info[str_size] = Py::None();
584 else
585 py_wc_info[str_size] = Py::Int( static_cast<int>( info.size ) );
586 #endif
587 #endif
588 py_info[str_wc_info] = wrapper_wc_info.wrapDict( py_wc_info );
589 }
590
591 return wrapper_info.wrapDict( py_info );
592 }
593 #endif
594
595 #if defined( PYSVN_HAS_CLIENT_INFO3 )
toObject(const svn_wc_conflict_version_t * info)596 Py::Object toObject
597 (
598 const svn_wc_conflict_version_t *info
599 )
600 {
601 if( info == NULL )
602 {
603 return Py::None();
604 }
605
606 Py::Dict d;
607
608 d[str_repos_url] = utf8_string_or_none( info->repos_url );
609 d[str_peg_rev] = toSvnRevNum( info->peg_rev );
610 d[str_path_in_repos] = utf8_string_or_none( info->path_in_repos );
611 d[str_node_kind] = toEnumValue( info->node_kind );
612 #if defined( PYSVN_HAS_SVN_1_8 )
613 d[str_repos_UUID] = utf8_string_or_none( info->repos_uuid );
614 #endif
615
616 return d;
617 }
618
toHex(const unsigned char * bytes,size_t length)619 Py::String toHex( const unsigned char *bytes, size_t length )
620 {
621 static char hex_digits[] = "0123456789abcdef";
622 std::string human;
623
624
625 for( size_t index=0; index < length; ++index )
626 {
627 human += hex_digits[ bytes[index] >> 4 ];
628 human += hex_digits[ bytes[index] & 0x0f ];
629 }
630
631 return Py::String( human );
632 }
633
toObject(const svn_client_info2_t & info,SvnPool & pool,const DictWrapper & wrapper_info,const DictWrapper & wrapper_lock,const DictWrapper & wrapper_wc_info)634 Py::Object toObject
635 (
636 const svn_client_info2_t &info,
637 SvnPool &pool,
638 const DictWrapper &wrapper_info,
639 const DictWrapper &wrapper_lock,
640 const DictWrapper &wrapper_wc_info
641 )
642 {
643 Py::Dict py_info;
644
645 // Where the item lives in the repository.
646 py_info[str_URL] = utf8_string_or_none( info.URL );
647
648 // The revision of the object. If path_or_url is a working-copy
649 // path, then this is its current working revnum. If path_or_url
650 // is a URL, then this is the repos revision that path_or_url lives in.
651 py_info[str_rev] = toSvnRevNum( info.rev );
652
653 // The root URL of the repository.
654 py_info[str_repos_root_URL] = utf8_string_or_none( info.repos_root_URL );
655
656 // The repository's UUID.
657 py_info[str_repos_UUID] = utf8_string_or_none( info.repos_UUID );
658
659 // The node's kind.
660 py_info[str_kind] = toEnumValue( info.kind );
661
662 py_info[str_size] = toFilesize( info.size );
663
664 // The last revision in which this object changed.
665 py_info[str_last_changed_rev] = toSvnRevNum( info.last_changed_rev );
666
667 // The date of the last_changed_rev.
668 py_info[str_last_changed_date] = toObject( info.last_changed_date );
669
670 // The author of the last_changed_rev.
671 py_info[str_last_changed_author] = utf8_string_or_none( info.last_changed_author );
672
673 // An exclusive lock, if present. Could be either local or remote.
674 if( info.lock == NULL )
675 {
676 py_info[str_lock] = Py::None();
677 }
678 else
679 {
680 py_info[str_lock] = toObject( *info.lock, wrapper_lock );
681 }
682
683 // Whether or not to ignore the next 10 wc-specific fields.
684 if( info.wc_info == NULL )
685 {
686 py_info[str_wc_info] = Py::None();
687 }
688 else
689 {
690 Py::Dict py_wc_info;
691
692 py_wc_info[str_schedule] = toEnumValue( info.wc_info->schedule );
693 py_wc_info[str_copyfrom_url] = utf8_string_or_none( info.wc_info->copyfrom_url );
694 py_wc_info[str_copyfrom_rev] = toSvnRevNum( info.wc_info->copyfrom_rev );
695 if( info.wc_info->checksum != NULL )
696 {
697 switch( info.wc_info->checksum->kind )
698 {
699 case svn_checksum_md5:
700 py_wc_info[str_checksum] = toHex( info.wc_info->checksum->digest, 16 );
701 break;
702 case svn_checksum_sha1:
703 py_wc_info[str_checksum] = toHex( info.wc_info->checksum->digest, 20 );
704 break;
705 default:
706 py_wc_info[str_checksum] = Py::None();
707 }
708 }
709 else
710 {
711 py_wc_info[str_checksum] = Py::None();
712 }
713
714 py_wc_info[str_changelist] = utf8_string_or_none( info.wc_info->changelist );
715 py_wc_info[str_depth] = toEnumValue( info.wc_info->depth );
716 #ifdef HAVE_LONG_LONG
717 if( info.wc_info->recorded_size == SVN_INFO_SIZE_UNKNOWN )
718 py_wc_info[str_recorded_size] = Py::None();
719 else
720 py_wc_info[str_recorded_size] = Py::LongLong( static_cast<PY_LONG_LONG>( info.wc_info->recorded_size ) );
721 #else
722 if( info.wc_info->recorded_size == SVN_INFO_SIZE_UNKNOWN )
723 py_wc_info[str_recorded_size] = Py::None();
724 else
725 py_wc_info[str_recorded_size] = Py::Int( static_cast<int>( info.wc_info->recorded_size ) );
726 #endif
727 py_wc_info[str_recorded_time] = toObject( info.wc_info->recorded_time );
728
729 // I'm guesses that recorded size is what used to be size and working_size
730 py_wc_info[str_size] = py_wc_info[str_recorded_size];
731 py_wc_info[str_working_size] = py_wc_info[str_recorded_size];
732
733 // I'm guesses that recorded date is what used to be text_time and prop_time
734 py_wc_info[str_text_time] = py_wc_info[str_recorded_time];
735 py_wc_info[str_prop_time] = py_wc_info[str_recorded_time];
736
737 // conflicts
738 int count = 0;
739 if( info.wc_info->conflicts != NULL )
740 {
741 count = info.wc_info->conflicts->nelts;
742 }
743
744 switch( count )
745 {
746 case 0:
747 py_wc_info[str_conflict_old] = Py::None();
748 py_wc_info[str_conflict_new] = Py::None();
749 py_wc_info[str_conflict_work] = Py::None();
750 py_wc_info[str_prejfile] = Py::None();
751 break;
752
753 case 1:
754 // map for backwards compatibility
755 {
756 svn_wc_conflict_description2_t *item = ((svn_wc_conflict_description2_t **)info.wc_info->conflicts->elts)[0];
757
758 py_wc_info[str_conflict_old] = utf8_string_or_none( item->my_abspath );
759 py_wc_info[str_conflict_new] = utf8_string_or_none( item->their_abspath );
760 py_wc_info[str_conflict_work] = utf8_string_or_none( item->merged_file );
761 py_wc_info[str_prejfile] = utf8_string_or_none( item->their_abspath );
762 }
763 break;
764
765 default:
766 {
767 Py::List all_conflicts;
768
769 for( int i=0; i<count; ++i )
770 {
771 Py::Dict conflict;
772
773 svn_wc_conflict_description2_t *item = ((svn_wc_conflict_description2_t **)info.wc_info->conflicts->elts)[i];
774
775 conflict[str_local_abspath] = path_string_or_none( item->local_abspath, pool );
776 conflict[str_node_kind] = toEnumValue( item->node_kind );
777 conflict[str_kind] = toEnumValue( item->kind );
778 if( item->kind == svn_wc_conflict_kind_property )
779 {
780 conflict[str_property_name] = utf8_string_or_none( item->property_name );
781 }
782 else
783 {
784 conflict[str_property_name] = Py::None();
785 }
786
787 if( item->kind == svn_wc_conflict_kind_text )
788 {
789 conflict[str_is_binary] = Py::Boolean( item->is_binary );
790 conflict[str_mime_type] = utf8_string_or_none( item->mime_type );
791 }
792 else
793 {
794 conflict[str_is_binary] = Py::None();
795 conflict[str_mime_type] = Py::None();
796 }
797 conflict[str_action] = toEnumValue( item->action );
798 conflict[str_reason] = toEnumValue( item->reason );
799 conflict[str_base_abspath] = path_string_or_none( item->base_abspath, pool );
800 conflict[str_their_abspath] = path_string_or_none( item->their_abspath, pool );
801 conflict[str_my_abspath] = path_string_or_none( item->my_abspath, pool );
802 conflict[str_merged_file] = path_string_or_none( item->merged_file, pool );
803
804 conflict[str_operation] = toEnumValue( item->operation );
805 conflict[str_src_left_version] = toObject( item->src_left_version );
806 conflict[str_src_right_version] = toObject( item->src_right_version );
807
808 all_conflicts.append( conflict );
809 }
810
811 py_wc_info[str_conflicts] = all_conflicts;
812 }
813 break;
814 }
815
816 py_wc_info[str_wcroot_abspath] = utf8_string_or_none( info.wc_info->wcroot_abspath );
817 #if defined(PYSVN_HAS_SVN_1_8)
818 py_wc_info[str_moved_to_abspath] = utf8_string_or_none( info.wc_info->moved_to_abspath );
819 py_wc_info[str_moved_from_abspath] = utf8_string_or_none( info.wc_info->moved_from_abspath );
820 #endif
821 py_info[str_wc_info] = wrapper_wc_info.wrapDict( py_wc_info );
822 }
823
824 return wrapper_info.wrapDict( py_info );
825 }
826 #endif
827
828 #if defined( PYSVN_HAS_CLIENT_LOCK )
toObject(const svn_lock_t & lock,const DictWrapper & wrapper_lock)829 Py::Object toObject
830 (
831 const svn_lock_t &lock,
832 const DictWrapper &wrapper_lock
833 )
834 {
835
836 Py::Dict py_lock;
837
838 py_lock[str_path] = utf8_string_or_none( lock.path );
839 py_lock[str_token] = utf8_string_or_none( lock.token );
840 py_lock[str_owner] = utf8_string_or_none( lock.owner );
841 py_lock[str_comment] = utf8_string_or_none( lock.comment );
842 py_lock[str_is_dav_comment] = Py::Boolean( lock.is_dav_comment != 0 );
843 if( lock.creation_date == 0 )
844 {
845 py_lock[str_creation_date] = Py::None();
846 }
847 else
848 {
849 py_lock[str_creation_date] = toObject( lock.creation_date );
850 }
851 if( lock.expiration_date == 0 )
852 {
853 py_lock[str_expiration_date] = Py::None();
854 }
855 else
856 {
857 py_lock[str_expiration_date] = toObject( lock.expiration_date );
858 }
859
860 return wrapper_lock.wrapDict( py_lock );
861 }
862 #endif
863
propsToObject(apr_hash_t * props,SvnPool & pool)864 Py::Object propsToObject( apr_hash_t *props, SvnPool &pool )
865 {
866 Py::Dict py_prop_dict;
867
868 for( apr_hash_index_t *hi = apr_hash_first( pool, props ); hi; hi = apr_hash_next( hi ) )
869 {
870 const void *key = NULL;
871 void *val = NULL;
872
873 apr_hash_this (hi, &key, NULL, &val);
874 const svn_string_t *propval = (const svn_string_t *)val;
875
876 py_prop_dict[ Py::String( (const char *)key ) ] = Py::String( propval->data, (int)propval->len );
877 }
878
879 return py_prop_dict;
880 }
881
882 #if defined( PYSVN_HAS_CLIENT_PROPGET5 )
inheritedPropsToObject(apr_array_header_t * inherited_props,SvnPool & pool)883 Py::Object inheritedPropsToObject( apr_array_header_t *inherited_props, SvnPool &pool )
884 {
885 Py::Dict all_inherited_props;
886
887 for (int j = 0; j < inherited_props->nelts; ++j)
888 {
889 svn_prop_inherited_item_t *item = ((svn_prop_inherited_item_t **)inherited_props->elts)[j];
890
891 Py::String path_or_url = utf8_string_or_none( item->path_or_url );
892 Py::Dict py_prop_dict = propsToObject( item->prop_hash, pool );
893
894 all_inherited_props[ path_or_url ] = py_prop_dict;
895 }
896
897 return all_inherited_props;
898 }
899 #endif
900
proplistToObject(Py::List & py_path_propmap_list,apr_array_header_t * props,SvnPool & pool)901 void proplistToObject( Py::List &py_path_propmap_list, apr_array_header_t *props, SvnPool &pool )
902 {
903 for (int j = 0; j < props->nelts; ++j)
904 {
905 svn_client_proplist_item_t *item = ((svn_client_proplist_item_t **)props->elts)[j];
906
907 Py::Object py_prop_dict( propsToObject( item->prop_hash, pool ) );
908
909 std::string node_name( item->node_name->data, item->node_name->len );
910
911 Py::Tuple py_path_proplist( 2 );
912 py_path_proplist[0] = Py::String( osNormalisedPath( node_name, pool ) );
913 py_path_proplist[1] = py_prop_dict;
914
915 py_path_propmap_list.append( py_path_proplist );
916 }
917 }
918
revnumListToObject(apr_array_header_t * revs,SvnPool & pool)919 Py::Object revnumListToObject( apr_array_header_t *revs, SvnPool &pool )
920 {
921 Py::List py_list;
922
923 for (int j = 0; j < revs->nelts; ++j)
924 {
925 svn_revnum_t revnum = ((svn_revnum_t *)revs->elts)[j];
926 py_list.append( toSvnRevNum( revnum ) );
927 }
928
929 return py_list;
930 }
931
asUtf8Bytes(Py::Object obj)932 Py::Bytes asUtf8Bytes( Py::Object obj )
933 {
934 Py::String any( obj );
935 Py::Bytes utf8( any.encode( name_utf8 ) );
936 return utf8;
937 }
938
targetsFromStringOrList(Py::Object arg,SvnPool & pool)939 apr_array_header_t *targetsFromStringOrList( Py::Object arg, SvnPool &pool )
940 {
941 int num_targets = 1;
942 if( arg.isList() )
943 {
944 Py::List paths( arg );
945 num_targets = paths.length();
946 }
947
948 apr_array_header_t *targets = apr_array_make( pool, num_targets, sizeof( const char * ) );
949
950 std::string type_error_message;
951 try
952 {
953 if( arg.isList() )
954 {
955 Py::List path_list( arg );
956
957 for( Py::List::size_type i=0; i<path_list.length(); i++ )
958 {
959 type_error_message = "expecting path list members to be strings (arg 1)";
960
961 Py::Bytes path_str( asUtf8Bytes( path_list[i] ) );
962 std::string norm_path( svnNormalisedIfPath( path_str.as_std_string(), pool ) );
963
964 *(char **)apr_array_push( targets ) = apr_pstrdup( pool, norm_path.c_str() );
965 }
966 }
967 else
968 {
969 type_error_message = "expecting path to be a string (arg 1)";
970 Py::Bytes path_str( asUtf8Bytes( arg ) );
971
972 std::string norm_path( svnNormalisedIfPath( path_str.as_std_string(), pool ) );
973
974 *(char **)apr_array_push( targets ) = apr_pstrdup( pool, norm_path.c_str() );
975 }
976 }
977 catch( Py::TypeError & )
978 {
979 throw Py::TypeError( type_error_message );
980 }
981
982 return targets;
983 }
984
arrayOfStringsFromListOfStrings(Py::Object arg,SvnPool & pool)985 apr_array_header_t *arrayOfStringsFromListOfStrings( Py::Object arg, SvnPool &pool )
986 {
987 apr_array_header_t *array = NULL;
988
989 std::string type_error_message;
990 try
991 {
992 type_error_message = "expecting list of strings";
993 Py::List path_list( arg );
994 Py::List::size_type num_targets = path_list.length();
995
996 array = apr_array_make( pool, num_targets, sizeof( const char * ) );
997
998 for( Py::List::size_type i=0; i<num_targets; i++ )
999 {
1000 type_error_message = "expecting list members to be strings";
1001
1002 Py::Bytes str( asUtf8Bytes( path_list[i] ) );
1003 *(char **)apr_array_push( array ) = apr_pstrdup( pool, str.as_std_string().c_str() );
1004 }
1005 }
1006 catch( Py::TypeError & )
1007 {
1008 throw Py::TypeError( type_error_message );
1009 }
1010
1011 return array;
1012 }
1013
toListOfStrings(Py::Object obj)1014 Py::List toListOfStrings( Py::Object obj )
1015 {
1016 Py::List list;
1017 if( obj.isList() )
1018 list = obj;
1019 else
1020 list.append( obj );
1021
1022 // check all members of the list are strings
1023 for( Py::List::size_type i=0; i<list.length(); i++ )
1024 {
1025 Py::String path_str( list[i] );
1026 }
1027
1028 return list;
1029 }
1030
hashOfStringsFromDictOfStrings(Py::Object arg,SvnPool & pool)1031 apr_hash_t *hashOfStringsFromDictOfStrings( Py::Object arg, SvnPool &pool )
1032 {
1033 Py::Dict dict( arg );
1034
1035 apr_hash_t *hash = apr_hash_make( pool );
1036
1037 std::string type_error_message;
1038 try
1039 {
1040 Py::List all_keys( dict.keys() );
1041
1042 for( Py::List::size_type i=0; i<all_keys.length(); i++ )
1043 {
1044 type_error_message = "expecting string key in dict";
1045 Py::Bytes key( asUtf8Bytes( all_keys[i] ) );
1046
1047 type_error_message = "expecting string value in dict";
1048 Py::Bytes value( asUtf8Bytes( dict[ key ] ) );
1049
1050 char *hash_key = apr_pstrdup( pool, key.as_std_string().c_str() );
1051 svn_string_t *hash_value = svn_string_create( value.as_std_string().c_str(), pool );
1052
1053 apr_hash_set( hash, hash_key, APR_HASH_KEY_STRING, hash_value );
1054 }
1055 }
1056 catch( Py::TypeError & )
1057 {
1058 throw Py::TypeError( type_error_message );
1059 }
1060
1061 return hash;
1062 }
1063
direntsToObject(apr_hash_t * dirents,SvnPool & pool)1064 Py::Object direntsToObject( apr_hash_t *dirents, SvnPool &pool )
1065 {
1066 Py::Dict py_dirents_dict;
1067
1068 for( apr_hash_index_t *hi = apr_hash_first( pool, dirents ); hi; hi = apr_hash_next( hi ) )
1069 {
1070 const void *key = NULL;
1071 void *val = NULL;
1072
1073 apr_hash_this (hi, &key, NULL, &val);
1074 const svn_fs_dirent_t *dirent = (const svn_fs_dirent_t *)val;
1075
1076 py_dirents_dict[ Py::String( (const char *)key ) ] = toEnumValue( dirent->kind );
1077 }
1078
1079 return py_dirents_dict;
1080 }
1081
1082 //------------------------------------------------------------
1083 //
1084 //
1085 //
1086 //------------------------------------------------------------
1087 #if defined( PYSVN_HAS_COMMIT_CALLBACK2_T )
1088
CommitInfoResult(SvnPool & pool)1089 CommitInfoResult::CommitInfoResult( SvnPool &pool )
1090 : m_all_results( apr_array_make( pool, 16, sizeof( svn_commit_info_t * ) ) )
1091 , m_pool( pool )
1092 {
1093 }
1094
~CommitInfoResult()1095 CommitInfoResult::~CommitInfoResult()
1096 {
1097 }
1098
count()1099 int CommitInfoResult::count()
1100 {
1101 if( m_all_results == NULL )
1102 return 0;
1103
1104 return m_all_results->nelts;
1105 }
1106
result(int index)1107 const svn_commit_info_t *CommitInfoResult::result( int index )
1108 {
1109 assert( m_all_results != NULL );
1110 assert( index >= 0 && index < m_all_results->nelts );
1111 return APR_ARRAY_IDX( m_all_results, index, const svn_commit_info_t * );
1112 }
1113
CommitInfoResult_callback(const svn_commit_info_t * commit_info,void * baton,apr_pool_t *)1114 extern "C" svn_error_t *CommitInfoResult_callback( const svn_commit_info_t *commit_info, void *baton, apr_pool_t * )
1115 {
1116 CommitInfoResult *result = CommitInfoResult::castBaton( baton );
1117
1118 if( result->m_all_results == NULL )
1119 {
1120 return svn_error_create( APR_ENOMEM, NULL, "no memory for commit info results" );
1121 }
1122
1123 svn_commit_info_t *copy = svn_commit_info_dup( commit_info, result->m_pool );
1124 if( copy == NULL )
1125 {
1126 return svn_error_create( APR_ENOMEM, NULL, "no memory for commit info results" );
1127 }
1128
1129 APR_ARRAY_PUSH( result->m_all_results, const svn_commit_info_t * ) = copy;
1130
1131 return SVN_NO_ERROR;
1132 }
1133
1134
toObject(const svn_commit_info_t * commit_info)1135 Py::Object toObject( const svn_commit_info_t *commit_info )
1136 {
1137 Py::Dict commit_info_dict;
1138
1139 commit_info_dict[ str_date ] = utf8_string_or_none( commit_info->date );
1140 commit_info_dict[ str_author ] = utf8_string_or_none( commit_info->author );
1141 if( commit_info->post_commit_err == NULL )
1142 {
1143 commit_info_dict[ str_post_commit_err ] = Py::None();
1144 }
1145 else
1146 {
1147 // this field is sometimes a pointer to a none UTF8 string
1148 commit_info_dict[ str_post_commit_err ] = utf8_string_or_none( commit_info->post_commit_err );
1149 }
1150
1151 if( SVN_IS_VALID_REVNUM( commit_info->revision ) )
1152 {
1153 commit_info_dict[ str_revision ] = toSvnRevNum( commit_info->revision );
1154 }
1155 else
1156 {
1157 commit_info_dict[ str_revision ] = Py::None();
1158 }
1159
1160 return commit_info_dict;
1161 }
1162
toObject(CommitInfoResult & commit_info,const DictWrapper & wrapper_commit_info,int commit_style)1163 Py::Object toObject( CommitInfoResult &commit_info, const DictWrapper &wrapper_commit_info, int commit_style )
1164 {
1165 if( commit_info.count() == 0 )
1166 {
1167 Py::Dict commit_info_dict;
1168
1169 commit_info_dict[ str_date ] = Py::None();
1170 commit_info_dict[ str_author ] = Py::None();
1171 commit_info_dict[ str_post_commit_err ] = Py::None();
1172 commit_info_dict[ str_revision ] = Py::None();
1173
1174 return commit_info_dict;
1175 }
1176
1177 if( commit_style == 0 )
1178 {
1179 // assume the last commit revision is the best to return
1180 const svn_commit_info_t *last = commit_info.result( commit_info.count()-1 );
1181
1182 if( !SVN_IS_VALID_REVNUM( last->revision ) )
1183 {
1184 return Py::None();
1185 }
1186
1187 return toSvnRevNum( last->revision );
1188 }
1189 else
1190 if( commit_style == 1 )
1191 {
1192 const svn_commit_info_t *last = commit_info.result( commit_info.count()-1 );
1193 return toObject( last );
1194 }
1195 else
1196 if( commit_style == 2 )
1197 {
1198 Py::List all_results;
1199
1200 for( int index=0; index<commit_info.count(); ++index )
1201 {
1202 Py::Dict py_commit_info( toObject( commit_info.result( index ) ) );
1203 all_results.append( wrapper_commit_info.wrapDict( py_commit_info ) );
1204 }
1205
1206 return all_results;
1207 }
1208 else
1209 {
1210 throw Py::RuntimeError( "commit_style value invalid" );
1211 }
1212 }
1213 #endif
1214