#!/bin/sh
#/
#/ \file
#/
#/ \brief       create or remove files after clean reboot
#/
#/ \author      Arthur
#/
#/ \date        December 21, 2012
#/
#/ \version     0.2
#/
#/ \todo
#/
#/ * Allow file mode and owner to be set when creating files or directories.
#/


################################################################################
#                                                                              #
#                              C O N S T A N T S                               #
#                                                                              #
################################################################################


RC_NAME=`basename $0 2>/dev/null`
RC_DIR=`dirname \`readlink -qe $0\` 2>/dev/null`
RC_VER="0.2"
RC_ID="file preparations reboot"

#
# Files and/or directories to cleanup.
# File patterns are allowed.
#
CLEAN_LIST="/tmp /var/lock /var/run"

#
# Files and/or directories to create.
# Directories must have a trailing `/',
# otherwise it will be taken as a file.
#
CREATE_LIST="/var/run/utmp /var/log/btmp /var/log/wtmp"


################################################################################
#                                                                              #
#                               I N C L U D E S                                #
#                                                                              #
################################################################################


. $RC_DIR/include/rc_utils


################################################################################
#                                                                              #
#                              F U N C T I O N S                               #
#                                                                              #
################################################################################


#-------------------------------------------------------------------------------


#/
#/ Output script usage and exit.
#/
#/ \returns
#/
#/ Always exit code 1.
#/
usage()
{
    $ECHO "Usage: $RC_NAME [-n][-q][-v][-h][-V]"

    exit 1
}


#-------------------------------------------------------------------------------


#/
#/ Output script version and exit.
#/
#/ \returns
#/
#/ Always exit code 1.
#/
version()
{
    $ECHO "$RC_NAME $RC_VER"

    exit 1
}


#-------------------------------------------------------------------------------


#/
#/ Cleanup files and directories. The given list of files and directories does
#/ not need to be in a specific order, nor does every item on the list has to
#/ exist. A file or directory which does not exist is simply ignored.
#/
#/ Filename expansion is performed on the list, so wildcards in files or
#/ directories are allowed.
#/
#/ \remarks
#/
#/ - The <tt>lost+found</tt> directory is always omitted.
#/ - Will not descent into directories of other filesystems.
#/
#/ \param[in] $1  List of files and/or directories to clean
#/                (whitespace-separated).
#/
clean()
{
    clean_list=$1
    file=""
    
    $ECHO_N "Cleaning Files and Directories..."

    for file in $clean_list
    do
        #
        # Determine file type and run appropriate command. Note, the original
        # Bourne shell does not support the `-e' of the `test' builtin, so we
        # have to check explicitly for a regular file, a socket, a symlink etc.
        #
        if test -f "$file" -o -h "$file" -o -p "$file" -o -b "$file" -o -c "$file"
        then                        # file
            $ECHO_N " $file"

            $DRY_RUN rm -f $file 2>/dev/null
        elif test -d "$file"        # directory
        then
            $ECHO_N " $file"

            clean_dir $file
        else                        # something non-existent
            :
        fi
    done

    $ECHO
}


#-------------------------------------------------------------------------------


#/
#/ Recursively clean directory. The function traverses the given directory and
#/ will remove the files and sub-directories in it. However, the function will
#/ not cross filesystems, so such a case has to be handled separately. Also,
#/ the <tt>lost+found</tt> directory will never be removed since it should be
#/ present on every filesystem.
#/
#/ \param[in] $1  Directory to clean.
#/
clean_dir()
{
    dir=$1
    file=""
    list=""
    
    # clean directory, but preserve `lost+found':
    list=`find $dir -xdev -mindepth 1 \( -name lost+found -prune -o -print \) 2>/dev/null`
    for file in $list
    do
        if test -d "$file"
        then
            # don't remove mountpoint directories:
            mountpoint -q $file 2>/dev/null
            if test $? -ne 0                # not a mountpoint
            then
                $DRY_RUN rm -fr $file 2>/dev/null
            fi
        else
            $DRY_RUN rm -f $file 2>/dev/null
        fi
    done
}


#-------------------------------------------------------------------------------


#/
#/ Create files and directories. A file or directory is only created if it
#/ didn't exist already, so files won't get overwritten or truncated. Note
#/ that such an occurrence is not considered an error.
#/
#/ When creating a directory, the filename must end in a slash (<tt>/</tt>)
#/ otherwise a regular file is created instead. Creating a directory tree is
#/ supported, so a parent directory and its sub-directories don't have to be
#/ created in separate steps.
#/
#/ \param[in] $1  List of files and/or directories to create
#/                (whitespace-separated).
#/
create()
{
    create_list=$1
    file=""
    type=""
    mode=""
    len=0
    last_ch=""
    
    $ECHO_N "Creating Files and Directories..."

    for file in $create_list
    do
        # get last character of file string:
        len=`expr length "$file" 2>/dev/null`
        last_ch=`expr substr "$file" "$len" 1 2>/dev/null`

        if test -n "$last_ch"       # should not be empty string
        then
            # check whether to create a file or a directory:
            if test "$last_ch" = "/"
            then
                type=d; mode=755
            else
                type=f; mode=644
            fi
            create_file $file f "" $mode
                   
            $ECHO_N " $file"
        fi
    done

    $ECHO
}


