#!/bin/sh
# isthmus - a package manager and platform or distro thing
# a package and system builder/manager, in busybox comaptible /bin/sh script
#
# Copyright (c) 2013,2014,2015,2016 Øyvind Kolås <[email protected]>
#                         2015,2016 Kacper Wysocki <[email protected]>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

main()  # invoked first, called from bottom of file
{
  if [ x"$1" = x"-a" ];then
    shift
    if [ "$#" -eq 0 ]; then echo "missing arch argument"; exit 1; fi
    FORCED_ARCH=$1
    shift
  fi
  eid_init  # note:
            # probably quite a bit of low-hanging fruit in making this take
            # less time in the commmon case
  if [ "$#" -eq 0 ]; then usage; exit 0; fi

  case $1 in      # pro-tip: search for cmd_foo to jump to its implementation
     "--help")              usage ;;
     "info")          shift;cmd_info $* ;;
     "add")           shift;cmd_add $* ;;
     "install")       shift;cmd_add $* ;;
     "arch")                cmd_arch $2 ;;
     "build")         shift;cmd_build $* ;;
     "compact")             cmd_compact_db ;;
     "del")           shift;cmd_del $* ;;
     "uninstall")     shift;cmd_del $* ;;
     "build-dep")     shift;cmd_build_dep $* ;;
     "bdep")          shift;cmd_build_dep $* ;;
     "dep")           shift;cmd_dep $*;;
     "revdep")        shift;cmd_revdep $*;;
     "contains-bin")  shift;cmd_contains_bin $*;;
     "files")               cmd_files $2;;
     "generate")      shift;cmd_generate $* ;;
     "help")                cmd_help $2 ;;
     "html")          shift;cmd_html $* ;;
     "generate-html") shift;cmd_generate_html $* ;;
     "installed")           cmd_installed ;;
     "binlist")             cmd_binlist ;;
     "check-bins")          cmd_check_bins ;;
     "list")                cmd_list ;;
     "list-html")           cmd_list_html ;;
     "log")                 cmd_log $2 ;;
     "make-symlinks")       cmd_make_symlinks;;
     "pre-uninstall-hook")  cmd_pre_uninstall_hook $2 ;;
     "post-install-hook")   cmd_post_install_hook $2 ;;
     "rebuild")       shift;cmd_rebuild $* ;;
     "remote")        shift;cmd_remote $* ;;
     "search")        shift;cmd_search $* ;;
     "shell")         shift;cmd_shell $* ;;
     "update")              cmd_update ;;
     "upgrade")       shift;cmd_upgrade $*;;
     "index")                cmd_index ;;
     "status")              cmd_status ;;
     "update-server")       cmd_update_server ;;
     "update-server-info")  cmd_update_server_info ;;
     "provides")            cmd_provides ;;
     "vfat")                set -xe; generate_vfat $2 ;;
     *)
       echo -e "unknown command: $1\n"
       usage ; exit 1 ;;
   esac
}

# The following part of the eid file, documents the multi-stage eid build
# process, maybe it is over-abstracted and should be cut down to a build
# and install stage, or even a unified build-and-install from
# having sources in a dir

meta_data()
{
  # the following are commonly used keys in the recipes
  name=        # the name of the package (to override filename)
  description= # short oneliner description of package
  documentation= # long form documentation for package
  homepage=      # homepage url for project, if known
  license=     # license(s) the package is under
  version=0    # version of the package

  git=         # a git url for repository to clone
  git_branch=  # a git branch to check out
  git_refspec= # a git refspec to build sources for

  url=         # http, ftp or https url to fetch from
  sha256sum=   # sha256sum of tarball/source item
  unpack=      # unpack location, defaults to $name-version
  deps=        # packages which are runtime dependencies
  build_deps=  # packages which are build-time dependencies

  isthmus_cross=   # if set then we'll try to cross-compile the package
  bins=        # (relevant) binaries provided by package
  sbins=       # (relevant) sbinaries provided by package
}


prepare_pre2()
{
  for x in $patches;do patch -p1 <"$ISTHMUS_BITS_PATH/$x" || : ; done ; :
  if [ -e "${config_sub:-config.sub}" ]; then
    sed -i 's/ linux-gnu\* |/ linux-gnu* | linux-musl* |/' "${config_sub:-config.sub}"
  fi
  # dropping defined( - which trips up newer perl with many build scripts
  for x in `find . | grep \\.pl$`; do
    sed -i 's/defined(/(/' $x
  done
}

prepare_pre()
{
  :
}

prepare(){
  arch=`uname -m`
  if [ x$arch = x ]; then
    arch= 'arm'
  fi
  if [ -x configure ];then
    if [ "x$CROSS_COMPILE" != "x" ]; then
      configure_args="$configure_args --host=$A"
      echo "configure_args= $configure_args"
    fi
      ./configure --prefix=/usr $configure_args
  else
    if [ -x autogen.sh ];then
      if [ "x$CROSS_COMPILE" != "x" ]; then
        configure_args="$configure_args --host=$A"
      fi
        ./autogen.sh --prefix=/usr $configure_args
    fi
  fi
}

stage()
{
  if [ -f makefile ]; then
      make ${MAKE_FLAGS} &&
      make install DESTDIR="$stage"
  fi
}

build()
{
  if [ -f Makefile ]; then
      make ${MAKE_FLAGS} &&
      make install DESTDIR="$stage"
  fi
  stage
}

