deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

以上のフォーラム以外の開発者の話題

モデレータ: YoN, nyu

thinkpadnerd
記事: 2035
登録日時: 15/10/25(日) 14:00

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by thinkpadnerd »

”タロ兵衛さんが書きました:
> 私の場合、sfsインストール後ではなく、sfs化する直前にsfs化用ディレクトリの./usr/share/applicationsにある.desktop を書き換えてしまってます。

それが正解だと思います。sfs をロードすると、*.desktop ファイルの内容が .jwmrc に反映されて jwm が再起動され、メニューが更新されます。

deb パッケージそのままでいいのなら自動変換が使えますが、修正が必要な場合は、結局手作業になります。とはいえ、元になるファイルが整えば、後は mksquashfs コマンドを実行するだけですが。
ThinkPad X121e (Precise 571 / Tahr 605 / Tahr64 606 / Xenial 7.5 / Bionic32)
CPU AMD C-50 1.0GHz, RAM 4GB
ThinkPad L440 (Xenial 7.5 / Bionic64 / Fossa64)
CPU intel Celeron 2950M 2.0GHz, RAM 4GB
アバター
ルビー
記事: 1240
登録日時: 14/11/24(月) 12:12

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ルビー »

deb2sfs.shの、sfs変換の動作とはあまり関係無い事ですが、不思議な事を見つけました。

SeaMonkey、Pale Moonで、「コード: 全て選択」でコピペして、スクリプトファイルを作成すると、行頭に半角スペース4文字が入ってしまいます。
理由は不明です。
Firefoxもそうだった様な記憶があるけれど、最近、使ってないので分かりません。

行頭の不要な空白文字を残したままでも、スクリプトの動作に問題はないはずだけど、一応、取り除いています。
Geany の置換で、正規表現を使って行頭の不要な空白を削除するのですが、deb2sfs.sh でやると、行頭の空白4文字だけでなく、インデント分の空白も削除されてしまいます。
つまり、正規表現を使わない、空白4文字だけでの置換になってしまいます。

使った正規表現は、
^xxxx  x=半角スペース
分かりにくいですが、連続の半角スペースは一つにまとめられてしまうので。

初版がインデント空白3文字だったので気が付きませんでしたが、インデント空白4文字になった修正版で分かりました。
理由が分かるまで、しばらく悩みましたが。

現象が起こるのが、571JPのGeany(0.19.1)だけで、Tahrpup(1.25)、Xenialpup(1.29)のGeanyでは正規表現の置換が効きます。
571JPのGeanyのバージョンが古いからと言えば、それまでなのですが、面白いのが、Windowsで常用している VxEditor でも、置換で正規表現が効かずに同じ現象になる事です。

面倒なので、行頭空白は残したままでいいやと思いましたが、deb2sfs.sh では、不要な行頭空白を削除しないと、オプション -h でエラーが出て、ヘルプメッセージが表示されませんでした。
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

少し機能を追加しました。マイナーのバージョンアップです。 :D

コード: 全て選択

#!/usr/bin/env sh

## DebianパッケージをSquashFSに変換する
#
# @author ssnys
# @version 0.2.0
# @date 2018-06-28
# @licence https://creativecommons.org/licenses/by/4.0/
# @see http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372

deb2sfs () (
    # shの設定
    set -eu

    # gettextの設定
    TEXTDOMAIN='deb2sfs.sh'; export TEXTDOMAIN

    # プログラム名
    name='deb2sfs.sh'
    # バージョン
    version='0.2.0'
    # 最終更新日
    update='2018-06-28'

    # ヘルプメッセージ
    helpMessage=`cat <<EOF
\`gettext 'Usage'\`: ${0} [\`gettext 'options'\`] \`gettext 'dest'\` [\`gettext 'package1'\`] [\`gettext 'package2'\`] \`gettext '...'\`

\`gettext 'Convert Debian package to SquashFS.'\`

\`gettext 'Options'\`:
  -c, --control                           \`gettext 'build Debian package control file'\`
  -n, --no-sfs                            \`gettext 'do not make SquashFS but directory'\`
  -o name[:value], --option=name[:value]  \`gettext 'option of mksquashfs command'\`
  -h, --help                              \`gettext 'display this help and exit'\`
  -v, --verbose                           \`gettext 'enable verbose output'\`
  -V, --version                           \`gettext 'output version information and exit'\`

\`gettext 'Support packages'\`: Debian package, Directory, SquashFS

\`gettext 'Example'\`:
  ${0} output.sfs *.deb
  ${0} -cv output.sfs *.deb
  ${0} -o b:1048576 -o comp:xz -o Xdict-size:100% output.sfs *.deb
  ${0} output.sfs 'http://www.example.com/example.deb'
\`gettext 'See'\`: <http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
EOF`
    # バージョンメッセージ
    versionMessage=`cat <<EOF
${name} version ${version} (update ${update})

\`gettext 'Licence'\`: CC-BY <https://creativecommons.org/licenses/by/4.0/>
\`gettext 'Using'\`: dpkg-deb, gettext, mksquashfs, sh, wget\`gettext ', etc.'\`
EOF`

    # 変数を固定
    readonly name version update helpMessage versionMessage

    ## 終了時に一時ディレクトリを削除する
    endcall () {
        [ -n "${verboseFlag-}" ] && translate '# Remove temporary directory and temporary file #'
        rm -fr ${verboseFlag-} "${tmpDirectory}" "${tmpFile}"
    }

    ## エラーメッセージを出力する
    errorMessage () {
        (
            message="${2}"
            shift 2

            translate "%s: ${message}" "${0}" "${@}" 1>&2
            translate "Try \`%s --help' for more information" "${0}" 1>&2
        )

        # EX_USAGE (/usr/include/sysexits.h)
        exit "${1}"
    }

    ## クォートをネストする
    quote () {
        echo "'`echo \"${1}\" | sed -re \"s/([']+)/'\\\"\1\\\"'/g\"`'"
    }

    ## メッセージを翻訳する
    translate () (
        message="${1}"
        shift
        printf "`gettext \"${message}\"`\n" "${@}"
    )

    # 引数の解析
    while [ 0 -lt "${#}" ]; do
        case "${1}" in
            # Debianパッケージのcontrol
            '-c' | '--control')
                controlFlag=1
                shift
                ;;
            # ヘルプメッセージ
            '-h' | '--help')
                echo "${helpMessage}"
                return 0
                ;;
            # SquashFSを作成しない
            '-n' | '--no-sfs')
                nosfsFlag=1
                shift
                ;;
            # mksquashfsのオプション
            '-o' | '--option')
                # オプションに引数がなかった場合
                [ "${#}" -lt 2 ] && {
                    errorMessage 64 "option '%s' requires an argument" "${1}"
                }

                options="${options-} "`quote "-${2}" | sed -e "s/:/' '/"`
                shift 2
                ;;
            # 冗長な表示
            '-v' | '--verbose')
                verboseFlag='--verbose'
                shift
                ;;
            # バージョンメッセージ
            '-V' | '--version')
                echo "${versionMessage}"
                return 0
                ;;
            # 以降オプション以外の引数
            '-' | '--')
                shift

                while [ 0 -lt "${#}" ]; do
                    parameters="${parameters-} "`quote "${1}"`
                    shift
                done
                ;;
            # ロングオプション
            --[!-]*=*)
                option="${1}"
                shift
                set -- "`echo \"${option}\" | cut -d '=' -f 1`" "`echo \"${option}\" | cut -d '=' -f 2-`" "${@}"
                ;;
            # 複合ショートオプション
            -[!-][!-]*)
                option="${1}"
                shift
                set -- "-`echo \"${option}\" | cut -c 2`" "-`echo \"${option}\" | cut -c 3-`" "${@}"
                ;;
            # 無効なオプション
            -*)
                errorMessage 64 "unrecognized option '%s'" "${1}"
                ;;
            # オプション以外の引数
            *)
                parameters="${parameters-} "`quote "${1}"`
                shift

                # POSIX標準ならば以降オプション以外の引数
                [ "`printenv 'POSIXLY_CORRECT' > /dev/null; echo \"${?}\"`" -eq 0 -o "`printenv 'ADSH_CMD_ARGORDER'`" = 'seq' ] && {
                    set -- '--' "${@}"
                }
                ;;
        esac
    done

    # 変数を固定
    readonly controlFlag nosfsFlag options parameters verboseFlag

    # オプション以外の引数の存在を確認
    [ -z "${parameters-}" ] && {
        errorMessage 64 'missing arguments'
    }

    # 位置パラメータを再セット
    eval "set ${parameters-}"

    # 先頭の位置パラメータから宛先を取り出す
    dest="${1}"
    shift

    # 一時ディレクトリを作成
    [ -n "${verboseFlag-}" ] && translate '# Make temporary directory and temporary file #'
    tmpDirectory=`mktemp -d`
    tmpFile=`mktemp`

    # 終了時に関数を呼び出す
    trap "endcall" EXIT
    trap "endcall; exit 129" 1
    trap "endcall; exit 130" 2
    trap "endcall; exit 131" 3
    trap "endcall; exit 143" 15

    # 変数を固定
    readonly dest tmpDirectory tmpFile

    # ${controlFlag}が有効ならば/etc/init.dを作成
    [ "${controlFlag-0}" -eq 1 ] && {
        initd="${tmpDirectory}/etc/init.d"
        [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' '/etc/init.d'
        mkdir -p ${verboseFlag-} "${initd}"
    }

    # 各パッケージを処理
    for package; do
        # URLならばタウンロード
        `echo "${package}" | grep -qE '^https?://|^ftps?://'` && {
            [ -n "${verboseFlag-}" ] && translate '# Download %s #' "${package}"
            wget -nv ${verboseFlag-} -O "${tmpFile}" "${package}"
            package="${tmpFile}"
        }

        # パッケージの種類によって処理を分岐
        case `file -bL "${package}"` in
            # Debianパッケージ
            'Debian binary package'*)
                # Debianパッケージからファイルを取り出す
                [ -n "${verboseFlag-}" ] && translate '# Extract files in %s #' "${package}"
                dpkg-deb ${verboseFlag-} -x "${package}" "${tmpDirectory}"

                # ${controlFlag}による処理の分岐
                [ "${controlFlag-0}" -eq 1 ] && {
                    debname=`dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}' "${package}"`

                    control="${tmpDirectory}/var/run/deb2sfs.sh/${debname}"

                    [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' "/var/run/deb2sfs.sh/${debname}"
                    mkdir -p ${verboseFlag-} "${control}"

                    # Debianパッケージからコントロールファイルを取り出す
                    [ -n "${verboseFlag-}" ] && translate '# Extract control files in %s #' "${package}"
                    dpkg-deb ${verboseFlag-} -e "${package}" "${control}"

                    # サービス起動スクリプトを作成
                    cat > "${initd}/${debname}.sh" <<EOF
#!/usr/bin/env sh

set -eu

control="/var/run/deb2sfs.sh/${debname}"
readonly control

case "\${1}" in
    'start')
        [ -x "\${control}/preinst" ] && "\${control}/preinst" 'install'
        [ -x "\${control}/postinst" ] && "\${control}/postinst" 'configure' "\`grep -m 1 '^Version:' \"\${control}/control\" | sed -e 's/^Version:[ ]*//'\`"
    ;;
    'stop')
        [ -x "\${control}/prerm" ] && "\${control}/prerm" 'remove'
        [ -x "\${control}/postrm" ] && "\${control}/postrm" 'remove'
    ;;
esac
EOF
                    [ -n "${verboseFlag-}" ] && translate '# Chenge mod %s #' "/etc/init.d/${debname}.sh"
                    chmod ${verboseFlag-} 755 "${initd}/${debname}.sh"
                }
                ;;
            # ディレクトリ
            'directory')
                [ -n "${verboseFlag-}" ] && translate '# Copy files in %s #' "${package}"
                cp -afT ${verboseFlag-} "${package}" "${tmpDirectory}"
                ;;
            # SquashFS
            'Squashfs filesystem'*)
                if [ -z "${verboseFlag-}" ]; then
                    unsquashfs -d "${tmpDirectory}" -f "${package}" > /dev/null
                else
                    translate '# Extract files in %s #' "${package}"
                    unsquashfs -d "${tmpDirectory}" -f "${package}"
                fi
                ;;
            # それ以外
            *)
                errorMessage 65 '%s is an unsupported file format' "${package}"
                ;;
        esac
    done

    # 一部ディレクトリの内容を上位ディレクトリに移動
    for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
        [ -d "${tmpDirectory}${path}" ] && {
            [ -n "${verboseFlag-}" ] && translate '# Move %s #' "${path}"
            cp -afT ${verboseFlag-} "${tmpDirectory}${path}" "${tmpDirectory}${path}/.."
            rm -fr ${verboseFlag-} "${tmpDirectory}${path}"
        }
    done

    # ${nosfsFlag}による処理の分岐
    if [ "${nosfsFlag-0}" -eq 1 ]; then
        # 一時ディレクトリの内容をコピー
        [ -n "${verboseFlag-}" ] && translate '# Copy temporary directory to %s #' "${dest}"
        cp -afT ${verboseFlag-} "${tmpDirectory}" "${dest}"
    else
        # SquashFSの作成
        [ -n "${verboseFlag-}" ] && translate '# Make %s #' "${dest}"
        eval "mksquashfs \"${tmpDirectory}\" \"${dest}\" ${options-}"
    fi
)

# メインの関数を呼び出す
deb2sfs "${@}"
以下の点を変更致しました。
  • ディレクトリやSquashFSに対応
  • 内部関数errorMessageで終了ステータスを指定可能に
  • gettextに渡すエラーメッセージを修正
今回のバージョンアップでDebianパッケージだけではなく、引数にディレクトリやSquashFSファイルも指定できるようになりました。これは既に作成されているSquashFSファイルの一部を変更する際などに有用です。例えばa.deb, b.deb, c.debを纏めたa-b-c.sfsが存在し、a.debのバージョンアップがあった場合、それを反映させるためには以下のようなコマンドで対応できるようになります。

コード: 全て選択

deb2sfs.sh new-a-b-c.sfs a-b-c.sfs a.deb
ルビー さんが書きました:
ssnys さんが書きました: slimjetのDebianパッケージを解凍しても問題ない十分なディスクスペースがあると仮定して、以下のようなコマンドで解凍できませんか?
VirtualBoxの571JPで、slimjet_i386.deb(59MB)を dpkg-deb -x で解凍可能な状態でも、sfs変換に失敗しました。
pupsaveの中(/root/~)、外(/mnt/home/~/)で試しましたが、同じ結果です。
ちなみに、pupsave 256MB では、空き不足になって解凍不可でした。

VirtualBoxのXenialpupでも、slimjet_i386.debのsfs変換を試してみましたが失敗。
そこで、R40(RAM256MB)の571JPで試してみると、同様のエラーメッセージで失敗しました。
VirtualBoxのRAMは 896MB なのですが、もしかして、これでは足りないのかもしれません。
変換に成功した901XのRAMは 1GB です。

901Xの571JPでは、pupsave(256MB)の空き不足で失敗するだろうと思って試したのですけど、あっさり成功しました。
SDHCカードにインストールしてあるpupsaveの書き込みは、ハードディスクにインストールした場合と違うので、その為だと思いますが、RAMは 1GB あるので成功したのかな。

大きなサイズのdebパッケージのsfs変換では、大きめのRAMがないと無理かもしれない。
`dpkg-deb -x`で解凍は可能なようですが、変換できないというのは摩訶不思議ですね。当方もまだ調査できていないため、何とも言えませんが、やはり空き容量不足なのでしょうか。
thinkpadnerd さんが書きました: deb パッケージのサイズ + deb を展開したファイルのサイズ + sfs のサイズ + α
が必要だと思いますが、展開の途中でスペースがなくなるというのはどういうことでしょうね。私は puppy がインストールされているパーティションが NTFS なので、別に ext2 のパーティションを用意して作業しています。
thinkpadnerdさんの仰る通り変換に最低でも「deb パッケージのサイズ + deb を展開したファイルのサイズ + sfs のサイズ 」の容量が必要です。パッケージから展開されたファイルなどは、スクリプト終了時に削除されるまで、`mktemp`コマンドで作成した一時ディレクトリ($TMPDIRが設定されていれば$TMPDIRが使用され、 設定されていなければ /tmp内に作成)に置かれます。作成した一時ディレクトリの場所のファイルシステムが何であるかによるのですが、tmpfsの場合、パッケージをメモリー上に展開するため、十分なメモリー容量が必要かと思われます。もしかしたらここら辺が関係しているのかもしれません。 :|
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