#-------------------------------------------------------------------------------


#/
#/ Create file or directory. File mode and ownership can be specified, but
#/ these parameters are optional. If they are omitted the file mode defaults to
#/ the user's \c umask setting while the file ownership defaults to the user
#/ and his/her primary group.
#/
#/ \param[in] $1  Name of file or directory to create.
#/
#/ \param[in] $2  One-letter file type:
#/                - \c f - Regular file
#/                - \c d - Directory
#/
#/ \param[in] $3  File owner (optional).
#/
#/ \param[in] $4  File mode (optional).
#/
#/ \returns
#/
#/ Return code 0 on success; the file was created and the file owner and
#/ file mode have been set, if they were specified. Code 1 is returned if
#/ any of these operations failed. Note that failing to create the file is
#/ also considered an error. Code 2 is returned in case of a usage error.
#/
create_file()
{
    assert create_file "-n '$1'"
    assert create_file "-n '$2'"
    
    file=$1
    type_s=$2        # type short text
    type_l=""        # type full text
    owner=$3
    mode=$4
    cmd=""
    ret=0
    
    case "$type_s" in
    f) cmd="touch";    type_l="file"      ;;
    d) cmd="mkdir -p"; type_l="directory" ;;
    *)
       error "unsupported file type: $type_s"
       return 2
       ;;
    esac

    #
    # We only need to test whether the file exists. The `-e' operator
    # of `test' is not portable, so we need to test for each file type.
    #
    if test -f $file -o -d $file -o -h $file -o -p $file -o -b $file -o -c $file
    then
        [ "$VERBOSE" = "1" ]  &&  $ECHO "$file: already exists"
        
        ret=1
    else
        [ "$VERBOSE" = "1" ]  &&  $ECHO "Creating $type_l \`$file'..."
        
        eval $DRY_RUN $cmd $file 2>/dev/null
        if test $? -ne 0
        then
            error "Failed creating \`$file'."
            
            ret=$?
        fi
        
        
        if test $ret -eq 0 -a -n "$mode"
        then
            [ "$VERBOSE" = "1" ]  && $ECHO "Setting mode of \`$file' to $mode."
            
            $DRY_RUN chmod $mode $file 2>/dev/null
            if test $? -ne 0
            then
                error "Failed setting mode to $mode."
                
                ret=$?
            fi
        fi
        
        if test $ret -eq 0 -a -n "$owner"
        then
            [ "$VERBOSE" = "1" ] && $ECHO "Setting owner of \`$file' to $owner."
            
            $DRY_RUN chown $owner $file 2>/dev/null
            if test $? -ne 0
            then
                error "Failed setting ownership to $owner."
                
                ret=$?
            fi
        fi
    fi
    
    return $ret
}


#-------------------------------------------------------------------------------


################################################################################
#                                                                              #
#                         S T A R T  O F  S C R I P T                          #
#                                                                              #
#                                                                              #
# Operation:                                                                   #
#                                                                              #
# o Process command line arguments.                                            #
# o Remove a number of directories in succession.                              #
#                                                                              #
################################################################################

QUIET=0                             # quiet flag (0 or 1)
VERBOSE=0                           # verbose flag (0 or 1)
DRY_RUN=                            # default is to actually execute commands

ACTION=""                           # action to perform...


#
# Handle command line OPTIONS.
#
while test $# -gt 0
do
    case "$1" in
    -n|--dry-run)
        DRY_RUN="$ECHO -e \n> "     # ensure we begin on a new line
        ;;
    -q|--quiet)
        QUIET=1
        exec >/dev/null             # mute stdout
        ;;
    -v|--verbose)
        VERBOSE=1
        ECHO_N=$ECHO                # don't inhibit newline;
                                    # this prevents clobbering of text
        ;;
    -h|--help)
        usage
        ;;
    -V|--version)
        version
        ;;
    -*)
        fatal "unknown option: $1"
        ;;
    *)                              # first non-OPTIONS argument
        break
        ;;
    esac
    shift
done

#       
# Handle influential environment variables.
#   
if test "$RC_VERBOSE" = "0" -o "$RC_VERBOSE" = "1"
then    
    VERBOSE=$RC_VERBOSE
fi      


clean  "$CLEAN_LIST"

create "$CREATE_LIST"