wrap_pre()
{
  rm -rf "$stage"/usr/lib/*.la
  rm -rf "$stage"/usr/lib/*/*.la
  rm -rf "$stage"/usr/share/info/*
  rm -rf "$stage"/usr/share/man/*
  rm -rf "$stage"/usr/share/gtk-doc
  $STRIP "$stage"/usr/lib/*/*.so > /dev/null 2>&1 || :
  $STRIP "$stage"/usr/lib/*.so > /dev/null 2>&1 || :
  $STRIP "$stage"/usr/bin/* > /dev/null 2>&1 || :
}



fetch_base()    {                 fetch     ; }

prepare_base()  {
    prepare_pre2 ; prepare_pre ;
    export INSTALL_ROOT="$stage"
    export DESTDIR="$stage"
    export PREFIX="/usr"
    prepare; }
build_base()    {
    export INSTALL_ROOT="$stage"
    export DESTDIR="$stage"
    export PREFIX="/usr"
    build  &&
    wrap_pre  ; }

post_install()  { :; }   # run after install, $stage and $ISTHMUS_SYS points to /isthmus
pre_uninstall() { :; }  # run before uninstall, same env as previous

fetch_bin()
{
  if [ ! -f $ISTHMUS_SYS/recipes/$1 ];then return ; fi
  compute_recipe_and_last_bin_hash $1

  if [ -f $ISTHMUS_SYS"/installed/$1" ];then
    installed_recipe_hash=`compute_recipe_hash $ISTHMUS_SYS"/installed/$1"`
    if [ ! x$installed_recipe_hash = x$recipe_hash ];then
        log_progress $1 'fetching updated binary'
    else
        return
    fi
  fi

  if [ "x$bin_hash" != "x" -a "x$bin_hash" != x"FAIL" ]; then
    if [ -f $ISTHMUS_BIN_PATH"/$bin_hash" ];then return; fi

    # copy binary from outside working chroot, if it exists
    [ -f "isthmus-"$ISTHMUS_ARCH"/$bin_hash" ] && [ ! -f $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH"/$bin_hash ] || cp "isthmus-"$ISTHMUS_ARCH"/$bin_hash" $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH" 2>/dev/null

    for remote in `cat repos`;do
      ( cd $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH" ;
        [ -f $bin_hash ] || $ISTHMUS_HTTP_FETCH $remote/isthmus-$ISTHMUS_ARCH/$bin_hash )
    done
    if [ ! -f $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/$bin_hash" ];then
      echo "Error: expected to find binary for $1 hash $bin_hash";
      bin_hash= ;
    else
      got_hash=`compute_hash $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/$bin_hash"`
      if [ x"$got_hash" != x"$bin_hash" ];then
        echo "hash mismatch for $1 - removing (should continue instead)"
        rm -v $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/$bin_hash"
        bin_hash= ;
      fi
    fi
  fi
}

usage()
{
  if [ $is_master = true ];then thing="$ISTHMUS_ARCH/ "; else thing='system'; fi
  echo "eid - arch: $ISTHMUS_ARCH"
  echo ""
  echo "Usage: eid <command> [command options]"
  echo ""
  echo "Available commands:"
  echo "  add <package(s)>     - add new package to $thing"
  echo "  del <package(s)>     - remove package from $thing"
  echo "  info <package(s)>    - get information about package"
  echo "  files <package>      - list files installed by a package"
  echo "  provides <path>      - search which packages provide a pathname"
  echo "  rebuild [package(s)] - rebuild package from source for $ISTHMUS_ARCH"
  echo "                         or no argument to build all installed recipes"
  echo "                         in build order"
  echo "  help <command>       - get help for a specific command"
  echo "  list                 - list packages"
  echo "  status               - show summary of changed/failed/unbuilt packages"
  echo "  search               - search for a pattern in description, title or"
  echo "                         installed file paths"
  echo "  update               - update the collection of recipes"
  echo "  installed            - compact list of packages installed on $thing"
  echo "  build <package|file> - build package for a recipie"
  echo "  log <package>  - examine/follow build log of package"
  if [ $is_master = true ];then
    echo "  arch                 - query or set architecture used, a query shows"
    echo "                         current and lists available architectures"
    echo "  update-server-info   - compact db and generate sha256sums for static sharing"
    echo "  generate <img> [dst] - generate a system image"
    echo "  shell                - open a shell in $ISTHMUS_ROOT/$thing"
  fi
  echo ""
}

log_progress()
{
  [ -n "$QUIET" ] && return
  targt=$1 ; shift 2> /dev/null
  if [ -f /bin/echo ]; then
  /bin/echo -en \
"\r                                          \r$targt:$*\r"
  else echo -en \
"\r                                          \r$targt:$*\r"
  fi
}

log_success ()
{
  targt=$1 ; shift 2> /dev/null
  if [ -f /bin/echo ]; then
  /bin/echo -en "\r                                          \r$targt$*\n"
  else echo -en "\r                                          \r$targt$*\n"
  fi
}
log_fail ()
{
  targt=$1 ; shift 2> /dev/null
  if [ -f /bin/echo ]; then
  /bin/echo -en "\r                                          \r:$targt FAIL $*\n"
  else echo -en "\r                                          \r:$targt fail $*\n"
  fi
}

html_help ()
{
  html_start isthmus
  cmd_help
  html_end
}

cmd_help()
{
  if [ "$#" -eq 0 ]; then
   for a in add del list info dep build-dep revdep provides arch installed shell build update-server-info rebuild compact files search update log generate make-symlinks;
   do
           echo ""
           cmd_help $a;
   done; return
  fi
  case $1 in
"install") cat <<==
eid install
-----------

alias for eid add
==
;;
"uninstall") cat <<==
eid uninstall
-------------

alias for eid del
==
;;
"add") cat <<==
eid add _package(s)_
--------------------

adds one or more packages and requisite dependencies,
downloading/updating/building them as needed.
==
;;

"arch") cat <<==
eid arch _new architecture_
---------------------------

with no arguments prints the current architecture and list available, if
an argument is provided changes the architecture.
==
;;

"build") cat <<==
eid build _recipe/package_
--------------------------

build a specific recipe/package, the full path to the recipe is preferred (used
internally by add).
==
;;

"build-dep") cat <<==
eid build-dep _package_
-----------------------

list build dependencies of a package, this is the packages that must be installed on a system to be able to build this package from the recipe
==
;;

"revdep") cat <<==
eid revdep _package_
-----------------------

list all packages that directly depend on a package, these are the packages that are directly affected by an update of this recipe
==
;;

"provides") cat <<==
eid revdep _path_
-----------------------

list all packages that provide the file path, these are the packages which contain files with the specified file or directory
==
;;

"compact") cat <<==
eid compact
-----------

removes all old binary builds and trims down the cache of recipe->binary
mappings to the latest builds.
==
;;

"del") cat <<==
eid del _package(s)_
--------------------

removes files installed by package(s), but does not remove dependencies.
==
;;

"dep") cat <<==
eid dep _package_
-----------------

lists the dependencies that must be installed before the binary of this package
can be installed.
==
;;


"files") cat <<==
eid files _package_
-------------------

list the files that belong to a package (the package must be installed for this to work).
==
;;

"generate") cat <<==
eid generate _image_
--------------------

generates a full root filssytem or distro image, as specified in a recipe
called image-name in one of the recipe folders, if no image is specified
the available images are listed.
==
;;

"help") cat <<==
eid help _command_
------------------

get help for the specified command
==
;;

"info") cat <<==
eid info _package(s)_
---------------------

gets detailed information about a package
==
;;

"installed") cat <<==
eid installed
-------------

outputs a list of all installed packages on the system / chroot
==
;;

"list") cat <<==
eid list
--------

print a list of available packages, their short description and states symbolized by:

  * -  available
  * _  binary package downloaded, but not installed
  * *  installed
  * %  installed, but recipe has changed
  * ^  installed, but recipe has changed and binary upgrade is available

  If no symbol is present recipe is present, but there is no known state for binaries.

==
;;

"log") cat <<==
eid log _package_
-----------------

show the build log for specified package, useful to determine why a build failed
==
;;

"make-symlinks") cat <<==
eid make-symlinks
-----------------

create symlink structure in /isthmus/symlinks to the isthmus binary for all binaries available for installation - providing on demand installation of packages.
==
;;

"pre-uninstall-hook") cat <<==
eid pre-uninstall-hook _package_
--------------------------------

run the pre-uninstall-hook of specified package (used internally)
==
;;

"post-install-hook") cat <<==
eid post-install-hook _package_
-------------------------------

run the post-install-hook of specified package (used internally)
==
;;

"rebuild") cat <<==
eid rebuild <package>
---------------------

issue a rebuild of a package, and build it even if the recipe hasn't changed.
==
;;

"remote") cat <<==
eid remote <command>
--------------------

run just eid remote for more info.
==
;;

"search") cat <<==
eid search <search term>
------------------------

search for package providing a binary, or description in package
==
;;

"shell") cat <<==
eid shell <package>
-------------------

Enter a chroot inside the build-root for the current architecture. Write exit
or press ctrl+d to get back to your own systems commandline. With an optional
package name, download tarball - apply patches; and enter dir.
==
;;

"update") cat <<==
eid update
----------

update collection of recipes from remote servers, see eid remote
for more information.
==
;;

"update-server-info") cat <<==
eid update-server-info
----------------------

Generates the state for statically sharing the top-level isthmus directory as
an isthmus repository. Compacts the db as a side-effect.
==
;;

"update-server") cat <<==
eid update-server
-----------------

synchronize the local master copy with a specified server (in eid source)
using rsync.
==
;;

"vfat") cat <<==
==
;;
     *)
       echo "do not know how to help with $1"
       exit 1 ;;
   esac
}

USING_NSPAWN=''

# ensures that there is a symlink describing the arch, if it
# doesn't exist determine by uname -m

guess_arch()
{
  UNAME=`uname -m`

  case $UNAME in
    "i486") echo i486 ;;
    "i586") echo i486 ;;
    "i686") echo i486 ;;
    "x86_64") echo i486 ;;
    "armv7h") echo arm ;;
    "armv7l") echo arm ;;
    "armv6h") echo arm ;;
    "armv6l") echo arm ;;
    "aarch64") echo arm;;
    *) echo "Architecture $UNAME not known by eid. We break and you get to keep the pieces!" >&2; exit 1;;
  esac
}
guess_host()
{
  UNAME=`uname -m`

  case $UNAME in
    "i486") echo i486 ;;
    "i586") echo i486 ;;
    "i686") echo i486 ;;
    "x86_64") echo amd64;;
    "armv7h") echo armhf ;;
    "armv7l") echo armhf ;;
    "armv6h") echo armhf ;;
    "armv6l") echo armhf ;;
    *) echo "$UNAME" ;;
  esac
}

ensure_arch()
{
  if [ ! -f ./arch ];then
    ln -s recipes/arch-`guess_arch` arch || ln -s local/arch-`guess_arch` arch
  fi
}

UPSTREAM=eid.cool:80

# UPSTREAM=localhost:8000

ensure_cross_compiler()
{
    if [ ! -f /opt/cross/$A/bin/$A-gcc ]; then
      echo -e "Warning: toolchain for $ISTHMUS_ARCH not found in /opt/cross/$A/\n
trying to download toolchain...\n\n"

      case $A in
        microblaze-*) muslver=1.1.6 ;;
        mips-*) muslver=1.1.12 ;;
        *) muslver=1.1.16 ;;
      esac
      hostarch=`guess_host`
      sudo mkdir -p /opt/cross;
      (
      cd /opt/cross
      sudo $ISTHMUS_HTTP_FETCH http://$UPSTREAM/sources/cross$hostarch-$A-$muslver.tar.xz
      if [ ! -f cross$hostarch-$A-$muslver.tar.xz ]; then
        echo -e "Got no musl cross compiler for $hostarch,\n
you might have to build one yourself:\n
https://github.com/GregorR/musl-cross\n"; exit 69
      fi
      sudo tar xvf cross$hostarch-$A-$muslver.tar.xz --no-same-owner --no-same-permissions
      sudo chmod o+rx /opt/cross/$A/ -R
      )
    fi
}

rebuild_recipes()
{
  # update eid package, if it exist as a local package
  if [ -f local/eid ];then
    grep -v '#eidsum:' local/eid > new_eid_pkg
    echo "#eidsum:"`sha256sum eid|cut -f 1 -d ' '`>> new_eid_pkg
    mv new_eid_pkg local/eid
  fi

  rm -rf new_recipes
  mkdir new_recipes
  cd new_recipes
  cp -R ../remote/*/recipes/* . 2>/dev/null
  cp -R ../local/* . 2> /dev/null
  cd ..
  rm -rf recipes
  mv new_recipes recipes
}

guess_cpus()
{
  # correctly accounts for multisocket multicore, but only on x86 and related architectures
  ncpus=`cat /proc/cpuinfo | egrep 'physical id|siblings' | tail -n2 | cut -f 2 -d ':' | sed 's/^ *//' | xargs | awk '{print ($1+1)*$2}'`
  [ $ncpus = 0 ] && ncpus=`cat /proc/cpuinfo | grep 'processor\s*:' | tail -n 1 | awk '{print $3+1}'`
  echo $ncpus
  return $ncpus
}


eid_init()
{
  if [ "$D" ];then
    set -$D
  fi
  EID="$0"

  is_master=true
  CHROOT=chroot
  MARKDOWN=markdown
  STRIP=strip

  EIDFAKETIME='1978-05-03 22:00:00'
  LFAKETIME=''

  if [ "$UID"x = ""x ];then UID=`id | sed s/\(.*// | sed s/.*=//`; fi


  if [ ! -f eid ]; then cd /isthmus; fi # we're probably in a distro
  ensure_arch
  if [ ! -f ./repos ];then
    if [ ! -d ./.git ];then
      cat > repos <<EOF
http://$UPSTREAM
EOF
    else touch repos; fi
  fi

  less --version 2>&1 | grep -q BusyBox && TAIL_COMMAND='tail -f' || TAIL_COMMAND='less -W +f'
  which curl > /dev/null && ISTHMUS_HTTP_FETCH='curl -f -k -L -O -#' || ISTHMUS_HTTP_FETCH='wget'
  which sudo > /dev/null && SUDO='sudo' || SUDO=
  if [ -f /usr/sbin/chroot ]; then CHROOT=/usr/sbin/chroot ;fi
  if [ -f /sbin/chroot ]; then CHROOT=/sbin/chroot ;fi

  if [ ! -f ./arch ];then
    # if we do not have an arch here we probably have no recipes
    ISTHMUS_ARCH=`guess_arch`
    cmd_update
  fi

  # XXX: rebuilds index from sources on each invocation, this is a bit
  # aggressive.
  if [ -f eid ]; then
    mkdir local 2>/dev/null
    rebuild_recipes
  fi

  if [ x$FORCED_ARCH = x ];then
    . ./arch
  else
    if [ ! -f ./local/arch-$FORCED_ARCH ];then
      echo " ./local/arch-$FORCED_ARCH doesn't exist"
      exit 1
    fi
    . ./local/arch-$FORCED_ARCH
  fi


  if [ -n "$ISTHMUS_NSPAWN" ] && which systemd-nspawn > /dev/null; then
    CHROOT=`which systemd-nspawn`
    CHROOT_ARGS="-q -D"
    if zgrep -q CONFIG_USER_NS=y /proc/config.gz; then
      CHROOT_ARGS="-q --private-users=$UID -D"
    fi
    USING_NSPAWN='true'
  else
    $CHROOT --help 2>&1 | grep -q userspec > /dev/null && CHROOT_ARGS="--userspec $UID:$UID" || CHROOT_ARGS="";
  fi

  if [ x$MAKE_FLAGS = x ]; then
    cpus=`guess_cpus`

    [ x$cpus = x0 ] && cpus=2 # even a single core benefits from a little more work
    export MAKE_FLAGS=-j$cpus
  fi
  if [ x$MAKE_FLAGS = "x-j" ]; then unset MAKE_FLAGS; fi

  if [ -f eid ]; then
    # add more detectors,.
    [ -f /usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 ] && \
       LFAKETIME=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1
    [ -f /usr/lib/faketime/libfaketime.so.1 ] &&
       LFAKETIME=/usr/lib/faketime/libfaketime.so.1

    ISTHMUS_ROOT=$PWD/$ISTHMUS_ARCH
    ISTHMUS_SYS=$PWD/$ISTHMUS_ARCH/isthmus

    mkdir $ISTHMUS_ARCH > /dev/null 2>&1 || true
    mkdir -p sources || true

    prep_chroot
    # only invoke this before trying to do a cross compile
  else
    is_master=false
    ISTHMUS_SYS=/isthmus
    ISTHMUS_ROOT=/
    cd $ISTHMUS_SYS;  # when running in a system the cwd is always /isthmus
    SUDO=''
  fi
}

cmd_search()
{
  query=$1
  set `ls -1R $ISTHMUS_SYS/recipes| grep -v ':' | grep -v arch- | grep -v image-`
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    meta_data
    if [ ! "$name" ];then name=$1; fi
    oldarch=$ISTHMUS_ARCH
    . $ISTHMUS_SYS/recipes/$1
    ISTHMUS_ARCH=$oldarch
    corpus="$name $repo $url $git $license $description"
    echo $corpus | grep -q $query && echo $1
    if [ -f $ISTHMUS_SYS"/file_listings/"$1 ];then
       cat $ISTHMUS_SYS"/file_listings/"$1|sed s:^:/: | grep -q "^$query\$" && echo "file path is in $1";
    fi
  fi
  shift; done
  echo
}