機能もほとんど変わらない、小幅な修正を行いました。 :D

コード: 全て選択

#!/usr/bin/env sh

## DebianパッケージをSquashFSに変換する
#
# @author ssnys
# @version 0.2.1
# @date 2018-07-07
# @licence https://creativecommons.org/licenses/by/4.0/
# @see http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372

deb2sfs () (
    # shの設定
    set -eu

    # gettextの設定
    TEXTDOMAIN='deb2sfs.sh'; export TEXTDOMAIN

    # プログラム名
    name='deb2sfs.sh'
    # バージョン
    version='0.2.1'
    # 最終更新日
    update='2018-07-07'

    # ヘルプメッセージ
    helpMessage=`cat <<EOF
\`gettext 'Usage'\`: ${0} [\`gettext 'options'\`] \`gettext 'dest'\` [\`gettext 'package1'\`] [\`gettext 'package2'\`] \`gettext '...'\`

\`gettext 'Convert Debian package to SquashFS.'\`

\`gettext 'Options'\`:
  -c, --control                           \`gettext 'build Debian package control file'\`
  -n, --no-sfs                            \`gettext 'do not make SquashFS but directory'\`
  -o name[:value], --option=name[:value]  \`gettext 'option of mksquashfs command'\`
  -h, --help                              \`gettext 'display this help and exit'\`
  -v, --verbose                           \`gettext 'enable verbose output'\`
  -V, --version                           \`gettext 'output version information and exit'\`
  -, --                                   \`gettext 'stop option parsing'\`

\`gettext 'Support packages'\`: Debian package, Directory, SquashFS

\`gettext 'Example'\`:
  ${0} output.sfs *.deb
  ${0} -cv output.sfs *.deb
  ${0} -o b:1048576 -o comp:xz -o Xdict-size:100% output.sfs *.deb
  ${0} output.sfs 'http://www.example.com/example_1.0.0_i386.deb'
\`gettext 'See'\`: <http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
EOF`
    # バージョンメッセージ
    versionMessage=`cat <<EOF
${name} version ${version} (update ${update})

\`gettext 'Licence'\`: CC-BY <https://creativecommons.org/licenses/by/4.0/>
\`gettext 'Using'\`: dpkg-deb, gettext, mksquashfs, sh, wget\`gettext ', etc.'\`
EOF`

    # 変数を固定
    readonly name version update helpMessage versionMessage

    ## 終了時に一時ディレクトリを削除する
    endcall () {
        [ -n "${verboseFlag-}" ] && translate '# Remove temporary directory and temporary file #'
        rm -fr ${verboseFlag-} "${tmpDirectory}" "${tmpFile}"
    }

    ## エラーメッセージを出力して終了する
    errorMessage () {
        (
            message="${2}"
            shift 2

            translate "%s: ${message}" "${0}" ${@+"$@"} 1>&2
            translate "Try \`%s --help' for more information" "${0}" 1>&2
        )

        exit "${1}"
    }

    ## クォートをネストする
    quote () {
        echo "'`echo \"${1}\" | sed -re \"s/([']+)/'\\\"\1\\\"'/g\"`'"
    }

    ## メッセージを翻訳する
    translate () (
        message="${1}"
        shift
        printf "`gettext \"${message}\"`\n" ${@+"$@"}
    )

    # 引数の解析
    while [ 0 -lt "${#}" ]; do
        case "${1}" in
            # Debianパッケージのcontrol
            '-c' | '--control')
                controlFlag=1
                shift
                ;;
            # ヘルプメッセージ
            '-h' | '--help')
                echo "${helpMessage}"
                return 0
                ;;
            # SquashFSを作成しない
            '-n' | '--no-sfs')
                nosfsFlag=1
                shift
                ;;
            # mksquashfsのオプション
            '-o' | '--option')
                # オプションに引数がなかった場合
                [ "${#}" -lt 2 ] && {
                    errorMessage 64 "option '%s' requires an argument" "${1}"
                }

                options="${options-} "`quote "-${2}" | sed -e "s/:/' '/"`
                shift 2
                ;;
            # 冗長な表示
            '-v' | '--verbose')
                verboseFlag='--verbose'
                shift
                ;;
            # バージョンメッセージ
            '-V' | '--version')
                echo "${versionMessage}"
                return 0
                ;;
            # 以降オプション以外の引数
            '-' | '--')
                shift

                while [ 0 -lt "${#}" ]; do
                    parameters="${parameters-} "`quote "${1}"`
                    shift
                done
                ;;
            # ロングオプション
            --[!-]*=*)
                option="${1}"
                shift
                set -- "`echo \"${option}\" | cut -d '=' -f 1`" "`echo \"${option}\" | cut -d '=' -f 2-`" ${@+"$@"}
                ;;
            # 複合ショートオプション
            -[!-][!-]*)
                option="${1}"
                shift
                set -- "-`echo \"${option}\" | cut -c 2`" "-`echo \"${option}\" | cut -c 3-`" ${@+"$@"}
                ;;
            # 無効なオプション
            -*)
                errorMessage 64 "unrecognized option '%s'" "${1}"
                ;;
            # オプション以外の引数
            *)
                parameters="${parameters-} "`quote "${1}"`
                shift

                # POSIX標準ならば以降オプション以外の引数
                [ "`printenv 'POSIXLY_CORRECT' > /dev/null; echo \"${?}\"`" -eq 0 -o "`printenv 'ADSH_CMD_ARGORDER'`" = 'seq' ] && {
                    set -- '--' ${@+"$@"}
                }
                ;;
        esac
    done

    # 変数を固定
    readonly controlFlag nosfsFlag options parameters verboseFlag

    # オプション以外の引数の存在を確認
    [ -z "${parameters-}" ] && {
        errorMessage 64 'missing arguments'
    }

    # 位置パラメータを再セット
    eval "set ${parameters-}"

    # 先頭の位置パラメータから宛先を取り出す
    dest="${1}"
    shift

    # 一時ディレクトリを作成
    [ -n "${verboseFlag-}" ] && translate '# Make temporary directory and temporary file #'
    tmpDirectory=`mktemp -d`
    tmpFile=`mktemp`
    initd="${tmpDirectory}/etc/init.d"

    # 変数を固定
    readonly dest tmpDirectory tmpFile initd

    # 終了時に関数を呼び出す
    trap 'endcall' 0 # EXIT
    trap 'endcall; exit 129' 1 # SIGHUP
    trap 'endcall; exit 130' 2 # SIGINT
    trap 'endcall; exit 131' 3 # SIGQUIT
    trap 'endcall; exit 143' 15 # SIGTERM

    # 各パッケージを処理
    for package; do
        # URLならばタウンロード
        `echo "${package}" | grep -qE '^https?://|^ftps?://'` && {
            [ -n "${verboseFlag-}" ] && translate '# Download %s #' "${package}"
            wget -nv ${verboseFlag-} -O "${tmpFile}" "${package}"
            package="${tmpFile}"
        }

        # パッケージの種類によって処理を分岐
        case `file -bL "${package}"` in
            # Debianパッケージ
            'Debian binary package'*)
                # Debianパッケージからファイルを取り出す
                [ -n "${verboseFlag-}" ] && translate '# Extract files in %s #' "${package}"
                dpkg-deb ${verboseFlag-} -x "${package}" "${tmpDirectory}"

                # ${controlFlag}による処理の分岐
                [ "${controlFlag-0}" -eq 1 ] && {
                    debname=`dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}.deb' "${package}"`
                    control="${tmpDirectory}/var/run/deb2sfs.sh/${debname}"

                    [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' "/var/run/deb2sfs.sh/${debname}"
                    mkdir -p ${verboseFlag-} "${control}"

                    # Debianパッケージからコントロールファイルを取り出す
                    [ -n "${verboseFlag-}" ] && translate '# Extract control files in %s #' "${package}"
                    dpkg-deb ${verboseFlag-} -e "${package}" "${control}"

                    # スクリプトの有無により処理を分岐
                    [ -x "${control}/preinst" -o -x "${control}/postinst" -o -x "${control}/prerm" -o -x "${control}/postrm" ] && {
                        # /etc/init.dを作成
                        [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' '/etc/init.d'
                        mkdir -p ${verboseFlag-} "${initd}"

                        # サービス起動スクリプトを作成
                        cat > "${initd}/${debname}.sh" <<EOF
#!/usr/bin/env sh

## Startup ${debname} maintainer scripts
#
# @date `date '+%Y-%m-%d'`
# @note Automatically generated by ${name} version ${version} (update ${update})

set -eu

control="/var/run/deb2sfs.sh/${debname}"
readonly control

case "\${1}" in
    'start')
        [ -x "\${control}/preinst" ] && "\${control}/preinst" 'install'
        [ -x "\${control}/postinst" ] && "\${control}/postinst" 'configure' "\`grep -m 1 '^Version:' \"\${control}/control\" | sed -re 's/^Version:[[:blank:]]*([^[:blank:]]+)[[:blank:]]*$/\1/'\`"
        ;;
    'stop')
        [ -x "\${control}/prerm" ] && "\${control}/prerm" 'remove'
        [ -x "\${control}/postrm" ] && "\${control}/postrm" 'remove'
        ;;
    'restart')
		shift
        "\${0}" 'stop' \${@+"\$@"}
        "\${0}" 'start' \${@+"\$@"}
        ;;
    *)
        echo "Usage: \${0} {start|stop|restart}" 1>&2

        # EX_USAGE (/usr/include/sysexits.h)
        exit 64
        ;;
esac
EOF
                        [ -n "${verboseFlag-}" ] && translate '# Chenge mod %s #' "/etc/init.d/${debname}.sh"
                        chmod ${verboseFlag-} 755 "${initd}/${debname}.sh"
                    }
                }
                ;;
            # ディレクトリ
            'directory')
                [ -n "${verboseFlag-}" ] && translate '# Copy files in %s #' "${package}"
                cp -afT ${verboseFlag-} "${package}" "${tmpDirectory}"
                ;;
            # SquashFS
            'Squashfs filesystem'*)
                if [ -z "${verboseFlag-}" ]; then
                    unsquashfs -d "${tmpDirectory}" -f "${package}" > /dev/null
                else
                    translate '# Extract files in %s #' "${package}"
                    unsquashfs -d "${tmpDirectory}" -f "${package}"
                fi
                ;;
            # それ以外
            *)
                errorMessage 65 '%s is an unsupported file format' "${package}"
                ;;
        esac
    done

    # 一部ディレクトリの内容を上位ディレクトリに移動
    for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
        [ -d "${tmpDirectory}${path}" ] && {
            [ -n "${verboseFlag-}" ] && translate '# Move %s #' "${path}"
            cp -afT ${verboseFlag-} "${tmpDirectory}${path}" "${tmpDirectory}${path}/.."
            rm -fr ${verboseFlag-} "${tmpDirectory}${path}"
        }
    done

    # ${nosfsFlag}による処理の分岐
    if [ "${nosfsFlag-0}" -eq 1 ]; then
        # 一時ディレクトリの内容をコピー
        [ -n "${verboseFlag-}" ] && translate '# Copy temporary directory to %s #' "${dest}"
        cp -afT ${verboseFlag-} "${tmpDirectory}" "${dest}"
    else
        # SquashFSの作成
        [ -n "${verboseFlag-}" ] && translate '# Make %s #' "${dest}"
        eval "mksquashfs \"${tmpDirectory}\" \"${dest}\" ${options-}"
    fi
)

# メインの関数を呼び出す
deb2sfs ${@+"$@"}
以前のバージョンからの変更点は以下の様になります。
  • ヘルプメッセージを修正しました
  • 様々なシェルに対応できるよう、"${@}"を${@+"$@"}に変更しました
  • preinst, postinst, prerm, postrmの何れも存在しない場合、サービス起動スクリプトを作成しないようになりました
  • サービス起動スクリプトの内容をより詳細にしました
最近Slackwareのパッケージがシンプルで使いやすく有用であることに気づいたため、今後Slackwareのパッケージに対応するかもしれません。
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

大幅なバージョンアップを行いました。

コード: 全て選択

#!/usr/bin/env sh

## パッケージをSquashFSに変換する
#
# @author ssnys
# @version 1.0.0
# @date 2018-07-23
# @licence https://creativecommons.org/licenses/by/4.0/
# @see http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372

