#!/bin/bash
#
# list_ids_in_pkg
#

###
### settings
###

set -e                # exit on any uncaught error
set +o histexpand     # don't expand history expressions
shopt -s nocasematch  # case-insensitive regular expressions

###
### global variables
###

opt_lax=''
opt_pkg=''
pkgdir=''

# prefer GNU xargs
xargs="$(/usr/bin/which gxargs || printf '/usr/bin/xargs')"

###
### functions
###

bundle_id_source_1 () {
    /usr/bin/find "$pkgdir" -name PackageInfo -print0 |                                \
    "$xargs" -0 /usr/bin/perl -0777 -ne                                                \
      'while (m{<pkg-info.*?\sid(?:entifier)?\s*=\s*"([^"]*?)"}sg) { print "$1\n" }'
}

bundle_id_source_2 () {
    /usr/bin/find "$pkgdir" -name Info.plist -print0 |                                 \
    "$xargs" -0 -I {} /usr/libexec/PlistBuddy {} -c 'Print :CFBundleIdentifier'
}

merge_sources () {
    /usr/bin/sort | /usr/bin/uniq
}

clean_sources () {
    /usr/bin/egrep -v '^com\.apple\.' |                                                \
    /usr/bin/egrep -v 'sparkle\.finish-installation$'
}

mark_up_sources () {
    /usr/bin/perl -pe 's{\n}{\000}sg' |                                                \
    "$xargs" -0 -I{} -n1 /bin/bash -c                                                  \
      'printf "{}"; /usr/sbin/pkgutil --pkg-info "{}" >/dev/null 2>&1 && printf " (+)"; printf "\n"'
}

process_args () {
    local arg
    if [[ "$#" -eq 0 ]]; then
        die "ERROR: A file argument is required"
    else
        opt_pkg="${!#}"   # last arg
    fi
    for arg in "$@"; do
        if [[ $arg =~ ^-+h(elp)?$ ]]; then
            printf "
                list_ids_in_pkg [ -lax ] <file.pkg>

                Given a package file, extract a list of candidate Package IDs
                which may be useful in a Cask uninstall stanza, eg

                    uninstall pkgutil: 'package.id.goes.here'

                The given package file need not be installed.

                The output of this script should be overly inclusive -- not
                every candidate package id in the output will be needed at
                uninstall time.

                Package IDs designated by Apple or common development frameworks
                will be excluded from the output. This can be turned off with
                the -lax argument.

                If a package id is already installed, it will be followed by
                a plus symbol '(+)' in the output. This can be verified via
                the command

                    /usr/sbin/pkgutil --pkg-info 'package.id.goes.here'

                Arguments

                   -lax   Turn off filtering of Apple or common development
                          framework package IDs from the output.

                See CONTRIBUTING.md and 'man pkgutil' for more information.

            " | sed -E 's/^ {16}//'
            exit
        elif [[ $arg =~ ^-+lax$ ]]; then
            opt_lax='true'
        elif [[ "$arg" = "$opt_pkg" ]]; then
            true
        else
            die "ERROR: Unknown argument '$arg'"
        fi
    done
    if [[ -h "$opt_pkg" ]]; then
        opt_pkg="$(/usr/bin/readlink "$opt_pkg")"
    fi
    if ! [[ -e "$opt_pkg" ]]; then
        die "ERROR: No such pkg file: '$opt_pkg'"
    fi
}

###
### main
###

_list_ids_in_pkg () {

    if [[ -d "$opt_pkg" ]]; then
        pkgdir="$opt_pkg"
    else
        local tmpdir="$(/usr/bin/mktemp -d -t list_ids_in_pkg)"
        trap "/bin/rm -rf -- '$tmpdir'" EXIT
        pkgdir="$tmpdir/unpack"
        /usr/sbin/pkgutil --expand "$opt_pkg" "$tmpdir/unpack" "$pkgdir"
    fi

    {
      # emit strings that look like bundle ids
      bundle_id_source_1;
      bundle_id_source_2;
    } |                     \
    merge_sources |         \
    ( [[ ! "$opt_lax" ]] && clean_sources || cat ) | \
    mark_up_sources

}

process_args "${@}"

# dispatch main
_list_ids_in_pkg

#