init_html()
{
  M_H="<h2>"
  M_HE="</h2>"
  M_P="<p>"
  M_PE="</p>"
  M_DD="<dd>"
  M_DL="<dl>"
  M_DLE="</dl>"
  M_DT="<dt>"
  M_DDE="</dd>"
  M_DTE="</dt>"
}

output_pkg()
{
  if [ x$M_H = x"<h2>" ];then echo -n "<a href='#$1'>"; fi
  echo -n "$2$1";
  if [ x$M_H = x"<h2>" ];then echo -n "</a> "; else echo -n " "; fi
}

get_mtime()
{
    stat $1 -c "%y" | sed 's/\.[0-9]*.*//'
}

cmd_info()
{
  if [ $# -eq 0 ];then set `ls -1R $ISTHMUS_SYS/isthmus/recipes| sort | grep -v ':' | grep -v arch- | grep -v image-`;fi
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    meta_data
    if [ ! "$name" ];then name=$1; fi
    oldarch=$ISTHMUS_ARCH
    . $ISTHMUS_SYS/recipes/$1
    ISTHMUS_ARCH=$oldarch

    compute_recipe_and_last_bin_hash $1

    if [ "x$name" = "x" ];then
      name=$1
    fi
    echo ""

    echo "$name $version    $description "
    if [ ! x"$documentation" = x ];then
      echo
      echo "$documentation"
    fi

    if [ ! "x$url" = "x" ];         then echo "url:        $url" ;fi
    if [ ! "x$git" = "x" ];         then echo "git:        $git" ;fi
    if [ ! "x$license" = "x" ];     then echo "license:    $license" ;fi
    if [ ! "x$git_branch" = "x" ];  then echo "git_branch: $git_branch";fi
    if [ ! "x$git_refspec" = "x" ]; then echo "git_refspec:$git_refspec" ;fi
                                      echo -n "status:     "
    check_status $1
    echo $pkg_status

    echo "recipe:     $recipe_hash ( $(get_mtime $ISTHMUS_SYS/recipes/$1) )"
    if [ ! "x$bin_hash" = "x" ]; then echo "binary:     $bin_hash ( $(get_mtime isthmus-$ISTHMUS_ARCH/$bin_hash) )" ;fi
    if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ];then
      echo -n "            compressed: "$((`cat isthmus-$ISTHMUS_ARCH/$bin_hash|wc -c`/1024))"kb"
      echo "    uncompressed:  "$((`bzcat isthmus-$ISTHMUS_ARCH/$bin_hash|wc -c`/1024))"kb"
    fi

    if [ ! x"$initrd_packages$packages" = "x" ];then

    echo -n "packages:"
    for pkg in $packages; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    echo ""
    echo -n "initrd_packages:"
    for pkg in $initrd_packages; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    else


    old_arch=$ISTHMUS_ARCH
    collect_dep $1
    ISTHMUS_ARCH=$old_arch

    echo -n "deps:      "
    for pkg in $global_deps; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    echo ""

    old_arch=$ISTHMUS_ARCH
    collect_build_dep $1
    ISTHMUS_ARCH=$old_arch

    echo -n "build deps:"
    for pkg in $global_build_deps; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    echo ""


    echo ""
    fi
    echo ""
    if [ x$M_H = x"<h2>" ];then echo "</div>"; fi
  else echo "unknown package $1" ;fi
  shift; done
}

cmd_info_html()
{
  if [ $# -eq 0 ];then set `ls -1R $ISTHMUS_SYS/recipes| sort | grep -v ':' | grep -v arch- | grep -v image-`;fi
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    meta_data
    if [ ! "$name" ];then name=$1; fi
    oldarch=$ISTHMUS_ARCH
    . $ISTHMUS_SYS/recipes/$1
    ISTHMUS_ARCH=$oldarch

    compute_recipe_and_last_bin_hash $1

    if [ "x$name" = "x" ];then
      name=$1
    fi
    echo ""
    echo "<div id='$1'>"

    echo $M_H"$name $version $M_HE  $M_P $description $M_PE"
    echo $M_DT"recipe:$M_DTE <a href='recipes/$1'>$M_DD$recipe_hash$M_DDE</a>"
    if [ ! "x$url" = "x" ];         then echo "<dt>source:</dt><dd><a href='$url'>$url</a></dd>" ;fi
    if [ ! "x$license" = "x" ];     then echo $M_DT"license:$M_DTE    $M_DD$license$M_DDE" ;fi
    if [ ! "x$git" = "x" ];         then echo $M_DT"git:$M_DTE        $M_DD$git$M_DDE" ;fi
    if [ ! "x$git_branch" = "x" ];  then echo $M_DT"git_branch:$M_DTE $M_DD$git_branch$M_DDE" ;fi
    if [ ! "x$git_refspec" = "x" ]; then echo $M_DT"git_refspec:$M_DTE$M_DD$git_refspec$M_DDE" ;fi
    echo "<dt>status:</dt>"

    check_status $1
    echo "<dd>$pkg_status</dd>"

    if [ ! "x$bin_hash" = "x" ]; then echo $M_DT"binary:$M_DTE     $M_DD$bin_hash$M_DDE" ;fi
    if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ];then
      echo -n "           $M_DT compressed:$M_DTE $M_DD"$((`cat isthmus-$ISTHMUS_ARCH/$bin_hash|wc -c`/1024))"kb"$M_DDE
      echo "   $M_DT uncompressed:$M_DTE $M_DD"$((`bzcat isthmus-$ISTHMUS_ARCH/$bin_hash|wc -c`/1024))"kb"$M_DDE
    fi


    ISTHMUS_ARCH=$oldarch

    compute_recipe_and_last_bin_hash $1

    old_arch=$ISTHMUS_ARCH
    collect_dep $1
    ISTHMUS_ARCH=$old_arch

    echo -n $M_DT"deps:      "$M_DTE $M_DD
    for pkg in $global_deps; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    echo "$M_DDE"

    old_arch=$ISTHMUS_ARCH
    collect_build_dep $1
    ISTHMUS_ARCH=$old_arch

    echo -n $M_DT"build deps:"$M_DTE $M_DD
    for pkg in $global_build_deps; do
      check_status $pkg
      output_pkg $pkg "$pkg_status_icon"
    done
    echo "$M_DDE"
    echo "$M_DLE"
    echo "</div>";
  fi
  shift; done
}

html_start()
{
  echo "<html><head><meta http-equiv='Content-Type' content='text/html, charset=UTF-8'/>"
  echo "<title>$*</title>"
  echo "<style type='text/css'>@import url('./isthmus.css')</style>"


  echo "</head><body>"
  echo "<div class='menu-wrap'>"
  echo "<div class='menu'>"
  echo "<a href='index.html'>isthmus</a>"
  echo "<a href='introduction.html'>introduction</a>"
  echo "<a href='eid.html'>eid</a>"
  echo "<a href='architecture.html'>architecture</a>"
  echo "<a href='adding-recipes.html'>adding recipes</a>"
  echo "<a href='misc.html'>misc</a>"
  echo "<a href='plan.html'>plan</a>"
  echo "<div class='date'>"`date +"%Y-%m-%d %H:%M"`"</div>"

  if [ -f isthmus-i486/log ];then
    echo "<pre>i486:" # XXX: should escape for HTML
    echo -n "<a href='isthmus-i486/`tail -n 3 isthmus-i486/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 3 isthmus-i486/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 3 isthmus-i486/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"

    echo -n "<a href='isthmus-i486/`tail -n 2 isthmus-i486/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 2 isthmus-i486/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 2 isthmus-i486/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"

    echo -n "<a href='isthmus-i486/`tail -n 1 isthmus-i486/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 1 isthmus-i486/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 1 isthmus-i486/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"
    echo -n "</pre>"
  fi


  if [ -f isthmus-arm/log ];then
    echo -n "<pre>arm:" # XXX: should escape for HTML
    echo -n "<a href='isthmus-arm/`tail -n 3 isthmus-arm/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 3 isthmus-arm/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 3 isthmus-arm/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"

    echo -n "<a href='isthmus-arm/`tail -n 2 isthmus-arm/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 2 isthmus-arm/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 2 isthmus-arm/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"

    echo -n "<a href='isthmus-arm/`tail -n 1 isthmus-arm/log|head -n 1|cut -f 1 -d ' '`-build'>"`tail -n 1 isthmus-arm/log | head -n 1 | cut -f 1 -d ' '`" "
      echo -n `tail -n 1 isthmus-arm/log | head -n 1 | cut -f 2 -d ' '`
      echo -n "</a>"
    echo -n "</pre>"
  fi

  if [ -d .git ];then
    echo -n "<pre>" # XXX: should escape for HTML
    git log ...HEAD~3
    echo -n "</pre>"
  fi

  echo "</div>"
  echo "</div>"
  echo "<h1>$*</h1>"
}

html_end()
{
  echo "</body></html>"
}

html_index()
{
  html_start isthmus
  $MARKDOWN README
  cmd_list_html
  html_end
}

html_packages()
{
  html_start isthmus packages
  cmd_list_html
  html_end
}

to_html_vim ()
{
  [ -f "$1" ] || return 1;
  vim +'syn on | run! syntax/2html.vim | wq | q' "$1"
}

html_eid()
{
  html_start eid the isthmus commandline interface

  echo "<p>The shell script that does all the work of isthmus is called eid, <a
  href='eid.sh.html'>read its sources</a>, look at the <a
  href='introduction.html'>introduction</a> and read the sub-command reference below
  for more on what it can do.</p>"
  echo `$EID help | $MARKDOWN`
  html_end
}

html_category()
{
  echo "<div class='category'>"
  echo "<h3>$1</h3>"
  set `ls -1R $1| sort | grep -v ':' | grep -v arch- | grep -v image-`;
  while [ "$1" ]; do
    check_status $1
    output_pkg $1 "$pkg_status_icon"
    shift
  done
  echo "</div>"
}

cmd_html()
{
  init_html
  if [ x$1 = xindex ]; then html_index ; exit; fi
  if [ x$1 = xpackages ]; then html_packages; exit; fi
  if [ x$1 = xeid ]; then html_eid; exit; fi
  if [ x$1 = xlist ]; then cmd_list_html ; exit; fi

  html_start $1
  if [ -f "docs/$1" ];then
     $MARKDOWN "docs/$1"
  fi
  html_end
}

cmd_generate_html()
{
  for section in index adding-recipes introduction architecture eid misc plan; do
    log_progress html $section.html
    $EID html $section > $section.html.new && mv $section.html.new $section.html
  done
  log_progress html eid.sh.html
  cp eid eid.sh && to_html_vim eid.sh

  log_success html
}

add_deps()
{
  global_deps="$1 $global_deps"
  if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    meta_data
    . $ISTHMUS_SYS/recipes/$1
    for pkg2 in $deps; do
      echo $global_deps | grep -q " $pkg2 " || add_deps $pkg2;
    done
  fi
}

# collects the dependencies of first argument in variable called global_deps
collect_dep()
{
  global_deps=''
  add_deps $1
  neu=''
  for a in $global_deps; do
    echo "$neu" | grep -q " $a " || neu="$neu $a  "
  done
  global_deps=$neu
}

