#!/bin/bash # # susegrep # # Copyright (C) by Volker Kuhlmann, http://volker.top.geek.nz/contact.html # All rights reserved. # Released under the terms of the GNU General Public License (GPL) Version 2. # See http://www.gnu.org/ for details. # # Volker Kuhlmann # 23, 24 Sep 1999 # 26, 27, 29 Jul; 16, 29 Aug; 19, 28 Sep; 5, 6, 25 Oct 2000 # 05 Jan; 3 Jul; 31 Aug; 6 Sep; 3 Oct; 12, 27 Nov 2001 # 10 Jan; 17 Mar; 16 May 2002 # SuSE 8.1: 16 Nov 2002 # SuSE 8.2: 10, 11, 17 May; 23 Jun; 24 Aug; 9, 28 Dec 2003 # SuSE 9.1: 5, 14, 20, 27 Aug; 27 Nov 2004 # SuSE 9.2: 13, 16, 25 May 2005 # SuSE 10.0: 13, 21, 23 Oct 2005; 30 May 2006 # SUSE 10.1: 2, 3 Jun 2006 # chr0(){ echo @;};chr1(){ echo .;} VERSION="4.6, 03 Jun 2006" AUTHOR="Volker Kuhlmann " COPYRIGHT="Copyright (C) 1999-2006" ### TO DO AND BUGS: # *) --GMT to display times in GMT #### #### Check bash is version 2 and load functions # unset VERSION_FUNCLIB_NUM checkbash2=1 requireversion=55 source functions_vk.bash || exit $? #### #### Constants and initial variables # # Directory into which to create the index (must be absolute path!!!) dumpdir="`pwd`" # Directory in which the index is expected when searching # (can be overridden with --data) indexdir="/usr/local/share/doc/SuSE" # Prefix to path which will also be tried if the --data DIR is not found and is # a relative path. # The characters in $indexdirprefchars will also be tried appended to the # prefix, and if the prefix does not end in "/", the last path component # stripped will also be tried. # (This allows e.g. --data 7.2 with the index in $indexdirpref-7.2) indexdirpref="$indexdir" # Space-separated list of characters which will be appended to $indexdirpref # in turn. An empty character is tried first in any case. indexdirprefchars="- _ ." # Number of CDs to index (default) numbercds="7" # Number of DVDs to index (default) numberdvds="4" ## The following are relative to $dumpdir (when creating index) and ## $indexdir (when searching index): # Directory with files containing a list of the packages' files filesdir="suse" # Directory with files containing the packages' descriptions descriptionsdir="desc" # File with a list of packages: long name, CD number series, short name fullnamesfile="Packages" # File with the summaries of the packages summaryfile="Summaries" # File with the sizes of the packages sizefile="Sizes" # File with the groups the packages are in groupfile="Groups" # File with a list of all groups used (this name appended to $groupfile) groupsusedsuffix="-used" # File with a list of all the source RPMs used sourcerpmfile="SourceRPMs" # File with a short listing of all files on the CDs cdlistfile0="CD==.ls" # The "==" is replaced by the CD number # File with MD5 checksums of files on CD cdmd5file0=".cd==.md5" # The "==" is replaced by the CD number # File with a short listing of the CD's ISO information cdinfofile0=".cd==.iso.info" # The "==" is replaced by the CD number # File with MD5 checksum of CD's ISO image cdisomd5file0=".cd==.iso.md5" # The "==" is replaced by the CD number ## Paths on the CDs # Directory with the rpms cd_rpm_path="suse" # Directory with full names of the rpms (symlinks) (last used SuSE 7.3) cd_fullnames_path="full-names" ## internal - no need to change tempfile="$dumpdir/00list.$$" basename="${0##*/}" Date() { Date1; } #### #### Version, Usage, Help # show_usage() { echo " Usage: $basename [OPTIONS] -n|-s|-f|-S|-g|-F PATTERN [GREP-OPTIONS] $basename [OPTIONS] -c|-C CDROM-mountpoint [CD/DVD-number] $basename [OPTIONS] -i CDROM-mountpoint [CD/DVD-number] $basename [OPTIONS] -I CDROM-mountpoint [number-of-CDs [number-of-DVDs]] Version $VERSION $COPYRIGHT by $AUTHOR " } show_help() { show_usage echo "\ Create indices of all packages on the CDs and DVDs of a SuSE-Linux distribution (once), and search the created indices (any time later). This program does not require yast, or any of the CD/DVDs when searching. Searchable indices: -n|--name PATTERN Search names of all packages for PATTERN (default) -s|--summary PATTERN Search package summaries for PATTERN -f|--file PATTERN Search file names installed by packages for PATTERN -S|--size PATTERN Search the package name / package size index for PATTERN -g|--group PATTERN Search the package name / rpm-group index for PATTERN -F|--packagefiles PATTERN List all files of packages whos name matches PATTERN. Create index: -i|--index CD [NUM] Create an index of SuSE-CD/DVD NUM, mounted at CD, in ./. If NUM is missing it's established from the CD itself. This needs GNU versions of find, xargs, awk, ls. -I|--wholeindex CD [CDs [DVDs]] As --index, but for the given number of CDs and DVDs. Default is $numbercds CDs and $numberdvds DVDs. -c|--check CD [NUM] Check MD5 sums of files on CD/DVD. Similar to --index. -C|--cdcheck CD [NUM] Check MD5 sum of CD/DVD's ISO image. Detects SuSE-version and media number automatically. CD can be an image file. OPTIONS: -h|--help Show help -v|--verbose Show packages processed when generating indices, or show grep commands generated for -n|-s|-f|-S|-g|-F -d|--data DIR Search the index in directory DIR --config [not yet finished] --nomd5 When creating the index, skip md5s (for testing only) --notray Suppress tray (un)loading during index generation All options must be before non-options on the command line. GREP-OPTIONS are any options as for grep. PATTERN is a pattern as for grep. CD is the mount point of the CD or DVD, e.g. /media/cdrom. NUM is the particular SuSE CD/DVD number currently in the drive. Searchable index (-d to change) in: $indexdir If the directory given with --data DIR does not exist, this path will be prepended: $indexdirpref TODO: * change index dir search with -d: if DIR contains /, use as given, otherwise append to \$indexdir. Make \$indexdir an array. * search all installed versions of the index simultaneously " } #### #### Error/Exit codes # exitwith() { exitwith_funclib "$@" case "$1" in # more codes in here ---> ErrNoCD) echo "Can't find directory or CD/DVD at '$2'" exit 2;; ErrNoDevice) echo "Can't find the device for mount point '$2'" exit 2;; ErrNoIndex) echo "Can't access index or required part thereof ($2)." test -n "$3" && \ echo "Searched in: $3" #test -n "$2" && echo "($2)" exit 2;; ErrNoCDNum) echo "Can't establish the number of the CD in the drive or file." echo "Please specify it on the command line." exit 4;; ErrNoOpen) exit 5;; ErrNoCDname) echo "Have no CD/DVD name/number. This shouldn't happen (internal error)." exit 6;; ErrNoCDtype) echo "Have no disk type (CD/DVD). This shouldn't happen (internal error)." exit 6;; # <--- more codes in here *) exitwith_illegal "$@";; esac } #### #### Parse command line parameters # parse_cmd_line() { local cmdline arg # not: idxmsg unset -v debug verb nomd5 notray lim3 cmdline=("$@") test "$1" = "-D" -o "$1" = "-debug" && debug=1 # pre-check for debug #set -- -opt1 "$@" # pre-init options while [ -n "$1" ]; do test $debug && echo "Current arg: $1" arg="${1#-}" test "$arg" == "$1" && break # stop if word starts with "-" test "$arg" == "-" && { shift; break; } # stop if word is "--" # allow both "-long" and "--long" for long options: # (when commenting this out, must enter long options like "-longopt"!) #arg="${arg#-}" case "$arg" in -debug) debug=1;; -nomd5) nomd5=1;; # for testing, don't do time-consuming md5 -lim3) lim3=3;; # limit file ops to the first 3 files -lim[0-9]*) lim3="${arg#-lim}";; -amd64ln) replace_rpmfiles_with_symlinks "$dumpdir/$filesdir" exit $?;; -notray) notray=1;; h|help|-help) Help;; -usage) Usage;; -version) Version;; v|-verbose) verb=1;; V|-version) Version;; n|-name) checkarg2 "$@"; shift; search_name "$@" exit $?;; s|-summary) checkarg2 "$@"; shift; search_summary "$@" exit $?;; f|-file) checkarg2 "$@"; shift; search_file "$@" exit $?;; F|-packagefiles) checkarg2 "$@"; shift; packagefiles=1; search_file "$@" exit $?;; S|-size) checkarg2 "$@"; shift; search_size "$@" exit $?;; g|-group) checkarg2 "$@"; shift; search_group "$@" exit $?;; i|-index) checkarg3o "$@" init_cdnum_mnt "$@" create_cd_index exit $?;; I|-wholeindex) checkarg3o "$@" shift create_whole_index "$@" exit $?;; c|-check) checkarg3o "$@" init_cdnum_mnt "$@" check_cdfiles_md5 exit $?;; C|-checkcd|-cdcheck) #checkarg3o "$@" init_cdnum "$@" check_iso_md5 exit $?;; d|-data) checkarg2 "$@"; indexdir="$2"; shift idxmsg='Searching index in: $indexdir' ;; -dvdonly) # undocumented list_dvd_only exit $?;; -create-zlist) # undocumented create-zlist exit $?;; Z|-config) # this is experimental, not yet finished checkarg2 "$@"; read_config_file "$2"; shift;; "") break;; # allow "-" as file arg *) exitwith ErrBadoption "$1";; #*) break;; # stop option scanning with first unknown option esac shift done # save option and file arguments for later: FILEARGS=("$@") OPTARGS=("${cmdline[@]:0:${#cmdline[@]} - ${#FILEARGS[@]}}") } #### #### Initialise variables # # In: $1: cmd option, $2: mount point, $3: cd/dvd number/name # Out: $cdname, $cdtype init_cdnum_mnt() { cdrom="$2" # e.g. /mnt/cdrom or /media/cdrom or /misc/cdrom etc. if checkarg3o "$@"; then cdname="$3" # e.g. 1 (alphanumeric) else cdname="`obtain_cd_number_from_mountpoint "$cdrom"`" test -z "$cdname" && exitwith ErrNoCDNum fi init_cdtype "$cdrom" #echo "cdrom=$cdrom, cdname=$cdname, cdtype=$cdtype"; exit 55 init_var_cdnum } # In: $1: cmd option, $2: mount point or ISO file # $3 (optional): cd/dvd number/name/designation # Out: $cdname, $cdtype init_cdnum() { cdrom="$2" if checkarg3o "$@"; then cdname="$3" # e.g. 1 (alphanumeric) else init_cdname "$cdrom" fi test -z "$cdname" && exitwith ErrNoCDNum init_cdtype "$cdrom" init_var_cdnum } # In: $1: mount point or ISO file # Out: $cdname init_cdname() { test -d "$1" \ && cdname="`obtain_cd_number_from_mountpoint "$1"`" \ || cdname="`obtain_cd_number_from_ISO "$1"`" } # In: $1: mount point or ISO file # Out: $cdtype init_cdtype() { test -d "$1" \ && cdtype="`obtain_cd_type "$1"`" \ || cdtype="`obtain_cd_type -img "$1"`" } # In: $cdname, $cdtype # Out: ... init_var_cdnum() { # Replace "==" in variables with CD number (CD number can be alphanumeric) # [ # bash 2.05b / SuSE 9.1, bash 3.00.0 / SusE 9.2: # Replace "#" in variables with CD number (CD number can be alphanumeric) # "${cdlistfile0/\\#/$cdname}" leaves the "#" in; # but it did work with bash 2.05b / SuSE 8.2. Use different characters. # ] cdlistfile="${cdlistfile0/==/$cdname}" cdinfofile="${cdinfofile0/==/$cdname}" cdmd5file="${cdmd5file0/==/$cdname}" cdisomd5file="${cdisomd5file0/==/$cdname}" # For DVDs, replace "cd" with "dvd" and "CD" with "DVD" in variables test "$cdtype" = "DVD" || return cdlistfile="${cdlistfile/cd/dvd}" cdlistfile="${cdlistfile/CD/DVD}" cdinfofile="${cdinfofile/cd/dvd}" cdinfofile="${cdinfofile/CD/DVD}" cdmd5file="${cdmd5file/cd/dvd}" cdmd5file="${cdmd5file/CD/DVD}" cdisomd5file="${cdisomd5file/cd/dvd}" cdisomd5file="${cdisomd5file/CD/DVD}" } # Search for the correct index data to be used, # and check the specified index part (file) exists. # In: $1: the requested index part/file; $indexdir: ... # Out: $indexdir: location of index with absolute path(?) # $indexz: $1 with .gz appended if it exists, $1 otherwise check_index() { init_indexdir indexz="$1" test -r "$indexdir/$indexz.gz" && { indexz="$1.gz" return } case "$1" in *.iso.md5) # There are problems with establishing the correct disk designation # for eval, live, and reduced (from dual-layer) disks. # Check if ISO sizes match, and/or if the .iso.md5 isn't found, search # for any .iso.info with the same volume size and use its .iso.md5. # Must have the SuSE version for this. test -d "$cdrom" \ && get_cdimg_fsinfo_iso9660 "$CD_DEVICE" \ || get_cdimg_fsinfo_iso9660 "$cdrom" if [ -r "$indexdir/$1" ]; then local iso="$indexdir/${1%.*}" if [ -r "$iso.info" ] \ && grep -isq '^volume size.* '$CDIMG_BLOCKS'$' "$iso.info" then : else ifset verb \ && echo 1>&2 "ISO size mismatch with: $iso.{info,md5}" check_index_otherisomd5 "$1" fi else ifset verb && echo 1>&2 "MD5 file not found: $indexdir/$1" check_index_otherisomd5 "$1" fi indexz="$cdisomd5file" ;; esac test -r "$indexdir/$indexz" || exitwith ErrNoIndex "$indexdir/$indexz" \ "./$indexdir/, $indexdirpref*$indexdir/"\ "`test "$indexdirpref2" && echo ", $indexdirpref2*$indexdir/"`" } # Search for a *.iso.info with a volume size matching $CDIMG_BLOCKS. # In: $1: .iso.md5 file name; CDIMG_BLOCKS; indexdir # Out: cdisomd5file, if new .iso.md5 is found check_index_otherisomd5() { local info md5 for info in "$indexdir/${1:0:1}"*.iso.info; do md5="${info%.*}.md5" if [ -r "$md5" ] \ && grep -isq '^volume size.* '$CDIMG_BLOCKS'$' "$info"; then ifset verb && echo 1>&2 "Trying: $md5" #cdname=...; init_var_cdnum cdisomd5file="${md5##*/}" fi done } init_indexdir() { init_indexdir_ test -n "$verb" -a -n "$idxmsg" && eval echo "$idxmsg" } init_indexdir_() { local c ipref local -a chars=("''" $indexdirprefchars) indexdirpref2="" # we need double-quoting of empty string for the fors below # If the --data DIR exists, or is an absolute path, don't search for # the real DIR test -d "$indexdir" -o "${indexdir:0:1}" == "/" && return for c in "${chars[@]}" ; do if [ -d "$indexdirpref$c$indexdir" ]; then indexdir="$indexdirpref$c$indexdir" return fi done ipref="${indexdirpref%/*}/" if [ "$ipref" != "$indexdirpref" ]; then indexdirpref2="$ipref" for c in "${chars[@]}" ; do if [ -d "$indexdirpref2$c$indexdir" ]; then indexdir="$indexdirpref2$c$indexdir" return fi done fi } read_config_file() { . "$1" } #### #### Functions # # Alphabetic sorting Sort() { # Variables LANG, LC_COLLATE are set in main of this script sort -f "$@" } # Make lines in file unique: unique() { Sort <"$1" | uniq >"$tempfile" && cat "$tempfile" >"$1" rm -f "$tempfile" } # We assume for now that we run on a linux system... gnufind() { find "$@"; } gnuxargs() { xargs "$@"; } gnuls() { ls "$@"; } #alias gnuls=ls gnuls="ls" gnuawk() { awk "$@"; } # Tray loading cd_tray_open() { ifset notray || cdrom_tray_open } cd_tray_close() { ifset notray || cdrom_tray_close } # Create a formatted dir listing. # This behaves as program listall by Volker Kuhlmann, in those parts # which are needed here. # 16 Nov 02: As the function library is needed anyway (which is in scriptutils) # and the ls program's output changed, it's no point maintaining a second # copy. Using listall from scriptutils now. listall__() { local STRIPCD="| sed 's, \./, ,'" local STRIPTB="| grep -v 'TRANS.TBL$'" local SHORT="| cut -c \$ss-" local SORT="| sort +6" local idfmt="-n" w=5 ss=26 local scmd='$5' sw=10 findopt="-mount -noleaf" awkprog=' BEGIN {months = "JanFebMarAprMayJunJulAugSepOctNovDec"} {fs = 67; l = length($0); if (l < fs) { printf "odd: %s\n", $0 >"/dev/stderr"; next; } while (fs < l && substr ($0, fs, 1) != " ") fs++; file = substr ($0, fs + 1);} /^[cb]/ {m = sprintf ("%02s", (index (months, $8) + 2) / 3); size = $5$6; dat = $11"-"m"-"$9" "$10;} ! /^[cb]/ {m = sprintf ("%02s", (index (months, $7) + 2) / 3); size = '"$scmd"'; dat = $10"-"m"-"$8" "$9;} {printf "%s %2s %'$w's/%-'$w's %'$sw's %s %s\n", $1, $2, $3, $4, size, dat, file} ' while true; do case "$1" in --short|-S|--sort|-s|--stripcd|-cd|-T) shift;; *) break;; esac done gnufind "$@" $findopt -print0 \ | eval gnuxargs -0 $gnuls -ldNU $idfmt --full-time \ $STRIPCD \ $STRIPTB \ | eval "gnuawk '$awkprog'" \ $SORT \ $SHORT } # In: $1: mount point # Out: stdout obtain_cd_number_from_mountpoint() { local num cpu test -d "$1/." || exitwith ErrNoCD "$1" # Is it a live disk? if [ -f "$1/cloop.img" ]; then num="live" ifset verb && echo 1>&2 "ISO: arch/disk: '$num'" echo "$num" return fi # check for architecture cpu= if [ -r "$1/content" -a -f "$1/content" ]; then while read -r; do case "$REPLY" in # x86/x86_64 DVD, SUSE 10.1 (10.0 has a different DISTPRODUCT): DISTPRODUCT\ *x86-x86_64) ;; # AMD64 DVDs (SuSE 9.1): DISTPRODUCT\ *x86_64) cpu="x86_64";; DISTPRODUCT\ *x86-64) cpu="x86_64";; DEFAULTBASE\ *x86_64) cpu="x86_64";; DEFAULTBASE\ *x86-64) cpu="x86_64";; esac done < "$1/content" fi # check for disk number ls -1d "$1/".S* "$1/"media.* 2>/dev/null | while read; do #echo 1>&2 "$REPLY" case "$REPLY" in "$1/".S*) num="${REPLY##*/}" num="${num#*-*-}" num="${num%.*}" num="${num#0}" num="${num#0}" ;; "$1/"media.*) num="${REPLY##*.}" ;; esac #echo 1>&2 "$num" # this runs in a subshell (of while ... done): # (don't show error here, make it caller's responsibility) #test -z "$num" && exitwith ErrNoCDNum num="${cpu:+-$cpu-}$num" ifset verb && echo 1>&2 "ISO: arch/disk: '$num'" echo "$num" break done } # Try and obtain a SuSE CD/DVD number from the ISO img. Volume ID seems best. # The CD/DVD "number" really is a designation which can also contain the # architecture etc. # In: $1: ISO img (device or file) # Out: stdout obtain_cd_number_from_ISO() { checkforprograms isoinfo local ii="`isoinfo -d -i "$1"`" obtain_cd_number_from_ISOinfo "$ii" } # Try and obtain a SuSE CD/DVD number from the ISO info as from isoinfo -d -i. # Volume ID seems best. # In: $1: ISO info output from isoinfo -d -i # Out: stdout obtain_cd_number_from_ISOinfo() { # get disk number from volume id local v="`echo "$1" | grep -i '^volume id:'`" a="" v="${v#*:}" v="${v##*.}" v="${v##*0}" # Check for a live disk case "$v" in *[Ll]ive*) v="live";; esac echo "$1" | grep -isq '^application id:.*live' && v="live" # get architecture from application id echo "$1" | grep -isq '^application id:.*x86.64' && a="-x86_64-" echo "$1" | grep -isq '^application id:.*x86.x86.64' && a="" #dual arch 10.1 ifset verb && echo 1>&2 "ISO: arch/disk: '$a' '$v'" echo "$a$v" } # Try and obtain a SuSE Version number from an ISO img. # In: $1: ISO img (device or file) # Out: stdout obtain_suse_version_from_ISO() { checkforprograms isoinfo local ii="`isoinfo -d -i "$1"`" obtain_suse_version_from_ISOinfo "$ii" } # Try and obtain a SuSE Version number from an ISO info as from isoinfo -d -i. # In: $1: ISO info output from isoinfo -d -i # Out: stdout obtain_suse_version_from_ISOinfo() { local w s="`echo "$1" | grep -i '^application id:'`" # SuSE version is beween last "-" and the "#": s="${s%#*}" s="${s##*-}" # If the version number is X.Y.Z (<= SuLi 8.0), strip .Z: test "${s%.*}" != "${s%%.*}" && s="${s%.*}" # If the version is a string and contains "Live", the version number has to # come from the VolID instead: case "$s" in *[Ll]ive*) s="`echo "$1" | grep -i '^volume id:'`" for w in $s; do case "$w" in [0-9].[0-9]|[0-9][0-9].[0-9]) s="${w## *}" break ;; esac done ;; esac ifset verb && echo 1>&2 "ISO: SuSE version: '$s'" echo "$s" } # Get the device for the given mount point # In: $cdrom: mount point # Out: $cddev: corresponding device obtain_cd_device() { get_cd_device_from_mountpoint "$cdrom" echo " CD/DVD is mounted on $CD_MOUNTPOINT" test -z "$CD_DEVICE" && exitwith ErrNoDevice "$cdrom" echo " CD/DVD device is $CD_DEVICE" cddev="$CD_DEVICE" } #### #### Functions which perform the action given on the command line # shown_version=0 show_version_index() { ifnset shown_version && { show_version; echo ""; } shown_version=1 } create_cd_index() { test -z "$cdname" && exitwith ErrNoCDname test -z "$cdtype" && exitwith ErrNoCDtype test -d "$cdrom/." || exitwith ErrNoCD "$cdrom" shopt -s nullglob # Field sizes: from SuSE 7.2 packages had long names; increase name width # for susegrep from SuSE 8.1 from %-10{NAME} to %-25{NAME}. # Affected are package summaries and sizes files. pnwidth=25 # Collect up this many rpms for processing at once rpmsatonce=100 find_rpms() { # Bummer! find ... -print -print0 drops the last line!! if ifset lim3; then # For testing, restrict to first 3 rpms local pcmd="| tr '\n' '\0'" test "$1" = "-print" && { pcmd=""; shift; } gnufind $cd_rpm_path -name "*.[rs]pm" "$@" -print \ | eval head -$lim3 $pcmd else local pcmd="-print0" test "$1" = "-print" && { pcmd="-print"; shift; } gnufind $cd_rpm_path -name "*.[rs]pm" "$@" $pcmd fi } myxargs() { gnuxargs -0 --no-run-if-empty "$@" } echo "" show_version_index show_start_time mkdir -p "$dumpdir" || exitwith ErrCreate "$dumpdir" # perhaps we should save cwd? we can always go back to $dumpdir echo "Processing $cdtype $cdname" if true; then ### Package information # (This can be run on multiple rpms at once.) echo "Listing the following information for each rpm on this $cdtype:" echo " * Summary" echo " * Source RPM" echo " * Group" local -a packs packs=() handle_packages() { local qformat test -n "$verb" && { n=${#packs[@]}; let n-- echo " max $rpmsatonce packages, up to: ${packs[$n]}"; } ### Package summaries file="$dumpdir/$summaryfile" touch -f "$file" || exitwith ErrCreate "$file" qformat="%-$pnwidth{NAME} %{SUMMARY}\n" rpm >>"$file" --query -p --queryformat "$qformat" "${packs[@]}" unique "$file" # make lines in file unique ### Source packages file="$dumpdir/$sourcerpmfile" touch -f "$file" || exitwith ErrCreate "$file" qformat="%-$pnwidth{NAME} %{SOURCERPM}\n" rpm --query -p --queryformat "$qformat" "${packs[@]}" \ | grep -v '(none)$' >>"$file" unique "$file" awk '{print $2;}' <"$file" | Sort | uniq >"${file}2" ### Package groups file="$dumpdir/$groupfile" touch -f "$file" || exitwith ErrCreate "$file" qformat="%-40{NAME} %{GROUP}\n" rpm >>"$file" --query -p --queryformat "$qformat" "${packs[@]}" unique "$file" # Save the used groups to a separate file sed 's/^[^ ]* *//' <"$file" >>"$file$groupsusedsuffix" unique "$file$groupsusedsuffix" } cd $cdrom exec 3< <( find_rpms -print ) while read -r; do packs=( "${packs[@]}" "$REPLY" ) if [ ${#packs[@]} -ge $rpmsatonce ]; then handle_packages packs=() fi done <&3 test ${#packs[@]} -gt 0 && handle_packages packs=() ### Package sizes, Package file lists # (This can only be run on one rpm at a time.) echo "Listing the" echo " * size of each rpm on this $cdtype," echo " * files of each rpm on this $cdtype, storing to '$filesdir':" # local fmt \ fmtbin="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} %-9{ARCH} %18{SIZE}\n"\ fmtsrc="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} src %18{SIZE}\n"\ fmtnsr="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} nosrc %18{SIZE}\n" file="$dumpdir/$sizefile" touch -f "$file" || exitwith ErrCreate "$file" # dir="$dumpdir/$filesdir" mkdir -p "$dir" # cd $cdrom find_rpms -print | while read -r; do test -n "$verb" && echo " $REPLY" case "$REPLY" in *.src.rpm|*.spm) fmt="$fmtsrc";; *.nosrc.rpm) fmt="$fmtnsr";; *) fmt="$fmtbin";; esac rpm >>"$file" --query -p --queryformat "$fmt" "$REPLY" # rpm -qlp "$REPLY" >"$dir/${REPLY##*/}.ls" done unique "$file" cd $dumpdir replace_rpmfiles_with_symlinks "$dir" else ### Package summaries echo "Listing the summary of each rpm on this $cdtype:" file="$dumpdir/$summaryfile" touch -f "$file" || exitwith ErrCreate "$file" cd $cdrom find_rpms | myxargs rpm --query -p \ --queryformat "%-$pnwidth{NAME} %{SUMMARY}\n" >>"$file" # Make lines in file unique: unique "$file" ### Package groups echo "Listing the group of each rpm on this $cdtype:" file="$dumpdir/$groupfile" touch -f "$file" || exitwith ErrCreate "$file" cd $cdrom find_rpms | myxargs rpm --query -p \ --queryformat "%-40{NAME} %{GROUP}\n" >>"$file" cd $dumpdir # Make lines in file unique: unique "$file" # Save the used groups to a separate file sed 's/^[^ ]* *//' <"$file" >>"$file$groupsusedsuffix" unique "$file$groupsusedsuffix" ### Package sizes echo "Listing the size of each rpm on this $cdtype:" file="$dumpdir/$sizefile" touch -f "$file" || exitwith ErrCreate "$file" cd $cdrom ## Sorry but the find/xargs takes 1s instead of 27s!! (All files disk ## buffered in memory). So no verbose output. #for rpm in suse/*/*.{rpm,spm}; do # test -n "$verb" && echo " $rpm" # rpm --query --queryformat \ # "%-10{NAME} %-15{VERSION} %-5{RELEASE} %17{SIZE}\n" -p "$rpm" \ # >>"$file" #done local fmt \ fmtbin="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} %-9{ARCH} %18{SIZE}\n"\ fmtsrc="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} src %18{SIZE}\n"\ fmtnsr="%-$pnwidth{NAME} %-18{VERSION} %-5{RELEASE} nosrc %18{SIZE}\n" find_rpms -print | while read -r; do case "$REPLY" in *.src.rpm|*.spm) fmt="$fmtsrc";; *.nosrc.rpm) fmt="$fmtnsr";; *) fmt="$fmtbin";; esac rpm >>"$file" --query -p --queryformat "$fmt" "$REPLY" done cd "$dumpdir" # Make lines in file unique: unique "$file" ### Package file lists ddls="${dumpdirls##$dumpdir}" ddls="${ddls#/}" echo "Listing the files of each rpm on this $cdtype, storing to '$filesdir':" dir="$dumpdir/$filesdir" mkdir -p "$dir" shopt -s nullglob cd $cdrom find_rpms -print | while read -r; do rpm=$REPLY test -n "$verb" && echo " $rpm" rpm -qlp $rpm >"$dir/${rpm##*/}.ls" done replace_rpmfiles_with_symlinks "$dir" fi ### Package index echo "Generating full-names and series index:" file="$dumpdir/$fullnamesfile" touch -f "$file" || exitwith ErrCreate "$file" if [ -e "$cdrom/$cd_fullnames_path" ]; then cd "$cdrom/$cd_fullnames_path" || exitwith ErrNoCD "$cdrom/$cd_fullnames_path" # sed: # lrwxrwxrwx 1 root root 23 Nov 12.. # 1999 i386/a2ps-4.12-30.i386.rpm -> ../../suse/ap1/a2ps.rpm # lrwxrwxrwx 1 root root 23 Nov 12 1999 i386/a2ps-4.12-30.i386.rpm/ap1/a2ps.rpm #ls -l */*.rpm \ leads to: /bin/ls: Argument list too long. cd_rpm_path="." find_rpms | myxargs ls -l \ | sed -e 's= -> ../../suse/=/=' \ | awk -F/ '{printf "%-50s %-4s %-6s %s\n", $3, tolower("'"${cdtype:0:3}$cdname"'"), $4, $5}'\ >>"$file" # Make lines in file unique: unique "$file" else echo " No '$cd_fullnames_path/' on $cdtype. Generating differently." cd "$cdrom" || exitwith ErrNoCD "$cdrom/$cd_fullnames_path" # -r--r--r-- 4 root root 114922 Jan 11 2001 ./Mandrake/SRPMS/getty_ps-2.0.7j-14mdk.src.rpm find_rpms | myxargs -n1 ls -1 | while read -r; do REPLY="${REPLY#./}" name="${REPLY##*/}" path="${REPLY%/*}"; test "$path" = "$name" && path="." echo "$path $name" done | awk >>"$file" \ '{printf "%-50s %-4s %s\n", $2, tolower("'"${cdtype:0:3}$cdname"'"), $1}' unique "$file" fi ### Check for bad symlinks echo "Checking for bad symlinks in '$cdtype:full-name/*/.':" if [ -e "$cdrom/$cd_fullnames_path" ]; then cd "$cdrom/$cd_fullnames_path" || exitwith ErrNoCD "$cdrom/$cd_fullnames_path" #ls -l -L */*.{rpm,spm} /bin/ls: Argument list too long cd_rpm_path="." find_rpms | myxargs ls -l -L | while read; do n="${REPLY#./*/}" #echo "$n" if [ "${n#* -> }" != "$n" ]; then echo " ${REPLY#[^ ]* [^ ]* [^ ]* [^ ]* [^ ]* [^ ]* [^ ]* [^ ]* }" fi done else echo " Skipping. No '$cd_fullnames_path/' on $cdtype." fi ### CD file list echo "Listing whole $cdtype:" file="$dumpdir/$cdlistfile" cd $cdrom listall --short --sort --stripcd -T --GMT . >"$file" ### CD ISO info echo "Storing ISO info of $cdtype in '$cdinfofile':" obtain_cd_device if [ ! -r "$cddev" ]; then echo " Can't read $cdtype device '$cddev' - can't store $cdtype's ISO information." fi if checkforprogram isoinfo; then test -r "$cddev" && isoinfo -i "$cddev" -d >"$dumpdir/$cdinfofile" else echo " No isoinfo - can't store $cdtype's ISO information." fi ### MD5 checksum of CD image echo "Storing MD5 checksum of $cdtype's ISO image in '$cdisomd5file':" if [ ! -r "$cddev" ]; then echo " Can't read $cdtype device '$cddev' - can't store $cdtype's ISO checksum." fi if type -p md5sum >/dev/null; then if ifnset nomd5; then test -r "$cddev" \ && iso_create_md5 "$cddev" >"$dumpdir/$cdisomd5file" else echo " Skipped - md5 disabled." fi else echo " No md5sum - can't store MD5 checksum." fi ### MD5 checksums of files echo "Storing MD5 checksums for all files on $cdtype in '$cdmd5file':" if type -p md5sum >/dev/null; then if ifnset nomd5; then exec 5>"$dumpdir/$cdmd5file" pushd "$cdrom/." >/dev/null if ifset lim3; then gnufind . -type f | head -$lim3 | tr '\n' '\0' else gnufind . -type f -print0 fi \ | gnuxargs -0 md5sum \ | Sort +1 \ | sed 's,^\([0-9a-zA-Z]*\) \([ *]\)\./\(.*\),\1 \2\3,' \ >&5 popd >/dev/null else echo " Skipped - md5 disabled." fi else echo " No md5sum - can't store MD5 checksums." fi ### Finish cd "$dumpdir" # unless susegrep terminates we can't unmount otherwise ringbell 1 "Ring bell: [SuSE index]\n" show_finish_time "" "Execution time for this disk:" } #create_cd_index # Create the index for all CDs # In: $1: mount point, $2: number of CDs (optional), $3: number of DVDs (opt.) create_whole_index() { local savetime local ast="******************"; ast="$ast$ast$ast$ast" if [ "${1:0:1}" = "/" ]; then cdrom="$1" # e.g. /mnt/cdrom or /media/cdrom or /misc/cdrom etc. else cdrom="`pwd`/$1" fi numbercds=${2-$numbercds} numberdvds=${3-$numberdvds} show_version_index show_start_time savetime=$_save_seconds echo "${NL}Creating index for $numbercds CD(s) and $numberdvds DVD(s).$NL" echo "Deleting index files in '$dumpdir':" echo -n " $fullnamesfile, $summaryfile, $sizefile, $groupfile, " echo "$groupfile$groupsusedsuffix, $sourcerpmfile" test -d "$dumpdir" && ( cd "$dumpdir"; rm 2>/dev/null "$fullnamesfile" "$summaryfile" "$sizefile" "$groupfile" \ $groupfile$groupsusedsuffix $sourcerpmfile ) handle_cd_dvd CD $numbercds handle_cd_dvd DVD $numberdvds echo "" echo "$ast" show_finish_time $savetime ringbell 3 "Ring bell: [SuSE index all]\n" } #create_whole_index # Handle all CDs or all DVDs # cd_tray_close + cd_tray_open will only do anything if the eject command # is available. # Currently no device can be specified with tray loading, if eject doesn't # figure it out or picks the wrong one you're out of luck. handle_cd_dvd() { local cdtype=$1 cd=1 gotcd=0 gotcdtype="" need chars sane while [ $cd -le $2 ]; do echo " $ast " test $cd -le 1 && echo "Now handling the ${cdtype}s: " REPLY="" until false; do read -p " Insert $cdtype number $cd and press , or type '.' to quit, or type '-' to skip this disk, or enter number/name of this $cdtype, (entering a number here will disable the check for the SuSE CD/DVD number): > " case "$REPLY" in ".") echo " Last $cdtype processed, finishing." break;; "-") break;; "") : ;; *) cdname="$REPLY" chars=\''"' cdname="`echo "$cdname" | tr -d "$chars"`" sane="`echo "$cdname" | tr -d '\000-\037\040\277'`" if [ "$sane" != "$cdname" ]; then echo "${BEL}\ The name of the CD/DVD entered contains illegal characters. Please enter only printable characters and no spaces." continue fi cd_tray_close break;; esac cd_tray_close cdname="`obtain_cd_number_from_mountpoint "$cdrom"`" if [ -z "$cdname" ]; then echo "Can't establish number of CD/DVD in $cdrom." echo "Is there a SuSE Linux CD/DVD in the drive?" cd_tray_open else gotcd=$cdname gotcdtype="`obtain_cd_type "$cdrom"`" test -z "$gotcdtype" && gotcdtype="???" need="but $cdtype number $cd is needed" # Take any non-i386 media as is, as long as it's of the correct # type. if test "$gotcdtype" != "$cdtype" \ || { test "${gotcd:0:1}" != "-" \ && test $gotcd -ne $cd; }; then echo "You have inserted $gotcdtype number $gotcd, $need." cd_tray_open else break fi fi done case "$REPLY" in ".") cd_tray_open break;; "-") : ;; *) init_var_cdnum create_cd_index # we must try to unmount or it could be a long wait for the next CD... umount "$cddev" cd_tray_open;; esac cd=$(($cd + 1)) done } #handle_cd_dvd # On the SuSE 9.2 DVD, about half of the packages for 64bit contain the same # files as the 32bit package. If identical, replace the x86_64 file with a # symlink. # In: $1: Directory containing the packages' files lists replace_rpmfiles_with_symlinks() { echo "Replacing identical x86_64 package contents with symlinks:" cd "$1" || return 1 gnufind -name '*.x86_64.rpm.ls' -print | sort | while read -r; do rpm="${REPLY##*/}" rpm586="${rpm%.x86_64.rpm.ls}.i586.rpm.ls" test -n "$verb" && echo " $rpm" if [ -f "$rpm586" ]; then if diff >/dev/null "$rpm" "$rpm586"; then rm "$rpm" ln -s "$rpm586" "$rpm" else test -n "$verb" && echo " Differs: $rpm" fi else test -n "$verb" && echo " No $rpm586" fi true done } check_cdfiles_md5() { test -z "$cdname" && usage Date; SECONDS=0 test -d "$cdrom/." || exitwith ErrNoCD "$cdrom" ### check MD5 checksums of all files checkforprograms md5sum echo "Checking MD5 sums from '$indexdir/$cdmd5file' for all files on $cdtype $cdname (mounted at $cdrom):" check_index "$cdmd5file" exec 5<"$indexdir/$cdmd5file" || exitwith ErrNoOpen pushd "$cdrom/." >/dev/null md5sum -c <&5 \ | awk ' BEGIN {f=0} $0 ~ / OK$/ {f+=1; next} {print} END {print "Checked",f,"files."} ' popd >/dev/null sec=$SECONDS Date echo "Total execution time: `sec2h $sec`" ringbell 1 "Ring bell: [SuSE check files MD5]\n" } #check_cdfiles_md5 check_iso_md5() { test -z "$cdname" && usage show_start_time ### check MD5 checksum of ISO image test -d "$cdrom" \ && obtain_cd_device \ || cddev="$cdrom" susever="`obtain_suse_version_from_ISO "$cddev"`" indexdir="$susever" init_cdtype "$cdrom" check_index "$cdisomd5file" echo "Checking MD5 sum from '$indexdir/$cdisomd5file' for $cdtype ISO image $cddev of SuSE Linux $susever, $cdtype $cdname:" iso_check_md5 "$cddev" "$indexdir/$cdisomd5file" show_finish_time ringbell 1 "Ring bell: [SuSE check ISO MD5]\n" } #check_iso_md5 loggrep() { test -n "$verb" && echo 1>&2 "+ $1 $2 '$3' ${@:4}" "$@" } search_name() { #echo "Searching: $indexdir/$fullnamesfile" check_index "$fullnamesfile" loggrep grep -i "$@" "$indexdir/$fullnamesfile" } search_summary() { #echo "Searching: $indexdir/$summaryfile" check_index "$summaryfile" loggrep grep -i "$@" "$indexdir/$summaryfile" } search_group() { #echo "Searching: $indexdir/$groupfile" check_index "$groupfile" loggrep grep -i "$@" "$indexdir/$groupfile" } search_size() { #echo "Searching name/size lists in: $indexdir/$sizefile" check_index "$sizefile" loggrep grep -i "$@" "$indexdir/$sizefile" } search_file() { #echo "Searching file lists in: $indexdir/$filesdir" check_index "$filesdir" if [ -f "$indexdir/$indexz" ]; then # $1 must be the pattern, $2- are the grep options local patt="$1"; shift # change the regexes for zgrep below, if -w is used (cludge!) local word= w for w in "$@"; do test "$w" = "-w" -o "$1" = "--word-regexp" && word='[^a-z0-9_]' done if ifset packagefiles; then # list all files of packages whos names matches pattern loggrep zgrep -i "$patt[^:]*:" "$@" "$indexdir/$indexz" else # list files in any package which match pattern loggrep zgrep -i "^[^:]*:.*$word$patt" "$@" "$indexdir/$indexz" fi else cd "$indexdir/$filesdir" if ifset packagefiles; then for n in `ls -1 * | grep -i "$@"`; do grep -i -H . "$n" done else for n in *; do grep -i -H "$@" "$n" done fi fi } list_dvd_only() { check_index "$fullnamesfile" checkforprograms Lines awk '{print $1}' <"$indexdir/$fullnamesfile" \ | egrep -v '(no\.|)src\.rpm' \ | Lines --once } create-zlist() { #check_index "$filesdir/" find "$filesdir" | sort | xargs --no-run-if-empty grep . \ | cut -c $((${#filesdir}+2))- | sed 's,\.\([rs]\)pm\.ls:,.\1pm:,' \ | gzip -9 > "$filesdir".gz } #### #### Main # umask 22 # That UTF-8 is currently (SuSE 9.1) diabolical with sorting (affecting # sort -k). # Settings these variables here will also affect listall. LANG= LC_ALL= LC_COLLATE=C export LANG LC_ALL LC_COLLATE parse_cmd_line "$@" if [ ${#FILEARGS} -gt 0 ]; then search_name "${FILEARGS[@]}"; exit $? else Usage fi