Lane East 的 blog

一百年很短,一秒钟很长

slackware 包管理(pkgtools)分析 installpkg

2010-03-16 02:30

installpkg 是用来安装 slackware 软件包的工具, 根据它的帮助, 可以知道它的各个参数:

-warn       只用来警告是否有文件被覆盖, 并不进行实际的安装
-root /mnt  用其它的目录来作根目录, 可以用在系统维护或是系统安装的时候, 如果没使用这个参数, 那么则使用环境变量中的 ROOT 设置
-infobox    安装的时候显示一个文本对话框, 以显信息
-menu       使用文本对话框来询问是否安装, 对指定了[required](ADD)优先级的包无效
-ask        和上一个参数(-menu)联合使用, 用以忽略优先级的设置, 即指定了
            [required](ADD)优先级的包也要询问是否安装, 这是帮助中说的, 实际上,
            从代码中看, 即使是 infobox 模式下, 或者是 skip 优先级, -ask 也是
            有用的
-priority ADD|REC|OPT|SKP
            手工指定包的优先级, 而不是从 tagfile 中读取. 可以使用的优先级有:
            # ADD: required, 系统必需的软件
            # REC: recommended, 推荐安装的软件
            # OPT: optional, 可以选的软件
            # SKP: skip, 忽略的软件
-tagfile /somedir/tagfile
            手工指定 tagfile, 而不是使用默认的位置存放的 tagfile.
            默认位置在软件包所在的目录.

installpkg 中还使用了 dialog 来显示文本界面的对话框, 主要用到了两种形式, 在此简单说明一下:

dialog --title "对话框标题" --infobox "显示的信息" 0 0 其中的 0 0 表示对话框的大小是自适应的

dialog --title "对话框标题" --menu "菜单的提示信息" 0 0 3 \
  "第一个选项" "第一个选项的说明"
  "第二个选项" "第二个选项的说明"
  "第三个选项" "第三个选项的说明" 2>result.txt

其中的 0 0 表示对话框大小是自适应的, 3 表示有 3 个选项, “2>result.txt“表示把选择的结果放进 result.txt 中

下面是省去了版权信息及英文注释的中文注释了的代码(为了节约篇幅), 代码的相关版权请参照原软件包:

#!/bin/sh
# 默认返回 0, 如果后面遇到错误, 则重新设置 EXITSTATUS 的值
EXITSTATUS=0

# 检查 tar 的版本是否符合要求
umask 022
TAR=tar-1.13
$TAR --help 1> /dev/null 2> /dev/null
if [ ! $? = 0 ]; then
  TAR=tar