add_build_deps()
{
  global_build_deps=" $1 $global_build_deps"
  if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    meta_data
    . $ISTHMUS_SYS/recipes/$1
    for pkg2 in $build_deps $deps; do
      echo "$global_build_deps" | grep -q " $pkg2 " || add_build_deps $pkg2;
    done
  fi
}

collect_build_dep()
{
  collect_dep $1
  global_build_deps=''
  add_build_deps $1
  neu2=''
  for c in $global_build_deps; do
    echo "$neu2" | grep -q "\ $c\ " || neu2="$neu2 $c "
  done
  global_build_deps="$neu2"
}

cmd_provides() { (cd $ISTHMUS_SYS/file_listings; for bin in "$@"; do grep -E "$bin" *; done) }
cmd_files()    { cat $ISTHMUS_SYS/file_listings/$1; }
cmd_dep()      { collect_dep $1;       echo $global_deps ; }
cmd_build_dep(){ collect_build_dep $1; echo $global_build_deps ;}

cmd_contains_bin()
{
  for a in `ls -1r $ISTHMUS_SYS/recipes | sort | grep -v ':'`;do
    if [ -f $ISTHMUS_SYS/recipes/$a ]; then
      meta_data
      . $ISTHMUS_SYS/recipes/$a
      for b in $bins $sbins; do
        if [ x$b = x$1 ]; then
           echo $a
           return
        fi
      done
    fi
  done
}

cmd_revdep()   {
  (cd $ISTHMUS_SYS/recipes/; for pkg in *; do
    if grep -q "deps=.*$1" $pkg; then rev_deps="$rev_deps $pkg"; fi; done; echo $rev_deps ); }

cmd_log(){
    pkg=$1;
    [ z"$pkg" = z ] && pkg=$(tail -n 1 isthmus-$ISTHMUS_ARCH/log | cut -f 1 -d' ')
    $TAIL_COMMAND isthmus-$ISTHMUS_ARCH/$pkg-build; }

cmd_binlist()
{
  echo "Legend:  - available _ downloaded * installed  % recipe changed  ^ binary upgrade  F build failed"
  old_arch=$ISTHMUS_ARCH
  for a in `ls -1r $ISTHMUS_SYS/recipes | sort | grep -v ':'`;do
    if [ -f $ISTHMUS_SYS/recipes/$a ]; then
      meta_data
      check_status $a

      if [ x"$bin_hash" != "x" ];then
        if [ x"$bin_hash" != "xFAIL" ];then
          echo "$a $bin_hash"
        fi
      fi
    fi
  done
  ISTHMUS_ARCH=$old_arch
}

cmd_check_bins()
{
  for bin_hash in `cmd_binlist | cut -f 2 -d ' '`; do
    if [ -e $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/$bin_hash ];then
      if [ x"$bin_hash" != x`compute_hash $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/$bin_hash` ];then
        echo rm -v $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/$bin_hash
        rm -v $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/$bin_hash
        echo rm -v isthmus-$ISTHMUS_ARCH/$bin_hash
        rm -v isthmus-$ISTHMUS_ARCH/$bin_hash
      fi
    fi
  done
}

cmd_list()
{
  echo "Legend:  - available _ downloaded * installed  % recipe changed  ^ binary upgrade  F build failed"
  old_arch=$ISTHMUS_ARCH
  for a in `ls -1r $ISTHMUS_SYS/recipes | sort | grep -v ':'`;do
    if [ -f $ISTHMUS_SYS/recipes/$a ]; then
      meta_data
      check_status $a
      echo -n " $pkg_status_icon "
      . $ISTHMUS_SYS/recipes/$a
      ISTHMUS_ARCH=$old_arch # and restoring the screw-ups from sourcing
      echo $a $version - $description;
    fi
  done
}

cmd_list_html()
{
  old_arch=$ISTHMUS_ARCH
  echo "<script>
  function foo(id){
    var packages=document.getElementsByClassName('package');
    for (var i = 0; i < packages.length; i++)
      packages[i].style.display='none';

    packages=document.getElementsByClassName('pkg');
    for (var i = 0; i < packages.length; i++)
        if (packages[i].innerHTML.indexOf(id) > -1)
      {
        packages[i].style.textDecoration='underline';
      }
      else
      {
        packages[i].style.textDecoration='none';
      }

    var para=document.getElementsByTagName('p');
    for (var i = 0; i < para.length; i++)
      para[i].style.display='none';
    var para=document.getElementsByTagName('h1');
    for (var i = 0; i < para.length; i++)
      para[i].style.display='none';
    document.getElementById(id).style.display='block';
    document.getElementById('ifrm').src='recipes/' + id;
    document.getElementById('ifrm').style.display='block';
    window.location.hash=id;
  }
  document.addEventListener('DOMContentLoaded', function(event) {
  if(window.location.hash.slice(1))
    foo(window.location.hash.slice(1))
  });
  window.addEventListener('hashchange', function(event) {
  if(window.location.hash.slice(1))
    foo(window.location.hash.slice(1))
  });
  </script>"
  echo "<div class='pkglist'>"
  arches=$($EID arch | grep ^available: | cut -d' ' -f 2-)
  for a in `ls -1r $ISTHMUS_SYS/recipes | sort | grep -v ':' | grep -v 'arch-'| grep -v 'image-'`;do
    if [ -f $ISTHMUS_SYS/recipes/$a ]; then
      meta_data
      check_status $a
      rh=`compute_recipe_hash recipes/$a`
      eclass=''
      for arch in $arches; do
        [ -d isthmus-$arch ] && eval "bh$arch='$(cat isthmus-$arch/log isthmus-$arch/remote-log | grep $rh|tail -n1)'"
        if [ x"$(eval echo \$bh$arch)" = x ]; then
          :
        elif eval "echo \$bh$arch" | grep FAIL >/dev/null; then
          eval "bh$arch=''"
        else
          eclass="$eclass $arch"
        fi
      done
      if [ x"$eclass" = x"" ]; then
        eclass=" nobin"
      fi

      . $ISTHMUS_SYS/recipes/$a

      if [ x"$homepage" = x ];then
         eclass="$eclass no_homepage"
      fi
      if [ x"$sha256sum" = x ];then
         eclass="$eclass no_sha256sum"
      fi
      if [ x"$license" = x ];then
         eclass="$eclass no_license"
      fi

    [ -f isthmus-i486/remote-log ] && bh=`cat isthmus-i486/remote-log | grep $rh|tail -n1|cut -f 4 -d ' '`
    [ x"$bh" = x ] && [ -f isthmus-i486/log ] && bh=`cat isthmus-i486/log | grep $rh|tail -n1 | cut -f 4 -d ' '`

      # check if i486 is reproducible - if it is insert extra class
      match=`grep $rh isthmus-i486/log isthmus-i486/remote-log | grep "$bh" - | wc -l`
      if [ $match -gt 1 ]; then
        eclass="$eclass reproducible"
      else
        eclass="$eclass unreproducible"
        # XXX check confidence
      fi

      echo "<span class='pkg$eclass' onClick='window.location.hash=\"$a\"'>"

      ISTHMUS_ARCH=$old_arch # and restoring the screw-ups from sourcing
      echo "$a"
      echo "</span> "
    fi
  done
  echo "</div>"

  for a in `ls -1r $ISTHMUS_SYS/recipes | sort | grep -v ':' | grep -v 'arch-'`;do
    if [ -f $ISTHMUS_SYS/recipes/$a ]; then
      meta_data
      check_status $a
      echo "<div class='package' id='$a'>"
      #echo -n " $pkg_status_icon "
      . $ISTHMUS_SYS/recipes/$a
      ISTHMUS_ARCH=$old_arch # and restoring the screw-ups from sourcing
      echo "<div class='info'>"

      #echo "<a href='recipes/$a' class='recipe'>recipe</a>"
    if [ ! x"$license" = x ];then
      echo -n "<div class='license' >$license</div>"
    fi

    #echo "<div>$pkg_status</div>"
    rh=`compute_recipe_hash recipes/$a`
    for arch in $arches; do
      bh=''
      [ -f isthmus-$arch/remote-log ] && bh=`cat isthmus-$arch/remote-log | grep $rh|tail -n1`
      if [ ! x"$bh" = x ];then
        echo "<span class='tag $arch'>$arch</span>"
      else
        [ -f isthmus-$arch/log ] && bh=`cat isthmus-$arch/log | grep $rh|tail -n1`
        if [ ! x"$bh" = x ];then echo "<span class='tag $arch'>($arch)</span>"; fi
      fi
    done

    if [ ! x"$homepage" = x ];then
      echo "<a href='$homepage'> <div class='pkgname'><b>$a</b> $version</div> </a>"
    else
      echo "<div class='pkgname'><b>$a</b> $version</div>"
    fi
    echo "<div class='description'>$description</div>"
    if [ ! x"$documentation" = x ];then
      echo "<div class='documentation'>$documentation</div>"
    fi
    #if [ ! x"$homepage" = x ];then
    #  echo "<div class='homepage'><a href='$homepage'>homepage</a></div>"
    #fi

    echo "<div>recipe: $rh</div>"
    for arch in $arches; do
      bh=''
      [ -f isthmus-$arch/remote-log ] && bh=`cat isthmus-$arch/remote-log | grep $rh|tail -n1|cut -f 4 -d ' '`
      [ x"$bh" = x ] && [ -f isthmus-$arch/log ] && bh=`cat isthmus-$arch/log | grep $rh|tail -n1 | cut -f 4 -d ' '`
      if [ ! x"$bh" = x ]; then
        echo "<div>$arch: $bh</div>"
      fi
    done
    for arch in $arches; do
      if [ -f "isthmus-$arch/$a-build" ];then
        echo "<br/><a href='isthmus-$arch/$a-build'>$arch build log</a>"
      fi
      if [ -f "$arch/isthmus/file_listings/$a" ];then
        echo "<br/><a href='$arch/isthmus/file_listings/$a'>$arch files</a>"
      fi
    done

    #if [ ! x$url = x ];then
    #  echo -n "<div class='url'>url: $url</div>"
    #fi
    #if [ ! x$git = x ];then
    #  echo -n "<div class='git'>git: $git</div>"
    #fi

    old_arch=$ISTHMUS_ARCH
    collect_dep $a
    ISTHMUS_ARCH=$old_arch

    echo -n "<div class='dependencies'>dependencies:<br/>"
    for pkg in $global_deps; do
      #check_status $a
      echo "<span class='pkg' onClick='window.location.hash=\"$pkg\"'>$pkg</span>"
    done
    echo "</div>"

    old_arch=$ISTHMUS_ARCH
    collect_build_dep $a
    ISTHMUS_ARCH=$old_arch

    echo -n "<div class='dependencies'>build dependencies:<br/>"
    for pkg in $global_build_deps; do
      #check_status $pkg
      #output_pkg $pkg "$pkg_status_icon"
      echo "<span class='pkg' onClick='window.location.hash=\"$pkg\"'>$pkg</span>"
    done
      echo "</div>"



      echo "</div>"
      echo "</span> <span class='version'>$version</span>";
      echo "</div>"
    fi
  done
  echo "<iframe style='display:none' id='ifrm' src='' width=100% height='200'> </iframe>"

  echo "<div style='clear:both;'>&nbsp;</div>"
}

cmd_del()
{
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    if [ -f $ISTHMUS_SYS"/installed/"$1 ]; then
      echo "removing files for $ISTHMUS_ARCH $1"
      for a in `tac $ISTHMUS_SYS'/file_listings/'$1`;do
        rm -f $ISTHMUS_ROOT/$a > /dev/null 2>&1 || :
        rmdir $ISTHMUS_ROOT/$a > /dev/null 2>&1 || :
      done
    else
      echo "$1 not installed"
    fi
  else echo "unknown package $1" ;fi
  shift; done
}

