#
# autodist repositories maintainance script
# Copyright (c) 2007-2024 by Silvan Calarco
#
. /etc/autodist/config
[ -r /etc/autodist/config-secret ] && . /etc/autodist/config-secret

basearch=i586

me=${0##*/}

function usage() {

   echo "autodist repositories maintainance script"
   echo "Copyright (c) 2007-2014 by Silvan Calarco"
   echo 
   echo "Usage:"
   echo "$me archive REPOSITORY PKGS ..."
   echo "$me diff REPOSITORY [PKGS ...] [-d REPOSITORY]"
   echo "$me distromatic REPOSITORY"
   echo "$me install REPOSITORY [PKGS ...] [-f]"
   echo "$me import REPOSITORY [PKGS ...] [-d REPOSITORY] [-s] [-y]"
   echo "$me inspect REPOSITORY {PKGS ...} [-d REPOSITORY]"
   echo "$me list"
   echo "$me query REPOSITORY PKGS ..."
   echo "$me release REPOSITORY [PKGS ...] [-d REPOSITORY] [-s] [-y]"
   echo "$me restore REPOSITORY PKGS ..."
   echo "$me search [-i] [-r regexp] STRING"
   echo "$me setwarning REPOSITORY {PKG ...} -t \"TEXT\""
   echo "$me verify REPOSITORY [PKGS ...]"
   echo
   echo " -d      use given repository as destination (default: base)"
   echo " -f      force operation"
   echo " -r      match repositories with given regexp"
   echo " -s      simulate operations to see if it would work"
   echo " -t      warning text"
   echo " -y      assume yes to all questions (be careful!)"
}

function get_packages_from_last_build() {
   local rep=$1
   [ "$rep" ] || return
   [ -r $SRCPKGLIST ] || {
      echo "ERROR: srcpkglist file missing for $origrepository repository; aborting." >&2
      exit 200
   }
   tmpfile=`mktemp`
   packages=()
   cat $SRCPKGLIST | sort -k3 -r > $tmpfile
   while read line; do
      set -- $line
      [ "$4" = "$rep" ] && packages=(${packages[*]} $1)
   done < $tmpfile
   rm -f $tmpfile
}

# get_pkg_srcinfo - search using srcpkginfo file to find source when no builds exist
# 
# $1: repository name
# $2: pkg name
get_pkg_srcinfo() {

   local rep pkg line

   [ $1 ] && rep=$1 || exit 200
   [ $2 ] && pkg=$2 || exit 200

   unset pkg_archs pkg_name pkg_version pkg_release

   line=`grep "^$pkg " $SRCPKGLIST`

   [ "$line" ] || return

   set -- $line

   [ "$4" = "$rep" ] || return

   pkg_name=$1
   pkg_arch=
   pkg_archs=
   pkg_version=$2
   pkg_release=$6
   pkg_repository=$4
   pkg_buildtime=$3
}

# get_pkg_buildinfo - uses distromatic generated build file for
#                     getting information on the repository
# 
# $1: repository name
# $2: architecture
# $3: pkg name
function get_pkg_buildinfo() {

   local pkg i a

   [ $1 ] && rep=$1 || exit 200
   [ $2 ] && buildarch=$2 || exit 200
   [ $3 ] && pkg=$3

   pkg_archs=();
   for a in ${AUTODIST_ARCHS[*]}; do
      pkg_header=();
      DISTROMATIC_BUILD_FILE=${LOCAL_REPS_BASE_DIR}/distromatic/$rep/builds-$a.sh
      [ -e $DISTROMATIC_BUILD_FILE ] && {
         . $DISTROMATIC_BUILD_FILE
         [ "$buildarch" = "any" -a "$pkg" = "" ] && buildarch=$a
         [ ${pkg_header[0]} ] && {
            pkg_archs=(${pkg_archs[*]} $a)
            [ "$buildarch" = "any" ] && buildarch=$a
         }
      }
   done

   pkg_header=();
   pkg_builds=();
   pkg_obsoletes=();
   pkg_list=();

   unset pkg_name pkg_arch pkg_version pkg_release \
         pkg_group pkg_license pkg_size pkg_buildtime pkg_altrep pkg_repository

   if [ "$buildarch" = "any" ]; then
#      echo "ERROR: package $pkg does not exist in $rep; aborting." >&2
      return;
   fi

   DISTROMATIC_BUILD_FILE=${LOCAL_REPS_BASE_DIR}/distromatic/$rep/builds-${buildarch}.sh
   if [ ! -e $DISTROMATIC_BUILD_FILE ]; then
      return;
   fi
   . $DISTROMATIC_BUILD_FILE


   for i in ${pkg_list[*]}; do
      if [ "$i" == "${pkg_header[0]}" ]; then
         pkg_name=${pkg_header[0]};
         # Note: pkg_arch reported in builds file is just last arch source was
         # built for, so we use repository arch instead
         pkg_arch=${pkg_header[1]};
         [ "$pkg_arch" = "noarch" ] || pkg_arch=$buildarch
         pkg_version=${pkg_header[2]};
         pkg_release=${pkg_header[3]};
         pkg_group=${pkg_header[4]};
         pkg_license=${pkg_header[5]};
         pkg_size=${pkg_header[6]};
         pkg_buildtime=${pkg_header[7]};
         pkg_altrep=${pkg_header[8]};
         pkg_repository=${pkg_header[9]};
         return 0
      fi
   done
   return 0
}

# get_pkg_info - uses distromatic generated build file to get packages information from the repository
# 
# $1: repository name
# $2: architecture
# $3: pkg name
function get_pkg_info() {

   local pkg buildarch rep line

   [ $1 ] && rep=$1 || exit 200
   [ $2 ] && buildarch=$2 || exit 200
   [ $3 ] && pkg=$3 || exit 200

   [ -e ${PKGLIST}.$buildarch ] || {
      echo "ERROR: get_pkg_info: file ${PKGLIST}.$buildarch missing; aborting."
      exit 1
   }

   line=`grep "^$pkg " ${PKGLIST}.$buildarch 2>/dev/null | tail -n1`
   set -- $line
   pkg_name=$1
   pkg_version=$2
   pkg_size=$3
   pkg_unknown=$4
   pkg_repository=$5
   pkg_unknown2=$6
   pkg_release=$7
   pkg_arch=$buildarch

   return 0
}

function import_file() {
 [ $1 ] || exit 200

 local f import_mode
 f=$1
 import_mode=$2
 if [ "$import_mode" = "backup" ]; then
    curl_delete_add="-Q '-DELE $f'"
 else
    curl_delete_add=""
 fi

 [ "$simulate" = "1" ] && return

 if [ "$ORIG_MODE" = "remote" ]; then
     if [ $ORIG_URL_LOCAL_ARCH -a ! -f $ORIG_URL_LOCAL_ARCH/$f ]; then
       echo "WARNING: package missing in local mirror; setting copy from remote repository." >&2
     fi
 
     if [ "$DEST_MODE" = "local" ]; then
        # remote -> local
        if [ $ORIG_URL_LOCAL_ARCH -a -f $ORIG_URL_LOCAL_ARCH/$f ]; then
           echo -n "(L) "
           # if file exists in a local mirror use it by preference
           cp $ORIG_URL_LOCAL_ARCH/$f $DEST_URL_ARCH/ || {
	      echo "ERROR: cannot move file $ORIG_URL_LOCAL_ARCH/$f to $DEST_URL_ARCH/$f; aborting." >&2
	      exit 200
           }
           #chown ftp$DEST_REPOSITORY:users $DEST_URL_ARCH/$f
           eval curl -s -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} $ORIG_URL_ARCH $curl_delete_add >/dev/null && {
	      rm -f $ORIG_URL_LOCAL_ARCH/$f
	      touch $ORIG_URL_LOCAL_ARCH
	   } || {
	      echo
	      echo "WARNING: cannot delete remote file $ORIG_URL_ARCH/$f; you'll have to delete it." >&2
           }
        else
           echo -n "(R) "
           curl -s -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} \
              --get $ORIG_URL_ARCH/$f \
              -o $DEST_URL_ARCH/$f $curl_delete_add || {
                 echo
	         echo "ERROR: cannot get file $ORIG_URL_ARCH/$f; aborting." >&2
	         exit 200
              }
        fi
     else
        # remote -> remote
        echo "ERROR: remote to remote file import is not implemented yet; aborting." >&2
	exit 200
     fi
 else
     if [ "$DEST_MODE" = "local" ]; then
        # local -> local
        cp $ORIG_URL_ARCH/$f $DEST_URL_ARCH/ || {
           echo "ERROR: cannot copy file $ORIG_URL_ARCH/$f to $DEST_URL_ARCH/$f; aborting." >&2
           exit 200
        }
        #chown ftp$DEST_REPOSITORY:users $DEST_URL_ARCH/$f
        touch $DEST_URL_ARCH
	if [ "$import_mode" = "backup" ]; then
           rm -f $ORIG_URL_ARCH/$f || {
              echo "ERROR: cannot remove file $ORIG_URL_ARCH/$f; aborting." >&2
              exit 200
           }
           touch $ORIG_URL_ARCH
	fi
     else
        # local -> remote
        echo -n "(R) "
        curl -s -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} \
           -T $ORIG_URL_ARCH/$f \
           $DEST_URL_ARCH/ || {
              echo
              echo "ERROR: cannot send file $ORIG_URL_ARCH/$f; aborting." >&2
              exit 200
        }
        rm -f $ORIG_URL_ARCH/$f || {
              echo
              echo "WARNING: cannot delete local file $ORIG_URL_ARCH/$f; you'll have to delete it." >&2
        }
        touch $ORIG_URL_ARCH
     fi
 fi

}

