Imported Upstream version 0.6.17
[platform/upstream/libsolv.git] / examples / tclsolv
1 #!/usr/bin/tclsh
2
3 package require solv
4 package require inifile
5 package require fileutil
6
7 set reposdir /etc/zypp/repos.d
8
9 ### some helpers
10
11 proc fileno {file} {
12   if [regexp -- {^file(\d+)$} $file match fd] {
13     return $fd
14   }
15   error "file not open"
16 }
17
18 set ::globalarray_cnt 0
19
20 proc globalarray {} {
21   set name "::globalarray_[incr ::globalarray_cnt]"
22   array set $name [list varName $name]
23   return $name
24 }
25
26 ### generic repo handling (cache reading/writing)
27
28 proc repo_calc_cookie_file {selfName filename} {
29   upvar $selfName self
30   set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
31   $chksum add "1.1"
32   $chksum add_stat $filename
33   return [$chksum raw]
34 }
35
36 proc repo_calc_cookie_fp {selfName fp} {
37   upvar $selfName self
38   set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
39   $chksum add "1.1"
40   $chksum add_fp $fp
41   return [$chksum raw]
42 }
43
44 proc repo_calc_cookie_ext {selfName f cookie} {
45   upvar $selfName self
46   set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
47   $chksum add "1.1"
48   $chksum add $cookie
49   $chksum add_fstat [$f fileno]
50   return [$chksum raw]
51 }
52
53 proc repo_cachepath {selfName {ext "-"}} {
54   upvar $selfName self
55   regsub {^\.} $self(name) _ path
56   if {$ext ne "-"} {
57     set path "${path}_$ext.solvx"
58   } else {
59     set path "${path}.solv"
60   }
61   regsub -all / $path _ path
62   return "/var/cache/solv/$path"
63 }
64
65 proc repo_generic_load {selfName pool} {
66   upvar $selfName self
67   set handle [ $pool add_repo $self(name) ]
68   set self(handle) $handle
69   $handle configure -priority [expr 99 - $self(priority)] -appdata $self(varName)
70   set dorefresh $self(autorefresh)
71   set metadata_expire $self(metadata_expire)
72   catch {
73     if {$metadata_expire == -1 || [clock seconds] - [file mtime [repo_cachepath self]] < $metadata_expire} {
74       set dorefresh 0
75     }
76   }
77   set self(cookie) {}
78   set self(extcookie) {}
79   if { !$dorefresh && [repo_usecachedrepo self] } {
80     puts "repo $self(name): cached"
81     return 1 
82   }
83   return 0 
84 }
85
86 proc repo_free_handle {selfName} {
87   upvar $selfName self
88   set handle $self(handle)
89   unset self(handle)
90   $handle free 1
91 }
92
93 proc repo_usecachedrepo {selfName {ext "-"} {mark 0}} {
94   upvar $selfName self
95   set repopath [repo_cachepath self $ext]
96   set code [catch {
97     set f [open $repopath "rb"]
98     seek $f -32 end
99     set fcookie [read $f 32]
100     set cookie [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
101     if {$cookie ne {} && $cookie ne $fcookie} {
102       close $f
103       return 0
104     }
105     set fextcookie {}
106     if {$ext eq "-" && $self(type) ne "system"} {
107       seek $f -64 end
108       set fextcookie [read $f 32]
109     }
110     seek $f 0 start
111     set ff [solv::xfopen_fd {} [fileno $f]]
112     close $f
113     set flags 0
114     if {$ext ne "-"} {
115       set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
116       if {$ext ne "DL"} {
117         set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
118       }
119     }
120     if {! [$self(handle) add_solv $ff $flags]} {
121       $ff close
122       return 0
123     }
124     $ff close
125     if {$self(type) ne "system" && $ext eq "-"} {
126       set self(cookie) $fcookie
127       set self(extcookie) $fextcookie
128     }
129     if {$mark} {
130       catch {
131         ::fileutil::touch -c -m -t [clock seconds] $repopath
132       }
133     }
134     return 1
135   } res]
136   return [expr {$code == 2 ? $res : 0}]
137 }
138
139 proc repo_writecachedrepo {selfName {ext "-"} {repodata "NULL"}} {
140   upvar $selfName self
141   if [info exists self(incomplete)] {
142     return
143   }
144   file mkdir "/var/cache/solv"
145   ::fileutil::tempdir "/var/cache/solv"
146   set tempfilename [::fileutil::tempfile ".newsolv-"]
147   ::fileutil::tempdirReset
148   set f [solv::xfopen $tempfilename "w+"]
149   file attributes $tempfilename -permissions 0444
150   if {$repodata eq {NULL}} {
151     $self(handle) write $f
152   } else {
153     $repodata write $f
154   }
155   $f flush
156   if {$self(type) ne "system" && $ext eq "-"} {
157     if {$self(extcookie) eq {}} {
158       set self(extcookie) [repo_calc_cookie_ext self $f $self(cookie)]
159     }
160     $f write $self(extcookie)
161   }
162   $f write [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
163   $f close
164   file rename -force -- $tempfilename [repo_cachepath self $ext]
165 }
166
167 proc repo_download {selfName file uncompress chksum {markincomplete 0}} {
168   upvar $selfName self
169   regsub {/$} $self(baseurl) {} url
170   set url "$url/$file"
171   set tempfilename [::fileutil::tempfile]
172   set f [open $tempfilename rb+]
173   file delete -- $tempfilename
174   if [catch {
175     exec -ignorestderr -- curl -f -s -L $url ">@$f"
176   }] {
177     seek $f 0 end
178     if {($chksum ne "" && $chksum ne "NULL") || [tell $f] != 0} {
179       puts "$file: download error"
180     }
181     close $f
182     return {NULL}
183   }
184   seek $f 0 start
185   if {$chksum ne "" && $chksum ne "NULL"} {
186     set fchksum [solv::new_Chksum [$chksum cget -type]]
187     if {$fchksum eq "" || $fchksum eq "NULL"} {
188       puts "$file: unknown checksum type"
189       if {$markincomplete} {
190         set self(incomplete) 1
191       }
192       close $f
193       return {NULL}
194     }
195     $fchksum add_fd [fileno $f]
196     if {[$fchksum != $chksum]} {
197       puts "$file: checksum mismatch"
198       if {$markincomplete} {
199         set self(incomplete) 1
200       }
201       close $f
202       return {NULL}
203     }
204   }
205   set ff [solv::xfopen_fd [expr {$uncompress ? $file : ""}] [fileno $f]]
206   close $f
207   return $ff
208 }
209
210 proc repo_generic_add_ext_keys {selfName ext repodata h} {
211   upvar $selfName self
212   if {$ext eq "DL"} {
213     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOSITORY_DELTAINFO
214     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_FLEXARRAY
215   } elseif {$ext eq "DU"} {
216     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_DISKUSAGE
217     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRNUMNUMARRAY
218   } elseif {$ext eq "FL"} {
219     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_FILELIST
220     $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRSTRARRAY
221   }
222 }
223
224 ### system
225
226 proc repo_system_load {selfName pool} {
227   upvar $selfName self
228   set handle [ $pool add_repo $self(name) ]
229   set self(handle) $handle
230   $handle configure -appdata $self(varName)
231   $pool configure -installed $handle
232   puts -nonewline "rpm database: "
233   set self(cookie) [repo_calc_cookie_file self "/var/lib/rpm/Packages"]
234   if [repo_usecachedrepo self] {
235     puts "cached"
236     return 1
237   }
238   puts "reading"
239   set f [solv::xfopen [repo_cachepath self]]
240   $handle add_rpmdb_reffp $f $solv::Repo_REPO_REUSE_REPODATA
241   repo_writecachedrepo self
242 }
243
244 ### repomd
245
246 proc repo_repomd_add_ext {selfName repodata what ext} {
247   upvar $selfName self
248   set where [repo_repomd_find self $what]
249   if {$where eq {}} {
250     return
251   }
252   set h [$repodata new_handle]
253   $repodata set_poolstr $h $solv::REPOSITORY_REPOMD_TYPE $what
254   $repodata set_str $h $solv::REPOSITORY_REPOMD_LOCATION [lindex $where 0]
255   $repodata set_checksum $h $solv::REPOSITORY_REPOMD_CHECKSUM [lindex $where 1]
256   repo_generic_add_ext_keys self $ext $repodata $h
257   $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
258 }
259
260 proc repo_repomd_add_exts {selfName} {
261   upvar $selfName self
262   set repodata [$self(handle) add_repodata 0]
263   repo_repomd_add_ext self $repodata "filelists" "FL"
264   $repodata internalize
265 }
266
267 proc repo_repomd_find {selfName what} {
268   upvar $selfName self
269   set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
270   $di prepend_keyname $solv::REPOSITORY_REPOMD
271   solv::iter d $di {
272     set dp [$d parentpos]
273     set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
274     set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
275     if {$filename ne "" && $checksum eq "NULL"} {
276       puts "no $filename file checksum"
277     } elseif {$filename ne ""} {
278       return [list $filename $checksum]
279     }
280   }
281   return {}
282 }
283
284 proc repo_repomd_load {selfName pool} {
285   upvar $selfName self
286   if [repo_generic_load self $pool] {
287     return 1
288   }
289   puts -nonewline "rpmmd repo '$self(name)': "
290   set f [repo_download self {repodata/repomd.xml} 0 {}]
291   if {$f eq {NULL}} {
292     puts "no repomd.xml file, skipped"
293     repo_free_handle self
294     return 0
295   }
296   set self(cookie) [repo_calc_cookie_fp self $f]
297   if [repo_usecachedrepo self "-" 1] {
298     puts "cached"
299     return 1
300   }
301   set handle $self(handle)
302   $handle add_repomdxml $f
303   puts "fetching"
304   set primary [repo_repomd_find self primary]
305   if {$primary ne {}} {
306     set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
307     if {$f ne {NULL}} {
308       $handle add_rpmmd $f {}
309       $f close
310     }
311     if [info exists self(incomplete)] {
312       return 0
313     }
314   }
315   set updateinfo [repo_repomd_find self primary]
316   if {$updateinfo ne {}} {
317     set f [repo_download self [lindex $updateinfo  0] 1 [lindex $updateinfo 1] 1]
318     if {$f ne {NULL}} {
319       $handle add_updateinfoxml $f
320       $f close
321     }
322   }
323   repo_repomd_add_exts self
324   repo_writecachedrepo self
325   $self(handle) create_stubs
326   return 1
327 }
328
329 proc repo_repomd_packagespath {selfName} {
330   return ""
331 }
332
333 proc repo_repomd_load_ext {selfName repodata} {
334   upvar $selfName self
335   switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
336     "deltainfo" {
337       set ext DL
338     }
339     "filelists" {
340       set ext FL
341     }
342     default {
343       return 0
344     }
345   }
346   puts -nonewline "\[$self(name):$ext: "
347   flush stdout
348   if [repo_usecachedrepo self $ext] {
349     puts "cached]"
350     return 1
351   }
352   puts "fetching]"
353   set handle $self(handle)
354   set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
355   set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
356   set f [repo_download self $filename 1 $filechecksum]
357   if {$f eq {NULL}} {
358     return 0
359   }
360   if {$ext eq "FL"} {
361     $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
362   }
363   $f close
364   repo_writecachedrepo self $ext $repodata
365   return 1
366 }
367
368 ### susetags
369
370 proc repo_susetags_add_ext {selfName repodata what ext} {
371   upvar $selfName self
372   set where [repo_susetags_find self $what]
373   if {$where eq {}} {
374     return
375   }
376   set h [$repodata new_handle]
377   $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
378   $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
379   repo_generic_add_ext_keys self $ext $repodata $h
380   $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
381 }
382
383 proc repo_susetags_add_exts {selfName} {
384   upvar $selfName self
385   set repodata [$self(handle) add_repodata 0]
386   repo_susetags_add_ext self $repodata "packages.FL" "FL"
387   repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
388   $repodata internalize
389 }
390
391 proc repo_susetags_find {selfName what} {
392   upvar $selfName self
393   set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
394   $di prepend_keyname $solv::SUSETAGS_FILE
395   solv::iter d $di {
396     set dp [$d parentpos]
397     set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
398     return [list $what $checksum]
399   }
400   return {}
401 }
402
403 proc repo_susetags_load {selfName pool} {
404   upvar $selfName self
405   if [repo_generic_load self $pool] {
406     return 1
407   }
408   puts -nonewline "susetags repo '$self(name)': "
409   set f [repo_download self {content} 0 {}]
410   if {$f eq {NULL}} {
411     puts "no content file, skipped"
412     repo_free_handle self
413     return 0
414   }
415   set self(cookie) [repo_calc_cookie_fp self $f]
416   if [repo_usecachedrepo self "-" 1] {
417     puts "cached"
418     return 1
419   }
420   set handle $self(handle)
421   $handle add_content $f
422   puts "fetching"
423   set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
424   set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
425   if {$descrdir eq {NULL}} {
426     set descrdir "suse/setup/descr"
427   }
428   set packages [repo_susetags_find self "packages.gz"]
429   if {$packages eq {}} {
430     set packages [repo_susetags_find self "packages"]
431   }
432   if {$packages ne {}} {
433     set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
434     if {$f ne {NULL}} {
435       $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
436       $f close
437       set packages [repo_susetags_find self "packages.en.gz"]
438       if {$packages eq {}} {
439         set packages [repo_susetags_find self "packages.en"]
440       }
441       if {$packages ne {}} {
442         set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
443         if {$f ne {NULL}} {
444           $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
445           $f close
446         }
447       }
448       $handle internalize
449     }
450   }
451   repo_susetags_add_exts self
452   repo_writecachedrepo self
453   $self(handle) create_stubs
454   return 1
455 }
456
457 proc repo_susetags_packagespath {selfName} {
458   upvar $selfName self
459   set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
460   return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
461 }
462
463 proc repo_susetags_load_ext {selfName repodata} {
464   upvar $selfName self
465   set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
466   set ext [string range $filename 9 10]
467   puts -nonewline "\[$self(name):$ext: "
468   flush stdout
469   if [repo_usecachedrepo self $ext] {
470     puts "cached]"
471     return 1
472   }
473   puts "fetching]"
474   set handle $self(handle)
475   set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
476   set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
477   if {$descrdir eq {NULL}} {
478     set descrdir "suse/setup/descr"
479   }
480   set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
481   set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
482   if {$f eq {NULL}} {
483     return 0
484   }
485   set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
486   if {$ext ne "DL"} {
487     set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
488   }
489   $handle add_susetags $f $defvendorid $ext $flags
490   $f close
491   repo_writecachedrepo self $ext $repodata
492   return 1
493 }
494
495 ### unknown
496
497 proc repo_unknown_load {selfName pool} {
498   upvar $selfName self
499   puts "unsupported repo '$self(name)': skipped"
500   return 0
501 }
502
503 ### poor man's OO
504
505 proc repo_load {selfName pool} {
506   upvar $selfName self
507   "repo_$self(type)_load" self $pool
508 }
509
510 proc repo_packagespath {selfName} {
511   upvar $selfName self
512   "repo_$self(type)_packagespath" self
513 }
514
515 proc repo_load_ext {selfName repodata} {
516   upvar $selfName self
517   "repo_$self(type)_load_ext" self $repodata
518 }
519
520 ###
521
522 proc load_stub {repodata} {
523   set code [catch {
524     upvar #0 [[$repodata cget -repo] cget -appdata] repo
525     if [info exists repo(handle)] {
526       return [repo_load_ext repo $repodata]
527     }
528     return 0
529   } res]
530   if {$code == 2} {
531     return $res
532   }
533   puts stderr $res
534   return 0
535 }
536
537 ###
538
539 set repoNames {}
540 foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
541   set ini [::ini::open $reponame r]
542   foreach alias [::ini::sections $ini] {
543     upvar #0 [globalarray] repo
544     array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
545     array set repo [::ini::get $ini $alias]
546     set repo(name) $alias
547     switch -exact -- $repo(type) {
548       rpm-md  { set repo(type) repomd }
549       yast2   { set repo(type) susetags }
550       default { set repo(type) unknown }
551     }
552     lappend repoNames $repo(varName)
553   }
554   ::ini::close $ini
555 }
556
557 set pool [solv::new_Pool]
558 $pool setarch
559 $pool set_loadcallback load_stub
560
561 upvar #0 [globalarray] sysrepo
562 array set sysrepo [list name {@System} type system]
563 repo_load sysrepo $pool
564
565 foreach repoName $repoNames {
566   upvar 0 $repoName repo
567   if {$repo(enabled)} {
568     repo_load repo $pool
569   }
570 }
571
572
573 set cmd [lindex $::argv 0]
574 set ::argv [lreplace $::argv 0 0]
575
576 array set cmdabbrev [ list \
577   in install \
578   rm erase \
579   ls list \
580   ve verify \
581   se search \
582 ]
583 if [info exists cmdabbrev($cmd)] {
584   set cmd $cmdabbrev($cmd)
585 }
586
587 if {$cmd eq "search"} {
588   set arg [lindex $::argv 0]
589   $pool createwhatprovides
590   set sel [$pool Selection]
591   set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
592   solv::iter d $di {
593     $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
594   }
595   foreach s [$sel solvables] {
596     puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
597   }
598   exit
599 }
600
601 $pool addfileprovides
602 $pool createwhatprovides
603
604 array set cmdactionmap [ list \
605   install $solv::Job_SOLVER_INSTALL \
606   erase $solv::Job_SOLVER_ERASE \
607   up $solv::Job_SOLVER_UPDATE \
608   dup $solv::Job_SOLVER_DISTUPGRADE \
609   verify $solv::Job_SOLVER_VERIFY \
610   list 0 \
611   info 0 \
612 ]
613
614 set jobs {}
615 foreach arg $::argv {
616   set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
617                   $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
618   switch -glob -- $arg {
619     "/*" {
620       set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
621       if {$cmd eq "erase"} {
622         set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
623       }
624     }
625   }
626   set sel [$pool select $arg $flags]
627   if [$sel isempty] {
628     set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
629     if {![$sel isempty]} {
630       puts "\[ignoring case for '$arg']"
631     }
632   }
633   if [$sel isempty] {
634     puts "nothing matches '$arg'"
635     exit 1
636   }
637   if {[$sel flags] & $solv::Selection_SELECTION_FILELIST} {
638     puts "\[using file list match for '$arg']"
639   }
640   if {[$sel flags] & $solv::Selection_SELECTION_PROVIDES} {
641     puts "\[using capability match for '$arg']"
642   }
643   lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
644 }
645
646 if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
647   set sel [$pool Selection_all]
648   lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
649 }
650
651 if {$jobs eq {}} {
652   puts "no package matched."
653   exit 1
654 }
655
656 if {$cmd eq "list" || $cmd eq "info"} {
657   foreach job $jobs {
658     foreach s [$job solvables] {
659       if {$cmd eq "info"} {
660         puts [format {Name:        %s} [$s str]]
661         puts [format {Repo:        %s} [[$s cget -repo] cget -name]]
662         puts [format {Summary:     %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
663         set str [$s lookup_str $solv::SOLVABLE_URL]
664         if {$str ne {}} {
665           puts [format {Url:         %s} $str]
666         }
667         set str [$s lookup_str $solv::SOLVABLE_LICENSE]
668         if {$str ne {}} {
669           puts [format {License      %s} $str]
670         }
671         puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
672         puts {}
673       } else {
674         puts [format {  - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
675         puts [format {    %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
676       }
677     }
678   }
679   exit
680 }
681
682 #$pool set_debuglevel 1
683 set solver [$pool Solver]
684 $solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
685 if {$cmd eq "erase"} {
686   $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
687 }
688
689 set problems [$solver solve $jobs]
690 if {$problems ne {}} {
691   set pcnt 1
692   foreach problem $problems {
693     puts [format {Problem %d/%d:} $pcnt [llength $problems]]
694     puts [$problem str]
695     incr pcnt
696   }
697   exit 1
698 }
699
700 set trans [$solver transaction]
701
702 if [$trans isempty] {
703   puts "Nothing to do."
704   exit
705 }
706
707 puts {}
708 puts "Transaction summary:"
709 puts {}
710 foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
711   switch -- [$cl cget -type] \
712     $solv::Transaction_SOLVER_TRANSACTION_ERASE {
713       puts [format {%d erased packages:} [$cl cget -count]]
714     } \
715     $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
716       puts [format {%d installed packages:} [$cl cget -count]]
717     } \
718     $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
719       puts [format {%d reinstalled packages:} [$cl cget -count]]
720     } \
721     $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
722       puts [format {%d downgraded packages:} [$cl cget -count]]
723     } \
724     $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
725       puts [format {%d changed packages:} [$cl cget -count]]
726     } \
727     $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
728       puts [format {%d upgraded packages:} [$cl cget -count]]
729     } \
730     $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
731       puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
732     } \
733     $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
734       puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
735     } \
736     default continue
737   foreach p [$cl solvables] {
738     set cltype [$cl cget -type]
739     if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
740       set op [$trans othersolvable $p]
741       puts [format {  - %s -> %s} [$p str] [$op str]]
742     } else {
743       puts [format {  - %s} [$p str]]
744     }
745   }
746   puts {}
747 }
748 puts [format {install size change: %d K} [$trans calc_installsizechange]]
749 puts {}
750
751 while 1 {
752   puts -nonewline "OK to continue (y/n)? "
753   flush stdout
754   set yn [gets stdin]
755   if {$yn eq "y"} {
756     break
757   }
758   if {$yn eq "n" || $yn eq "q"} {
759     exit
760   }
761 }
762
763 set newpkgs [$trans newsolvables]
764 array set newpkgs_f {}
765 if {$newpkgs ne {}} {
766   set downloadsize 0
767   foreach p $newpkgs {
768     set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
769   }
770   puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
771   foreach p $newpkgs {
772     upvar #0 [[$p cget -repo] cget -appdata] repo
773     set location [$p lookup_location]
774     if {$location eq {}} {
775       continue
776     }
777     set location "[repo_packagespath repo][lindex $location 0]"
778     set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
779     set f [repo_download repo $location 0 $checksum]
780     set newpkgs_f([$p cget -id]) $f
781     puts -nonewline "."
782     flush stdout
783   }
784   puts {}
785 }
786
787 puts "Committing transaction:"
788 $trans order
789 foreach p [$trans steps] {
790   set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
791   if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
792     puts "erase [$p str]"
793     regsub {^[0-9]+:} [$p cget -evr] {} nvr
794     set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
795     exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
796   } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
797     puts "install [$p str]"
798     set f $newpkgs_f([$p cget -id])
799     set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
800     $f cloexec 0
801     exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"
802   }
803 }