cmd_installed()
{
  for a in `ls -1 $ISTHMUS_SYS"/installed"`;do echo -n $a" ";done
  echo ""
}

# XXX: only make symlinks for packages with binaries available

cmd_make_symlinks ()
{
  mkdir -p $ISTHMUS_SYS"/symlinks" || :
  rm -rf $ISTHMUS_SYS"/symlinks/"*
  mkdir -p $ISTHMUS_SYS"/symlinks/sbin" || :
  for a in `ls -R1 $ISTHMUS_SYS"/recipes" | grep -v ':'`;do
      meta_data
      cd $ISTHMUS_SYS
      if [ -f recipes/$a ]; then
        . recipes/$a
        cd $ISTHMUS_SYS"/symlinks"
        for b in $bins;do ln -s /usr/bin/eid $b;done
        cd $ISTHMUS_SYS"/symlinks/sbin"
        for b in $sbins;do ln -s /usr/bin/eid $b;done
      fi
  done
}

run_chroot_faketime()
{
  args="$*"
  if [ x"$USING_NSPAWN" = x ];then
    [ -f $ISTHMUS_ARCH/proc/self/status ] || $SUDO mount -o bind /proc $ISTHMUS_ARCH/proc
    [ -e $ISTHMUS_ARCH/dev/tty1 ] || $SUDO mount -o bind /dev $ISTHMUS_ARCH/dev

    if [ -f $ISTHMUS_ARCH/usr/lib/faketime/libfaketime.so.1 ];then
      $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
      /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
      LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 FAKETIME="$EIDFAKETIME" \
        $args
      status=$?
    else
      $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
      /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
        $args
      status=$?
    fi

    # XXX: we might umount when not needed, when entered from many shells
    $SUDO umount $ISTHMUS_ARCH/proc || :
    $SUDO umount $ISTHMUS_ARCH/dev || :
  else
    if [ -f $ISTHMUS_ARCH/usr/lib/faketime/libfaketime.so.1 ];then
      $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
      /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
      LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 FAKETIME="$EIDFAKETIME" \
        $args
      status=$?
    else
      $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
      /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
        $args
      status=$?
    fi
  fi

  post_prep_chroot
  return $status
}

run_chroot()
{
  args="$*"
  if [ x"$USING_NSPAWN" = x ];then
    [ -f $ISTHMUS_ARCH/proc/self/status ] || $SUDO mount -o bind /proc $ISTHMUS_ARCH/proc
  fi
  if [ x"$USING_NSPAWN" = x ];then
    $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
    /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
      $args
    status=$?
    $SUDO umount $ISTHMUS_ARCH/proc || :
  else
    $SUDO $CHROOT $CHROOT_ARGS $ISTHMUS_ARCH  \
    /bin/env -i UID=$UID USER=$USER HOME=/ PATH=/isthmus/bin:/usr/bin:/bin:/isthmus/symlinks PKG_CONFIG_PATH=/usr/lib/pkgconfig "MAKE_FLAGS=$MAKE_FLAGS" \
      $args
    status=$?
  fi

  post_prep_chroot
  return $status
}

rebuild_all()
{
 neu3=''
 for a in `ls -1 $ISTHMUS_SYS"/installed"`;do
   collect_dep $a
   add_build_deps $a
   for b in $global_build_deps; do
     echo "$neu3" | grep -q " $b " || neu3="$neu3 $b  "
   done
 done
 for pkg in $neu3; do cmd_add $pkg;
 done
 echo -e "\r                                      \r"
}

cmd_rebuild()
{
 if [ "$#" = "0" ]; then
   rebuild_all
   return 0
 fi
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    build_one $1
  else echo "unknown package $1" ;fi
  shift; done
}

cmd_shell()
{
  arch=`guess_arch`
  if [ ! -f $ISTHMUS_ARCH/bin/busybox ]; then
    $EID add busybox
  fi
  # ensure static qemu binary installed on arm chroots running on x86 hosts.
  case $ISTHMUS_ARCH in
    arm*) case `guess_arch` in
      arm*) ;;
      aarch64) ;;
      *) $EID add qemu-user-arm ;;
    esac
  esac
  if [ x$# = x0 ]; then
    run_chroot /bin/sh -i
  else
    if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
      echo prepping $1
      dir=`pwd`
      isthmus_build_init $ISTHMUS_SYS/recipes/$1
      fetch_base ; prepare_base
      cd $dir
      if [ $is_master = true ]; then
        mv $ISTHMUS_WORK_PATH/$unpack $ISTHMUS_ARCH/$ISTHMUS_WORK_PATH/$unpack
        cat > $ISTHMUS_SYS/tmp/enter_chroot <<EOF
#!/bin/sh
cd $ISTHMUS_WORK_PATH/$unpack
sh -i
rm /tmp/enter_chroot
EOF
        run_chroot /bin/sh /tmp/enter_chroot
      else
        cd $ISTHMUS_WORK_PATH/$unpack
        sh -i
      fi
    else
      echo unknown recipe $1
    fi
  fi
}

cmd_update()
{
  oldcwd=`pwd`
  [ x$ISTHMUS_SYS != x ] && cd $ISTHMUS_SYS

  for remote in `cat repos 2>/dev/null`;do
    remote_hash=`echo $remote|sha256sum|cut -f 1 -d ' '`
    echo "Updating remote $remote..."
    mkdir -p remote/$remote_hash
    mkdir -p remote/$remote_hash/isthmus-$ISTHMUS_ARCH
    cd remote/$remote_hash
    echo $remote > url
    rm -rf tmp;mkdir tmp;cd tmp; $ISTHMUS_HTTP_FETCH $remote/bits.tar.gz.sha256;cd ..
    if [ -f bits.tar.gz.sha256 ];then
      if [ ! x`cat bits.tar.gz.sha256` = x`cat tmp/bits.tar.gz.sha256` ];then
        cp -f tmp/bits.tar.gz.sha256 .
        rm -rf bits.tar.gz
        log_progress update bits.tar.gz
        $ISTHMUS_HTTP_FETCH $remote/bits.tar.gz
        tar xf bits.tar.gz
      fi
    else
      rm -rf bits.tar.gz;
      log_progress update bits.tar.gz
      $ISTHMUS_HTTP_FETCH $remote/bits.tar.gz && cp -f tmp/bits.tar.gz.sha256 .
      tar xf bits.tar.gz
    fi

    rm -rf tmp;mkdir tmp;cd tmp; $ISTHMUS_HTTP_FETCH $remote/recipes.tar.gz.sha256;cd ..
    if [ -f recipes.tar.gz.sha256 ];then
      if [ ! x`cat recipes.tar.gz.sha256` = x`cat tmp/recipes.tar.gz.sha256` ];then
        cp -f tmp/recipes.tar.gz.sha256 .
        rm -rf recipes.tar.gz
        log_progress update recipes.tar.gz
        $ISTHMUS_HTTP_FETCH $remote/recipes.tar.gz
        tar xf recipes.tar.gz
      fi
    else
      rm -rf recipes.tar.gz;
      log_progress update recipes.tar.gz
      $ISTHMUS_HTTP_FETCH $remote/recipes.tar.gz && cp -f tmp/recipes.tar.gz.sha256 .
      tar xf recipes.tar.gz
    fi
    mkdir -p isthmus-$ISTHMUS_ARCH
    cd isthmus-$ISTHMUS_ARCH
    rm -rf tmp;mkdir tmp;cd tmp; $ISTHMUS_HTTP_FETCH $remote/isthmus-$ISTHMUS_ARCH/log.sha256;cd ..
    if [ -f log.sha256 ];then
      if [ ! x`cat log.sha256` = x`cat tmp/log.sha256` ];then
        cp -f tmp/log.sha256 .
        rm -rf log
        log_progress update $remote/isthmus-$ISTHMUS_ARCH/log
        $ISTHMUS_HTTP_FETCH $remote/isthmus-$ISTHMUS_ARCH/log
      fi
    else
      rm -rf log
      log_progress update $remote/isthmus-$ISTHMUS_ARCH/log
      $ISTHMUS_HTTP_FETCH $remote/isthmus-$ISTHMUS_ARCH/log && cp -f tmp/log.sha256 .
    fi
    cd ..
  done
 cd $oldcwd
 log_progress update rebuild_recipes
 rebuild_recipes
 mkdir bits 2>/dev/null && :
 cd bits
 cp -Rv ../remote/*/bits/* . 2>/dev/null
 cd ..
 ensure_arch

 mkdir -p isthmus-$ISTHMUS_ARCH || :
 rm -f isthmus-$ISTHMUS_ARCH/remote-log || :
 [ -d remote ] && cat remote/*/isthmus-$ISTHMUS_ARCH/log > isthmus-$ISTHMUS_ARCH/remote-log 2>/dev/null || touch isthmus-$ISTHMUS_ARCH/remote-log
}

cmd_upgrade()
{
  echo upgrading $1 $2
}

cmd_index()
{
  echo "`cat isthmus-$ISTHMUS_ARCH/log.sha256`  $ISTHMUS_ARCH"
  sha256sum recipes.tar.gz
  sha256sum bits.tar.gz
  sha256sum eid
}

cmd_status()
{
  all=`cmd_list`
  echo $ISTHMUS_ARCH changed: \
 `echo "$all" | grep ' %' | grep -v Legend | cut -f 3 -d ' ' | grep -v image | grep -v arch`
  echo $ISTHMUS_ARCH unbuilt: \
 `echo "$all" | grep -v Legend | grep '   ' | cut -f 4 -d ' ' | grep -v image | grep -v arch`
  echo $ISTHMUS_ARCH failed: \
 `echo "$all" | grep ' F ' | grep -v Legend | cut -f 3 -d ' ' | grep -v image | grep -v arch`
}

cmd_compact_db(){
  cmd_binlist > isthmus-$ISTHMUS_ARCH/binlist
  cp isthmus-$ISTHMUS_ARCH/binlist $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH
  cd isthmus-$ISTHMUS_ARCH
  for a in `ls -1 | grep -v log | grep -v hash | grep -v binlist`; do grep -q $a binlist || rm $a; done
  rm binlist
  cd ..
  cd $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH
  for a in `ls -1 | grep -v log | grep -v hash | grep -v binlist`; do grep -q $a binlist || rm $a; done
  rm binlist
  cd ../../..
}