fi
if [ ! "`LC_MESSAGES=C $TAR --version`" = "tar (GNU tar) 1.13

Copyright (C) 1988, 92,93,94,95,96,97,98, 1999 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Written by John Gilmore and Jay Fenlason." ]; then
  echo "WARNING: pkgtools are unstable with tar > 1.13."
  echo "         You should provide a \"tar-1.13\" in your \$PATH."
  sleep 5
fi

# 帮助信息
usage() {
 cat << EOF
Usage: installpkg [options] package_name

Installpkg is used to install a .tgz package like this:
   installpkg xf_bin.tgz

options:      -warn (warn if files will be overwritten, but do not install)
              -root /mnt (install someplace else, like /mnt)
              -infobox (use dialog to draw an info box)
              -menu (confirm package installation with a menu, unless
                    the priority is [required] or ADD)
              -ask (used with menu mode: always ask if a package should be
                   installed regardless of what the package's priority is)
              -priority ADD|REC|OPT|SKP  (provide a priority for the entire
                    package list to use instead of the priority in the
                    tagfile)
              -tagfile /somedir/tagfile (specify a different file to use
                    for package priorities.  The default is "tagfile" in
                    the package's directory)

EOF
}

# 消除多余的空白
# 后面用它来处理 gzip -l 的输出, 并将处理完的结果送给 cut,
# 避免了多个空白对 cut 的处理
crunch() {
  while read FOO ; do
    echo $FOO
  done
}

# 由软件包的文件名获取相应的软件名
# 支持老式包名(软件名.tgz)和新式包名(软件名-版本号-平台-打包次数.tgz)
package_name() {
  # 去掉结尾的 .tgz
  STRING=`basename $1 .tgz`
  # 检查是否是老式包名
  # 当包名中不含 "-" 的时候,"cut -f 1 -d -" 和 "cut -f 2 -d -" 的执行结果相同,
  # 此处通过这个特性来确定包名中是否含有 "-", 这是不严密的, 因为可能会存在第一
  # 段和第二段内容相同的情况(应该很少见). 为什么不直接比较 cut -f 1 -d - 和
  # $STRING 本身呢?
  if [ "`echo $STRING | cut -f 1 -d -`" = "`echo $STRING | cut -f 2 -d -`" ]; then
    echo $STRING
  else
  # 包名中包含 "-", 不过有可能是因为软件名中含有 "-"(例如: util-linux), 
  # 所以仍有可能是老式包名
    # 计算文件名被 "-" 分割为了几段, 少于4段的则仍旧为老式的文件名
    INDEX=1
    while [ ! "`echo $STRING | cut -f $INDEX -d -`" = "" ]; do
      INDEX=`expr $INDEX + 1`
    done
    INDEX=`expr $INDEX - 1`
    # 少于4段的, 就仍然是旧式的软件包
    if [ "$INDEX" = "2" -o "$INDEX" = "3" ]; then
      echo $STRING
    else
    # 多于 4 段, 为新式文件名(此做法有一定的限制: 在软件名中包含大于等于 3 个
    # "-"的时候会误判,不过目前没见到过有这样的软件名)
      # 由于版本号,平台和打包次数均不含 "-",所以从后面去掉三段(显示从第 1 段到第
      # $INDEX - 3 段),剩下的部分作为软件名,这样能正确处理软件名中包含"-"的情况
      NAME=`expr $INDEX - 3`
      NAME="`echo $STRING | cut -f 1-$NAME -d -`"
      echo $NAME
      # cruft for later ;)
      #VER=`expr $INDEX - 2`
      #VER="`echo $STRING | cut -f $VER -d -`"
      #ARCH=`expr $INDEX - 1`
      #ARCH="`echo $STRING | cut -f $ARCH -d -`"
      #BUILD="`echo $STRING | cut -f $INDEX -d -`"
    fi
  fi
}

# 参数处理, installpkg 程序的真正入口
# 默认为 install 模式(命令行安装模式)
MODE=install
while [ 0 ]; do
  # warn 模式(只提示要安装, 删除哪些东西, 但是并不真正执行)
  if [ "$1" = "-warn" ]; then
    MODE=warn
    shift 1
  # infobox 模式(使用文本对话框来显安装信息, 但是无需选择是否安装)
  elif [ "$1" = "-infobox" ]; then
    MODE=infobox
    shift 1
  # menu 模式(使用文本对话框来询问是否要安装, 不询问优先级为[required]的软件包)
  elif [ "$1" = "-menu" ]; then
    MODE=menu
    shift 1
  # menu 模式下, 即使优先级为[required]的软件包, 也询问是否安装
  elif [ "$1" = "-ask" ]; then
    ALWAYSASK="yes"
    shift 1
  # 使用用户指定的 tagfile
  elif [ "$1" = "-tagfile" ]; then
    if [ -r "$2" ]; then
      USERTAGFILE="$2"
    elif [ -r "`pwd`/$2" ]; then
      USERTAGFILE="`pwd`/$2"
    else
      usage
      exit
    fi
    shift 2
  # 使用用户指定的优先级
  elif [ "$1" = "-priority" ]; then
    if [ "$2" = "" ]; then
      usage
      exit
    fi
    USERPRIORITY="$2"
    shift 2
  # 指定其它的安装位置, 可以用在系统安装或者是维护的时候
  elif [ "$1" = "-root" ]; then
    if [ "$2" = "" ]; then
      usage
      exit
    fi
    ROOT="$2"
    shift 2
  else
  # 遇到第一个非以上众多参数之一的, 则退出参数处理
  # 后面的参数都会被当作是软件名, 这是 installpkg 的一个限制:
  # 比如 installpkg packagename.tgz -warn 中的 -warn 也会被认为是个软件包名
    break
  fi
done

# 检查所需目录结构是否存在, 并且是否是目录, 如果不是, 则建立, 并设置好相应权限
# 相应的目录有 /var/log/packages, /var/log/removed_packages,
# /var/log/removed_scripts, /var/log/scripts, /var/log/setup
ADM_DIR="$ROOT/var/log"
for PKGDBDIR in packages removed_packages removed_scripts scripts setup ; do
  if [ ! -d $ADM_DIR/$PKGDBDIR ]; then
    rm -rf $ADM_DIR/$PKGDBDIR # make sure it's not a symlink or something stupid
    mkdir -p $ADM_DIR/$PKGDBDIR
    chmod 755 $ADM_DIR/$PKGDBDIR 
  fi
done

# 检查是否有临时目录
TMP=$ADM_DIR/setup/tmp
# If the $TMP directory doesn't exist, create it:
if [ ! -d $TMP ]; then
  rm -rf $TMP # make sure it's not a symlink or something stupid
  mkdir -p $TMP
  chmod 700 $TMP
fi

# 无参数(或者是处理完 -warn 之类的参数后没有软件包名称)时显示帮助
if [ $# = 0 ]; then
  usage;
  exit
fi

# warn 模式
if [ "$MODE" = "warn" ]; then
  # 循环处理所有的包
  while [ -f "$1" ]; do
    echo "#### Scanning the contents of $1..."
    mkdir -p $TMP/scan$$
    # 在括号中的命令会使用一个子 shell, 在这个子 shell 中的 cd 命令不会影响到
    # installpkg 自身的工作目录
    ( cd $TMP/scan$$ ; $TAR xzf - install ) < $1 2> /dev/null 
    if [ -r $TMP/scan$$/install/doinst.sh ]; then
      # 包中包含 install/doinst.sh, 且其中包含 ' rm -rf' 的内容
      if cat $TMP/scan$$/install/doinst.sh | grep ' rm -rf ' 1>/dev/null 2>/dev/null ; then
        # 收集含有 ' rm -rf' 的行,并处理后输出
        cat $TMP/scan$$/install/doinst.sh | grep ' rm -rf ' > $TMP/scan$$/install/delete
        echo "The following locations will be completely WIPED OUT to allow symbolic"
        echo "links to be made. (We're talking 'rm -rf') These locations may be files,"
        echo "or entire directories.  Be sure you've backed up anything at these"
        echo "locations that you want to save before you install this package:"
        cat $TMP/scan$$/install/delete | cut -f 3,7 -d ' ' | tr ' ' '/'
      fi
      if [ -d $TMP/scan$$ ]; then
        # 直接使用
        # rm -rf $TMP/scan$$/install 2>/dev/null
        # rmdir scan$$ 2>/dev/null
        # 不是更好?
        # 另: 不直接使用 rm -rf $TMP/scan$$ 2>/dev/null 是因为 rmdir 只删除
        # 空目录, 所以不会影响 $TMP/scan$$ 下的的其它文件(如果存在的话)
        ( cd $TMP/scan$$ ; rm -rf install ) 2> /dev/null
        ( cd $TMP ; rmdir scan$$ ) 2> /dev/null
      fi
    fi
    echo "The following files will be overwritten when installing this package."
    echo "Be sure they aren't important before you install this package:"
    # tar 的 vv 参数会让 tar 显示更详细的文件信息(列出文件类型权限等),
    # 这样就可以用来过滤掉目录
    # 下面的这个地方没必要用这个括号, 直接 $TAR tzvvf $1 | grep -v 'drwx' 就可以
    # 在这可以看出, warn 模式给出的提示只是 *可能* 会被覆盖的
    ( $TAR tzvvf - ) < $1 | grep -v 'drwx'
    echo
    shift 1
  done
  # 全部的包都 warn 完之后退出, 不进行后面的安装
  exit
fi

# 各种安装模式(install, infobox, menu 模式)
for package in $* ; do

  # 运行命令时包名称未带 .tgz 则加上,以便统一处理
  if [ ! -r "$package" -a -r "$package.tgz" ]; then
    package=$package.tgz
  fi

  # 去掉包名中的 .tgz 部分
  shortname="`basename $package .tgz`"
  # 包所在的目录
  packagedir="`dirname $package`"
  # 调用 package_name 函数来获取软件名
  packagebase="`package_name $shortname`"

  # 拒绝非 .tgz 结尾的包(设置返回值为 3)
  # 前面已经有 packagedir='`dirname $package`", 为什么这里不直接使用?
  if [ ! -r "`dirname $package`/$shortname.tgz" ]; then
    EXITSTATUS=3
    if [ "$MODE" = "install" ]; then
      echo "Cannot install $package: package does not end in .tgz"
    fi
    continue;
  fi

  # 处理包的优先级
  unset PRIORITY
  if [ "$USERTAGFILE" = "" ]; then
    # 同样, 前面已经有 packagedir='`dirname $package`", 为什么这里不直接使用?
    TAGFILE="`dirname $package`/tagfile"   
  else
    TAGFILE="$USERTAGFILE"
  fi
  if [ ! -r "$TAGFILE" ]; then
    TAGFILE=/dev/null
  fi
  # 读取优先级信息:
  if grep "^$packagebase:" "$TAGFILE" | grep ADD > /dev/null 2> /dev/null ; then
    PRIORITY="ADD"
  elif grep "^$packagebase:" "$TAGFILE" | grep REC > /dev/null 2> /dev/null ; then
    PRIORITY="REC"
  elif grep "^$packagebase:" "$TAGFILE" | grep OPT > /dev/null 2> /dev/null ; then
    PRIORITY="OPT"
  elif grep "^$packagebase:" "$TAGFILE" | grep SKP > /dev/null 2> /dev/null ; then
    PRIORITY="SKP"
  fi
  # 根据不同的优先级, 设定 PMSG, 用于后面的显示
  if [ "$PRIORITY" = "ADD" ]; then
    PMSG="[required]"
  elif [ "$PRIORITY" = "REC" ]; then
    PMSG="[recommended]"
  elif [ "$PRIORITY" = "OPT" ]; then
    PMSG="[optional]"
  elif [ "$PRIORITY" = "SKP" ]; then
    PMSG="[skip]"
  else
    PMSG=""
  fi

  # 获取软件包相应的描述文件
  DESCRIPTION="/dev/null"
  # 在软件包相同的目录下查找 disk* package_descriptions $shortname.txt
  # $packagebase.txt, 看是否含有描述信息
  for file in $packagedir/disk* $packagedir/package_descriptions $packagedir/$shortname.txt $packagedir/$packagebase.txt ; do
    if grep "^$packagebase:" "$file" 1> /dev/null 2> /dev/null ; then
      DESCRIPTION="$file"
    elif grep "^$shortname:" "$file" 1> /dev/null 2> /dev/null ; then
      DESCRIPTION="$file"
    fi
  done
  # 未在软件包外找到描述文件, 则在软件包内查找. 需要解压缩
  if [ "$DESCRIPTION" = "/dev/null" ]; then
    mkdir -p $TMP/scan$$
    # 常用的方法, 在子 shell 中改变工作目录, 从而不影响整个脚本的工作目录
    ( cd $TMP/scan$$ ; $TAR xzf - install ) < $package 2> /dev/null
    if grep "^$packagebase:" "$TMP/scan$$/install/slack-desc" 1> /dev/null 2> /dev/null ; then
      DESCRIPTION="$TMP/scan$$/install/slack-desc"
    elif grep "^$shortname:" "$TMP/scan$$/install/slack-desc" 1> /dev/null 2> /dev/null ; then
      DESCRIPTION="$TMP/scan$$/install/slack-desc"
    fi
  fi

  # 包合法性检查
  if [ ! -f $package ]; then # 非普通文件
    EXITSTATUS=4
    if [ "$MODE" = "install" ]; then
      echo "Cannot install $package: package is not a regular file"
    fi
    continue;
  fi
  gzip -l $package 1> /dev/null 2> /dev/null
  if [ ! "$?" = "0" ]; then # 无法用 gzip -l 来操作软件包
    EXITSTATUS=2 # failed gzip -l
    if [ "$MODE" = "install" ]; then
      echo "Cannot install $package: package is corrupt (failed 'gzip -l $package')"
    fi
    continue;
  fi

  # 获取压缩前后的文件大小信息, 存入临时文件
  COMPRESSED=`gzip -l $package | grep -v uncompressed_name | crunch | cut -f 1 -d ' '`
  UNCOMPRESSED=`gzip -l $package | grep -v uncompressed_name | crunch | cut -f 2 -d ' '`
  COMPRESSED="`expr $COMPRESSED / 1024` K"
  UNCOMPRESSED="`expr $UNCOMPRESSED / 1024` K"
#  MD5SUM=`md5sum $package | cut -f 1 -d ' '`
  cat $DESCRIPTION | grep "^$packagebase:" | cut -f 2- -d : | cut -b2- 1> $TMP/tmpmsg$$ 2> /dev/null
  if [ "$shortname" != "$packagebase" ]; then
    cat $DESCRIPTION | grep "^$shortname:" | cut -f 2- -d : | cut -b2- 1>> $TMP/tmpmsg$$ 2> /dev/null
  fi
  # slackware 包的描述文件最高为 13 行, 少于 12 行时用空行补到 12 行(最后一行为文件尺寸信息)
  LENGTH=`cat $TMP/tmpmsg$$ | wc -l`
  while [ $LENGTH -lt 12 ]; do
    echo >> $TMP/tmpmsg$$
    LENGTH=`expr $LENGTH + 1`
  done
  # 第 13 行, 文件尺寸信息
  echo "Size: Compressed: $COMPRESSED, uncompressed: $UNCOMPRESSED." >> $TMP/tmpmsg$$
  # 根据英文的注释, 新版本的 dialog 需要在每一行的末尾多加一个 '\n',
  # 否则输出的内容会比较混乱, 但是似乎不是所有版本的 dialog 都这样,
  # 不过就算加上也不会有什么影响
  cat << EOF > $TMP/controlns$$
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
EOF
  # 将提取出来的软件描述和 dialog 需要的行尾 '\n' 连接起来
  # -d "" 表示两个文件的对应行连接的时候用 "" 来分隔, 其实就是中间不加入分隔
  paste -d "" $TMP/tmpmsg$$ $TMP/controlns$$ > $TMP/pasted$$
  rm -f $TMP/controlns$$
  mv $TMP/pasted$$ $TMP/tmpmsg$$
  # install 模式,不理会优先级的设定, 即使是 SKP 的软件也安装
  if [ "$MODE" = "install" ]; then
    if [ "$PMSG" = "" ]; then
      echo "Installing package $shortname... "
    else
      echo "Installing package $shortname ($PMSG)... "
    fi
    echo "PACKAGE DESCRIPTION:"
    cat $DESCRIPTION | grep "^$packagebase:" | uniq
    if [ "$shortname" != "$packagebase" ]; then
      cat $DESCRIPTION | grep "^$shortname:" | uniq
    fi
  # infobox 模式, 不安装 SKP 优先级的软件
  elif [ "$MODE" = "infobox" -a ! "$PRIORITY" = "SKP" ]; then
    dialog --title "Installing package ==>$shortname<== $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0
  # menu 模式下, 优先级为 ADD(required) 的包, 并且未使用 -ask 参数要求询问,
  # 则无需询问, 直接用 dialog 显示相关信息
  elif [ "$MODE" = "menu" -a "$PRIORITY" = "ADD" -a ! "$ALWAYSASK" = "yes" ]; then
    dialog --title "Installing package ==>$shortname<== $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0
  # menu 模式下, 用户指定优先级为 ADD(required) 的包, 不管包原先的优先级是什么
  # 都无需询问, 直接用 dialog 显相关信息
  elif [ "$MODE" = "menu" -a "$USERPRIORITY" = "ADD" ]; then
    dialog --title "Installing package ==>$shortname<== $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0
  # menu/infobox 模式下, 优先级为 SKP 的包, 且未使用 -ask 参数要求询问, 
  # 则直接删除临时文件, 不作其它处理
  elif [ "$MODE" = "menu" -a "$PRIORITY" = "SKP" -a ! "$ALWAYSASK" = "yes" ]; then
    rm -f $TMP/tmpmsg$$
    continue # 处理下一个软件包
  # infobox 模式, skip 优先级, 无 -ask, 则直接删除临时文件, 不作其它处理
  # 从下面这一条和上面这一条可以看出, -ask 并不像帮助中说的那样, 只对 warn 模式或
  # ADD 优先级起作用
  elif [ "$MODE" = "infobox" -a "$PRIORITY" = "SKP" -a ! "$ALWAYSASK" = "yes" ]; then
    rm -f $TMP/tmpmsg$$
    continue
  # 剩下的情况都是需要使用菜单来询问是否真的要安装
  else
    dialog --title "Package Name: ==>$shortname<== $PMSG" --menu "`cat $TMP/tmpmsg$$`" 0 0 3 \
    "Yes" "Install package $shortname" \
    "No" "Do not install package $shortname" \
    "Quit" "Abort software installation completely" 2> $TMP/reply$$
    # 当选择的是上面三个选项的时候, dialog 返回 0,
    # 而当直接选择"取消"按钮, 则返回非 0, 与选择 No 选项一样处理
    if [ ! $? = 0 ]; then
      echo "No" > $TMP/reply$$
    fi
    REPLY="`cat $TMP/reply$$`"
    rm -f $TMP/reply$$ $TMP/tmpmsg$$
    if [ "$REPLY" = "Quit" ]; then
      # 返回值 99: 用户选择 Quit, 退出 *所有的* 软件包的安装, 不光是当前这一个
      exit 99
    elif [ "$REPLY" = "No" ]; then
      continue # 用户选择不安装本软件包, 跳过
    fi
  fi

  # 得到将要安装的文件列表
  $TAR tzf $package 1> $TMP/tmplist$$ 2> /dev/null
  TARERROR=$?
  # 无法用 tar tzf 列出包的内容, tar 包不正确
  if [ ! "$TARERROR" = "0" ]; then
    EXITSTATUS=1
    if [ "$MODE" = "install" ]; then
      echo "Unable to install $package: tar archive is corrupt (tar returned error code $TARERROR)"
    fi
    rm -f $TMP/tmplist$$
    continue
  fi
  # 删除文件列表中的符号链接
  cat $TMP/tmplist$$ | grep -v "/$" | while read file ; do
    if [ -L "$ROOT/$file" ]; then
      rm -f "$ROOT/$file"
    fi
  done
  rm -f $TMP/tmplist$$

  # 记录将要安装的包的信息
  echo "PACKAGE NAME:     $shortname" > $ADM_DIR/packages/$shortname
  echo "COMPRESSED PACKAGE SIZE:     $COMPRESSED" >> $ADM_DIR/packages/$shortname
  echo "UNCOMPRESSED PACKAGE SIZE:     $UNCOMPRESSED" >> $ADM_DIR/packages/$shortname
  echo "PACKAGE LOCATION: $package" >> $ADM_DIR/packages/$shortname
#  echo "PACKAGE MD5SUM: $MD5SUM" >> $ADM_DIR/packages/$shortname
  echo "PACKAGE DESCRIPTION:" >> $ADM_DIR/packages/$shortname
  cat $DESCRIPTION | grep "^$packagebase:" >> $ADM_DIR/packages/$shortname 2> /dev/null
  if [ "$shortname" != "$packagebase" ]; then
    cat $DESCRIPTION | grep "^$shortname:" >> $ADM_DIR/packages/$shortname 2> /dev/null
  fi
  echo "FILE LIST:" >> $ADM_DIR/packages/$shortname
  # 此处使用了很多的 tar 参数, 其中关键的有:
  #   -U 在解压要重写的文件前先删除它们,避免了覆盖已存在文件时的问题
  #   -p 解包时保留文件的权限信息, 软件包中的文件的权限在安装的时候是应该保留的
  #   -v 显示出解包出来的文件列表, 用它来记录所安装的文件/目录
  # 此外还有一个 -l, 它在 gnu tar 1.13 版的时候表示 --one-file-system,
  # 而 gnu tar 1.14 之后就不再赞成使用 -l 表示 --one-file-system 了, 到了 gnu tar 1.15.91
  # 之后 -l 就表示 --check-links 了, 这大概也是 pkgtools 中非要用 tar 1.13 的原因吧.
  # 但是 gnu 的文档中说: "Used when creating an archive.", 是不是表示这里可以不要这个 -l?
  ( cd $ROOT/ ; $TAR -xzlUpvf - ) < $package >> $TMP/$shortname 2> /dev/null
  # 这个地方的 grep 是有问题的, slackware 期望要安装的文件列表里面有且只有一个
  # 行首为 "./" 的行, 而这里的 grep '^./' 查找的是行首为一个字符后面跟着 "/" 的行,
  # 所以当包内的文件中含有单个字母名称的目录时就会出现问题
  if [ "`cat $TMP/$shortname | grep '^./' | wc -l | tr -d ' '`" = "1" ]; then
    # Good.  We have a package that meets the Slackware spec.
    cat $TMP/$shortname >> $ADM_DIR/packages/$shortname
  else
    # 非 makepkg 制作的包的内容可能不符合 pkgtools 所需要的格式,则在记录中作出相应调整
    echo './' >> $ADM_DIR/packages/$shortname
    cat $TMP/$shortname | grep -v '^./$' | cut -b3- >> $ADM_DIR/packages/$shortname
  fi
  rm -f $TMP/$shortname

  # 如果 ldconfig 可以执行,则运行它,这个是 glibc 的某种机制,相应可参考 LFS 的相关手册
  if [ -x /sbin/ldconfig ]; then
    /sbin/ldconfig
  fi
  # 执行安装脚本
  # 此处执行安装脚本的时候传递了 -install 参数, 但是实际上脚本中未必会理会这个参数,
  # 而 removepkg/upgradepkg 中也没有用到 doinst.sh, 大概是为了以后的扩展吧.
  if [ -f $ROOT/install/doinst.sh ]; then
    if [ "$MODE" = "install" ]; then
      echo "Executing install script for $shortname..."
    fi
    ( cd $ROOT/ ; sh install/doinst.sh -install; )
  fi
  # 将 $ROOT/install 下的相关内容移动到包记录的目录下
  if [ -d $ROOT/install ]; then
    if [ -r $ROOT/install/doinst.sh ]; then
      cp $ROOT/install/doinst.sh $ADM_DIR/scripts/$shortname
      chmod 755 $ADM_DIR/scripts/$shortname
    fi
    # 删除 /install/doinst.sh /install/slack-*,而如果 /install 下存在其它文件,不删除,
    # 这说明 slackware 的包管理系统只占用了这样的几个文件名
    ( cd $ROOT/install ; rm -f doinst.sh slack-* 1> /dev/null 2>&1 )
    rmdir $ROOT/install 1> /dev/null 2>&1
  fi
  # 删除临时文件
  if [ -d "$TMP/scan$$" ]; then
    rm -rf "$TMP/scan$$"
  fi
  rm -f $TMP/tmpmsg$$ $TMP/reply$$
  # install 模式下安装完成后再输出一个空行,作为提示和分隔
  if [ "$MODE" = "install" ]; then
    echo
  fi
done

exit $EXITSTATUS

分类:

评论

  预览后可提交