package2sfs () (
    # shの設定
    set -eu

    # gettextの設定
    TEXTDOMAIN='package2sfs.sh'; export TEXTDOMAIN

    # プログラム名
    name='package2sfs.sh'
    # バージョン
    version='1.0.0'
    # 最終更新日
    update='2018-07-23'

    # ヘルプメッセージ
    helpMessage=`cat <<EOF
\`gettext 'Usage'\`: ${0} [\`gettext 'options'\`] \`gettext 'dest'\` [\`gettext 'package1'\`] [\`gettext 'package2'\`] \`gettext '...'\`

\`gettext 'Convert package to SquashFS.'\`

\`gettext 'Options'\`:
  -c, --control                           \`gettext 'build package control file'\`
  -n, --no-sfs                            \`gettext 'do not make SquashFS but directory'\`
  -o name[:value], --option=name[:value]  \`gettext 'option of mksquashfs command'\`
  -h, --help                              \`gettext 'display this help and exit'\`
  -v, --verbose                           \`gettext 'enable verbose output'\`
  -V, --version                           \`gettext 'output version information and exit'\`
  -, --                                   \`gettext 'stop option parsing'\`

\`gettext 'Support packages'\`: Debian package, Directory, Image file(ext2, ext3, ext4), Puppy's Extra Treats, SquashFS, tar.gz, tar.xz

\`gettext 'Example'\`:
  ${0} output.sfs *.deb
  ${0} -cv output.sfs *.deb
  ${0} -o b:1048576 -o comp:xz -o Xdict-size:100% output.sfs *.deb
  ${0} output.sfs 'http://www.example.com/example_1.0.0_i386.deb'
\`gettext 'See'\`: <http://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
EOF`
    # バージョンメッセージ
    versionMessage=`cat <<EOF
${name} version ${version} (update ${update})

\`gettext 'Licence'\`: CC-BY <https://creativecommons.org/licenses/by/4.0/>
\`gettext 'Using'\`: dpkg-deb, gettext, mksquashfs, pet2tgz, tar, unsquashfs, wget\`gettext ', etc.'\`
EOF`

    # 変数を固定
    readonly name version update helpMessage versionMessage

    ## Debianパッケージを展開する
    deb2dir () {
        # Debianパッケージからファイルを取り出す
        [ -n "${verboseFlag-}" ] && translate '# Extract files in %s #' "${1}"
        dpkg-deb ${verboseFlag-} -x "${1}" "${2}"

        # ${controlFlag}による処理の分岐
        [ "${controlFlag-0}" -eq 1 ] && {
            filename=`dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}.deb' "${1}"`
            control="${2}/var/run/${name}/${filename}"

            [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' "/var/run/${name}/${filename}"
            mkdir -p ${verboseFlag-} "${control}"

            # Debianパッケージからコントロールファイルを取り出す
            [ -n "${verboseFlag-}" ] && translate '# Extract control files in %s #' "${1}"
            dpkg-deb ${verboseFlag-} -e "${1}" "${control}"

            # スクリプトの有無により処理を分岐
            [ -x "${control}/preinst" -o -x "${control}/postinst" -o -x "${control}/prerm" -o -x "${control}/postrm" ] && startupScript "${2}" "${filename}" "`cat <<'EOF'
    'start')
        [ -x \"${control}/preinst\" ] && \"${control}/preinst\" 'install'
        [ -x \"${control}/postinst\" ] && \"${control}/postinst\" 'configure' \"\`grep -m 1 '^Version:' \\"${control}/control\\" | sed -re 's/^Version:[[:blank:]]*([^[:blank:]]+)[[:blank:]]*$/\1/'\`\"
        ;;
    'stop')
        [ -x \"${control}/prerm\" ] && \"${control}/prerm\" 'remove'
        [ -x \"${control}/postrm\" ] && \"${control}/postrm\" 'remove'
        ;;
EOF`"
        }

        return 0
    }

    ## 終了時に一時ディレクトリを削除する
    endcall () {
        [ -n "${verboseFlag-}" ] && translate '# Remove temporary directory and temporary file #'
        rm -fr ${verboseFlag-} "${tmpDirectory}"
    }

    ## エラーメッセージを出力して終了する
    errorMessage () {
        (
            message="${2}"
            shift 2

            translate "%s: ${message}" "${0}" ${@+"$@"} 1>&2
            translate "Try \`%s --help' for more information" "${0}" 1>&2
        )

        exit "${1}"
    }

    ## Puppy's Extra Treatsパッケージを展開する
    pet2dir () {
        bname=`basename "${package}" '.pet'`
        tmpPet=`mktemp -d`
        cp -af ${verboseFlag-} "${1}" "${tmpPet}/package.pet"
        pet2tgz "${tmpPet}/package.pet"

        # 圧縮ファイルからファイルを取り出す
        [ -n "${verboseFlag-}" ] && translate '# Extract files in %s #' "${1}"
        tar -C "${2}" -f "${tmpPet}/package.tar.gz" -xz ${verboseFlag-}

        [ -d "${2}/${bname}" ] && {
            cp -afT "${2}/${bname}" "${2}"
            rm -fr ${verboseFlag-} "${2}/${bname}"
        }

        rm -fr ${verboseFlag-} "${tmpPet}"

        # ${controlFlag}による処理の分岐
        [ -x "${2}/pinstall.sh" -o -x "${2}/puninstall.sh" -o -f "${2}/pet.specs" ] && {
            [ "${controlFlag-0}" -eq 1 ] && {
                filename=`basename "${package}"`

                # スクリプトの有無により処理を分岐
                [ -x "${2}/pinstall.sh" -o -x "${2}/puninstall.sh" ] && startupScript "${2}" "${filename}" "`cat <<'EOF'
    'start')
        [ -x \"${control}/pinstall.sh\" ] && \"${control}/pinstall.sh\"
        ;;
    'stop')
        [ -x \"${control}/puninstall.sh\" ] && \"${control}/puninstall.sh\"
        ;;
EOF`"

                [ -n "${verboseFlag-}" ] && translate '# Move %s files #' '/pinstall.sh, /puninstall.sh, /pet.specs'
                mkdir -p ${verboseFlag-} "${2}/var/run/${name}/${filename}"

                cp -af ${verboseFlag-}\
                    `[ -x "${2}/pinstall.sh" ] && echo "${2}/pinstall.sh"`\
                    `[ -x "${2}/puninstall.sh" ] && echo "${2}/puninstall.sh"`\
                    `[ -f "${2}/pet.specs" ] && echo "${2}/pet.specs"`\
                    "${2}/var/run/${name}/${filename}"
            }

            rm -fr ${verboseFlag-} "${2}/pinstall.sh" "${2}/puinstall.sh" "${2}/pet.specs"
        }

        return 0
    }

    ## Slackwareパッケージを展開する
    pkg2dir () {
        `file -bL "${1}" | grep -Eq '^xz compressed data'` && tarOption='-J'

        # 圧縮ファイルからファイルを取り出す
        [ -n "${verboseFlag-}" ] && translate '# Extract files in %s #' "${1}"
        tar -C "${2}" -f "${1}" -x "${tarOption--z}" ${verboseFlag-}

        # ${controlFlag}による処理の分岐
        [ -d "${2}/install" ] && {
            [ "${controlFlag-0}" -eq 1 ] && {
                filename=`basename "${1}"`

                # スクリプトの有無により処理を分岐
                [ -f "${2}/install/doinst.sh" ] && {
                    chmod ${verboseFlag-} 755 "${2}/install/doinst.sh"

                    startupScript "${2}" "${filename}" "`cat <<'EOF'
    'start')
        [ -x \"${control}/doinst.sh\" ] && \"${control}/doinst.sh\"
        ;;
    'stop')
        ;;
EOF`"
                }

                [ -n "${verboseFlag-}" ] && translate '# Move %s directory #' '/install'
                mkdir -p ${verboseFlag-} "${2}/var/run/${name}/${filename}"
                cp -afT ${verboseFlag-} "${2}/install" "${2}/var/run/${name}/${filename}"
            }

            rm -fr ${verboseFlag-} "${2}/install"
        }

        return 0
    }

    ## クォートをネストする
    quote () (
        echo "'`echo \"${1}\" | sed -re \"s/([']+)/'\\\"\1\\\"'/g\"`'"
    )

    ## サービス起動スクリプトを作成する
    startupScript () {
        # /etc/init.dを作成
        [ -n "${verboseFlag-}" ] && translate '# Make %s directory #' '/etc/init.d'
        mkdir -p ${verboseFlag-} "${1}/etc/init.d"

        # サービス起動スクリプトを作成
        cat > "${1}/etc/init.d/${2}.sh" <<EOF
#!/usr/bin/env sh

## Startup ${2} maintainer scripts
#
# @date `date '+%Y-%m-%d'`
# @note Automatically generated by ${name} version ${version} (update ${update})

set -eu

control="/var/run/${name}/${2}"
readonly control

cd /

case "\${1}" in
${3}
    'restart')
        shift
        "\${0}" 'stop' \${@+"\$@"}
        "\${0}" 'start' \${@+"\$@"}
        ;;
    *)
        echo "Usage: \${0} {start|stop|restart}" 1>&2

        # EX_USAGE (/usr/include/sysexits.h)
        exit 64
        ;;
esac
EOF

        # パーミッションを変更し実行可能に
        [ -n "${verboseFlag-}" ] && translate '# Chenge mod %s #' "/etc/init.d/${2}.sh"
        chmod ${verboseFlag-} 755 "${1}/etc/init.d/${2}.sh"
    }

    ## メッセージを翻訳する
    translate () (
        message="${1}"
        shift
        printf "`gettext \"${message}\"`\n" ${@+"$@"}
    )

    # 引数の解析
    while [ 0 -lt "${#}" ]; do
        case "${1}" in
            # パッケージのcontrol
            '-c' | '--control')
                controlFlag=1
                shift
                ;;
            # ヘルプメッセージ
            '-h' | '--help')
                echo "${helpMessage}"
                return 0
                ;;
            # SquashFSを作成しない
            '-n' | '--no-sfs')
                nosfsFlag=1
                shift
                ;;
            # mksquashfsのオプション
            '-o' | '--option')
                # オプションに引数がなかった場合
                [ "${#}" -lt 2 ] && {
                    errorMessage 64 "option '%s' requires an argument" "${1}"
                }

                options="${options-} "`quote "-${2}" | sed -e "s/:/' '/"`
                shift 2
                ;;
            # 冗長な表示
            '-v' | '--verbose')
                verboseFlag='--verbose'
                shift
                ;;
            # バージョンメッセージ
            '-V' | '--version')
                echo "${versionMessage}"
                return 0
                ;;
            # 以降オプション以外の引数
            '-' | '--')
                shift

                while [ 0 -lt "${#}" ]; do
                    parameters="${parameters-} "`quote "${1}"`
                    shift
                done
                ;;
            # ロングオプション
            --[!-]*=*)
                option="${1}"
                shift
                set -- "`echo \"${option}\" | cut -d '=' -f 1`" "`echo \"${option}\" | cut -d '=' -f 2-`" ${@+"$@"}
                ;;
            # 複合ショートオプション
            -[!-][!-]*)
                option="${1}"
                shift
                set -- "-`echo \"${option}\" | cut -c 2`" "-`echo \"${option}\" | cut -c 3-`" ${@+"$@"}
                ;;
            # 無効なオプション
            -*)
                errorMessage 64 "unrecognized option '%s'" "${1}"
                ;;
            # オプション以外の引数
            *)
                parameters="${parameters-} "`quote "${1}"`
                shift

                # POSIX標準ならば以降オプション以外の引数
                [ "`printenv 'POSIXLY_CORRECT' > /dev/null; echo \"${?}\"`" -eq 0 -o "`printenv 'ADSH_CMD_ARGORDER'`" = 'seq' ] && {
                    set -- '--' ${@+"$@"}
                }
                ;;
        esac
    done

    # 変数を固定
    readonly controlFlag nosfsFlag options parameters verboseFlag

    # オプション以外の引数の存在を確認
    [ -z "${parameters-}" ] && {
        errorMessage 64 'missing arguments'
    }

    # 位置パラメータを再セット
    eval "set ${parameters-}"

    # 先頭の位置パラメータから宛先を取り出す
    dest="${1}"
    shift

    # 一時ディレクトリを作成
    [ -n "${verboseFlag-}" ] && translate '# Make temporary directory and temporary file #'
    tmpDirectory=`mktemp -d`
    tmpContent="${tmpDirectory}/content"
    tmpDownload="${tmpDirectory}/download"
    tmpPackage="${tmpDirectory}/package"
    mkdir -p ${verboseFlag-} "${tmpContent}" "${tmpPackage}"

    # 変数を固定
    readonly dest tmpDirectory tmpContent tmpDownload tmpPackage

    # 終了時に関数を呼び出す
    trap 'endcall' 0 # EXIT
    trap 'exit 129' 1 # SIGHUP
    trap 'exit 130' 2 # SIGINT
    trap 'exit 131' 3 # SIGQUIT
    trap 'exit 143' 15 # SIGTERM

    # 各パッケージを処理
    for package; do
        # URLならばタウンロード
        `echo "${package}" | grep -qE '^https?://|^ftps?://'` && {
            filename=`basename "${package}"`

            [ -n "${verboseFlag-}" ] && translate '# Download %s #' "${package}"
            wget -nv -O "${tmpDownload}/${filename}" ${verboseFlag-} "${package}"

            package="${tmpDownload}/${filename}"
        }

        # パッケージの種類によって処理を分岐
        case `file -bL "${package}"` in
            # Debianパッケージ
            'Debian binary package'*)
                deb2dir "${package}" "${tmpContent}"
                ;;
            # ディレクトリ
            'directory')
                [ -n "${verboseFlag-}" ] && translate '# Copy files in %s #' "${package}"
                cp -afT ${verboseFlag-} "${package}" "${tmpContent}"
                ;;
            # Slackware GZIP or Puppy's Extra Treats
            'gzip compressed data'*)
                if `echo "${package}" | grep -qE '\.pet$'`; then
                    pet2dir "${package}" "${tmpPackage}"
                else
                    pkg2dir "${package}" "${tmpPackage}"
                fi
                ;;
            # Image file (ext2, ext3, ext4)
            'Linux rev 1.0 ext'[2-4]' filesystem data'*)
                loopDevice=`losetup -f`

                [ -n "${verboseFlag-}" ] && translate '# Mount %s #' "${package}"
                losetup -r "${loopDevice}" "${package}"
                mount -o 'defaults' -r ${verboseFlag:+-v} "${loopDevice}" "${tmpPackage}"

                [ -n "${verboseFlag-}" ] && translate '# Copy files in %s #' "${package}"
                cp -afT ${verboseFlag-} "${tmpPackage}" "${tmpContent}"

                [ -n "${verboseFlag-}" ] && translate '# Unmount %s #' "${package}"
                umount -d ${verboseFlag:+-v} "${tmpPackage}"
                ;;
            # SquashFS
            'Squashfs filesystem'*)
                if [ -z "${verboseFlag-}" ]; then
                    unsquashfs -d "${tmpContent}" -f "${package}" > /dev/null
                else
                    translate '# Extract files in %s #' "${package}"
                    unsquashfs -d "${tmpContent}" -f "${package}"
                fi
                ;;
            # Slackware XZ
            'xz compressed data'*)
                pkg2dir "${package}" "${tmpPackage}"
                ;;
            # それ以外
            *)
                errorMessage 65 '%s is an unsupported file format' "${package}"
                ;;
        esac

        # 解凍したファイルをメインのディレクトリに移動
        cp -afT ${verboseFlag-} "${tmpPackage}" "${tmpContent}"
        rm -fr ${verboseFlag-} "${tmpPackage}"/* "${tmpPackage}"/.[!.]* "${tmpPackage}"/..?*
    done

    # 一部ディレクトリの内容を上位ディレクトリに移動
    for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
        [ -d "${tmpContent}${path}" ] && {
            [ -n "${verboseFlag-}" ] && translate '# Move %s #' "${path}"
            cp -afT ${verboseFlag-} "${tmpContent}${path}" "${tmpContent}${path}/.."
            rm -fr ${verboseFlag-} "${tmpContent}${path}"
        }
    done

    # ${nosfsFlag}による処理の分岐
    if [ "${nosfsFlag-0}" -eq 1 ]; then
        # 一時ディレクトリの内容をコピー
        [ -n "${verboseFlag-}" ] && translate '# Copy temporary directory to %s #' "${dest}"
        cp -afT ${verboseFlag-} "${tmpContent}" "${dest}"
    else
        # SquashFSの作成
        [ -n "${verboseFlag-}" ] && translate '# Make %s #' "${dest}"
        eval "mksquashfs \"${tmpContent}\" \"${dest}\" ${options-}"
    fi
)

# メインの関数を呼び出す
package2sfs ${@+"$@"}
以前のバージョンからの変更点は以下の様になります。
  • Slackwareのパッケージ(tgz, txz)に対応しました
  • PET(Puppy's Extra Treats)パッケージに対応しました
  • イメージファイル(ext2, ext3, ext4)に対応しました
  • 名前をdeb2sfs.shからpackage2sfsに変更しました
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
tanaka
記事: 4
登録日時: 20/11/30(月) 22:35

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by tanaka »

これって例えば、20個くらいのdebファイルをフォルダに入れてそのフォルダを指定してsfsを作成することは可能でしょうか?

例 ./deb2sfs aaaa.sfs /mnt/sdc1
thinkpadnerd
記事: 2035
登録日時: 15/10/25(日) 14:00

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by thinkpadnerd »

tanaka さんが書きました: 20/12/01(火) 16:27 20個くらいのdebファイルをフォルダに入れてそのフォルダを指定してsfsを作成することは可能でしょうか?
3個の deb ファイルで試したところ、sfs が作成されました。20個は試したことがありませんが、可能だと思います。生成された sfs がちゃんと機能するかどうかは、実際に動かしてみないと分かりません。

追記
フォルダを指定するとうまくいきません。フォルダに移動して実行する必要があります。例えば、/mnt/sda5/work に deb ファイルがある場合
# cd /mnt/sda5/work
# /root/my-applications/package2sfs example.sfs *.deb

(この場合 package2sfs は /root/my-applications にあります。)
ThinkPad X121e (Precise 571 / Tahr 605 / Tahr64 606 / Xenial 7.5 / Bionic32)
CPU AMD C-50 1.0GHz, RAM 4GB
ThinkPad L440 (Xenial 7.5 / Bionic64 / Fossa64)
CPU intel Celeron 2950M 2.0GHz, RAM 4GB
tanaka
記事: 4
登録日時: 20/11/30(月) 22:35

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by tanaka »

返信ありがとうございます!とりあえず実際に試してみようと思います。分かりやすく教えてくださりありがとうございました!
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

最近また Puppy Linux の使用を再開したのですが、過去に作成したスクリプトがあまりに拙かったのでリファクタリングを行いました。

コード: 全て選択

#!/usr/bin/env sh

### Script: package2sfs
##
## パッケージを SquashFS に変換する。
##
## Metadata:
##
##   id - 38559dfa-cda3-4325-a702-d29aabbb6f85
##   author - <ssnys at https://sakurapup.com/forum1/memberlist.php?mode=viewprofile&u=4849>
##   version - 2.0.2
##   date - 2022-10-28
##   since - 2018-06-22
##   copyright - Copyright (C) 2018-2022 ssnys. Some rights reserved.
##   license - <CC-BY at https://creativecommons.org/licenses/by/4.0/>
##
## See Also:
##
##   * <Project homepage at https://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
##
## Help Output:
##
## ------ Text ------
## Usage:
##   package2sfs [OPTION]... DEST [PACKAGE]...
##
## Options:
##   -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
##                               build package control file
##   -d,     --{no-}directory    do not make SquashFS but directory
##   -o,     --option NAME[=VALUE] 
##                               option of mksquashfs command
##           --no-option         reset -o, --option
##   -v,     --{no-}verbose      enable verbose output
##   -h,     --help              display this help and exit
##   -V,     --version           output version information and exit
##
## Exit Status:
##     0 - successful termination
##    64 - command line usage error
##    65 - data format error
##    66 - cannot open input
##    67 - addressee unknown
##    68 - host name unknown
##    69 - service unavailable
##    70 - internal software error
##    71 - system error (e.g., can't fork)
##    72 - critical OS file missing
##    73 - can't create (user) output file
##    74 - input/output error
##    75 - temp failure; user is invited to retry
##    76 - remote error in protocol
##    77 - permission denied
##    78 - configuration error
##   129 - received SIGHUP
##   130 - received SIGINT
##   131 - received SIGQUIT
##   143 - received SIGTERM
##
## Examples:
##   package2sfs output.sfs *.deb
##   package2sfs -cv output.sfs *.pet
##   package2sfs -c2,3 output.sfs package1.tgz package2.tgz package3.tgz
##   package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
##   package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
## ------------------

readonly 'NAME=package2sfs'
readonly 'VERSION=2.0.2'
readonly 'UPDATE=2022-10-28'

set -efu
umask '0022'
LC_ALL='C'
IFS=$(printf ' \t\n_'); IFS="${IFS%_}"
PATH="${PATH-}${PATH:+:}$(command -p getconf 'PATH')"
UNIX_STD='2003' # HP-UX POSIX mode
XPG_SUS_ENV='ON' # AIX POSIX mode
XPG_UNIX98='OFF' # AIX UNIX 03 mode
POSIXLY_CORRECT='1' # GNU Coreutils POSIX mode
COMMAND_MODE='unix2003' # macOS UNIX 03 mode
export 'IFS' 'LC_ALL' 'PATH' 'UNIX_STD' 'XPG_SUS_ENV' 'XPG_UNIX98' 'POSIXLY_CORRECT' 'COMMAND_MODE'

readonly 'EX_OK=0'           # successful termination
readonly 'EX__BASE=64'       # base value for error messages

readonly 'EX_USAGE=64'       # command line usage error
readonly 'EX_DATAERR=65'     # data format error
readonly 'EX_NOINPUT=66'     # cannot open input
readonly 'EX_NOUSER=67'      # addressee unknown
readonly 'EX_NOHOST=68'      # host name unknown
readonly 'EX_UNAVAILABLE=69' # service unavailable
readonly 'EX_SOFTWARE=70'    # internal software error
readonly 'EX_OSERR=71'       # system error (e.g., can't fork)
readonly 'EX_OSFILE=72'      # critical OS file missing
readonly 'EX_CANTCREAT=73'   # can't create (user) output file
readonly 'EX_IOERR=74'       # input/output error
readonly 'EX_TEMPFAIL=75'    # temp failure; user is invited to retry
readonly 'EX_PROTOCOL=76'    # remote error in protocol
readonly 'EX_NOPERM=77'      # permission denied
readonly 'EX_CONFIG=78'      # configuration error

readonly 'EX__MAX=78'        # maximum listed value

trap 'case "${?}" in 0) end_call;; *) end_call "${EX_SOFTWARE}";; esac' 0 # EXIT
trap 'end_call 129' 1  # SIGHUP
trap 'end_call 130' 2  # SIGINT
trap 'end_call 131' 3  # SIGQUIT
trap 'end_call 143' 15 # SIGTERM

### Function: end_call
##
## 一時ディレクトリを削除しスクリプトを終了する。
##
## Parameters:
##
##   $1 - 終了ステータス。
##
## Returns:
##
##   $1 の終了ステータス。

end_call() {
    trap '' 0 # EXIT
    rm -fr -- ${tmpDir:+"${tmpDir}"}
    exit "${1:-0}"
}

### Function: option_error
##
## エラーメッセージを出力する。
##
## Parameters:
##
##   $1 - エラーメッセージ。
##
## Returns:
##
##   終了コード64。

option_error() {
    printf '%s: %s\n' "${0##*/}" "${1}" >&2
    printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

    end_call "${EX_USAGE}"
}