function backup_local_file() {
 [ $1 ] || return

 [ -e $LOCAL_BACKUP ] || mkdir -p $LOCAL_BACKUP
 movefiles=$1

 #`find $DEST_URL_ARCH -maxdepth 1 -regex ".*/${pkgname}-[^-]*-[^-]*"`
 for m in $movefiles; do
    echo "backing up $m"
    if [ "$simulate" != "1" ]; then
       mv $m $LOCAL_BACKUP/ || {
          echo "ERROR: can't move $m to $LOCAL_BACKUP; aborting." >&2
          exit 200
       }
       touch `dirname $m`
    fi
 done

}

function backup_package() {
   local rep reg i

   [ "$1" ] || return
   [ "$2" ] && rep=$2 || rep=$destrepository
   [ "$3" ] && reg=$3 || reg=$DESTREGFILE

   archive_pkg=$1

   get_pkg_buildinfo $rep any $archive_pkg

   if [ ! "$pkg_name" ]; then
      get_pkg_srcinfo $rep $archive_pkg
      if [ "$pkg_name" ]; then
         echo "WARNING: only source package has been found in repository" >&2
      fi
   fi

   if [ "$pkg_name" ]; then
      LOCAL_BACKUP=$DEST_URL_LOCAL/$rep/old/${pkg_name}_${BACKUP_DATE}
      PKG_FILENAME="$pkg_name-$pkg_version-$pkg_release.src.rpm"

      if [ -f $DEST_URL_LOCAL/$rep/SRPMS.base/$PKG_FILENAME ]; then
         backup_local_file $DEST_URL_LOCAL/$rep/SRPMS.base/$PKG_FILENAME
      else
         echo "WARNING: package $PKG_FILENAME does not exist in local repository">&2
      fi

      if [ "$simulate" != "1" -a "$DEST_MODE" = "remote" ]; then
         curl -s -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} $DEST_URL/$rep/SRPMS.base/ -Q "-DELE $PKG_FILENAME" >/dev/null || {
            echo "WARNING: cannot delete remote file $DEST_URL/$rep/SRPMS.base/$PKG_FILENAME; you'll have to delete it." >&2
         }
      fi

      for a in ${pkg_archs[*]}; do
          get_pkg_buildinfo $rep $a $archive_pkg

          for i in ${pkg_builds[*]}; do
             PKG_FILENAME="$i-$pkg_version-$pkg_release.$pkg_arch.rpm"
             if [ -f $DEST_URL_LOCAL/$rep/RPMS.$a/$PKG_FILENAME ]; then
                backup_local_file $DEST_URL_LOCAL/$rep/RPMS.$a/$PKG_FILENAME
             else
                echo "WARNING: package $PKG_FILENAME does not exist in local repository" >&2
             fi

             if [ "$simulate" != "1" -a "$DEST_MODE" = "remote" ]; then
                curl -s -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} $DEST_URL/$rep/RPMS.$a/ -Q "-DELE $PKG_FILENAME" >/dev/null || {
	           echo "WARNING: cannot delete remote file $DEST_URL/$rep/RPMS.$pkg_arch/$PKG_FILENAME; you'll have to delete it." >&2
	        }
             fi
          done
      done

      # write register
      [ "$simulate" != "1" ] && {
         echo "`date +%Y%m%d%H%M`  Package <a href=\"/distribution/distromatic.html?tag=$rep&pkg=$pkg_name.source\">$pkg_name</a> ($pkg_version-$pkg_release) archived" >> $reg
#	   | \
#          tee -a $ORIGREGFILE $DESTREGFILE >/dev/null
#          echo "`date +%Y%m%d%H%M` \"\" \"package $pkg_name ($pkg_version-$pkg_release) archived from $rep\"" >> $reg
      }
   else
      echo "WARNING: package $archive_pkg does not exists in $rep; skipping." >&2
   fi
}