cmd_update_server_info()
{
  #log_progress "update-server-info" "compacting db"
  #cmd_compact_db
  log_progress "update-server-info" "tarring up recipes"
  if [ x"$LFAKETIME" = x ];then
    tar czf recipes.tar.gz recipes
    tar czf bits.tar.gz bits
  else
    LD_PRELOAD=$LFAKETIME FAKETIME="$EIDFAKETIME" \
    tar czf recipes.tar.gz recipes
    LD_PRELOAD=$LFAKETIME FAKETIME="$EIDFAKETIME" \
    tar czf bits.tar.gz bits
  fi
  echo `sha256sum recipes.tar.gz|cut -f 1 -d ' '`>recipes.tar.gz.sha256
  echo `sha256sum bits.tar.gz|cut -f 1 -d ' '`>bits.tar.gz.sha256
  echo `sha256sum eid|cut -f 1 -d ' '`> eid.sha256
  echo `sha256sum isthmus-$ISTHMUS_ARCH/log|cut -f 1 -d ' '`>isthmus-$ISTHMUS_ARCH/log.sha256
  log_success "update-server-info"
  cmd_index > index
  cp eid isthmus-$ISTHMUS_ARCH/`sha256sum eid|cut -f 1 -d ' '`

  cp recipes.tar.gz isthmus-$ISTHMUS_ARCH/`sha256sum recipes.tar.gz|cut -f 1 -d ' '`
  cp bits.tar.gz isthmus-$ISTHMUS_ARCH/`sha256sum bits.tar.gz|cut -f 1 -d ' '`
  cp index isthmus-$ISTHMUS_ARCH/`sha256sum index|cut -f 1 -d ' '`
  cp isthmus-$ISTHMUS_ARCH/log isthmus-$ISTHMUS_ARCH/`cat isthmus-$ISTHMUS_ARCH/log.sha256`
  echo "`sha256sum index|cut -f 1 -d ' '`  index" > isthmus-$ISTHMUS_ARCH/index
  echo "`sha256sum index|cut -f 1 -d ' '`  index"
}

cmd_update_server()
{
  #log_progress "update-server" compacting db
  #cmd_update_server_info
  if [ x$ISTHMUS_REMOTE_RSYNC = x ]; then
    echo "set ISTHMUS_REMOTE_RSYNC environment variable"
    exit
  fi
  log_progress "update-server" rsync
  rsync -avz --del --progress isthmus-$ISTHMUS_ARCH/ $ISTHMUS_REMOTE_RSYNC/isthmus-$ISTHMUS_ARCH
  rsync --progress eid README recipes.tar.gz eid.sha256 recipes.tar.gz.sha256 bits.tar.gz.sha256 bits.tar.gz $ISTHMUS_REMOTE_RSYNC/
  rm -rf recipes.tar.gz
  log_success "update-server"
}

unpack_to_listing()
{
  olddir=`pwd`
  cd $ISTHMUS_ROOT
  mkdir -p file_listings > /dev/null 2>&1;
  tar xhvf $ISTHMUS_SYS"/isthmus-"$ISTHMUS_ARCH/$2 > file_listings/$1
  cd $olddir
}

install_all_deps()
{
  pkg=$1
  target_pkg=$2
  if [ -f $ISTHMUS_SYS/recipes/$pkg ];then
    fetch_bin $pkg  # sets bin_hash and recipe_hash
    check_need_rebuild $pkg

    if [ x$need_rebuild = xtrue ]; then
      satisfied=false
    else
      # XXX: relying on pkg_status_icon is hacky
      if [ x$pkg_status_icon = x"^" -o ! -f $ISTHMUS_SYS"/installed/"$pkg ]; then
        if [ -f $ISTHMUS_SYS"/isthmus-"$ISTHMUS_ARCH/$bin_hash ]; then
          log_progress $target_pkg unpacking $pkg
          unpack_to_listing $pkg $bin_hash
          log_progress $pkg unpacked
        fi
      fi
    fi
  fi
}

build_one()
{
  pkg=$1
  if [ $is_master = true ]; then
    if grep -q isthmus_cross $ISTHMUS_SYS/recipes/$pkg; then
     ensure_cross_compiler
     log_progress $pkg cross-building
     if grep -q isthmus_nofaketime $ISTHMUS_SYS/recipes/$pkg; then
         log_progress $pkg cross-building forced-nonfaketime
         $EID build $ISTHMUS_SYS/recipes/$pkg
       else
       if [ x"$LFAKETIME" = x ];then
        log_progress $pkg "cross-building (no system faketime found)"
         $EID build $ISTHMUS_SYS/recipes/$pkg
       else
         log_progress $pkg cross-building with faked time
         LD_PRELOAD=$LFAKETIME FAKETIME="$EIDFAKETIME" \
          $EID build $ISTHMUS_SYS/recipes/$pkg
       fi
     fi
    else
     if grep -q isthmus_nofaketime $ISTHMUS_SYS/recipes/$pkg; then
      log_progress $pkg building in chroot without faketime
      run_chroot /usr/bin/eid build /isthmus/recipes/$pkg
     else
      log_progress $pkg building in chroot
      run_chroot_faketime /usr/bin/eid build /isthmus/recipes/$pkg
     fi
    fi
  else
     if grep -q isthmus_nofaketime $ISTHMUS_SYS/recipes/$pkg; then
       log_progress $pkg building
       /usr/bin/eid build /isthmus/recipes/$pkg

     else
       if [ x"$LFAKETIME" = x ];then
         log_progress $pkg building faketimed
         LD_PRELOAD=$LFAKETIME FAKETIME="$EIDFAKETIME" \
         /usr/bin/eid build /isthmus/recipes/$pkg
       else
         log_progress $pkg building
         /usr/bin/eid build /isthmus/recipes/$pkg
       fi
     fi
  fi
}

build_if_needed_one()
{
  pkg=$1
  if [ -f $ISTHMUS_SYS/recipes/$pkg ];then
    #log_progress $pkg
    fetch_bin $pkg
    check_need_rebuild $pkg

    if [ $need_rebuild = true ]; then
      build_one $pkg
      build_status=$?
      [ -n "$REGEN_HTML" ] && [ "$EID" != eid ] && $EID generate-html
      if [ $build_status != 0 ]; then
        log_fail $pkg
        exit 1
      fi

      check_need_rebuild $pkg

      if [ $need_rebuild = false ];then
        if [ ! -f $ISTHMUS_SYS"/installed/"$pkg ]; then
          if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ];then
            log_progress $pkg unpacking
            unpack_to_listing $pkg $bin_hash
          fi
          log_success $pkg
        fi
      else
        if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ];then
          log_progress $pkg unpackingC
        else
          log_fail_hashes $1
          log_fail $pkg problem building
        fi
      fi
    else
      if [ ! -f $ISTHMUS_SYS"/installed/"$pkg ]; then
        if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ];then
          log_progress $pkg unpackingD
          unpack_to_listing $pkg $bin_hash
        fi
      fi
      log_success $pkg
    fi
  fi
}