### Function: append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $@ - 追加する要素。

append_array_posix() {
    while [ 2 -le "${#}" ]; do
        __append_array_posix "${1}" "${2}"

        eval "shift 2; set -- '${1}'" '${@+"${@}"}'
    done
}

### Function: __append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 追加する要素。

__append_array_posix() {
    set "${1}" "${2-}" ''

    until [ "${2#*\'}" '=' "${2}" ]; do
        set -- "${1}" "${2#*\'}" "${3}${2%%\'*}'\"'\"'"
    done

    eval "${1}=\"\${${1}-}\${${1}:+ }'\${3}\${2}'\""
}

### Function: regex_match
##
## 文字列が正規表現に一致するか検査する。
##
## Parameters:
##
##   $1 - 検査する文字列。
##   $@ - 正規表現。
##
## Returns:
##
##   0か1の真理値。

regex_match() {
    awk -- '
        BEGIN {
            for(i = 2; i < ARGC; i++) {
                if(ARGV[1] !~ ARGV[i]) {
                    exit 1
                }
            }

            exit
        }
    ' ${@+"${@}"} || return 1

    return 0
}

### Function: add_option
##
## オプションを追加する
##
## Parameters:
##
##   $1 - Debian パッケージ
##   $2 - オプションの名前と値

add_option() {
    case "${2}" in
        *'='*) append_array_posix "${1}" "-${2%%=*}" "${2#*=}";;
        *) append_array_posix "${1}" "-${2}";;
    esac
}

### Function: deb2dir
##
## Debian パッケージを展開する
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 展開先のディレクトリ

deb2dir() (
    package="${1}"
    dir="${2}"

    # Debian パッケージからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf '# Extract files in %s #\n' "${package}";;
    esac

    dpkg-deb --extract ${prog_v+-v} -- "${package}" "${dir}"

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}.deb' -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # Debian パッケージからコントロールファイルを取り出す
        case "${verboseFlag}" in
            [!0]*) printf "# Extract control files in '%s' #\\n" "${package}";;
        esac

        dpkg-deb --control ${prog_v+-v} -- "${package}" "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -x "${dir}${controlDir}/preinst" ] || [ -x "${dir}${controlDir}/postinst" ] || [ -x "${dir}${controlDir}/prerm" ] || [ -x "${dir}${controlDir}/postrm" ]; then
            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/preinst' ]; then
            '${controlDir}/preinst' 'install'
        fi

        if [ -x '${controlDir}/postinst' ]; then
            '${controlDir}/postinst' 'configure' '$(dpkg-deb -W --showformat='${Version}' -- "${package}")'
        fi
        ;;
    'stop')
        if [ -x '${controlDir}/prerm' ]; then
            '${controlDir}/prerm' 'remove'
        fi

        if [ -x '${controlDir}/postrm' ]; then
            '${controlDir}/postrm' 'remove'
        fi
        ;;
__EOF__
        fi
    esac
)

### Function: pet2dir
##
## Puppy's Extra Treats パッケージを展開する
##
## Parameters:
##
##   $1 - PET パッケージ
##   $2 - 展開先のディレクトリ

pet2dir() (
    package="${1}"
    dir="${2}"

    basename=$(basename -- "${package}" '.pet')
    tmpPet=$(mktemp -d)

    cp -f ${prog_v+-v} -- "${package}" "${tmpPet}/package.pet"

    if pet2tgz "${tmpPet}/package.pet"; then :; else
        rm -fr -- "${tmpPet}"

        printf "%s: '%s' is broken.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        return 1
    fi

    # Puppy's Extra Treats パッケージからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf "# Extract files in '%s' #\\n" "${package}";;
    esac

    tar -xz -C "${tmpPet}" -f "${tmpPet}/package.tar.gz" ${prog_v+-v}

    if [ -d "${tmpPet}/${basename}" ]; then
        find -- "${tmpPet}/${basename}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}" ';'
    fi

    rm -fr ${prog_v+-v} -- "${tmpPet}"

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(basename -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -x "${dir}/pinstall.sh" ] || [ -x "${dir}/puninstall.sh" ]; then
            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/pinstall.sh' ]; then
            '${controlDir}/pinstall.sh'
        fi
        ;;
    'stop')
        if [ -x '${controlDir}/puninstall.sh' ]; then
            '${controlDir}/puninstall.sh'
        fi
        ;;
__EOF__
        fi

        for controlFile in 'pinstall.sh' 'puninstall.sh' 'pet.specs'; do
            if [ -f "${dir}/${controlFile}" ]; then
                case "${verboseFlag}" in
                    [!0]*) printf "# Move '%s' files #\\n" "${dir}/${controlFile}";;
                esac

                cp -f ${prog_v+-v} -- "${dir}/${controlFile}" "${dir}${controlDir}"
            fi
        done
    esac

    rm -fr ${prog_v+-v} -- "${dir}/pinstall.sh" "${dir}/puinstall.sh" "${dir}/pet.specs"
)

### Function: pkg2dir
##
## Slackware パッケージを展開する
##
## Parameters:
##
##   $1 - Slackware パッケージ
##   $2 - 展開先のディレクトリ

pkg2dir() (
    package="${1}"
    dir="${2}"

    # 圧縮ファイルからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf '# Extract files in %s #\n' "${package}";;
    esac

    case "${package}" in
        *'.tgz') tar -xz -C "${dir}" -f "${package}" ${prog_v+-v};;
        *'.txz') tar -xJ -C "${dir}" -f "${package}" ${prog_v+-v};;
    esac

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(basename -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -f "${dir}/install/doinst.sh" ]; then
            chmod ${prog_v+-v} -- '755' "${dir}/install/doinst.sh"

            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/doinst.sh' ]; then
            '${controlDir}/doinst.sh'
        fi
        ;;
    'stop') ;;
__EOF__
        fi

        case "${verboseFlag}" in
            [!0]*) printf "# Move '%s' directory #\\n" "${dir}/install";;
        esac

        if [ -d "${dir}/install" ]; then
            find -- "${dir}/install/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}${controlDir}" ';'
        fi
    esac

    rm -fr ${prog_v+-v} -- "${dir}/install"
)

### Function: startup_script
##
## サービス起動スクリプトを作成する
##
## Parameters:
##
##   $1 - 作成先のファイルパス

startup_script() (
    startup="${1}"
    basename=$(basename -- "${startup}" '.sh')
    basedir=$(dirname -- "${startup}")

    shift

    case "${verboseFlag}" in
        [!0]*) printf "# Make '%s' directory #\\n" "${basedir}";;
    esac

    mkdir -p ${prog_v+-v} -- "${basedir}"

    # サービス起動スクリプトを作成
    case "${verboseFlag}" in
        [!0]*) printf "# Create service startup script in '%s' #\\n" "${startup}";;
    esac

    # サービス起動スクリプトを作成
    cat >"${startup}" <<__EOF__
#!/bin/sh

## Startup ${basename} maintainer scripts
#
# @date $(date -u -- '+%Y-%m-%d %H:%M:%S UTC')
# @note Automatically generated by ${NAME} version ${VERSION} (update ${UPDATE})

set -efu

cd -- '/'

case "\${1}" in
$(cat)
    'restart')
        shift
        "\${0}" 'stop' \${@+"\${@}"}
        "\${0}" 'start' \${@+"\${@}"}
        ;;
    *)
        printf 'Usage: %s {start|stop|restart}\\n' "\${0}" >&2

        # EX_USAGE (/usr/include/sysexits.h)
        exit '${EX_USAGE}'
        ;;
esac
__EOF__

    # パーミッションを変更し実行が可能に
    case "${verboseFlag}" in
        [!0]*) printf "# Chenge mod '%s' #\\n" "${startup}";;
    esac

    chmod ${prog_v+-v} -- '755' "${startup}"
)

# @getoptions
parser_definition() {
    setup REST abbr:true error:option_error plus:true no:0 help:usage \
        -- 'Usage:' "  ${2##*/} [OPTION]... DEST [PACKAGE]..." \
        '' 'Options:'

    option control     -c --{no-}control   init:@no on:'*' no: validate:'regex_match "${OPTARG}" "^\\*?$|^[1-9][0-9]*(,[1-9][0-9]*)*$"' var:'PACKAGE_NUMBER_LIST' -- 'build package control file'
    flag   dirFlag     -d --{no-}directory init:@no -- 'do not make SquashFS but directory'
    param  :'add_option "options" "${OPTARG}"' -o --option validate:'regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"' var:'NAME[=VALUE]' -- 'option of mksquashfs command'
    flag   options     --no-option         init:@no on: no: -- 'reset -o, --option'
    flag   verboseFlag -v --{no-}verbose   init:@no counter:1 -- 'enable verbose output'
    disp   :usage      -h --help           -- 'display this help and exit'
    disp   :'echo "${NAME} ${VERSION}"'    -V --version -- 'output version information and exit'

    msg -- '' 'Exit Status:' \
        '    0 - successful termination' \
        '   64 - command line usage error' \
        '   65 - data format error' \
        '   66 - cannot open input' \
        '   67 - addressee unknown' \
        '   68 - host name unknown' \
        '   69 - service unavailable' \
        '   70 - internal software error' \
        "   71 - system error (e.g., can't fork)" \
        '   72 - critical OS file missing' \
        "   73 - can't create (user) output file" \
        '   74 - input/output error' \
        '   75 - temp failure; user is invited to retry' \
        '   76 - remote error in protocol' \
        '   77 - permission denied' \
        '   78 - configuration error' \
        '  129 - received SIGHUP' \
        '  130 - received SIGINT' \
        '  131 - received SIGQUIT' \
        '  143 - received SIGTERM'

    msg -- '' 'Examples:' \
        "  ${2##*/} output.sfs *.deb" \
        "  ${2##*/} -cv output.sfs *.pet" \
        "  ${2##*/} -c2,3 output.sfs package1.tgz package2.tgz package3.tgz" \
        "  ${2##*/} -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir" \
        "  ${2##*/} output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'"
}
# @end

# @gengetoptions parser -i parser_definition parse "${1}"
# Generated by getoptions (BEGIN)
# URL: https://github.com/ko1nksm/getoptions (v3.3.0)
control=''
dirFlag='0'
options=''
verboseFlag='0'
REST=''
parse() {
  OPTIND=$(($#+1))
  while OPTARG= && [ $# -gt 0 ]; do
    set -- "${1%%\=*}" "${1#*\=}" "$@"
    while [ ${#1} -gt 2 ]; do
      case $1 in (*[!a-zA-Z0-9_-]*) break; esac
      case '--control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --control"
      esac
      case '--no-control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-control"
      esac
      case '--directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --directory"
      esac
      case '--no-directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-directory"
      esac
      case '--option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --option"
      esac
      case '--no-option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-option"
      esac
      case '--verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --verbose"
      esac
      case '--no-verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-verbose"
      esac
      case '--help' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --help"
      esac
      case '--version' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --version"
      esac
      break
    done
    case ${OPTARG# } in
      *\ *)
        eval "set -- $OPTARG $1 $OPTARG"
        OPTIND=$((($#+1)/2)) OPTARG=$1; shift
        while [ $# -gt "$OPTIND" ]; do OPTARG="$OPTARG, $1"; shift; done
        set "Ambiguous option: $1 (could be $OPTARG)" ambiguous "$@"
        option_error "$@" >&2 || exit $?
        echo "$1" >&2
        exit 1 ;;
      ?*)
        [ "$2" = "$3" ] || OPTARG="$OPTARG=$2"
        shift 3; eval 'set -- "${OPTARG# }"' ${1+'"$@"'}; OPTARG= ;;
      *) shift 2
    esac
    case $1 in
      --?*=*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
        ;;
      --no-*|--without-*) unset OPTARG ;;
      -[co]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
        ;;
      -[dvhV]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
        OPTARG= ;;
      +*) unset OPTARG ;;
    esac
    case $1 in
      '-c'|'--control'|'--no-control')
        set -- "$1" "$@"
        [ ${OPTARG+x} ] && {
          case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
          [ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='*'
        } || OPTARG=''
        regex_match "${OPTARG}" "^\*?$|^[1-9][0-9]*(,[1-9][0-9]*)*$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^\*?$|^[1-9][0-9]*(,[1-9][0-9]*)*$"; break; }
        control="$OPTARG"
        shift ;;
      '-d'|'--directory'|'--no-directory')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG='0'
        dirFlag="$OPTARG"
        ;;
      '-o'|'--option')
        [ $# -le 1 ] && set "required" "$1" && break
        OPTARG=$2
        regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"; break; }
        add_option "options" "${OPTARG}"
        shift ;;
      '--no-option')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='' || OPTARG=''
        options="$OPTARG"
        ;;
      '-v'|'--verbose'|'--no-verbose')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
        verboseFlag="$((${verboseFlag:-0}+$OPTARG))"
        ;;
      '-h'|'--help')
        usage
        exit 0 ;;
      '-V'|'--version')
        echo "${NAME} ${VERSION}"
        exit 0 ;;
      --)
        shift
        while [ $# -gt 0 ]; do
          REST="${REST} \"\${$(($OPTIND-$#))}\""
          shift
        done
        break ;;
      [-+]?*) set "unknown" "$1"; break ;;
      *)
        REST="${REST} \"\${$(($OPTIND-$#))}\""
    esac
    shift
  done
  [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
  case $1 in
    unknown) set "Unrecognized option: $2" "$@" ;;
    noarg) set "Does not allow an argument: $2" "$@" ;;
    required) set "Requires an argument: $2" "$@" ;;
    pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
    notcmd) set "Not a command: $2" "$@" ;;
    *) set "Validation error ($1): $2" "$@"
  esac
  option_error "$@" >&2 || exit $?
  echo "$1" >&2
  exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage:
  package2sfs [OPTION]... DEST [PACKAGE]...