function restore_local_file() {
 [ $1 ] || return
 [ $2 ] || return
 [ $4 ] || return

 local restorefiles=$1
 local backupprefix=$2
 local ARCH=$3
 local restorerepository=$4

 #`find $DEST_URL_ARCH -maxdepth 1 -regex ".*/${pkgname}-[^-]*-[^-]*"`
 for r in $restorefiles; do
    [ "$ARCH" ] && restoredest=$DEST_URL_LOCAL/$restorerepository/RPMS.$ARCH/ || restoredest=$DEST_URL_LOCAL/$restorerepository/SRPMS.base/
    echo "restoring $r to $restorerepository"
    if [ "$simulate" != "1" ]; then
       cp ${backupprefix}/$r $restoredest || {
          echo "ERROR: can't copy $p to $restoredest; aborting." >&2
          exit 200
       }
       touch $restoredest
    fi
 done

}

function restore_package() {
   local rep reg i

   [ "$1" ] || return
   [ "$2" ] && rep=$2 || rep=$destrepository
   [ "$3" ] && reg=$3 || reg=$DESTREGFILE

   restore_pkg=$1

   get_pkg_buildinfo $rep any $restore_pkg

   if [ ! "$pkg_name" ]; then
      echo "Info: package $restore_pkg does not exists in $rep"
   fi

   LOCAL_RESTORE_PREFIX=$DEST_URL_LOCAL/$rep/old/${restore_pkg}_

   local cnt=0
   local RESTORE_NAMES=()
   ls ${LOCAL_RESTORE_PREFIX}* &>/dev/null && \
   for f in ${LOCAL_RESTORE_PREFIX}*; do
     [ "$cnt" = 0 ] && echo "Available backups: "
     cnt=`expr $cnt + 1`
     RESTORE_NAMES=(${RESTORE_NAMES[*]} `echo ${f/*_/}`)
     echo "($cnt) ${RESTORE_NAMES[$cnt-1]}"
   done
   [ "$cnt" == "0" ] && {
      echo "Sorry, no backups availables for ${restore_pkg} in $rep"
      return
   }
   local ans=0
   while [ $ans -le 0 2>/dev/null -o $ans -gt $cnt 2>/dev/null ]; do
      echo
      echo -n "Please select the entry to restore or press ENTER to skip (1-$cnt): "
      read ans
      [ "$ans" ] || return
      [ $ans -eq $ans 2>/dev/null ] || ans=0
   done

   echo "Restoring: "
   for f in `ls ${LOCAL_RESTORE_PREFIX}${RESTORE_NAMES[$ans-1]}`; do
      echo -n "${f/*\/} "
   done
   echo
   echo -n "Ok to restore [y/N]? "
   read ans1
   [ "$ans1" != "y" -a "$ans1" != "Y" ] && return

   for a in ${AUTODIST_ARCHS[*]}; do
      for f in `ls ${LOCAL_RESTORE_PREFIX}${RESTORE_NAMES[$ans-1]}/*.${a}.rpm 2>/dev/null`; do
         restore_local_file ${f/*\/} ${LOCAL_RESTORE_PREFIX}${RESTORE_NAMES[$ans-1]}/ $a $rep
      done
   done

   for f in `ls ${LOCAL_RESTORE_PREFIX}${RESTORE_NAMES[$ans-1]}/*.src.rpm 2>/dev/null`; do
         restore_local_file ${f/*\/} ${LOCAL_RESTORE_PREFIX}${RESTORE_NAMES[$ans-1]}/ "" $rep
   done

   # write register
   [ "$simulate" != "1" ] && {
      echo "`date +%Y%m%d%H%M`  Package <a href=\"/distribution/distromatic.html?tag=$rep&pkg=$restore_pkg.source\">$restore_pkg</a> restored" >> $reg
   }
}

function import_package() {
 [ $1 ] || exit 200
 
 local import_pkg import_mode

 import_pkg=$1
 import_mode=$2

 # check for all architectures
 for a in ${AUTODIST_ARCHS[*]}; do
    # check release in dest repository
    get_pkg_buildinfo $destrepository $a $import_pkg
    [ "$pkg_name" ] && {
       dest_ver=$pkg_version
       dest_rel=$pkg_release
       get_pkg_buildinfo $origrepository $a $import_pkg
       [ "$pkg_name" ] || {
          for b in ${AUTODIST_ARCHS[*]}; do
             if [ "$b" != "$a" ]; then
                # Get upstream version in other architecture, then if same version -> merge
                get_pkg_buildinfo $origrepository $b $import_pkg
             fi
             [ "$pkg_name" ] && break
          done
          if [ "$dest_ver-$dest_rel" != "$pkg_version-$pkg_release" ]; then
             [ "$a" == "$basearch" -a "$force" != "1" ] && {
                echo "ERROR: package $import_pkg for $a does not exist in $origrepository and can't merge due to different versions; skipping." >&2
                return 254
             }
             if [ "$force" = "1" ]; then
                echo "WARNING: package $import_pkg for $a is missing in $origrepository but present in $destrepository." >&2
                echo "Import forced. You will need to port package to the missing arch." >&2
             else
                echo "ERROR: package $import_pkg for $a is missing in $origrepository. This would break package in $destrepository($a) repository." >&2
                return 255
             fi
          fi
       }
    }
 done

 # check release in dest repository
 get_pkg_buildinfo $destrepository any $import_pkg

 [ "$pkg_version" ] && {
     destpkgname="$pkg_name"
     destpkgversion="$pkg_version-$pkg_release"
#     destpkgarch="$pkg_arch"
 } || destpkgversion="none"

 get_pkg_buildinfo $origrepository any $import_pkg

 [ "$pkg_version" ] && {
     origpkgname="$pkg_name"
     origpkgversion="$pkg_version-$pkg_release" 
#     origpkgarch="$pkg_arch"
 } || origpkgversion="none"

 [ $origpkgname ] || { 
    echo "ERROR: package $import_pkg does not exist in $origrepository; aborting." >&2
    exit 200
 }

 DEST_URL_ARCH=$DEST_URL/$destrepository/SRPMS.base/
 ORIG_URL_ARCH=$ORIG_URL/$origrepository/SRPMS.base/
 ORIG_URL_LOCAL_ARCH=$ORIG_URL_LOCAL/$origrepository/SRPMS.base/

 PKG_FILENAME="$origpkgname-$origpkgversion.src.rpm"

 [ "$ORIG_MODE" = "remote" ] && 
    ORIG_FILELIST=`curl -s -l -u${AUTODIST_REPOSITORIES_REMOTE_FTPUSER}:${AUTODIST_REPOSITORIES_REMOTE_FTPPASS} --url $ORIG_URL_ARCH/` ||
    ORIG_FILELIST=`ls $ORIG_URL_ARCH`

 check_existence=0;

 for i in $ORIG_FILELIST; do
    [ "$i" = "$PKG_FILENAME" ] && check_existence=1;
 done

 if [ $check_existence = 1 ]; then

    [ "$destpkgversion" = "$origpkgversion" ] && {
       echo "WARNING: same version of $origpkgname exists in destination" >&2
    }

    if [ "$assume_yes" != "1" ]; then
       echo -n "Import $PKG_FILENAME $origpkgversion($origrepository) -> $destpkgversion($destrepository) [y/N]?"
       read ans
    fi

    [ "$assume_yes" = "1" -o "$ans" = "y" -o "$ans" = "Y" ] && {

       echo -n "Importing $PKG_FILENAME "

       import_file $PKG_FILENAME $import_mode

       get_pkg_buildinfo $origrepository any $import_pkg

       for a in ${pkg_archs[*]}; do
          get_pkg_buildinfo $origrepository $a $import_pkg
          for i in ${pkg_builds[*]}; do
             PKG_FILENAME="$i-$origpkgversion.$pkg_arch.rpm"
             DEST_URL_ARCH=$DEST_URL/$destrepository/RPMS.$a/
             ORIG_URL_ARCH=$ORIG_URL/$origrepository/RPMS.$a/
             ORIG_URL_LOCAL_ARCH=$ORIG_URL_LOCAL/$origrepository/RPMS.$a/
             echo -n "$PKG_FILENAME "
             import_file $PKG_FILENAME $import_mode
          done
       done
       echo

    # write register
    [ "$simulate" != "1" ] && {
       echo "`date +%Y%m%d%H%M`  Package <a href=\"/distribution/distromatic.html?tag=$destrepository&pkg=$import_pkg\">$import_pkg</a> ($origpkgversion -> $destpkgversion) imported from $origrepository to $destrepository" | \
          tee -a $ORIGREGFILE $DESTREGFILE >/dev/null
    }

#    if [ "$import_mode" = "backup" ]; then
       # backup old stuff in destination repository
       [ "$destpkgversion" != "none" -a \
         "$destpkgversion" != "$origpkgversion" ] && {
          backup_package $import_pkg $destrepository $DESTREGFILE
       }
       # remove distromatic extra files associated with this package
       [ -e ${LOCAL_REPS_BASE_DIR}/distromatic/$rep/warnings/$import_pkg.in ] && {
          rm -f ${LOCAL_REPS_BASE_DIR}/distromatic/$rep/warnings/$import_pkg.in ||
            echo "WARNING: cannot remove file ${LOCAL_REPS_BASE_DIR}/distromatic/$rep/warnings/$import_pkg.in" >&2
       }
#    fi

    # Sync new release with autodist-git
    [ "$AUTODIST_GIT_SYNC" != "" ] && autodist-git syncpkg ${import_pkg}

    #for i in ${pkg_obsoletes}; do
    #   PKG_FILENAME="$i-$pkg_version-$pkg_release.$namearch.rpm"
    #   DEST_URL_ARCH=$DEST_URL/$destrepository/RPMS.$namearch/$PKG_FILENAME
    #   [ -e $DEST_URL_ARCH ] && echo "WARNING: obsoleted package $i exists" >&2
    #   backup_package $i $destrepository $DESTREGFILE
    #   #echo rm $DEST_URL_ARCH
    #done

   } # ans = y

 else # check_existence != 1
    echo "ERROR: $import_pkg reported by distromatic does no longer exist" >&2
    return 253
 fi
 return 0
}

# FIXME: only works with basearch
function extract_diffinfo() {
   PKG=$1
   REP=$2
   TMP=$3
   local i

   get_pkg_buildinfo $REP $basearch $PKG
   if [ "$pkg_name" ]; then
     PKG_FILENAME="${LOCAL_REPS_BASE_DIR}/$REP/SRPMS.base/$pkg_name-$pkg_version-$pkg_release.src.rpm"
     [ -e "$PKG_FILENAME" ] || {
        echo "ERROR: package $PKG_FILENAME missing in $origrepository; skipping" >&2
        return 1     
     }
     local filesize=`stat -c %s $PKG_FILENAME`
     [ $filesize -gt 1073741824 ] && {
        echo "WARNING: $PKG_FILENAME size of $filesize is more than 1GB; skipping" >&2
        return 1
     }
     rpm -qp $PKG_FILENAME --requires > $TMP/buildrequires

     autospec -q -x $PKG_FILENAME -F \*.spec --destdir $TMP >/dev/null || {
        echo "ERROR: could not extract specfile from $PKG_FILENAME; skipping package" >&2
	return 1
     }
     [ -e "$TMP_SPEC_DIR/$pkg_name.spec" ] || {
        SPEC_FOUND="`ls $TMP_SPEC_DIR/*.spec`"
	mv $SPEC_FOUND $TMP_SPEC_DIR/$pkg_name.spec
        echo "WARNING: specfile name should be $pkg_name.spec instead of ${SPEC_FOUND/*\//} in $REP repository" >&2
     }
     > $TMP/requires
     > $TMP/provides
     for i in ${pkg_builds[*]}; do
          PKG_FILENAME="${LOCAL_REPS_BASE_DIR}/$REP/RPMS.$basearch/$i-$pkg_version-$pkg_release.$pkg_arch.rpm"
	  [ -e "$PKG_FILENAME" ] || {
	      echo "ERROR: package $PKG_FILENAME missing in $origrepository; skipping" >&2
	      return 1
	  }
          rpm -qp $PKG_FILENAME --requires >> $TMP/requires
          rpm -qp $PKG_FILENAME --provides >> $TMP/provides
          rpm -qlp $PKG_FILENAME >> $TMP/files
     done
   else
     #echo "WARNING: can't find package $PKG in $REP repository" >&2
     return 1
   fi
   return 0
}

[ $1 ] || { usage; exit 0; }

origrepository=
destrepository=base
packages=
command=
simulate=0

while [ "$1" ]; do
   case $1 in
      -d)
         destrepository=$2; shift ;;
      -r)
         searchrep=$2; shift ;;
      -s)
         simulate=1 ;;
      -t)
         shift
         warningtext="$@"
	 break ;;
      -f)
         force=1 ;;
      -i)
         ignorecase=1 ;;
      -y)
         assume_yes=1 ;;
      *)
         if [ "$command" ]; then
               case "$command" in
	          "import"|"release"|"query"|"verify"|"archive"|"restore"|"diff"|"inspect"|"install"|"setwarning"|"distromatic")
		      [ "$origrepository" ] &&
		         packages="$packages $1" ||
			 origrepository=$1
		      ;;
		  "search")
		     [ "$searchstring" ] && {
		        echo "ERROR: invalid option $1; aborting." >&2
		        exit 1
		     }
		     searchstring="$1"
		     ;;
		  *)  usage
		      echo "ERROR: invalid option $1; aborting." >&2
		      exit 1
		      ;;
	       esac
	 else
	    case "$1" in
	       "import"|"release"|"query"|"verify"|"archive"|"restore"|"list"|"diff"|"inspect"|"install"|"setwarning"|"distromatic"|"search") command=$1 ;;
	       *)
	           usage
	           echo "Errror: $1 is not a valid command; aborting."
		   exit 1
		   ;;
	    esac
	 fi
	 ;;
    esac
    shift
done
[ "$command" = "" ] && { usage; exit 0; }

#[ "$command" = "list" ] && echo "Local repositories:"
for a in ${AUTODIST_REPOSITORIES_LOCAL_REPS[*]}; do
  [ "$a" = "$destrepository" ] && DEST_MODE=local;
  [ "$a" = "$origrepository" ] && ORIG_MODE=local;
  [ "$command" = "list" ] && echo "$a"
done
#[ "$command" = "list" ] && echo "Remote repositories:"
for a in ${AUTODIST_REPOSITORIES_REMOTE_REPS[*]}; do
  [ "$a" = "$destrepository" ] && DEST_MODE=remote;
  [ "$a" = "$origrepository" ] && ORIG_MODE=remote;
  [ "$command" = "list" ] && echo "$a"
done
[ "$command" = "list" ] && exit 0;

[ "$command" = "search" ] && {
   [ "$ignorecase" ] && GREP_OPTS="-i"
   for rep in ${AUTODIST_REPOSITORIES_LOCAL_REPS[*]} ${AUTODIST_REPOSITORIES_REMOTE_REPS[*]}; do
      [[ "$rep" =~ "$searchrep" ]] || continue
      [ -r ${LOCAL_REPS_BASE_DIR}/$rep/SRPMS.base ] || continue
      ls --color=none ${LOCAL_REPS_BASE_DIR}/$rep/SRPMS.base | grep $GREP_OPTS "$searchstring" 2>/dev/null | \
      while read PKGLINE; do
        [ "$PKGLINE" ] && {
           echo "$rep(source): ${PKGLINE/ *}"
        }
     done
     for a in ${AUTODIST_ARCHS[*]}; do
        [ -r ${LOCAL_REPS_BASE_DIR}/$rep/RPMS.$a ] || continue
         ls --color=none ${LOCAL_REPS_BASE_DIR}/$rep/RPMS.$a | grep $GREP_OPTS "$searchstring" 2>/dev/null | \
         while read PKGLINE; do
            [ "$PKGLINE" ] && {
               echo "$rep($a): ${PKGLINE/ *}"
            }
         done
      done
   done
   exit 0
}

[ "$origrepository" ] || { usage; exit 200; }
SRCPKGLIST="${LOCAL_REPS_BASE_DIR}/$origrepository/srcpkglist"
PKGLIST="${LOCAL_REPS_BASE_DIR}/$origrepository/pkglist"

[ "$DEST_MODE" ] || { echo "ERROR: $destrepository is not a valid repository; aborting." >&2; exit 200; }
[ "$ORIG_MODE" ] || { echo "ERROR: $origrepository is not a valid repository; aborting." >&2; exit 200; }
[ "$DEST_MODE" = "remote" ] && { echo "Waring: destination is a remote repository; this is an EXPERIMENTAL feature."; }

[ "$command" = "query" ] && {
   [ "$packages" ] || { usage; exit 1; }
   for i in $packages; do
      get_pkg_buildinfo $origrepository any $i
      if [ ! "$pkg_name" ]; then
         echo "$i: package not found in $origrepository repository"
      else
         for a in ${pkg_archs[*]}; do
            get_pkg_buildinfo $origrepository $a $i
            if [ "$pkg_name" ]; then
               echo "Repository:$origrepository($a)"
               echo "Name:      $pkg_name"
               echo "BuildArch: $pkg_arch"
               echo "Version:   $pkg_version"
               echo "Release:   $pkg_release"
               echo "Group:     $pkg_group"
               echo "License:   $pkg_license"
               echo "Size:      $pkg_size"
               echo "Builds:    ${pkg_builds[*]}"
               echo "Obsoletes: ${pkg_obsoletes[*]}"
               echo
            fi
         done
     fi
   done
   exit 0;
}

[ "$command" = "verify" ] && {
   [ "$packages" ] || {
      get_pkg_buildinfo $origrepository any
      packages=${pkg_list[*]}
   }
   for i in $packages; do
      get_pkg_buildinfo $origrepository any $i
      if [ ! "$pkg_name" ]; then
         echo "$i: package not found in $origrepository repository"
      else
         PKG_FILENAME="$i-$pkg_version-$pkg_release.src.rpm"
         rpm2cpio ${LOCAL_REPS_BASE_DIR}/${origrepository}/SRPMS.base/$PKG_FILENAME &>/dev/null || {
         echo "WARNING: source package $PKG_FILENAME is empty or corrupted." >&2
         }

         for a in ${AUTODIST_ARCHS[*]}; do
            get_pkg_buildinfo $origrepository $a $i
            if [ "$pkg_name" ]; then
               for l in ${pkg_builds[*]}; do
                  PKG_FILENAME="$l-$pkg_version-$pkg_release.$pkg_arch.rpm"
                  rpm2cpio ${LOCAL_REPS_BASE_DIR}/${origrepository}/RPMS.$a/$PKG_FILENAME &>/dev/null || {
                     echo "WARNING: package $PKG_FILENAME($a) is empty or corrupted." >&2
                  }
               done
            fi
         done
      fi
   done
   exit 0;
}

[ "$command" = "diff" ] && {
   [ "$packages" ] || {
      get_pkg_buildinfo $origrepository any
      packages=${pkg_list[*]}
   }
   TMP_SPEC_DIR=`mktemp -d --tmpdir=/dev/shm`
   for i in $packages; do
      echo
      echo "*******************************************************************"
      echo "$i package check:"
      echo "*******************************************************************"

      extract_diffinfo $i $origrepository $TMP_SPEC_DIR || continue
      [ -e $TMP_SPEC_DIR/$i.spec ] || {
         echo "ERROR: could not extract specfile for $i in $origrepository repository; skipping" >&2
	 continue
      }
      mv $TMP_SPEC_DIR/$i.spec $TMP_SPEC_DIR/$i.spec.origrep
      mv $TMP_SPEC_DIR/files $TMP_SPEC_DIR/files.origrep
      sort -u $TMP_SPEC_DIR/buildrequires > $TMP_SPEC_DIR/buildrequires.origrep
      sort -u $TMP_SPEC_DIR/requires > $TMP_SPEC_DIR/requires.origrep
      sort -u $TMP_SPEC_DIR/provides > $TMP_SPEC_DIR/provides.origrep
   
      extract_diffinfo $i $destrepository $TMP_SPEC_DIR || {
         echo "Looks like a new package; inspecting data:"
         echo ""
         echo "SPECFILE:"
         echo "========="
         cat $TMP_SPEC_DIR/$i.spec.origrep
         echo ""
         echo "REQUIRES:"
         echo "========="
         cat $TMP_SPEC_DIR/requires.origrep
         echo ""
         echo "PROVIDES:"
         echo "========="
         cat $TMP_SPEC_DIR/provides.origrep
	 echo ""
         echo "FILES:"
         echo "======"
         cat $TMP_SPEC_DIR/files.origrep
         continue
      }
      [ -e $TMP_SPEC_DIR/$i.spec ] || {
         echo "ERROR: could not extract specfile for $i in $destrepository repository; skipping" >&2
      }
      mv $TMP_SPEC_DIR/$i.spec $TMP_SPEC_DIR/$i.spec.destrep
      mv $TMP_SPEC_DIR/files $TMP_SPEC_DIR/files.destrep
      sort -u $TMP_SPEC_DIR/buildrequires > $TMP_SPEC_DIR/buildrequires.destrep
      sort -u $TMP_SPEC_DIR/requires > $TMP_SPEC_DIR/requires.destrep
      sort -u $TMP_SPEC_DIR/provides > $TMP_SPEC_DIR/provides.destrep

      echo "Showing differences between package version in $origrepository and $destrepository:"
      echo ""
      echo "SPECFILE:"
      echo "========="
      diff -u $TMP_SPEC_DIR/$i.spec.destrep $TMP_SPEC_DIR/$i.spec.origrep
      echo ""
      echo "BUILDREQUIRES:"
      echo "=============="
      diff -u $TMP_SPEC_DIR/buildrequires.destrep $TMP_SPEC_DIR/buildrequires.origrep
      echo ""
      echo "REQUIRES:"
      echo "========="
      diff -u $TMP_SPEC_DIR/requires.destrep $TMP_SPEC_DIR/requires.origrep
      echo ""
      echo "PROVIDES:"
      echo "========="
      diff -u $TMP_SPEC_DIR/provides.destrep $TMP_SPEC_DIR/provides.origrep
      echo ""
      echo "FILES:"
      echo "======"
      diff -u $TMP_SPEC_DIR/files.destrep $TMP_SPEC_DIR/files.origrep
      echo
   done
   rm -rf $TMP_SPEC_DIR
   exit 0;
}

[ "$command" = "setwarning" ] && {
   [ "$packages" ] || { usage; exit 1; }
   TMP_SPEC_DIR=`mktemp -d`
   for i in $packages; do
      extract_diffinfo $i $origrepository $TMP_SPEC_DIR
      [ -e $TMP_SPEC_DIR/$i.spec ] || {
         echo "ERROR: could not extract specfile for $i in $origrepository repository; aborting." >&2
	 exit 1
      }
      echo "$warningtext" > ${LOCAL_REPS_BASE_DIR}/distromatic/$origrepository/warnings/$i.in
   done  
   [ "$TMP_SPEC_DIR" != "/" ] && rm -rf $TMP_SPEC_DIR
   exit 0
}

[ "$command" = "inspect" ] && {
   [ "$packages" ] || { usage; exit 1; }
   TMP_SPEC_DIR=`mktemp -d`
   for i in $packages; do
      extract_diffinfo $i $origrepository $TMP_SPEC_DIR
      [ -e $TMP_SPEC_DIR/$i.spec ] || {
         echo "ERROR: could not extract specfile for $i in $origrepository repository; aborting." >&2
	 exit 1
      }
      echo "$i: details of package in $origrepository repository"
      echo ""
      echo "SPECFILE:"
      echo "========="
      cat $TMP_SPEC_DIR/$i.spec
      echo ""
      echo "REQUIRES:"
      echo "========="
      cat $TMP_SPEC_DIR/requires
      echo ""
      echo "PROVIDES:"
      echo "========="
      cat $TMP_SPEC_DIR/provides
   done
   [ "$TMP_SPEC_DIR" != "/" ] && rm -rf $TMP_SPEC_DIR
   exit 0;
}

[ "$command" = "install" ] && {
   [ "$packages" ] || { usage; exit 1; }
   myarch=`uname -p`
   if [ "$myarch" = "*686" -o "$myarch" = "athlon" -o "$myarch" = "pentium*" ]; then
      myarch="i586"
   fi
   for i in $packages; do
      pkg=$i
      pkgarch=
      if [ "${i/.*}" != "${i}" ]; then
         pkg=${i/.*}
         pkgarch=${i/*.}
      fi
      if [ "$pkgarch" -a "$pkgarch" != "$myarch" ]; then
         get_pkg_info $origrepository $pkgarch $pkg
         [ "$pkg_name" ] || {
            echo "WARNING: $pkg not found in $origrepository for arch $pkgarch; skipping."
            continue
         }
      else
         get_pkg_info $origrepository $myarch $pkg
         [ "$pkg_name" ] || {
            echo "WARNING: $pkg not found in $origrepository for arch $myarch; skipping."
            continue
         }
      fi
      pkg_file=${pkg_name}-${pkg_version}-${pkg_release}.$pkg_arch.rpm
      pkg_path=${LOCAL_REPS_BASE_DIR}/$pkg_repository/RPMS.$pkg_arch/${pkg_file}
      [ -r $pkg_path ] || {
         echo "WARNING: file $pkg_path should exist but doesn't; skipping."
         continue
      }
      echo "Installing $pkg_file..."
      if [ "$force" = "1" ]; then
         rpm -i $pkg_path --force
         rpmret=$?
         [ $rpmret -gt 0 ] && echo "WARNING: rpm returned $rpmret"
         if [ "$pkg_arch" != "$myarch" ]; then
            pkg_file=${pkg_name}-${pkg_version}-${pkg_release}.$myarch.rpm
            pkg_path=${LOCAL_REPS_BASE_DIR}/$pkg_repository/RPMS.$myarch/${pkg_file}
            echo "Other arch installation forced; reinstalling native arch package after..."
            rpm -i $pkg_path --force
            rpmret=$?
            [ $rpmret -gt 0 ] && echo "WARNING: rpm returned $rpmret"
         fi
      else
         rpm -i $pkg_path
         rpmret=$?
         [ $rpmret -gt 0 ] && echo "WARNING: rpm returned $rpmret"
      fi
   done
   exit 0;
}

[ "$command" = "distromatic" ] && {
   [ -r $SRCPKGLIST ] || {
      echo "ERROR: srcpkglist file missing for $origrepository repository; aborting." >&2
      exit 1
   }
   [ -d ${LOCAL_REPS_BASE_DIR}/$origrepository/specs ] || mkdir ${LOCAL_REPS_BASE_DIR}/$origrepository/specs
   [ -d ${LOCAL_REPS_BASE_DIR}/$origrepository/patches ] || mkdir ${LOCAL_REPS_BASE_DIR}/$origrepository/patches

   while read line; do
      set -- $line
      [ -e ${LOCAL_REPS_BASE_DIR}/$origrepository/SRPMS.base/$1-$2-$6.src.rpm ] && {
         [ ${LOCAL_REPS_BASE_DIR}/$origrepository/SRPMS.base/$1-$2-$6.src.rpm -nt \
           ${LOCAL_REPS_BASE_DIR}/$origrepository/specs/$1.spec ] && {
#          echo ${LOCAL_REPS_BASE_DIR}/$origrepository/SRPMS.base/$1-$2-$6.src.rpm
            autospec -x ${LOCAL_REPS_BASE_DIR}/$origrepository/SRPMS.base/$1-$2-$6.src.rpm -F '*.spec' \
                     --destdir ${LOCAL_REPS_BASE_DIR}/$origrepository/specs/ -q >/dev/null
            touch ${LOCAL_REPS_BASE_DIR}/$origrepository/specs/$1.spec
            grep -i "^Patch[0-9]*:" ${LOCAL_REPS_BASE_DIR}/$origrepository/specs/$1.spec &>/dev/null && {
               autospec -x ${LOCAL_REPS_BASE_DIR}/$origrepository/SRPMS.base/$1-$2-$6.src.rpm -F '*.patch' \
                        --destdir ${LOCAL_REPS_BASE_DIR}/$origrepository/patches/ -q >/dev/null
            }
         }
      }
   done < $SRCPKGLIST
   exit 0
}

[ "$simulate" = "1" ] && echo "Simulation mode enabled."
#
# import and other active commands
#
if [ "$DEST_MODE" = "remote" ]; then
   DEST_URL=${AUTODIST_REPOSITORIES_REMOTE_FTP}/pub/openmamba
else
   DEST_URL=${LOCAL_REPS_BASE_DIR}
fi
DEST_URL_LOCAL=${LOCAL_REPS_BASE_DIR}

# ORIG_URL_LOCAL is set if a local copy of the repository exists
# and will be preferred for file transfer optimizations
if [ "$ORIG_MODE" = "remote" ]; then
  ORIG_URL=${AUTODIST_REPOSITORIES_REMOTE_FTP}/pub/openmamba
else
  ORIG_URL=${LOCAL_REPS_BASE_DIR}
fi
ORIG_URL_LOCAL=${LOCAL_REPS_BASE_DIR}

BACKUP_DATE=`date +%y%m%d.%H%M%S`
LOCAL_BACKUP=$DEST_URL/$destrepository/old

# operation files are always in the local copy of the repository
ORIGREGFILE=${LOCAL_REPS_BASE_DIR}/$origrepository/operations.log.html
DESTREGFILE=${LOCAL_REPS_BASE_DIR}/$destrepository/operations.log.html


[ "$command" = "archive" ] && {
   DEST_URL=$ORIG_URL
   DEST_URL_LOCAL=$ORIG_URL_LOCAL
   DEST_MODE=$ORIG_MODE

   [ "$packages" ] || { usage; exit 1; }

   for i in $packages; do
      backup_package $i $origrepository $ORIGREGFILE
   done

   exit 0
}

[ "$command" = "restore" ] && {
   DEST_URL=$ORIG_URL
   DEST_URL_LOCAL=$ORIG_URL_LOCAL
   DEST_MODE=$ORIG_MODE

   [ "$ORIG_MODE" = "remote" ] && {
      echo "ERROR: restore is only implemented in local repository; exiting." >&2
      exit 1
   }
   [ "$packages" ] || { usage; exit 1; }

   for i in $packages; do
      restore_package $i $origrepository $ORIGREGFILE
   done

   exit 0

}

[ "$command" = "import" -o "$command" = "release" ] && {

   [ "$origrepository" = "$destrepository" ] && {
      echo "ERROR: source and destination repository cannot be the same; aborting." >&2; exit 200; }

   if [ "$command" = "import" ]; then
      echo "Importing $1: $origrepository ($ORIG_MODE) => $destrepository ($DEST_MODE)"
      backup_mode=backup
   else
      echo "Releasing $1: $origrepository ($ORIG_MODE) => $destrepository ($DEST_MODE)"
      backup_mode=release
   fi

   if [ ! "$packages" ]; then
      get_packages_from_last_build $origrepository
   fi

   RET=0
   for i in ${packages[*]}; do
      import_package $i $backup_mode
      IMPORT_RET=$?
      if [ $IMPORT_RET -eq 255 -a "${AUTODIST_REPOSITORIES_TRANSITIONAL_REP}" -a \
           "${AUTODIST_REPOSITORIES_TRANSITIONAL_REP}" != "$destrepository" -a \
           "${AUTODIST_REPOSITORIES_TRANSITIONAL_REP}" != "$origrepository" -a \
           "${AUTODIST_REPOSITORIES_TRANSITIONAL_REP/-*}" = "${destrepository/-*}" ]; then
         echo "Use -f to force import to $destrepository; now trying to import to ${AUTODIST_REPOSITORIES_TRANSITIONAL_REP} instead."
         destrepositorysave=$destrepository
         destrepository=${AUTODIST_REPOSITORIES_TRANSITIONAL_REP}
         import_package $i $backup_mode
         IMPORT_RET=$?
         destrepository=$destrepositorysave
      fi
      if [ $IMPORT_RET -ne 0 ]; then
         RET=`expr $RET + 1`
      fi
   done

   exit $RET
}

usage
echo "ERROR: $command is not a valid command; aborting." >&2
exit 1