cmd_add()
{
  while [ "$1" ]; do if [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    target_pkg=$1
    meta_data
    collect_build_dep $1

    log_progress $1

    satisfied=true
    # try to install all run dependencies as binaries
    for pkg in $global_deps; do
      install_all_deps $pkg $target_pkg
    done

    if [ x$satisfied = xfalse ]; then
      satisfied=true
      for pkg in $global_deps; do
        install_all_deps $pkg $target_pkg
      done
    fi

    if [ x$satisfied = xtrue ]; then log_success $1; return 0; fi

    # try to install all build dependencies as binaries
    for pkg in $global_build_deps; do
      install_all_deps $pkg $target_pkg
    done
    # try to build all in build deps chain, skipping ones we have
    for pkg in $global_build_deps; do
      build_if_needed_one $pkg || build_fail="$pkg $build_fail"
    done
    [ -z "$build_fail" ] || log_success $1
  else echo -e "\r$1: unknown package" ;fi
  shift; done
}

post_prep_chroot()
{
  ( cd $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH ;
    cp $a ../../../isthmus-$ISTHMUS_ARCH/log log > /dev/null 2>&1 ;
    for a in *; do ln -f $a ../../../isthmus-$ISTHMUS_ARCH/$a ; done ;
    cd ../../../isthmus-$ISTHMUS_ARCH/
    for a in *; do ln -f $a ../$ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/$a ; done ;
  )
}

sync_bin_repo()
{
  ( cd $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH ;
    cp ../../../isthmus-$ISTHMUS_ARCH/log log > /dev/null 2>&1 ;
    touch remote-log
    cp ../../../isthmus-$ISTHMUS_ARCH/remote-log remote-log > /dev/null 2>&1 || :;
    for a in *; do ln -f $a ../../../isthmus-$ISTHMUS_ARCH/$a ; done ;
    cd ../../../isthmus-$ISTHMUS_ARCH/
    for a in *; do ln -f $a ../$ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/$a ; done ;
  )
}

prep_chroot()
{
  for dir in recipes bits ; do
    mkdir -p $ISTHMUS_ARCH/isthmus/$dir > /dev/null 2>&1; cp -r $dir/* $ISTHMUS_ARCH/isthmus/$dir
  done
  echo "ISTHMUS_ARCH=$ISTHMUS_ARCH" > $ISTHMUS_ARCH/isthmus/arch || true
  mkdir -p $ISTHMUS_ARCH/isthmus/sources

  # symlinks sources with sources dir outside arch
  ( cd sources; for a in *; do [ -f $a ] && ln -f $a ../$ISTHMUS_ARCH/isthmus/sources/$a; done ) || true
  ( cd $ISTHMUS_ARCH/isthmus/sources; for a in *; do [ -f $a ] && ln -f $a  ../../../sources/ ; done ) || true

  mkdir -p $ISTHMUS_ARCH/usr/bin
  cp eid $ISTHMUS_ARCH/usr/bin/eid # install ourself
  mkdir -p $ISTHMUS_ARCH/dev

  if [ -f $ISTHMUS_ARCH/dev/null ] ; then
    true
  else
    if [ x"$USING_NSPAWN" = x ];then
      mknod $ISTHMUS_ARCH/dev/null c 1 3 2>/dev/null
    fi
  fi

  mkdir -p $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH || true
  mkdir -p isthmus-$ISTHMUS_ARCH || true

  ln -f repos $ISTHMUS_ARCH/isthmus/repos

  sync_bin_repo
  mkdir -p $ISTHMUS_ARCH/isthmus/symlinks || true

  if [ ! -f $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/log ]; then
    cp isthmus-$ISTHMUS_ARCH/log $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/log || :
  fi
  touch $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/log
  cp isthmus-$ISTHMUS_ARCH/remote-log $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/remote-log 2>/dev/null || :
  touch $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/remote-log
}

compute_recipe_and_last_bin_hash()
{
  recipe_hash=`compute_recipe_hash $ISTHMUS_SYS/recipes/$1`
  bin_hash=`cat $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/log  $ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH/remote-log | grep $recipe_hash|tail -n1| cut -f 4 -d ' '`
  path=`ls -1 $ISTHMUS_SYS/recipes/$1`
}

compute_recipe_hash()
{
  echo `(echo "isthmus-1"; cat "$1" ) | sha256sum  | cut -f 1 -d ' '`
}

compute_hash()
{
    echo `cat "$1" | sha256sum  | cut -f 1 -d ' '`
}

check_status()
{
  if [ ! -f $ISTHMUS_SYS/recipes/$1 ];then pkg_status='missing'; pkg_status_icon='!';return ;fi
  pkg_status='recipe'
  pkg_status_icon=' '
  compute_recipe_and_last_bin_hash $1

  if [ "x$bin_hash" = "x" ];then
    if [ -f $ISTHMUS_SYS"/installed/"$1 ];then
      pkg_status='recipe changed' ; pkg_status_icon='%'
    fi
  else
    if [ "x$bin_hash" = "xFAIL" ];then
      pkg_status='build failed' ; pkg_status_icon='F'
    else
      if [ -f $ISTHMUS_SYS"/installed/"$1 ];then
        irecipe_hash=`compute_recipe_hash $ISTHMUS_SYS"/installed/$1"`
        if [ x$irecipe_hash = x$recipe_hash ]; then
          pkg_status='installed'  ;  pkg_status_icon='*'
        else
          pkg_status='binary upgradable' ;  pkg_status_icon='^'
        fi
      else
        pkg_status='available'    ;  pkg_status_icon='-'
        if [ -f isthmus-$ISTHMUS_ARCH/$bin_hash ]; then
          pkg_status='downloaded'    ;  pkg_status_icon='_'
        fi
      fi
    fi
  fi
}

# calling this function has the side effect of setting the global
need_rebuild=false
check_need_rebuild()
{
  need_rebuild=false
  if [ ! -f $ISTHMUS_SYS/recipes/$1 ]; then return 0; fi
  check_status $1
  if [ x"$pkg_status" = x"recipe" ];then need_rebuild=true; fi
  if [ x"$pkg_status" = x"recipe changed" ];then need_rebuild=true; fi
  if [ ! -f $ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/$bin_hash" ];then need_rebuild=true; fi
}

generate_image()
{
    echo "ERROR: unknown image type: please define generate_image() in your recipe"
}


cmd_generate()
{
  ISTHMUS_TARGET='isthmus'
  if [ "$#" -lt 1 ];then
    echo -e "\ngenerate needs an argument. Good choices:\n  "
    ( cd recipes; for a in image-*; do echo -n $a" " | sed s:.*image-::;done)
    echo
    exit 0
  fi

  . ./arch

  if [ -f `pwd`/recipes/image-$1 ]; then
    . `pwd`/recipes/image-$1
  else
    if [ -f $1 ]; then
      . $1
    else
      echo "didnt find image $1"
      exit 1
    fi
  fi
  if [ x"$2" != x ]; then
      ISTHMUS_TARGET="$2"
  fi

  for pkg in $packages;do
    log_progress $pkg verifying
    fetch_bin $pkg  # sets bin_hash and recipe_hash
    check_need_rebuild $pkg
    if [ $need_rebuild = false ]; then
      log_success $pkg
    else
      log_progress $pkg verifying
      if $EID add $pkg;then log_success $pkg verified; else log_fail $pkg; exit 1; fi
    fi
  done

  sync_bin_repo

  case "$isthmus_image_type" in
    vfat)     generate_vfat   ;;
    chroot)   generate_chroot ;;
    extfs)    generate_systemfs  ;;
    *)        generate_image  ;;
  esac
}

generate_chroot()
{
  echo "Creating root dir structure in ./$ISTHMUS_TARGET"
  rm -rf $ISTHMUS_TARGET
  make_root $ISTHMUS_TARGET "$packages"
}

generate_init()
{
  log_progress $ISTHMUS_TARGET "creating image"
  log_progress $ISTHMUS_TARGET initrd
  make_initrd initrd "$initrd_packages"
  log_progress $ISTHMUS_TARGET disk
  make_disk $ISTHMUS_TARGET
}

generate_fini()
{
  mkdir -p mnt/isthmus
  log_progress $ISTHMUS_TARGET initrd install
  $SUDO cp -v initrd.gz mnt/isthmus/initrd.gz
  log_progress $ISTHMUS_TARGET boot part sync
  sync
  $SUDO umount mnt || true
  $SUDO rm -rf mnt
}

generate_vfat()
{
  generate_init
  image=partition.img
  fstype='vfat -F 32 -n ISTHMUS'
  mountopts="-o loop,uid=$UID"
  make_fsmount
  mkdir mnt/isthmus

  bootimage_pre

  make_sys mnt
  copy_all_files

  cp mnt/isthmus/vmlinuz $ISTHMUS_TARGET-vmlinuz
  # ( cd mnt; tar xvf ../bits/luaroot.tgz )

  generate_fini

  syslinux -d /isthmus -i partition.img

  bootimage_post

  echo "generated vfat $ISTHMUS_TARGET"
}

make_root()
{
  tmpdir=$1
  initrd=$1
  rm -rf $initrd.gz
  mkdir -p $tmpdir

  top=`pwd`

  cd $tmpdir
  log_progress root-$1
  for pkg in $2;do
    log_progress root-$1 $pkg
    compute_recipe_and_last_bin_hash $pkg
    printf "%20s %s\n" $pkg $bin_hash
    tar xhf ../isthmus-$ISTHMUS_ARCH/$bin_hash
  done
  echo -e "\r                                  "
  mkdir -p usr/lib
  cp -a ../$ISTHMUS_ARCH/usr/lib/libgcc_s.so* usr/lib
  cp -a ../$ISTHMUS_ARCH/usr/lib/libc.so usr/lib
  mkdir -p lib
  cp -a ../$ISTHMUS_ARCH/lib/libc.so lib
  cp -a ../$ISTHMUS_ARCH/lib/ld-musl-* lib

  mkdir -p isthmus/symlinks || true

  rm -rf usr/share/doc
  rm -rf usr/share/gtk-doc
  rm -rf usr/share/man
  rm -rf usr/lib/*.a
  rm -rf usr/include
  strip usr/lib/*.so >/dev/null  2>&1 || true
  strip usr/bin/* > /dev/null  2>&1 || true

  cat > init <<EOF
#!/bin/sh

echo "$0"
exec /bin/init
EOF
  chmod a+x init
  post_install
  cd $top
  log_success root-$1
}

make_initrd()
{
  rm -rf $1-tmp
  log_progress initrd root
  make_root $1-tmp "$2"
  cd $1-tmp
  find lib/ usr/lib/ -name '*.a' -delete
  log_progress initrd cpio
  find . | cpio --create --format='newc' > ../$1
  log_progress initrd post_install
  post_install
  cd ..
  rm -rf $1-tmp
  rm -f $1.gz
  log_progress initrd gzip
  gzip $1
  ls -sh $1.gz
  log_success initrd
}

make_sys()
{
  target=$1
  mkdir -p $target/isthmus/bits || $SUDO mkdir -p $target/isthmus/bits || true
  $SUDO mkdir -p $target/isthmus/recipes || true
  $SUDO mkdir -p $target/isthmus/isthmus-$ISTHMUS_ARCH || true
  $SUDO mkdir -p $target/isthmus/file_listings || true
  $SUDO cp -R $ISTHMUS_ARCH/isthmus/bits/* $target/isthmus/bits || true
  $SUDO cp -R $ISTHMUS_ARCH/isthmus/file_listings/* $target/isthmus/file_listings || true
  $SUDO cp $ISTHMUS_ARCH/isthmus/recipes/* $target/isthmus/recipes || true
  $SUDO cp $ISTHMUS_ARCH/isthmus/installed/* $target/isthmus/recipes
  touch $target/isthmus/isthmus-$ISTHMUS_ARCH/log
  touch $target/isthmus/isthmus-$ISTHMUS_ARCH/remote-log
  echo "ISTHMUS_ARCH=$ISTHMUS_ARCH" > $target/isthmus/arch || true
  #$SUDO cp -R sources/*  $target/isthmus/sources || true
}

copy_all_files ()
{
  $SUDO mkdir -p isthmus/isthmus-$ISTHMUS_ARCH
  $SUDO cp -R $ISTHMUS_ARCH/isthmus/isthmus-$ISTHMUS_ARCH/* $target/isthmus/isthmus-$ISTHMUS_ARCH || true
}

image=$ISTHMUS_TARGET
fsblocks=1024000
fstype="ext2"
mountopts="-o loop"
make_fsmount()
{
  echo $image:$fstype creating, size $fsblocks
  $SUDO umount mnt 2>/dev/null || true
  if [ ! -b "$image" ]; then
      rm -f $image
      dd if=/dev/zero of=$image bs=512 count=$fsblocks || exit 1
  elif  [ ! -w $image ]; then log_progress $image settling; sleep 1; fi
  log_progress $image mkfs.$fstype
  /sbin/mkfs.$fstype $image >/dev/null || exit 1
  sync
  $SUDO rm -rf mnt
  mkdir -p mnt
  log_progress $image mounting
  $SUDO mount $mountopts $image mnt || exit 1
  $SUDO chown $UID:$UID mnt/
}
generate_systemfs()
{
  make_fsmount
  log_progress $image filling
  make_root mnt "$packages"
  log_progress $image sys
  make_sys mnt
  log_progress $image sync
  sync

  $SUDO chown root:root -R mnt/
  $SUDO umount mnt
  log_success $image
}

vfatsize=600  # lower than this and resizes start failing
totblocks=$((1024*1024*$vfatsize/512))
poffset=2048

#vfatsize=999 # lower than this and resizes start failing

make_disk()
{
  image=$1
  echo "make_disk: $image $totblocks"
  if [ ! -b "$image" ]; then
    rm -f $image
    dd if=/dev/zero of=$image bs=512 count=$totblocks
  fi
  /sbin/fdisk $image > /dev/null 2>&1 << EOF
n
p
1
2048

a
1
t
b
w
p
EOF
  /sbin/fdisk -l $image
}

# make bootable grub and syslinux
bootimage_pre()
{
  $SUDO cp `ls -1 $ISTHMUS_ARCH/boot/vmlinuz-* |sort | tail -n1` mnt/isthmus/vmlinuz
  $SUDO cp bits/syslinux.cfg mnt/isthmus || true
  $SUDO mkdir -p mnt/EFI/BOOT || true
  $SUDO grub-mkstandalone --compress=xz -o mnt/EFI/BOOT/BOOTX64.EFI \
      -O x86_64-efi --install-modules="part_gpt search linux minicmd all_video efi_uga efi_gop test normal gzio gettext terminal crypto extcmd boot tar archelp memdisk video_bochs video_cirrus video video_fb fshelp part_msdos fat ext2 " "boot/grub/grub.cfg=bits/grub.cfg"
}

bootimage_post()
{
  dd conv=notrunc bs=512 count=1 if=$SYSLINUX/mbr.bin of=$ISTHMUS_TARGET
  dd conv=notrunc bs=512 count=$(($totblocks-$poffset)) if=partition.img of=$ISTHMUS_TARGET seek=$poffset

  rm partition.img
}

cmd_arch()
{
  if [ ! $is_master = true ];then
    echo "system-arch: $ISTHMUS_ARCH"
    return
  fi
  if [ $# -eq 0 ];then
    echo "current:   $ISTHMUS_ARCH"
    echo -n "available: "
    (cd recipes;for a in arch-*;do echo -n $a" "|sed s/.*arch-//;done)
    echo ""
  else
    if [ -f recipes/arch-$1 ];then
      rm arch
      ln -sf recipes/arch-$1 arch
      echo "set architecture to: $1"
      cmd_update
    else
      echo "Don't know the $1 architecture"
    fi
  fi
}

run_build_stages()
{
  ldp="$LD_PRELOAD"
  # if running preload faketime, don't faketime configure due to paranoid sanity checks
  fetch_base && if [ x"$LD_PRELOAD" != x ]; then unset LD_PRELOAD && prepare_base && LD_PRELOAD="$ldp"; else prepare_base; fi && build_base && return 0
  return 1
}

isthmus_build_init()
{
  origpwd=`pwd`

  ISTHMUS_BASE=$origpwd

  ISTHMUS_SRC_PATH=$ISTHMUS_SYS/sources
  ISTHMUS_WORK_PATH=$ISTHMUS_SYS/tmp
  ISTHMUS_BITS_PATH=$ISTHMUS_SYS/bits
  ISTHMUS_BIN_PATH=$ISTHMUS_SYS/isthmus-$ISTHMUS_ARCH
  ISTHMUS_BIN_INDEX_PATH=$ISTHMUS_BIN_PATH/log
  [ -n "$ISTHMUS_SYS" ] && install -d "$ISTHMUS_SYS"
  install -d "$ISTHMUS_WORK_PATH"
  install -d "$ISTHMUS_BIN_PATH"
  touch $ISTHMUS_BIN_INDEX_PATH
  install -d "$ISTHMUS_SRC_PATH"

  [ -z "$CC" ] && CC=gcc

  name=`basename "$1"`
  stage=`mktemp -d`
  version=0

  if [ -f `pwd`/"$1" ]; then
    recipe_path=`pwd`/"$1"
  elif [ -f $1 ]; then
    recipe_path="$1"
  elif [ -f local/$1 ]; then
    recipe_path="local/$1"
  elif [ -f recipes/$1 ]; then
    recipe_path="recipes/$1"
  elif [ -f $ISTHMUS_SYS/recipes/$1 ]; then
    recipe_path="$ISTHMUS_SYS/recipes/$1"
  else
    echo "first argument must be a package recipe"
    exit 1
  fi

  . $recipe_path
  if [ "x$git_branch" != "x" ]; then version=$git_branch; fi
  if [ "x$git_refspec" != "x" ]; then version=$git_refspec; fi
  if [ "x$git" != "x" ] && [ "x$unpack" = "x" ]; then unpack=$name; fi

  [ "x$url" != "x" ] && tarball_name=`echo "$url" | sed 's:[^/]*/::g'`
  [ "x$unpack" = "x" ] && unpack=$name-$version

  export LD_PATH="$stage/lib:$stage/usr/lib:/lib:/usr/lib"
  return 0
}


cmd_build()
{
  isthmus_build_init $1

  if [ -d "$ISTHMUS_BIN_PATH" ];then
    if [ -n "$V" ]; then
      ( run_build_stages || exit 1 ) \
        | tee "$ISTHMUS_BIN_PATH/$name-build" 2>&1
      build_status=$?
    else
      ( run_build_stages || exit 1 ) \
        > "$ISTHMUS_BIN_PATH/$name-build" 2>&1
      build_status=$?
    fi
  else
    run_build_stages || exit 1
    build_status=0
  fi
  if [ $build_status = 0 ]; then
    create_package ; install_package ; log_bin_hashes $1 ; clean_temporary
  else
    log_fail_hashes $1
    log_fail $1
    exit 1
  fi
}

cmd_post_install_hook()
{
  if [ $is_master = true ]; then
    stage=$ISTHMUS_ARCH
    cd $stage
  else
    stage=''
    cd /
  fi
  if [ -f `pwd`/"$1" ]; then
    recipe_path=`pwd`/"$1"
  elif [ -f $1 ]; then
    recipe_path="$1"
  else
    echo "first argument must be a package recipe"
    exit 1
  fi
  . $recipe_path
  post_install
}

cmd_pre_uninstall_hook()
{
  if [ $is_master = true ]; then
    stage=$ISTHMUS_ARCH
    cd $stage
  else
    stage=''
    cd /
  fi
  if [ -f `pwd`/"$1" ]; then
    recipe_path=`pwd`/"$1"
  elif [ -f $1 ]; then
    recipe_path="$1"
  else
    echo "first argument must be a package recipe"
    exit 1
  fi
  . $recipe_path
  pre_uninstall
}

http_fetch()
{
  odir=`pwd`
  cd "$ISTHMUS_SRC_PATH"
  for remote in `cat $ISTHMUS_SYS/repos`; do
    repo_url=$remote/sources/$tarball_name
    $ISTHMUS_HTTP_FETCH "$repo_url"
    if [ $? == 0 ]; then
      cd $odir
      return 0
    fi
  done
  [ -n "$url" ] && $ISTHMUS_HTTP_FETCH "$url" || :
}

http_fetch_and_checksum()
{
  odir=`pwd`
  cd "$ISTHMUS_SRC_PATH"
  if [ `compute_hash $tarball_name` = $sha256sum  ];then
    cd $odir
    return 0
  fi
  # try isthmus mirrors first - avoiding hammering on original servers
  for remote in `cat $ISTHMUS_SYS/repos`;do
    rm -f "$tarball_name"
    repo_url=$remote/sources/$tarball_name
    $ISTHMUS_HTTP_FETCH "$repo_url"
    if [ `compute_hash $tarball_name` = $sha256sum  ]; then
      cd $odir
      return 0
    fi
  done
  rm -f "$tarball_name"
  $ISTHMUS_HTTP_FETCH "$url"
  if [ !  `compute_hash $tarball_name` = $sha256sum  ];then
    # downloaded shasum doesn't match recipe, refetch
    rm -f "$tarball_name"
    $ISTHMUS_HTTP_FETCH "$url"
    hash=`compute_hash $tarball_name`
    if [ ! $hash = $sha256sum  ];then
      echo "ERROR: hash of download did not match recipe"
      echo "$hash ($tarball_name)"
      echo "$sha256sum (recipe)"
      exit 1
    fi
  fi
  cd $odir
  return 0
}

fetch()
{
  cd "$ISTHMUS_WORK_PATH"
  if [ "x$local_src" != "x" ];then
    cp -Rv $local_src .
    cd $unpack
  else
    if [ "x$git" != "x" ]; then
      cd "$ISTHMUS_SRC_PATH";
      if [ ! -d $unpack ]; then
        if [ "x$git_branch" != "x" ];then
          git clone --depth 1 --recursive $git --branch $git_branch
        else
          if [ "x$git_refspec" != "x" ];then
            git clone --recursive $git
          else
            git clone --depth 1 --recursive $git
          fi
        fi
        cd $unpack
      else
        cd $unpack
        git fetch --depth 1 --recurse-submodules
      fi
      if [ "$git_refspec" ];then
        git checkout $git_refspec
      else
        if [ $(basename $PWD) != "$unpack" ]; then
            echo "WONTREACH: was about to git reset $PWD"
            exit 99
        fi
        git reset origin --hard
      fi
      rm -rf "$ISTHMUS_WORK_PATH/$unpack"
      mkdir -p "$ISTHMUS_WORK_PATH/$unpack"
      cp -Rva * "$ISTHMUS_WORK_PATH/$unpack" > /dev/null
      [ -d .mm ] && cp -Rva .mm "$ISTHMUS_WORK_PATH/$unpack" > /dev/null
      cd "$ISTHMUS_WORK_PATH"
    elif [ "x$url" != "x" ]; then
      if [ "x$sha256sum" != "x" ];then
       http_fetch_and_checksum
      else
        [ -f "$ISTHMUS_SRC_PATH/$tarball_name" ] || http_fetch
      fi
    fi
    cd "$ISTHMUS_WORK_PATH"
    if [ -f "$ISTHMUS_SRC_PATH/$tarball_name" ]; then
      if echo $tarball_name | grep ".tar" -;then
        tar xf "$ISTHMUS_SRC_PATH/$tarball_name"
      elif echo $tarball_name | grep ".tgz" -;then
        tar xf "$ISTHMUS_SRC_PATH/$tarball_name"
      elif echo $tarball_name | grep "ddate" -;then # ugly hack, ddate archive should be fixed
        tar xf "$ISTHMUS_SRC_PATH/$tarball_name"
      else
        #unzip "$ISTHMUS_SRC_PATH/$tarball_name"
        mkdir $unpack
      fi
      if [ $? != 0 ]; then
        echo "$tarball_name: broken tarball"
        exit 1
      fi
      if [ "x$unpack" = "x" ]; then
        unpack=`basename "$tarball_name"|sed -e s:\.gz$:: -e s:\.zip$:: -e s:\.bz2$:: -e s:\.xz$:: -e s:\.tar$::`
      fi
      cd "$unpack"
    fi
    if [ "x$git" != "x" ];then
      cd $unpack
    fi
  fi
}


create_package ()
{
  if [ -n "`ls "$stage"`" ]; then
    install -d "$stage"/isthmus/installed || :
    cp "$recipe_path" "$stage"/isthmus/installed
    (cd "$stage"; find . | xargs touch -h -t 197805032200 )
    (cd "$stage"; tar cjf "$ISTHMUS_WORK_PATH"/$name-$version.mrg ./*)
    bin_sha=`compute_hash $ISTHMUS_WORK_PATH"/$name-$version.mrg"`
  fi
}

install_package()
{
  (cd "$ISTHMUS_ROOT"; tar xhvf "$ISTHMUS_WORK_PATH"/$name-$version.mrg > "$stage"/file_list)
  cat "$stage"/file_list > "$ISTHMUS_SYS"/file_listings/$name
}

clean_temporary()
{
  rm -rf "$stage"
  cd "$ISTHMUS_WORK_PATH"
  [ -z "$V" ] &&  rm -rf "$unpack"
}

log_bin_hashes()
{
  if [ -f "$ISTHMUS_WORK_PATH"/$name-$version.mrg ]; then
    if [ -f "$ISTHMUS_SRC_PATH/$tarball_name" ]; then
       echo $name $version `compute_recipe_hash "$recipe_path"` $bin_sha \
     `sha256sum "$ISTHMUS_SRC_PATH/$tarball_name"|cut -f 1 -d ' '||echo none` >> $ISTHMUS_BIN_INDEX_PATH
    else
       echo $name $version `compute_recipe_hash "$recipe_path"` $bin_sha none >> $ISTHMUS_BIN_INDEX_PATH
    fi
    mv "$ISTHMUS_WORK_PATH"/$name-$version.mrg "$ISTHMUS_BIN_PATH"/$bin_sha || :
    ln -sf $bin_sha "$ISTHMUS_BIN_PATH"/$name-$version.mrg || :
  fi
}

log_fail_hashes()
{
  rh=`compute_recipe_hash $1`
  tvar="none"
  if [ -f "$ISTHMUS_SRC_PATH/$tarball_name" ]; then
    tvar=`sha256sum "$ISTHMUS_SRC_PATH/$tarball_name"|cut -f 1 -d ' '||echo none`
  fi
  echo $1 $version $rh FAIL $var >> "$ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/log
  echo echo $1 $version $rh FAIL $var ">>" "$ISTHMUS_SYS"/isthmus-$ISTHMUS_ARCH/log
}

cmd_remote_list()
{
  echo "recipe upstreams:"
  no=0
  for a in `cat repos`;do echo "$no  $a";no=$(($no+1));done
}

cmd_remote_add(){ echo $1 >> repos ; cmd_remote_list ; }
cmd_remote_del(){ grep -v $1 repos > tmp;mv tmp -f repos; cmd_remote_list; }

remote_usage()
{
  echo -e "\nUsage: isthmus remote [command options]"
  echo ""
  echo "Available commands:"
  echo "  remote add <url>     - add new repo "
  echo "  remote del <url>     - remove repo"
  echo "  remote list          - (or nor argument, lists current repos)"
  echo ""
  echo "These commands are just manipulating the text file repos, which can also"
  echo "be manually tuned."
}

cmd_remote()
{
  if [ "$#" -eq 0 ]; then cmd_remote_list; exit 0; fi
  case $1 in
     "--help")      remote_usage ;;
     "list")        cmd_remote_list ;;
     "add")         shift;cmd_remote_add $* ;;
     "del")         shift;cmd_remote_del $* ;;
     *);;
   esac
}

if [ ! `basename $0` = eid ];then
  # we're executing a symlink to eid, this is a request to install and run
  # the package containing the indicated binary
  #
  # should be made to be a different command to work with binaries not
  # named same as package

  main add $( eid contains-bin `basename $0` )
  exec `basename $0` $*
else
  main $*
fi