Options:
  -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
                              build package control file
  -d,     --{no-}directory    do not make SquashFS but directory
  -o,     --option NAME[=VALUE] 
                              option of mksquashfs command
          --no-option         reset -o, --option
  -v,     --{no-}verbose      enable verbose output
  -h,     --help              display this help and exit
  -V,     --version           output version information and exit

Exit Status:
    0 - successful termination
   64 - command line usage error
   65 - data format error
   66 - cannot open input
   67 - addressee unknown
   68 - host name unknown
   69 - service unavailable
   70 - internal software error
   71 - system error (e.g., can't fork)
   72 - critical OS file missing
   73 - can't create (user) output file
   74 - input/output error
   75 - temp failure; user is invited to retry
   76 - remote error in protocol
   77 - permission denied
   78 - configuration error
  129 - received SIGHUP
  130 - received SIGINT
  131 - received SIGQUIT
  143 - received SIGTERM

Examples:
  package2sfs output.sfs *.deb
  package2sfs -cv output.sfs *.pet
  package2sfs -c2,3 output.sfs package1.tgz package2.tgz package3.tgz
  package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
  package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
GETOPTIONSHERE
}
# Generated by getoptions (END)
# @end

parse ${@+"${@}"}
eval "set -- ${REST}"

# コマンドライン引数が不足している
case "${#}" in '0')
    printf '%s: Requires an argument: DEST\n' "${0##*/}" >&2
    printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

    end_call "${EX_USAGE}"
    ;;
esac

# コマンド毎に詳細表示を行うか
if [ '2' -le "${verboseFlag}" ]; then
    prog_v='1'
fi

# 先頭の位置パラメータから宛先を取り出す
dest="${1}"
shift

# 一時ディレクトリを作成
case "${verboseFlag}" in
    [!0]*) printf '# Make temporary directory and temporary file #\n';;
esac

tmpDir=$(mktemp -d)
tmpContent="${tmpDir}/content"
tmpDownload="${tmpDir}/download"
tmpPackage="${tmpDir}/package"

mkdir -p ${prog_v+-v} -- "${tmpContent}" "${tmpDownload}" "${tmpPackage}"

# 各パッケージを処理
for package in ${@+"${@}"}; do
    index=$((${index:-0} + 1))

    # パッケージの固有の設定ファイルを展開するか
    case "${control}" in
        '*' | "${index}" | *",${index}" | "${index},"* | *",${index},"*) controlFlag='1';;
        *) controlFlag='0';;
    esac

    # URL ならばタウンロード
    case "${package}" in
        'http://'*/* | 'https://'*/* | 'ftp://'*/*)
            filename=$(basename -- "${package}")

            case "${verboseFlag}" in
                [!0]*) printf "# Download '%s' #\\n" "${package}";;
            esac

            if command -v 'wget' >'/dev/null'; then
                wget --no-verbose --output-document "${tmpDownload}/${filename}" ${prog_v+-v} -- "${package}"
            elif command -v 'curl' >'/dev/null'; then
                curl --compressed --fail --location --output "${tmpDownload}/${filename}" --silent --show-error ${prog_v+-v} -- "${package}"
            else
                printf "'wget' or 'curl' is required.\\n" >&2
                printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

                end_call "${EX_UNAVAILABLE}"
            fi

            package="${tmpDownload}/${filename}"
            ;;
    esac

    # 読み込みの許可がない
    if [ '!' -r "${package}" ]; then
        printf "%s: No permission to read '%s'.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        end_call "${EX_NOINPUT}"
    # ディレクトリ
    elif [ -d "${package}" ]; then
        case "${verboseFlag}" in
            [!0]*) printf "# Copy files in '%s' #\\n" "${package}";;
        esac

        find -- "${package}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

        continue
    elif [ '!' -f "${package}" ]; then
        printf "%s: '%s' is not a regular file.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        end_call "${EX_DATAERR}"
    fi

    # パッケージファイルの種類によって処理を分岐
    case "${package}" in
        # Debian パッケージ
        *[!./]'.deb') deb2dir "${package}" "${tmpContent}";;
        # Puppy's Extra Treats
        *[!./]'.pet') pet2dir "${package}" "${tmpPackage}";;
        # Slackware GZIP or Slackware XZ
        *[!./]'.tgz' | '.txz') pkg2dir "${package}" "${tmpPackage}";;
        # イメージファイル (ext2, ext3, ext4) と SquashFS ファイル
        *[!./].[234]fs | *[!./]'.sfs')
            case "${verboseFlag}" in
                [!0]*) printf "# Mount '%s' #\\n" "${package}";;
            esac

            case "${package}" in
                *[!./].[234]fs) mount -o 'loop' -r -t 'ext2,ext3,ext4' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
                *[!./]'.sfs') mount -r -t 'squashfs' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
            esac

            case "${verboseFlag}" in
                [!0]*) printf "# Copy the files in '%s' #\\n" "${package}";;
            esac

            find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

            case "${verboseFlag}" in
                [!0]*) printf "# Unmount '%s' #\\n" "${package}";;
            esac

            umount -d ${prog_v+-v} -- "${tmpPackage}"
            ;;
        # 互換性のないファイル
        *)
            printf "%s: '%s' is not a compatible file.\\n" "${0##*/}" "${package}" >&2
            printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

            end_call "${EX_DATAERR}"
            ;;
    esac

    # 解凍したファイルをメインのディレクトリに移動
    find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'
    find -- "${tmpPackage}/" -path '*[!/]' -prune -exec rm -fr ${prog_v+-v} -- '{}' '+'
done

# 一部ディレクトリの内容を上位ディレクトリに移動
for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
    if [ -d "${tmpContent}${path}" ]; then
        case "${verboseFlag}" in
            [!0]*) printf "# Move '%s' #\\n" "${path}";;
        esac

        find -- "${tmpContent}${path}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}${path}/.." ';'
        rm -fr ${prog_v+-v} -- "${tmpContent}${path}"
    fi
done

# ${dirFlag} による処理の分岐
case "${dirFlag}" in
    '1')
        # 一時ディレクトリの内容を ${dest} にコピー
        case "${verboseFlag}" in
            [!0]*) printf "# Copy temporary directory to '%s' #\\n" "${dest}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dest}"
        find -- "${tmpContent}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dest}" ';'
        ;;
    *)
        # SquashFS ファイルを作成
        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' #\\n" "${dest}";;
        esac

        eval 'mksquashfs "${tmpContent}" "${dest}"' "${options}"
        ;;
esac
リファクタリングなので、目新しい機能になどは特にありませんが、一部のオプション名の変更などがあります。主な詳しい変更点は以下です。
  • -c, --control で任意のパッケージのみコントロールファイルを展開することが可能になりました。例えば -c2,3 --control=2,3 で2個目と3個めパッケージのみコントロールファイルを展開します。
  • -n, --no-sfs は -d, --directory に置き換わりました。
  • -v で冗長な表示、-v -v でさらに冗長な表示を行うように変更されました。
  • ファイルの識別を内容ではなく、拡張子によって行うように変更されました。
  • wget が存在しない場合、curl を使用するようになりました。
  • その他エラーメッセージなどが改善されました。
package2sfs は現在、以下の内容から Puppy Linux 用の新たな SFS ファイルを作成できます。
  • ディレクトリ
  • Debian パッケージ (*.deb)
  • PET パッケージ (*.pet)
  • Slackware パッケージ (*.tgz, *.txz)
  • SquashFS(SFS) ファイル (*.sfs)
  • ext2, ext3, ext4 のイメージファイル (*.2fs, *.3fs, *.4fs)
詳しい使用方法は以下のコマンドを実行してみてください。

コード: 全て選択

./package2sfs --help
Precise-571JP で動作することを確認しております。よろしければ利用してみてくださいね。 :)

2022年10月29日追記

ちまちまとバグ取りや高速化を行っています。バージョンも 2.0.2 まで上がりました。
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

ほんの少しだけ機能を追加しました。バージョンは2.1.0になりました。

コード: 全て選択

#!/usr/bin/env sh

### Script: package2sfs
##
## パッケージを SquashFS に変換する。
##
## Metadata:
##
##   id - 38559dfa-cda3-4325-a702-d29aabbb6f85
##   author - <ssnys at https://sakurapup.com/forum1/memberlist.php?mode=viewprofile&u=4849>
##   version - 2.1.0
##   date - 2022-10-31
##   since - 2018-06-22
##   copyright - Copyright (C) 2018-2022 ssnys. Some rights reserved.
##   license - <CC-BY at https://creativecommons.org/licenses/by/4.0/>
##
## See Also:
##
##   * <Project homepage at https://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
##
## Help Output:
##
## ------ Text ------
## Usage:
##   package2sfs [OPTION]... DEST [PACKAGE]...
##
## Options:
##   -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
##                               build package control file
##   -d,     --{no-}directory    do not make SquashFS but directory
##   -o,     --option NAME[=VALUE] 
##                               option of mksquashfs command
##           --no-option         reset -o, --option
##   -v,     --{no-}verbose      enable verbose output
##   -h,     --help              display this help and exit
##   -V,     --version           output version information and exit
##
## Exit Status:
##     0 - successful termination
##    64 - command line usage error
##    65 - data format error
##    66 - cannot open input
##    67 - addressee unknown
##    68 - host name unknown
##    69 - service unavailable
##    70 - internal software error
##    71 - system error (e.g., can't fork)
##    72 - critical OS file missing
##    73 - can't create (user) output file
##    74 - input/output error
##    75 - temp failure; user is invited to retry
##    76 - remote error in protocol
##    77 - permission denied
##    78 - configuration error
##   129 - received SIGHUP
##   130 - received SIGINT
##   131 - received SIGQUIT
##   143 - received SIGTERM
##
## Examples:
##   package2sfs output.sfs *.deb
##   package2sfs -cv output.sfs *.pet
##   package2sfs -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz
##   package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
##   package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
## ------------------

readonly 'NAME=package2sfs'
readonly 'VERSION=2.1.0'
readonly 'UPDATE=2022-10-31'

set -efu
umask '0022'
LC_ALL='C'
IFS=$(printf ' \t\n_'); IFS="${IFS%_}"
PATH="${PATH-}${PATH:+:}$(command -p getconf 'PATH')"
UNIX_STD='2003' # HP-UX POSIX mode
XPG_SUS_ENV='ON' # AIX POSIX mode
XPG_UNIX98='OFF' # AIX UNIX 03 mode
POSIXLY_CORRECT='1' # GNU Coreutils POSIX mode
COMMAND_MODE='unix2003' # macOS UNIX 03 mode
export 'IFS' 'LC_ALL' 'PATH' 'UNIX_STD' 'XPG_SUS_ENV' 'XPG_UNIX98' 'POSIXLY_CORRECT' 'COMMAND_MODE'

readonly 'EX_OK=0'           # successful termination
readonly 'EX__BASE=64'       # base value for error messages

readonly 'EX_USAGE=64'       # command line usage error
readonly 'EX_DATAERR=65'     # data format error
readonly 'EX_NOINPUT=66'     # cannot open input
readonly 'EX_NOUSER=67'      # addressee unknown
readonly 'EX_NOHOST=68'      # host name unknown
readonly 'EX_UNAVAILABLE=69' # service unavailable
readonly 'EX_SOFTWARE=70'    # internal software error
readonly 'EX_OSERR=71'       # system error (e.g., can't fork)
readonly 'EX_OSFILE=72'      # critical OS file missing
readonly 'EX_CANTCREAT=73'   # can't create (user) output file
readonly 'EX_IOERR=74'       # input/output error
readonly 'EX_TEMPFAIL=75'    # temp failure; user is invited to retry
readonly 'EX_PROTOCOL=76'    # remote error in protocol
readonly 'EX_NOPERM=77'      # permission denied
readonly 'EX_CONFIG=78'      # configuration error

readonly 'EX__MAX=78'        # maximum listed value

trap 'case "${?}" in 0) end_call;; *) end_call "${EX_SOFTWARE}";; esac' 0 # EXIT
trap 'end_call 129' 1  # SIGHUP
trap 'end_call 130' 2  # SIGINT
trap 'end_call 131' 3  # SIGQUIT
trap 'end_call 143' 15 # SIGTERM

### Function: end_call
##
## 一時ディレクトリを削除しスクリプトを終了する。
##
## Parameters:
##
##   $1 - 終了ステータス。
##
## Returns:
##
##   $1 の終了ステータス。

end_call() {
    trap '' 0 # EXIT
    rm -fr -- ${tmpDir:+"${tmpDir}"}
    exit "${1:-0}"
}

### Function: option_error
##
## エラーメッセージを出力する。
##
## Parameters:
##
##   $1 - エラーメッセージ。
##
## Returns:
##
##   終了コード64。

option_error() {
    printf '%s: %s\n' "${0##*/}" "${1}" >&2
    printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

    end_call "${EX_USAGE}"
}

### Function: append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $@ - 追加する要素。

append_array_posix() {
    while [ 2 -le "${#}" ]; do
        __append_array_posix "${1}" "${2}"

        eval "shift 2; set -- '${1}'" '${@+"${@}"}'
    done
}

### Function: __append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 追加する要素。

__append_array_posix() {
    set "${1}" "${2-}" ''

    until [ "${2#*\'}" '=' "${2}" ]; do
        set -- "${1}" "${2#*\'}" "${3}${2%%\'*}'\"'\"'"
    done

    eval "${1}=\"\${${1}-}\${${1}:+ }'\${3}\${2}'\""
}

### Function: regex_match
##
## 文字列が正規表現に一致するか検査する。
##
## Parameters:
##
##   $1 - 検査する文字列。
##   $@ - 正規表現。
##
## Returns:
##
##   0か1の真理値。

regex_match() {
    awk -- '
        BEGIN {
            for(i = 2; i < ARGC; i++) {
                if(ARGV[1] !~ ARGV[i]) {
                    exit 1
                }
            }

            exit
        }
    ' ${@+"${@}"} || return 1

    return 0
}

### Function: add_option
##
## オプションを追加する
##
## Parameters:
##
##   $1 - Debian パッケージ
##   $2 - オプションの名前と値

add_option() {
    case "${2}" in
        *'='*) append_array_posix "${1}" "-${2%%=*}" "${2#*=}";;
        *) append_array_posix "${1}" "-${2}";;
    esac
}

### Function: deb2dir
##
## Debian パッケージを展開する
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 展開先のディレクトリ

deb2dir() (
    package="${1}"
    dir="${2}"

    # Debian パッケージからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf '# Extract files in %s #\n' "${package}";;
    esac

    dpkg-deb --extract ${prog_v+-v} -- "${package}" "${dir}"

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}.deb' -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # Debian パッケージからコントロールファイルを取り出す
        case "${verboseFlag}" in
            [!0]*) printf "# Extract control files in '%s' #\\n" "${package}";;
        esac

        dpkg-deb --control ${prog_v+-v} -- "${package}" "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -x "${dir}${controlDir}/preinst" ] || [ -x "${dir}${controlDir}/postinst" ] || [ -x "${dir}${controlDir}/prerm" ] || [ -x "${dir}${controlDir}/postrm" ]; then
            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/preinst' ]; then
            '${controlDir}/preinst' 'install'
        fi

        if [ -x '${controlDir}/postinst' ]; then
            '${controlDir}/postinst' 'configure' '$(dpkg-deb -W --showformat='${Version}' -- "${package}")'
        fi
        ;;
    'stop')
        if [ -x '${controlDir}/prerm' ]; then
            '${controlDir}/prerm' 'remove'
        fi

        if [ -x '${controlDir}/postrm' ]; then
            '${controlDir}/postrm' 'remove'
        fi
        ;;
__EOF__
        fi
    esac
)

### Function: pet2dir
##
## Puppy's Extra Treats パッケージを展開する
##
## Parameters:
##
##   $1 - PET パッケージ
##   $2 - 展開先のディレクトリ

