mesa upgraded to version 17.3.1
[dragora.git] / jul / jul.tcl
blobd9b8ddef17f16fcb513f03b690af42249b76ad20
1 #!/usr/bin/tclsh
2 # Copyright 2015,2016,2017 Lucas Sköldqvist <frusen@dragora.org>
3 # License: GPLv3
5 package require sqlite3
7 set version "0.5.4"
8 set arch ""
10 array set repolist {
11 gungre {
12 {frusen kelsoo mprodrigues tom mmpg}
13 gungre.db
14 http://gungre.ch/jul/
18 if {[file exists $::env(HOME)/.julrc] == 1} {
19 source $::env(HOME)/.julrc
20 } elseif {[file exists $::env(HOME)/.jul/config.tcl] == 1} {
21 source $::env(HOME)/.jul/config.tcl
24 proc list_repo {args} {
25 global repolist
26 global arch
28 # if there is no argument, search for all
29 if {[lindex $args 0] == {}} {
30 set query "WHERE"
31 } else {
32 set query "WHERE name LIKE '%$args%' and"
35 if {$arch != ""} {
36 if {$query != ""} {
37 append query " and arch = '$arch' and"
38 } else {
39 set query "WHERE arch = '$arch' and"
43 set result {}
44 foreach repo [lreverse [array names repolist]] {
45 set db_file $::env(HOME)/.jul/[get_db_file $repo]
46 if {[catch {sqlite3 db $db_file -create false} fid]} {
47 puts stderr "jul: search: Unable to open database file."
48 return
51 foreach re [lindex $repolist($repo) 0] {
52 db eval "SELECT rowid,* FROM package $query repo='$re'" {
53 set pkg(rowid) $rowid
54 set pkg(name) $name
55 set pkg(version) $version
56 set pkg(repo) $repo
57 set pkg(arch) $arch
58 set pkg(build) $build
59 db eval "SELECT desc FROM description \
60 JOIN package USING(name) \
61 WHERE lang = 'en' AND name='$name'" {
62 set pkg(desc) $desc
64 lappend result [array get pkg]
68 db close
71 return $result
74 # Returns the file name of the repository 'name'.
75 proc get_db_file {name} {
76 global repolist
77 return [lindex $repolist($name) 1]
80 proc add {args} {
81 global repolist
83 set cmd [lindex $args 0]
85 set z 0
86 set c 0
88 # receive a list of all the packages
89 set pkg_list [list_repo [lindex $args 1]]
91 if {[llength $pkg_list] == 0} {
92 puts "jul: $cmd: No packages found."
93 return
96 set i 1
97 set finds {}
98 foreach key $pkg_list {
99 array set pkg $key
100 lappend finds $pkg(rowid)
101 puts -nonewline " \[$i\] $pkg(repo) $pkg(name)-$pkg(version)"
102 puts "-$pkg(arch)-$pkg(build)"
103 array unset pkg
104 incr i
107 set z 0
108 set c 0
109 while {$z == 0} {
110 puts "Select a package to $cmd or `q' to quit."
111 puts -nonewline "Pressing `enter' selects the top package: "
112 flush stdout
113 set c [gets stdin]
114 set z [checkanswer $c [llength $pkg_list]]
115 # If enter was pressed, select the first package.
116 if {$z == 2} {set c "1"}
119 # lists start at 0
120 incr c -1
122 set result {}
123 foreach repo [lreverse [array names repolist]] {
124 set db_file $::env(HOME)/.jul/[get_db_file $repo]
125 if {[catch {sqlite3 db $db_file -create false} fid]} {
126 puts stderr "jul: $cmd: Unable to open database file."
127 return
130 db eval "SELECT * FROM repository JOIN package \
131 ON repository.name = package.repo \
132 WHERE package.rowid=[lindex $finds $c]" {
133 set p(name) $name
134 set p(version) $version
135 set p(arch) $arch
136 set p(build) $build
137 set p(url) $url
138 lappend result [array get p]
141 db close
144 foreach item [getplist] {
145 if {"$p(name)-$p(version)-$p(arch)-$p(build)" == $item} {
146 puts "jul: $cmd: Package already installed."
147 return
151 cd "$::env(HOME)/.jul/cache"
152 set fn $p(name)-$p(version)-$p(arch)-$p(build).tlz
153 set pkg $p(url)$p(name)/$fn
154 puts -nonewline "Downloading $p(name)... "
155 flush stdout
156 getfile $pkg
157 puts -nonewline "Downloading checksum... "
158 getfile $pkg.sha1sum
159 puts -nonewline "Verifying... "
161 if {[verify_file $fn.sha1sum] == -1} {
162 return -1
164 puts "done"
166 set pfile $::env(HOME)/.jul/cache/$fn
167 catch {exec su -c "pkg $cmd $pfile"} results options
168 puts $results
171 # List the changes in 'repo'.
172 proc changes {repo} {
173 global repolist
175 if {$repo == ""} {
176 puts "jul: changes: You must specify a repository."
177 exit
178 } elseif {$repo == "-h"} {
179 help changes
182 if {[lsearch -exact [lreverse [array names repolist]] $repo] == -1} {
183 puts "jul: changes: `$repo' is not a valid repository."
184 exit
187 if {[file exists $::env(HOME)/.jul/repos/$repo.changes] == 1} {
188 set f [open $::env(HOME)/.jul/repos/$repo.changes]
189 fcopy $f stdout
190 close $f
191 } else {
192 puts -nonewline "jul: changes: Can't list changes for `$repo'."
193 puts " Try synchronising."
197 # Check if 'value' is in 'range' and return 1 if that is the case. Exit if
198 # 'value' is 'q'.
199 proc checkanswer {value range} {
200 if {$value == "q"} {exit}
201 if {$value == ""} {return 2}
203 if {[string is integer -strict $value] == 1} {
204 # $value < $range because we start to count from 0 and
205 # array size does not
206 if {[expr {$value >= 1}] && [expr {$value <= $range}]} {
207 return 1
211 return 0
214 # Remove '$HOME/.jul'.
215 proc clean {args} {
216 if {$args == "-h"} {help clean}
218 if {$args != "-y"} {
219 puts "You're about to delete `$::env(HOME)/.jul/' and all of its "
220 puts "content."
221 puts -nonewline "Proceed? (Y/n) "
222 flush stdout
224 set c [read stdin 1]
225 if {$c == "n" || $c == "N"} {
226 puts "Aborted"
227 } else {
228 file delete -force $::env(HOME)/.jul
229 puts "Deleted"
231 } else {
232 file delete -force $::env(HOME)/.jul
233 puts "Deleted $::env(HOME)/.jul and all of its content."
237 proc getfile {url} {
238 if {[catch {exec curl -sfO $url} results options]} {
239 set details [dict get $options -errorcode]
241 puts "failed"
242 puts -nonewline "Could not download file: "
244 if {[lindex $details 0] eq "CHILDSTATUS"} {
245 set status [lindex $details 2]
246 if {$status == 22} {
247 puts "HTTP error code > 400"
248 puts "The file was probably not found."
249 puts "Please report this!"
250 } elseif {$status == 23} {
251 puts "Write error in $::env(PWD)"
253 } elseif {[lindex $details 1] eq "ENOENT"} {
254 puts "Could not find `curl'. Make sure it is installed."
255 } else {
256 puts "Unknown error. Please report this!"
258 } else {
259 puts "done"
263 proc verify_file {fn} {
264 if {[catch {exec sha1sum -c $fn} results options]} {
265 puts "failed\n"
266 puts $results
267 puts ""
268 return -1
272 # Print the usage of the command passed as 'args' or the help screen if no
273 # command is passed.
274 proc help {args} {
275 if {$args == ""} {
276 usage
277 exit
280 switch -exact $args {
281 changes {
282 puts "Usage: jul changes \[options\] repository"
283 puts "Shows the changelog of repository.\n"
284 puts "Changes command options:"
285 puts " -h display this help and exit"
287 clean {
288 puts "Usage: jul clean \[options\]"
289 puts "Removes ~/.jul and all of its content.\n"
290 puts "Clean command options:"
291 puts " -h display this help and exit"
292 puts " -y skip (y/N) prompt"
294 default {
295 puts "jul: help: $args no such command."
299 exit
302 # Lists the installed packages.
303 proc listpkgs {pattern} {
304 foreach item [getplist] {
305 if {$pattern != ""} {
306 if {[string match "*$pattern*" $item] == 1} {
307 puts $item;
309 } else {
310 puts $item
315 # Return a sorted list of all installed packages.
316 proc getplist {} {
317 set lst {}
318 foreach file [glob -nocomplain -directory \
319 "/var/db/pkg" -tails -types f *] {
320 lappend lst $file
322 return [lsort $lst]
325 proc printColumnarLines {lines} {
326 foreach fields $lines {
327 set col 0
328 foreach field $fields {
329 set w [string length $field]
330 if {![info exist width($col)] || $width($col) < $w} {
331 set width($col) $w
333 incr col
337 foreach fields $lines {
338 set col 0
339 foreach field $fields {
340 puts -nonewline [format "%-*s " $width($col) $field]
341 incr col
343 puts "";
347 # Search for packages.
348 proc search {args} {
349 # receive a list of all the packages
350 set pkg_list [list_repo [lindex $args 0]]
352 # if the number of elements in $pkg_list is 0, put an error and return
353 if {[llength $pkg_list] == 0} {
354 puts "jul: search: No packages found."
355 return
358 set lines {}
359 foreach key $pkg_list {
360 array set p $key
361 lappend lines [list $p(repo) \
362 $p(name)-$p(version)-$p(arch)-$p(build) $p(desc)]
365 printColumnarLines $lines
368 proc lstrepo {} {
369 # fill finds with all available packages
370 array set finds [list_repo [lindex "" 0]]
372 for {set x 0} {$x < [array size finds]} {incr x} {
373 set s [split_pkg [lindex $finds($x) 0]]
374 puts $s
378 # Split the package string 'p' into a list and return it.
379 proc split_pkg {p} {
380 # remove the trailing '.tlz'
381 set p [string trimright $p ".tlz"]
383 # split 'p' at every '-' found
384 set psplit [split $p -]
386 if {[llength $psplit] > 4} {
387 # number of dashes in the package name part
388 set dashes [expr {[llength $psplit] - 4}]
390 # the new package name, with dashes
391 set newname [join [lrange $psplit 0 $dashes] -]
393 # replace the elements for the 'package name' with 'newname'
394 set psplit [lreplace $psplit 0 $dashes $newname]
397 return $psplit
400 proc update {} {
401 lstrepo
404 # Get and verify files.
405 proc dosync {repo} {
406 global repolist
407 puts "$repo syncing"
408 puts -nonewline "Downloading [get_db_file $repo]... "
409 getfile [lindex $repolist($repo) 2][get_db_file $repo]
410 puts -nonewline "Downloading checksum... "
411 getfile [lindex $repolist($repo) 2][get_db_file $repo].sha1sum
412 puts -nonewline "Verifying... "
413 if {[verify_file $repo.db.sha1sum] == -1} {
414 return -1
416 puts "done"
419 # TODO: refactor
420 proc sync {} {
421 global repolist
423 if {[file exists $::env(HOME)/.jul/cache] == 0} {
424 file mkdir $::env(HOME)/.jul/cache
427 cd "$::env(HOME)/.jul"
429 # loop through all repositories
430 foreach repo [lreverse [array names repolist]] {
431 set rpo [get_db_file $repo]
433 # synchronise if there is no database
434 if {[file isfile $rpo] == 0} {
435 dosync $repo
436 } else {
437 # read the local and remote version
438 # TODO: only reads first line
439 set f [open $::env(HOME)/.jul/$rpo.sha1sum]
440 set lver [gets $f]
441 close $f
443 set rver_file [lindex $repolist($repo) 2]$rpo.sha1sum
444 if {[catch {set rver [exec curl -sf $rver_file]} results options]} {
445 set details [dict get $options -errorcode]
447 puts "Trying to download $rver_file"
448 puts -nonewline "Could not download file: "
450 if {[lindex $details 0] eq "CHILDSTATUS"} {
451 set status [lindex $details 2]
452 if {$status == 22} {
453 puts "HTTP error code > 400"
454 puts "The file was probably not found."
455 puts "Please report this!"
456 } elseif {$status == 23} {
457 puts "Write error in $::env(PWD)"
459 } else {
460 puts "Unknown error. Please report this!"
463 exit
466 # if the local and remote versions are the same, verify
467 # the files
468 if {$lver == $rver} {
469 puts "$repo is up to date"
470 puts -nonewline "Verifying... "
472 # synchronise if the verification fails
473 if {[verify_file $repo.db.sha1sum] == -1} {
474 dosync $repo
475 } else {
476 puts "done"
478 } else {
479 dosync $repo
485 proc usage {} {
486 puts "Usage: jul <command> \[options] \[package|keyword|command]"
487 puts "\nCommands:"
488 puts " changes lists recent changes in the repositories"
489 puts " clean removes ~/.jul and all of its content"
490 puts " help display information for a command or this screen"
491 puts " add/install fetch and install packages from repositories"
492 puts " list list installed or downloaded packages"
493 puts " search search repositories for packages"
494 puts " sync synchronise with repositories"
495 puts " upgrade fetch and upgrade packages from repositories"
496 puts " version show version of this program"
499 if {$argc > 0} {
500 switch -exact [lindex $::argv 0] {
501 add {add add [lindex $::argv 1]}
502 changes {changes [lindex $::argv 1]}
503 clean {clean [lindex $::argv 1]}
504 help {help [lindex $::argv 1]}
505 install {add add [lindex $::argv 1]}
506 "list" {listpkgs [lindex $::argv 1]}
507 search {search [lindex $::argv 1]}
508 sync {sync}
509 update {update}
510 upgrade {add upgrade [lindex $::argv 1]}
511 version {puts "This is jul version $version"}
512 default {puts "jul: [lindex $::argv 0]: No such command."}
514 } else {
515 usage