pet2dir() (
    package="${1}"
    dir="${2}"

    basename=$(basename -- "${package}" '.pet')
    tmpPet=$(mktemp -d)

    cp -f ${prog_v+-v} -- "${package}" "${tmpPet}/package.pet"

    if pet2tgz "${tmpPet}/package.pet"; then :; else
        rm -fr -- "${tmpPet}"

        printf "%s: '%s' is broken.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        return 1
    fi

    # Puppy's Extra Treats パッケージからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf "# Extract files in '%s' #\\n" "${package}";;
    esac

    tar -xz -C "${tmpPet}" -f "${tmpPet}/package.tar.gz" ${prog_v+-v}

    if [ -d "${tmpPet}/${basename}" ]; then
        find -- "${tmpPet}/${basename}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}" ';'
    fi

    rm -fr ${prog_v+-v} -- "${tmpPet}"

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(basename -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -x "${dir}/pinstall.sh" ] || [ -x "${dir}/puninstall.sh" ]; then
            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/pinstall.sh' ]; then
            '${controlDir}/pinstall.sh'
        fi
        ;;
    'stop')
        if [ -x '${controlDir}/puninstall.sh' ]; then
            '${controlDir}/puninstall.sh'
        fi
        ;;
__EOF__
        fi

        for controlFile in 'pinstall.sh' 'puninstall.sh' 'pet.specs'; do
            if [ -f "${dir}/${controlFile}" ]; then
                case "${verboseFlag}" in
                    [!0]*) printf "# Move '%s' files #\\n" "${dir}/${controlFile}";;
                esac

                cp -f ${prog_v+-v} -- "${dir}/${controlFile}" "${dir}${controlDir}"
            fi
        done
    esac

    rm -fr ${prog_v+-v} -- "${dir}/pinstall.sh" "${dir}/puinstall.sh" "${dir}/pet.specs"
)

### Function: pkg2dir
##
## Slackware パッケージを展開する
##
## Parameters:
##
##   $1 - Slackware パッケージ
##   $2 - 展開先のディレクトリ

pkg2dir() (
    package="${1}"
    dir="${2}"

    # 圧縮ファイルからファイルを取り出す
    case "${verboseFlag}" in
        [!0]*) printf '# Extract files in %s #\n' "${package}";;
    esac

    case "${package}" in
        *'.tgz') tar -xz -C "${dir}" -f "${package}" ${prog_v+-v};;
        *'.txz') tar -xJ -C "${dir}" -f "${package}" ${prog_v+-v};;
    esac

    # ${controlFlag} による処理の分岐
    case "${controlFlag}" in '1')
        filename=$(basename -- "${package}")
        controlDir="/var/run/${NAME}/${filename}"

        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' directory #\\n" "${dir}${controlDir}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

        # スクリプトの有無により処理を分岐
        if [ -f "${dir}/install/doinst.sh" ]; then
            chmod ${prog_v+-v} -- '755' "${dir}/install/doinst.sh"

            startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
    'start')
        if [ -x '${controlDir}/doinst.sh' ]; then
            '${controlDir}/doinst.sh'
        fi
        ;;
    'stop') ;;
__EOF__
        fi

        case "${verboseFlag}" in
            [!0]*) printf "# Move '%s' directory #\\n" "${dir}/install";;
        esac

        if [ -d "${dir}/install" ]; then
            find -- "${dir}/install/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}${controlDir}" ';'
        fi
    esac

    rm -fr ${prog_v+-v} -- "${dir}/install"
)

### Function: startup_script
##
## サービス起動スクリプトを作成する
##
## Parameters:
##
##   $1 - 作成先のファイルパス

startup_script() (
    startup="${1}"
    basename=$(basename -- "${startup}" '.sh')
    basedir=$(dirname -- "${startup}")

    shift

    case "${verboseFlag}" in
        [!0]*) printf "# Make '%s' directory #\\n" "${basedir}";;
    esac

    mkdir -p ${prog_v+-v} -- "${basedir}"

    # サービス起動スクリプトを作成
    case "${verboseFlag}" in
        [!0]*) printf "# Create service startup script in '%s' #\\n" "${startup}";;
    esac

    # サービス起動スクリプトを作成
    cat >"${startup}" <<__EOF__
#!/bin/sh

## Startup ${basename} maintainer scripts
#
# @date $(date -u -- '+%Y-%m-%d %H:%M:%S UTC')
# @note Automatically generated by ${NAME} version ${VERSION} (update ${UPDATE})

set -efu

cd -- '/'

case "\${1}" in
$(cat)
    'restart')
        shift
        "\${0}" 'stop' \${@+"\${@}"}
        "\${0}" 'start' \${@+"\${@}"}
        ;;
    *)
        printf 'Usage: %s {start|stop|restart}\\n' "\${0}" >&2

        # EX_USAGE (/usr/include/sysexits.h)
        exit '${EX_USAGE}'
        ;;
esac
__EOF__

    # パーミッションを変更し実行が可能に
    case "${verboseFlag}" in
        [!0]*) printf "# Chenge mod '%s' #\\n" "${startup}";;
    esac

    chmod ${prog_v+-v} -- '755' "${startup}"
)

# @getoptions
parser_definition() {
    setup REST abbr:true error:option_error plus:true no:0 help:usage \
        -- 'Usage:' "  ${2##*/} [OPTION]... DEST [PACKAGE]..." \
        '' 'Options:'

    option control     -c --{no-}control   init:@no on:'1-' no: validate:'regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$"' var:'PACKAGE_NUMBER_LIST' -- 'build package control file'
    flag   dirFlag     -d --{no-}directory init:@no -- 'do not make SquashFS but directory'
    param  :'add_option "options" "${OPTARG}"' -o --option validate:'regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"' var:'NAME[=VALUE]' -- 'option of mksquashfs command'
    flag   options     --no-option         init:@no on: no: -- 'reset -o, --option'
    flag   verboseFlag -v --{no-}verbose   init:@no counter:1 -- 'enable verbose output'
    disp   :usage      -h --help           -- 'display this help and exit'
    disp   :'echo "${NAME} ${VERSION}"'    -V --version -- 'output version information and exit'

    msg -- '' 'Exit Status:' \
        '    0 - successful termination' \
        '   64 - command line usage error' \
        '   65 - data format error' \
        '   66 - cannot open input' \
        '   67 - addressee unknown' \
        '   68 - host name unknown' \
        '   69 - service unavailable' \
        '   70 - internal software error' \
        "   71 - system error (e.g., can't fork)" \
        '   72 - critical OS file missing' \
        "   73 - can't create (user) output file" \
        '   74 - input/output error' \
        '   75 - temp failure; user is invited to retry' \
        '   76 - remote error in protocol' \
        '   77 - permission denied' \
        '   78 - configuration error' \
        '  129 - received SIGHUP' \
        '  130 - received SIGINT' \
        '  131 - received SIGQUIT' \
        '  143 - received SIGTERM'

    msg -- '' 'Examples:' \
        "  ${2##*/} output.sfs *.deb" \
        "  ${2##*/} -cv output.sfs *.pet" \
        "  ${2##*/} -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz" \
        "  ${2##*/} -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir" \
        "  ${2##*/} output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'"
}
# @end

# @gengetoptions parser -i parser_definition parse "${1}"
# Generated by getoptions (BEGIN)
# URL: https://github.com/ko1nksm/getoptions (v3.3.0)
control=''
dirFlag='0'
options=''
verboseFlag='0'
REST=''
parse() {
  OPTIND=$(($#+1))
  while OPTARG= && [ $# -gt 0 ]; do
    set -- "${1%%\=*}" "${1#*\=}" "$@"
    while [ ${#1} -gt 2 ]; do
      case $1 in (*[!a-zA-Z0-9_-]*) break; esac
      case '--control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --control"
      esac
      case '--no-control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-control"
      esac
      case '--directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --directory"
      esac
      case '--no-directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-directory"
      esac
      case '--option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --option"
      esac
      case '--no-option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-option"
      esac
      case '--verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --verbose"
      esac
      case '--no-verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-verbose"
      esac
      case '--help' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --help"
      esac
      case '--version' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --version"
      esac
      break
    done
    case ${OPTARG# } in
      *\ *)
        eval "set -- $OPTARG $1 $OPTARG"
        OPTIND=$((($#+1)/2)) OPTARG=$1; shift
        while [ $# -gt "$OPTIND" ]; do OPTARG="$OPTARG, $1"; shift; done
        set "Ambiguous option: $1 (could be $OPTARG)" ambiguous "$@"
        option_error "$@" >&2 || exit $?
        echo "$1" >&2
        exit 1 ;;
      ?*)
        [ "$2" = "$3" ] || OPTARG="$OPTARG=$2"
        shift 3; eval 'set -- "${OPTARG# }"' ${1+'"$@"'}; OPTARG= ;;
      *) shift 2
    esac
    case $1 in
      --?*=*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
        ;;
      --no-*|--without-*) unset OPTARG ;;
      -[co]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
        ;;
      -[dvhV]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
        OPTARG= ;;
      +*) unset OPTARG ;;
    esac
    case $1 in
      '-c'|'--control'|'--no-control')
        set -- "$1" "$@"
        [ ${OPTARG+x} ] && {
          case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
          [ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='1-'
        } || OPTARG=''
        regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$"; break; }
        control="$OPTARG"
        shift ;;
      '-d'|'--directory'|'--no-directory')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG='0'
        dirFlag="$OPTARG"
        ;;
      '-o'|'--option')
        [ $# -le 1 ] && set "required" "$1" && break
        OPTARG=$2
        regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"; break; }
        add_option "options" "${OPTARG}"
        shift ;;
      '--no-option')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='' || OPTARG=''
        options="$OPTARG"
        ;;
      '-v'|'--verbose'|'--no-verbose')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
        verboseFlag="$((${verboseFlag:-0}+$OPTARG))"
        ;;
      '-h'|'--help')
        usage
        exit 0 ;;
      '-V'|'--version')
        echo "${NAME} ${VERSION}"
        exit 0 ;;
      --)
        shift
        while [ $# -gt 0 ]; do
          REST="${REST} \"\${$(($OPTIND-$#))}\""
          shift
        done
        break ;;
      [-+]?*) set "unknown" "$1"; break ;;
      *)
        REST="${REST} \"\${$(($OPTIND-$#))}\""
    esac
    shift
  done
  [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
  case $1 in
    unknown) set "Unrecognized option: $2" "$@" ;;
    noarg) set "Does not allow an argument: $2" "$@" ;;
    required) set "Requires an argument: $2" "$@" ;;
    pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
    notcmd) set "Not a command: $2" "$@" ;;
    *) set "Validation error ($1): $2" "$@"
  esac
  option_error "$@" >&2 || exit $?
  echo "$1" >&2
  exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage:
  package2sfs [OPTION]... DEST [PACKAGE]...

Options:
  -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
                              build package control file
  -d,     --{no-}directory    do not make SquashFS but directory
  -o,     --option NAME[=VALUE] 
                              option of mksquashfs command
          --no-option         reset -o, --option
  -v,     --{no-}verbose      enable verbose output
  -h,     --help              display this help and exit
  -V,     --version           output version information and exit

Exit Status:
    0 - successful termination
   64 - command line usage error
   65 - data format error
   66 - cannot open input
   67 - addressee unknown
   68 - host name unknown
   69 - service unavailable
   70 - internal software error
   71 - system error (e.g., can't fork)
   72 - critical OS file missing
   73 - can't create (user) output file
   74 - input/output error
   75 - temp failure; user is invited to retry
   76 - remote error in protocol
   77 - permission denied
   78 - configuration error
  129 - received SIGHUP
  130 - received SIGINT
  131 - received SIGQUIT
  143 - received SIGTERM

Examples:
  package2sfs output.sfs *.deb
  package2sfs -cv output.sfs *.pet
  package2sfs -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz
  package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
  package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
GETOPTIONSHERE
}
# Generated by getoptions (END)
# @end

parse ${@+"${@}"}
eval "set -- ${REST}"

# コマンドライン引数が不足している
case "${#}" in '0')
    printf '%s: Requires an argument: DEST\n' "${0##*/}" >&2
    printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

    end_call "${EX_USAGE}"
    ;;
esac

# コマンド毎に詳細表示を行うか
if [ '2' -le "${verboseFlag}" ]; then
    prog_v='1'
fi

# 先頭の位置パラメータから宛先を取り出す
dest="${1}"
shift

# 一時ディレクトリを作成
case "${verboseFlag}" in
    [!0]*) printf '# Make temporary directory and temporary file #\n';;
esac

awkRangeCheckScript=$(
    cat <<-'__EOF__'
    BEGIN {
        number = ARGV[1]
        split(ARGV[2], ranges, ",")

        for(i in ranges) {
            if(index(ranges[i], "-")) {
                split(ranges[i], array, "-")

                if(array[1] != "" && array[2] != "" && array[2] < array[1]) {
                    tmp = array[1]
                    array[1] = array[2]
                    array[2] = tmp
                }

                if((array[1] == "" || array[1] <= number) && (array[2] == "" || number <= array[2])) {
                    exit
                }
            } else if(ranges[i] == number) {
                exit
            }
        }

        exit 1
    }
    __EOF__
)
tmpDir=$(mktemp -d)
tmpContent="${tmpDir}/content"
tmpDownload="${tmpDir}/download"
tmpPackage="${tmpDir}/package"

mkdir -p ${prog_v+-v} -- "${tmpContent}" "${tmpDownload}" "${tmpPackage}"

# 各パッケージを処理
for package in ${@+"${@}"}; do
    index=$((${index:-0} + 1))
    controlFlag='0'

    if awk -- "${awkRangeCheckScript}" "${index}" "${control}"; then
        controlFlag='1'
    fi

    # URL ならばタウンロード
    case "${package}" in
        'http://'*/* | 'https://'*/* | 'ftp://'*/*)
            filename=$(basename -- "${package}")

            case "${verboseFlag}" in
                [!0]*) printf "# Download '%s' #\\n" "${package}";;
            esac

            if command -v 'wget' >'/dev/null'; then
                wget --no-verbose --output-document "${tmpDownload}/${filename}" ${prog_v+-v} -- "${package}"
            elif command -v 'curl' >'/dev/null'; then
                curl --compressed --fail --location --output "${tmpDownload}/${filename}" --silent --show-error ${prog_v+-v} -- "${package}"
            else
                printf "'wget' or 'curl' is required.\\n" >&2
                printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

                end_call "${EX_UNAVAILABLE}"
            fi

            package="${tmpDownload}/${filename}"
            ;;
    esac

    # 読み込みの許可がない
    if [ '!' -r "${package}" ]; then
        printf "%s: No permission to read '%s'.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        end_call "${EX_NOINPUT}"
    # ディレクトリ
    elif [ -d "${package}" ]; then
        case "${verboseFlag}" in
            [!0]*) printf "# Copy files in '%s' #\\n" "${package}";;
        esac

        find -- "${package}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

        continue
    elif [ '!' -f "${package}" ]; then
        printf "%s: '%s' is not a regular file.\\n" "${0##*/}" "${package}" >&2
        printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

        end_call "${EX_DATAERR}"
    fi

    # パッケージファイルの種類によって処理を分岐
    case "${package}" in
        # Debian パッケージ
        *[!./]'.deb') deb2dir "${package}" "${tmpContent}";;
        # Puppy's Extra Treats
        *[!./]'.pet') pet2dir "${package}" "${tmpPackage}";;
        # Slackware GZIP or Slackware XZ
        *[!./]'.tgz' | '.txz') pkg2dir "${package}" "${tmpPackage}";;
        # イメージファイル (ext2, ext3, ext4) と SquashFS ファイル
        *[!./].[234]fs | *[!./]'.sfs')
            case "${verboseFlag}" in
                [!0]*) printf "# Mount '%s' #\\n" "${package}";;
            esac

            case "${package}" in
                *[!./].[234]fs) mount -o 'loop' -r -t 'ext2,ext3,ext4' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
                *[!./]'.sfs') mount -r -t 'squashfs' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
            esac

            case "${verboseFlag}" in
                [!0]*) printf "# Copy the files in '%s' #\\n" "${package}";;
            esac

            find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

            case "${verboseFlag}" in
                [!0]*) printf "# Unmount '%s' #\\n" "${package}";;
            esac

            umount -d ${prog_v+-v} -- "${tmpPackage}"
            ;;
        # 互換性のないファイル
        *)
            printf "%s: '%s' is not a compatible file.\\n" "${0##*/}" "${package}" >&2
            printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

            end_call "${EX_DATAERR}"
            ;;
    esac

    # 解凍したファイルをメインのディレクトリに移動
    find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'
    find -- "${tmpPackage}/" -path '*[!/]' -prune -exec rm -fr ${prog_v+-v} -- '{}' '+'
done

# 一部ディレクトリの内容を上位ディレクトリに移動
for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
    if [ -d "${tmpContent}${path}" ]; then
        case "${verboseFlag}" in
            [!0]*) printf "# Move '%s' #\\n" "${path}";;
        esac

        find -- "${tmpContent}${path}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}${path}/.." ';'
        rm -fr ${prog_v+-v} -- "${tmpContent}${path}"
    fi
done

# ${dirFlag} による処理の分岐
case "${dirFlag}" in
    '1')
        # 一時ディレクトリの内容を ${dest} にコピー
        case "${verboseFlag}" in
            [!0]*) printf "# Copy temporary directory to '%s' #\\n" "${dest}";;
        esac

        mkdir -p ${prog_v+-v} -- "${dest}"
        find -- "${tmpContent}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dest}" ';'
        ;;
    *)
        # SquashFS ファイルを作成
        case "${verboseFlag}" in
            [!0]*) printf "# Make '%s' #\\n" "${dest}";;
        esac

        eval 'mksquashfs "${tmpContent}" "${dest}"' "${options}"
        ;;
esac
  • -c, --control オプションで範囲を指定できるようになりました。例えば --control=1,3-5,9- ですと、引数の1個目、3個目から5個目、9個目以降のパッケージにてコントロールファイルが展開されます。
皆様の良い Puppy ライフを願って。
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
ssnys
記事: 64
登録日時: 14/02/19(水) 17:50

Re: deb2sfs.sh - deb(Debianパッケージ)をsfs(SquashFS)に変換するシェルスクリプト

投稿記事 by ssnys »

RPM パッケージに対応しました。CentOS などのパッケージが利用できるようになりました。バージョンは2.2.0になりました。

コード: 全て選択

#!/usr/bin/env sh

### Script: package2sfs
##
## パッケージを SquashFS に変換する。
##
## Metadata:
##
##   id - 38559dfa-cda3-4325-a702-d29aabbb6f85
##   author - <ssnys at https://sakurapup.com/forum1/memberlist.php?mode=viewprofile&u=4849>
##   version - 2.2.2
##   date - 2023-12-26
##   since - 2018-06-22
##   copyright - Copyright (C) 2018-2022 ssnys. Some rights reserved.
##   license - <CC-BY at https://creativecommons.org/licenses/by/4.0/>
##
## See Also:
##
##   * <Project homepage at https://sakurapup.com/forum1/viewtopic.php?f=31&t=3372>
##
## Help Output:
##
## ------ Text ------
## Usage:
##   package2sfs [OPTION]... DEST [PACKAGE]...
##
## Options:
##   -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
##                               build package control file
##   -d,     --{no-}directory    do not make SquashFS but directory
##   -o,     --option NAME[=VALUE] 
##                               option of mksquashfs command
##           --no-option         reset -o, --option
##   -v,     --{no-}verbose      enable verbose output
##   -h,     --help              display this help and exit
##   -V,     --version           output version information and exit
##
## Exit Status:
##     0 - successful termination
##    64 - command line usage error
##    65 - data format error
##    66 - cannot open input
##    67 - addressee unknown
##    68 - host name unknown
##    69 - service unavailable
##    70 - internal software error
##    71 - system error (e.g., can't fork)
##    72 - critical OS file missing
##    73 - can't create (user) output file
##    74 - input/output error
##    75 - temp failure; user is invited to retry
##    76 - remote error in protocol
##    77 - permission denied
##    78 - configuration error
##   129 - received SIGHUP
##   130 - received SIGINT
##   131 - received SIGQUIT
##   143 - received SIGTERM
##
## Examples:
##   package2sfs output.sfs *.deb
##   package2sfs -cv output.sfs *.pet
##   package2sfs -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz
##   package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
##   package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
## ------------------

readonly 'NAME=package2sfs'
readonly 'VERSION=2.2.2'
readonly 'UPDATE=2023-12-26'

set -efu
umask '0022'
readonly "LC_ALL_ORG=${LC_ALL-}"
LC_ALL='C'
IFS=$(printf ' \t\n_'); IFS="${IFS%_}"
PATH="${PATH-}${PATH:+:}$(command -p getconf 'PATH')"
UNIX_STD='2003' # HP-UX POSIX mode
XPG_SUS_ENV='ON' # AIX POSIX mode
XPG_UNIX98='OFF' # AIX UNIX 03 mode
POSIXLY_CORRECT='1' # GNU Coreutils POSIX mode
COMMAND_MODE='unix2003' # macOS UNIX 03 mode
export 'LC_ALL' 'IFS' 'PATH' 'UNIX_STD' 'XPG_SUS_ENV' 'XPG_UNIX98' 'POSIXLY_CORRECT' 'COMMAND_MODE'

readonly 'EX_OK=0'           # successful termination
readonly 'EX__BASE=64'       # base value for error messages

readonly 'EX_USAGE=64'       # command line usage error
readonly 'EX_DATAERR=65'     # data format error
readonly 'EX_NOINPUT=66'     # cannot open input
readonly 'EX_NOUSER=67'      # addressee unknown
readonly 'EX_NOHOST=68'      # host name unknown
readonly 'EX_UNAVAILABLE=69' # service unavailable
readonly 'EX_SOFTWARE=70'    # internal software error
readonly 'EX_OSERR=71'       # system error (e.g., can't fork)
readonly 'EX_OSFILE=72'      # critical OS file missing
readonly 'EX_CANTCREAT=73'   # can't create (user) output file
readonly 'EX_IOERR=74'       # input/output error
readonly 'EX_TEMPFAIL=75'    # temp failure; user is invited to retry
readonly 'EX_PROTOCOL=76'    # remote error in protocol
readonly 'EX_NOPERM=77'      # permission denied
readonly 'EX_CONFIG=78'      # configuration error

readonly 'EX__MAX=78'        # maximum listed value

trap 'case "${?}" in 0) end_call;; *) end_call "${EX_SOFTWARE}";; esac' 0 # EXIT
trap 'end_call 129' 1  # SIGHUP
trap 'end_call 130' 2  # SIGINT
trap 'end_call 131' 3  # SIGQUIT
trap 'end_call 143' 15 # SIGTERM

alias org_lc='LC_ALL="${LC_ALL_ORG}"'

### Function: end_call
##
## 一時ディレクトリを削除しスクリプトを終了する。
##
## Parameters:
##
##   $1 - 終了ステータス。
##
## Returns:
##
##   $1 の終了ステータス。

end_call() {
	trap '' 0 # EXIT
	rm -fr -- ${tmpDir:+"${tmpDir}"}
	exit "${1:-0}"
}

### Function: option_error
##
## エラーメッセージを出力する。
##
## Parameters:
##
##   $1 - エラーメッセージ。
##
## Returns:
##
##   終了コード64。

option_error() {
	printf '%s: %s\n' "${0##*/}" "${1}" >&2
	printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

	end_call "${EX_USAGE}"
}

### Function: append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $@ - 追加する要素。

append_array_posix() {
	while [ 2 -le "${#}" ]; do
		__append_array_posix "${1}" "${2}"

		eval "shift 2; set -- '${1}'" '${@+"${@}"}'
	done
}

### Function: __append_array_posix
##
## 配列風文字列に要素を追加する。
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 追加する要素。

__append_array_posix() {
	set "${1}" "${2-}" ''

	until [ "${2#*\'}" '=' "${2}" ]; do
		set -- "${1}" "${2#*\'}" "${3}${2%%\'*}'\"'\"'"
	done

	eval "${1}=\"\${${1}-}\${${1}:+ }'\${3}\${2}'\""
}

### Function: regex_match
##
## 文字列が正規表現に一致するか検査する。
##
## Parameters:
##
##   $1 - 検査する文字列。
##   $@ - 正規表現。
##
## Returns:
##
##   0か1の真理値。

regex_match() {
	awk -- '
		BEGIN {
			for(i = 2; i < ARGC; i++) {
				if(ARGV[1] !~ ARGV[i]) {
					exit 1
				}
			}

			exit
		}
	' ${@+"${@}"} || return 1

	return 0
}

### Function: add_option
##
## オプションを追加する
##
## Parameters:
##
##   $1 - Debian パッケージ
##   $2 - オプションの名前と値

add_option() {
	case "${2}" in
		*'='*) append_array_posix "${1}" "-${2%%=*}" "${2#*=}";;
		*) append_array_posix "${1}" "-${2}";;
	esac
}

### Function: deb2dir
##
## Debian パッケージを展開する
##
## Parameters:
##
##   $1 - 結果を代入する変数名。
##   $2 - 展開先のディレクトリ

deb2dir() (
	package="${1}"
	dir="${2}"

	# Debian パッケージからファイルを取り出す
	case "${verboseFlag}" in [!0]*)
		printf '# Extract files in %s #\n' "${package}"
	esac

	dpkg-deb --extract ${prog_v+-v} -- "${package}" "${dir}"

	# ${controlFlag} による処理の分岐
	case "${controlFlag}" in '1')
		filename=$(dpkg-deb -W --showformat='${Package}_${Version}_${Architecture}.deb' -- "${package}")
		controlDir="/var/run/${NAME}/${filename}"

		case "${verboseFlag}" in [!0]*)
			printf "# Make '%s' directory #\\n" "${dir}${controlDir}"
		esac

		mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

		# Debian パッケージからコントロールファイルを取り出す
		case "${verboseFlag}" in [!0]*)
			printf "# Extract control files in '%s' #\\n" "${package}"
		esac

		dpkg-deb --control ${prog_v+-v} -- "${package}" "${dir}${controlDir}"

		# スクリプトの有無により処理を分岐
		if [ -x "${dir}${controlDir}/preinst" ] || [ -x "${dir}${controlDir}/postinst" ] || [ -x "${dir}${controlDir}/prerm" ] || [ -x "${dir}${controlDir}/postrm" ]; then
			startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
	'start')
		if [ -x '${controlDir}/preinst' ]; then
			'${controlDir}/preinst' 'install'
		fi

		if [ -x '${controlDir}/postinst' ]; then
			'${controlDir}/postinst' 'configure' '$(dpkg-deb -W --showformat='${Version}' -- "${package}")'
		fi
		;;
	'stop')
		if [ -x '${controlDir}/prerm' ]; then
			'${controlDir}/prerm' 'remove'
		fi

		if [ -x '${controlDir}/postrm' ]; then
			'${controlDir}/postrm' 'remove'
		fi
		;;
__EOF__
		fi
	esac
)

### Function: pet2dir
##
## Puppy's Extra Treats パッケージを展開する
##
## Parameters:
##
##   $1 - PET パッケージ
##   $2 - 展開先のディレクトリ

pet2dir() (
	package="${1}"
	dir="${2}"

	basename=$(basename -- "${package}" '.pet')
	tmpPet=$(mktemp -d)

	cp -f ${prog_v+-v} -- "${package}" "${tmpPet}/package.pet"

	if ! pet2tgz "${tmpPet}/package.pet"; then
		rm -fr -- "${tmpPet}"

		printf "%s: '%s' is broken.\\n" "${0##*/}" "${package}" >&2
		printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

		return 1
	fi

	# Puppy's Extra Treats パッケージからファイルを取り出す
	case "${verboseFlag}" in [!0]*)
		printf "# Extract files in '%s' #\\n" "${package}"
	esac

	tar -xz -C "${tmpPet}" -f "${tmpPet}/package.tar.gz" ${prog_v+-v}

	if [ -d "${tmpPet}/${basename}" ]; then
		find -- "${tmpPet}/${basename}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}" ';'
	fi

	rm -fr ${prog_v+-v} -- "${tmpPet}"

	# ${controlFlag} による処理の分岐
	case "${controlFlag}" in '1')
		filename=$(basename -- "${package}")
		controlDir="/var/run/${NAME}/${filename}"

		case "${verboseFlag}" in [!0]*)
			printf "# Make '%s' directory #\\n" "${dir}${controlDir}"
		esac

		mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

		# スクリプトの有無により処理を分岐
		if [ -x "${dir}/pinstall.sh" ] || [ -x "${dir}/puninstall.sh" ]; then
			startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
	'start')
		if [ -x '${controlDir}/pinstall.sh' ]; then
			'${controlDir}/pinstall.sh'
		fi
		;;
	'stop')
		if [ -x '${controlDir}/puninstall.sh' ]; then
			'${controlDir}/puninstall.sh'
		fi
		;;
__EOF__
		fi

		for controlFile in 'pinstall.sh' 'puninstall.sh' 'pet.specs'; do
			if [ -f "${dir}/${controlFile}" ]; then
				case "${verboseFlag}" in [!0]*)
					printf "# Move '%s' files #\\n" "${dir}/${controlFile}"
				esac

				cp -f ${prog_v+-v} -- "${dir}/${controlFile}" "${dir}${controlDir}"
			fi
		done
	esac

	rm -fr ${prog_v+-v} -- "${dir}/pinstall.sh" "${dir}/puinstall.sh" "${dir}/pet.specs"
)

### Function: rpm2dir
##
## RPM パッケージを展開する
##
## Parameters:
##
##   $1 - RPM パッケージ
##   $2 - 展開先のディレクトリ

rpm2dir() (
	package="${1}"
	dir="${2}"

	# 圧縮ファイルからファイルを取り出す
	case "${verboseFlag}" in [!0]*)
		printf '# Extract files in %s #\n' "${package}"
	esac

	rpm2cpio "${1}" | (
		cd -- "${2}"
		cpio -id --quiet ${prog_v+-v}
	)

	# ${controlFlag} による処理の分岐
	case "${controlFlag}" in '1')
		filename=$(basename -- "${package}")
		controlDir="/var/run/${NAME}/${filename}"

		case "${verboseFlag}" in [!0]*)
			printf "# Make '%s' directory #\\n" "${dir}${controlDir}"
		esac

		mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"
	esac
)

### Function: pkg2dir
##
## Slackware パッケージを展開する
##
## Parameters:
##
##   $1 - Slackware パッケージ
##   $2 - 展開先のディレクトリ

pkg2dir() (
	package="${1}"
	dir="${2}"

	# 圧縮ファイルからファイルを取り出す
	case "${verboseFlag}" in [!0]*)
		printf '# Extract files in %s #\n' "${package}"
	esac

	case "${package}" in
		*'.tgz') tar -xz -C "${dir}" -f "${package}" ${prog_v+-v};;
		*'.txz') tar -xJ -C "${dir}" -f "${package}" ${prog_v+-v};;
	esac

	# ${controlFlag} による処理の分岐
	case "${controlFlag}" in '1')
		filename=$(basename -- "${package}")
		controlDir="/var/run/${NAME}/${filename}"

		case "${verboseFlag}" in [!0]*)
			printf "# Make '%s' directory #\\n" "${dir}${controlDir}"
		esac

		mkdir -p ${prog_v+-v} -- "${dir}${controlDir}"

		# スクリプトの有無により処理を分岐
		if [ -f "${dir}/install/doinst.sh" ]; then
			chmod ${prog_v+-v} -- '755' "${dir}/install/doinst.sh"

			startup_script "${dir}/etc/init.d/${filename}.sh" <<__EOF__
	'start')
		if [ -x '${controlDir}/doinst.sh' ]; then
			'${controlDir}/doinst.sh'
		fi
		;;
	'stop') ;;
__EOF__
		fi

		case "${verboseFlag}" in [!0]*)
			printf "# Move '%s' directory #\\n" "${dir}/install"
		esac

		if [ -d "${dir}/install" ]; then
			find -- "${dir}/install/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dir}${controlDir}" ';'
		fi
	esac

	rm -fr ${prog_v+-v} -- "${dir}/install"
)

### Function: startup_script
##
## サービス起動スクリプトを作成する
##
## Parameters:
##
##   $1 - 作成先のファイルパス

startup_script() (
	startup="${1}"
	basename=$(basename -- "${startup}" '.sh')
	basedir=$(dirname -- "${startup}")

	shift

	case "${verboseFlag}" in [!0]*)
		printf "# Make '%s' directory #\\n" "${basedir}"
	esac

	mkdir -p ${prog_v+-v} -- "${basedir}"

	# サービス起動スクリプトを作成
	case "${verboseFlag}" in [!0]*)
		printf "# Create service startup script in '%s' #\\n" "${startup}"
	esac

	# サービス起動スクリプトを作成
	cat >"${startup}" <<__EOF__
#!/bin/sh

## Startup ${basename} maintainer scripts
#
# @date $(date -u -- '+%Y-%m-%d %H:%M:%S UTC')
# @note Automatically generated by ${NAME} version ${VERSION} (update ${UPDATE})

set -efu

cd -- '/'

case "\${1}" in
$(cat)
	'restart')
		shift
		"\${0}" 'stop' \${@+"\${@}"}
		"\${0}" 'start' \${@+"\${@}"}
		;;
	*)
		printf 'Usage: %s {start|stop|restart}\\n' "\${0}" >&2

		# EX_USAGE (/usr/include/sysexits.h)
		exit '${EX_USAGE}'
		;;
esac
__EOF__

	# パーミッションを変更し実行が可能に
	case "${verboseFlag}" in [!0]*)
		printf "# Chenge mod '%s' #\\n" "${startup}"
	esac

	chmod ${prog_v+-v} -- '755' "${startup}"
)

# @getoptions
parser_definition() {
	setup REST abbr:true error:option_error plus:true no:0 help:usage \
		-- 'Usage:' "  ${2##*/} [OPTION]... DEST [PACKAGE]..." \
		'' 'Options:'

	option control     -c --{no-}control   init:@no on:'1-' no: validate:'regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$"' var:'PACKAGE_NUMBER_LIST' -- 'build package control file'
	flag   dirFlag     -d --{no-}directory init:@no -- 'do not make SquashFS but directory'
	param  :'add_option "options" "${OPTARG}"' -o --option validate:'regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"' var:'NAME[=VALUE]' -- 'option of mksquashfs command'
	flag   options     --no-option         init:@no on: no: -- 'reset -o, --option'
	flag   verboseFlag -v --{no-}verbose   init:@no counter:1 -- 'enable verbose output'
	disp   :usage      -h --help           -- 'display this help and exit'
	disp   :'echo "${NAME} ${VERSION}"'    -V --version -- 'output version information and exit'

	msg -- '' 'Exit Status:' \
		'    0 - successful termination' \
		'   64 - command line usage error' \
		'   65 - data format error' \
		'   66 - cannot open input' \
		'   67 - addressee unknown' \
		'   68 - host name unknown' \
		'   69 - service unavailable' \
		'   70 - internal software error' \
		"   71 - system error (e.g., can't fork)" \
		'   72 - critical OS file missing' \
		"   73 - can't create (user) output file" \
		'   74 - input/output error' \
		'   75 - temp failure; user is invited to retry' \
		'   76 - remote error in protocol' \
		'   77 - permission denied' \
		'   78 - configuration error' \
		'  129 - received SIGHUP' \
		'  130 - received SIGINT' \
		'  131 - received SIGQUIT' \
		'  143 - received SIGTERM'

	msg -- '' 'Examples:' \
		"  ${2##*/} output.sfs *.deb" \
		"  ${2##*/} -cv output.sfs *.pet" \
		"  ${2##*/} -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz" \
		"  ${2##*/} -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir" \
		"  ${2##*/} output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'"
}
# @end

# @gengetoptions parser -i parser_definition parse "${1}"
# Generated by getoptions (BEGIN)
# URL: https://github.com/ko1nksm/getoptions (v3.3.0)
control=''
dirFlag='0'
options=''
verboseFlag='0'
REST=''
parse() {
  OPTIND=$(($#+1))
  while OPTARG= && [ $# -gt 0 ]; do
    set -- "${1%%\=*}" "${1#*\=}" "$@"
    while [ ${#1} -gt 2 ]; do
      case $1 in (*[!a-zA-Z0-9_-]*) break; esac
      case '--control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --control"
      esac
      case '--no-control' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-control"
      esac
      case '--directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --directory"
      esac
      case '--no-directory' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-directory"
      esac
      case '--option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --option"
      esac
      case '--no-option' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-option"
      esac
      case '--verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --verbose"
      esac
      case '--no-verbose' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --no-verbose"
      esac
      case '--help' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --help"
      esac
      case '--version' in
        "$1") OPTARG=; break ;;
        $1*) OPTARG="$OPTARG --version"
      esac
      break
    done
    case ${OPTARG# } in
      *\ *)
        eval "set -- $OPTARG $1 $OPTARG"
        OPTIND=$((($#+1)/2)) OPTARG=$1; shift
        while [ $# -gt "$OPTIND" ]; do OPTARG="$OPTARG, $1"; shift; done
        set "Ambiguous option: $1 (could be $OPTARG)" ambiguous "$@"
        option_error "$@" >&2 || exit $?
        echo "$1" >&2
        exit 1 ;;
      ?*)
        [ "$2" = "$3" ] || OPTARG="$OPTARG=$2"
        shift 3; eval 'set -- "${OPTARG# }"' ${1+'"$@"'}; OPTARG= ;;
      *) shift 2
    esac
    case $1 in
      --?*=*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
        ;;
      --no-*|--without-*) unset OPTARG ;;
      -[co]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
        ;;
      -[dvhV]?*) OPTARG=$1; shift
        eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
        OPTARG= ;;
      +*) unset OPTARG ;;
    esac
    case $1 in
      '-c'|'--control'|'--no-control')
        set -- "$1" "$@"
        [ ${OPTARG+x} ] && {
          case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
          [ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='1-'
        } || OPTARG=''
        regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^$|^[1-9][0-9]*(-([1-9][0-9]*)?)?(,[1-9][0-9]*(-([1-9][0-9]*)?)?)*$"; break; }
        control="$OPTARG"
        shift ;;
      '-d'|'--directory'|'--no-directory')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG='0'
        dirFlag="$OPTARG"
        ;;
      '-o'|'--option')
        [ $# -le 1 ] && set "required" "$1" && break
        OPTARG=$2
        regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$" || { set -- regex_match:$? "$1" regex_match "${OPTARG}" "^[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?(=.*)?$"; break; }
        add_option "options" "${OPTARG}"
        shift ;;
      '--no-option')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG='' || OPTARG=''
        options="$OPTARG"
        ;;
      '-v'|'--verbose'|'--no-verbose')
        [ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
        eval '[ ${OPTARG+x} ] &&:' && OPTARG=1 || OPTARG=-1
        verboseFlag="$((${verboseFlag:-0}+$OPTARG))"
        ;;
      '-h'|'--help')
        usage
        exit 0 ;;
      '-V'|'--version')
        echo "${NAME} ${VERSION}"
        exit 0 ;;
      --)
        shift
        while [ $# -gt 0 ]; do
          REST="${REST} \"\${$(($OPTIND-$#))}\""
          shift
        done
        break ;;
      [-+]?*) set "unknown" "$1"; break ;;
      *)
        REST="${REST} \"\${$(($OPTIND-$#))}\""
    esac
    shift
  done
  [ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
  case $1 in
    unknown) set "Unrecognized option: $2" "$@" ;;
    noarg) set "Does not allow an argument: $2" "$@" ;;
    required) set "Requires an argument: $2" "$@" ;;
    pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
    notcmd) set "Not a command: $2" "$@" ;;
    *) set "Validation error ($1): $2" "$@"
  esac
  option_error "$@" >&2 || exit $?
  echo "$1" >&2
  exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage:
  package2sfs [OPTION]... DEST [PACKAGE]...

Options:
  -c,     --{no-}control[=PACKAGE_NUMBER_LIST] 
                              build package control file
  -d,     --{no-}directory    do not make SquashFS but directory
  -o,     --option NAME[=VALUE] 
                              option of mksquashfs command
          --no-option         reset -o, --option
  -v,     --{no-}verbose      enable verbose output
  -h,     --help              display this help and exit
  -V,     --version           output version information and exit

Exit Status:
    0 - successful termination
   64 - command line usage error
   65 - data format error
   66 - cannot open input
   67 - addressee unknown
   68 - host name unknown
   69 - service unavailable
   70 - internal software error
   71 - system error (e.g., can't fork)
   72 - critical OS file missing
   73 - can't create (user) output file
   74 - input/output error
   75 - temp failure; user is invited to retry
   76 - remote error in protocol
   77 - permission denied
   78 - configuration error
  129 - received SIGHUP
  130 - received SIGINT
  131 - received SIGQUIT
  143 - received SIGTERM

Examples:
  package2sfs output.sfs *.deb
  package2sfs -cv output.sfs *.pet
  package2sfs -c1,3-4,5- output.sfs pkg1.tgz pkg2.tgz pkg3.tgz pkg4.tgz pkg5.tgz
  package2sfs -o b=1048576 -o comp=xz -o Xdict-size=100% output.sfs dir
  package2sfs output.sfs 'http://www.example.com/example_1.0.0_i386.sfs'
GETOPTIONSHERE
}
# Generated by getoptions (END)
# @end

parse ${@+"${@}"}
eval "set -- ${REST}"

# コマンドライン引数が不足している
case "${#}" in '0')
	printf '%s: Requires an argument: DEST\n' "${0##*/}" >&2
	printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

	end_call "${EX_USAGE}"
esac

# コマンド毎に詳細表示を行うか
if [ '2' -le "${verboseFlag}" ]; then
	prog_v='1'
fi

# 先頭の位置パラメータから宛先を取り出す
dest="${1}"
shift

# 一時ディレクトリを作成
case "${verboseFlag}" in [!0]*)
	printf '# Make temporary directory and temporary file #\n'
esac

awkRangeCheckScript=$(
	cat <<-'__EOF__'
	BEGIN {
		number = ARGV[1]
		split(ARGV[2], ranges, ",")

		for(i in ranges) {
			if(index(ranges[i], "-")) {
				split(ranges[i], array, "-")

				if(array[1] != "" && array[2] != "" && array[2] < array[1]) {
					tmp = array[1]
					array[1] = array[2]
					array[2] = tmp
				}

				if((array[1] == "" || array[1] <= number) && (array[2] == "" || number <= array[2])) {
					exit
				}
			} else if(ranges[i] == number) {
				exit
			}
		}

		exit 1
	}
	__EOF__
)
tmpDir=$(mktemp -d)
tmpContent="${tmpDir}/content"
tmpDownload="${tmpDir}/download"
tmpPackage="${tmpDir}/package"

mkdir -p ${prog_v+-v} -- "${tmpContent}" "${tmpDownload}" "${tmpPackage}"

# 各パッケージを処理
for package in ${@+"${@}"}; do
	index=$((${index:-0} + 1))
	controlFlag='0'

	if awk -- "${awkRangeCheckScript}" "${index}" "${control}"; then
		controlFlag='1'
	fi

	# URL ならばタウンロード
	case "${package}" in 'http://'*/* | 'https://'*/* | 'ftp://'*/*)
		filename=$(basename -- "${package}")

		case "${verboseFlag}" in [!0]*)
			printf "# Download '%s' #\\n" "${package}"
		esac

		if command -v 'wget' >'/dev/null'; then
			wget --no-verbose --output-document "${tmpDownload}/${filename}" ${prog_v+-v} -- "${package}"
		elif command -v 'curl' >'/dev/null'; then
			curl --compressed --fail --location --output "${tmpDownload}/${filename}" --silent --show-error ${prog_v+-v} -- "${package}"
		else
			printf "'wget' or 'curl' is required.\\n" >&2
			printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

			end_call "${EX_UNAVAILABLE}"
		fi

		package="${tmpDownload}/${filename}"
	esac

	# 読み込みの許可がない
	if [ '!' -r "${package}" ]; then
		printf "%s: No permission to read '%s'.\\n" "${0##*/}" "${package}" >&2
		printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

		end_call "${EX_NOINPUT}"
	# ディレクトリ
	elif [ -d "${package}" ]; then
		case "${verboseFlag}" in [!0]*)
			printf "# Copy files in '%s' #\\n" "${package}"
		esac

		find -- "${package}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

		continue
	elif [ '!' -f "${package}" ]; then
		printf "%s: '%s' is not a regular file.\\n" "${0##*/}" "${package}" >&2
		printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

		end_call "${EX_DATAERR}"
	fi

	# パッケージファイルの種類によって処理を分岐
	case "${package}" in
		# Debian パッケージ
		*[!./]'.deb') deb2dir "${package}" "${tmpContent}";;
		# Puppy's Extra Treats
		*[!./]'.pet') pet2dir "${package}" "${tmpPackage}";;
		# RPM パッケージ
		*[!./]'.rpm') rpm2dir "${package}" "${tmpPackage}";;
		# Slackware GZIP or Slackware XZ
		*[!./]'.tgz' | *[!./]'.txz') pkg2dir "${package}" "${tmpPackage}";;
		# イメージファイル (ext2, ext3, ext4) と SquashFS ファイル
		*[!./].[234]fs | *[!./]'.sfs')
			case "${verboseFlag}" in [!0]*)
				printf "# Mount '%s' #\\n" "${package}"
			esac

			case "${package}" in
				*[!./].[234]fs) mount -o 'loop' -r -t 'ext2,ext3,ext4' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
				*[!./]'.sfs') mount -r -t 'squashfs' ${prog_v+-v} -- "${package}" "${tmpPackage}";;
			esac

			case "${verboseFlag}" in [!0]*)
				printf "# Copy the files in '%s' #\\n" "${package}"
			esac

			find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'

			case "${verboseFlag}" in [!0]*)
				printf "# Unmount '%s' #\\n" "${package}"
			esac

			umount -d ${prog_v+-v} -- "${tmpPackage}"
			;;
		# 互換性のないファイル
		*)
			printf "%s: '%s' is not a compatible file.\\n" "${0##*/}" "${package}" >&2
			printf "Try '%s' for more information.\\n" "${0##*/} --help" >&2

			end_call "${EX_DATAERR}"
			;;
	esac

	# 解凍したファイルをメインのディレクトリに移動
	find -- "${tmpPackage}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}" ';'
	find -- "${tmpPackage}/" -path '*[!/]' -prune -exec rm -fr ${prog_v+-v} -- '{}' '+'
done

# 一部ディレクトリの内容を上位ディレクトリに移動
for path in '/lib/i386-linux-gnu' '/usr/bin/i386-linux-gnu' '/usr/lib/i386-linux-gnu'; do
	if [ -d "${tmpContent}${path}" ]; then
		case "${verboseFlag}" in [!0]*)
			printf "# Move '%s' #\\n" "${path}"
		esac

		find -- "${tmpContent}${path}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${tmpContent}${path%/*}" ';'
		rm -fr ${prog_v+-v} -- "${tmpContent}${path}"
	fi
done

# ${dirFlag} による処理の分岐
case "${dirFlag}" in
	'1')
		# 一時ディレクトリの内容を ${dest} にコピー
		case "${verboseFlag}" in [!0]*)
			printf "# Copy temporary directory to '%s' #\\n" "${dest}"
		esac

		mkdir -p ${prog_v+-v} -- "${dest}"
		find -- "${tmpContent}/" -path '*[!/]' -prune -exec cp -fRP ${prog_v+-v} -- '{}' "${dest}" ';'
		;;
	*)
		# SquashFS ファイルを作成
		case "${verboseFlag}" in [!0]*)
			printf "# Make '%s' #\\n" "${dest}"
		esac

		eval 'mksquashfs "${tmpContent}" "${dest}"' "${options}"
		;;
esac
package2sfs は現在、以下の内容から Puppy Linux 用の新たな SFS ファイルを作成できます。
  • ディレクトリ
  • Debian パッケージ (*.deb)
  • PET パッケージ (*.pet)
  • RPM パッケージ (*.rpm)
  • Slackware パッケージ (*.tgz, *.txz)
  • SquashFS(SFS) ファイル (*.sfs)
  • ext2, ext3, ext4 のイメージファイル (*.2fs, *.3fs, *.4fs)
2023年02月22日追記

*.txz が処理できないバグを修正しました。バージョンは2.2.1になりました。

2024年01月11日追記

'/lib/i386-linux-gnu', '/usr/bin/i386-linux-gnu', '/usr/lib/i386-linux-gnu' における処理を少し改善しました。これで 'devx_precise_5.7.1.sfs' などを正常に展開できます。バージョンは2.2.2になりました。
💻Lenovo G550(CPU: Intel Celeron 900 @ 2.20GHz, RAM: 971MB, HDD: 149.05GiB) + Precise-571JP
🖥HITACHI FLORA 310 DL7(CPU: Intel Pentium III 700 MHz, RAM: 384MB) + Precise-571JP
返信する