#!/bin/sh
#
# ------------------------------------------------------------------
#  pidin.sh
# 
#  October 2010, Michael C. Scott
# 
#  Copyright (c) 2010-2011, 2013 by Cisco Systems, Inc.
#  All rights reserved.
# ------------------------------------------------------------------


# The pidin script begins by parsing the input arguments using getopts to parse
# the options and a simple loop to handle arguments. Most options/arguments
# result in pidin iterating over processes/threads and printing column
# formatted data.
#
# The data to be printed is specified in one of two ways:
# 1. By a format string passed in using the "-f" or "-F" options.
# 2. Some arguments set a pre-defined format string.
#
# If the string was specified by the user it is checked to ensure that it
# contains valid characters. Next the string is converted into:
# 1. A printf format string
# 2. A list of headers
# 3. A list of items to retrieve
#
# The script then prints the headers using the printf format string and the
# list of headers. Next it iterates over all running processes or threads.
# Whether iteration is over processes or threads is determined by the
# characters used in the formatting string.
#
# For each process/thread the relevant data is retrieved and then printed
# using the printf format string.
#
# Arguments/options that don't involve printing information on a per process
# or per thread basis are documented individually.


# PIDIN_FORMAT_CHARACTERS
#
# A string that contains all the allowed formatting characters.
# Certain arguments trigger the script to use other formatting characters
# which cannot be passed in using the "-f" and "-F" options. These are listed
# below.
# Q - session
# q - start time (process)
# R - utime
# r - stime
# t - cutime
# U - cstime
# u - start time (thread)
# V - sutime
# v - sibling
# W - child
# w - user id
# X - group id
# x - effective user id
# Y - effective group id
# y - saved user id
# Z - saved group id
PIDIN_FORMAT_CHARACTERS="AaBbcdEehIiJlmNnPpSsT"


# pidin_process_group_filter
#
# If the "-P <pgrp>" option is passed to pidin it will filter the results so
# that only processes in the specified process group are shown. This variable
# holds the process group to filter on or -1 if no filtering is to be applied.
pidin_process_group_filter=-1


# pidin_sort_by_session
#
# If the "session" argument is passed to pidin then it will sort the results
# by session id rather than by process id.
pidin_sort_by_session=0


# pidin_runmask_count and pidin_file_descriptor_count
#
# The runmask ('i') formatting charactes is processed slightly differently from
# other formatting characters. The relevant information for this formatting
# character is displayed on a separate line rather than in a column. Each
# occurence of the relevant letter in the format string results in one copy
# of the relevant information being printed per thread/process.
pidin_runmask_count=0


# pidin_print_per_thread
#
# By default pidin only prints information on a per process basis. If certain
# formatting characters appear in the formatting string this switches to a per
# thread basis. This flag tracks which mode is in use.
pidin_print_per_thread=0


# pidin_format_string
#
# Holds the format string that will be used to determine what information to
# print for each process/thread. A default value is specified but this can be
# overridden by the "-f" or "-F" options. In addition various arguments are
# synonyms for using the "-F" otion with a given format string.
pidin_format_string="%a %b %N %p %J %B"


# pidin_output_*
#
# These variables store information about the information that should be
# output and the format to output it in.
# pidin_output_format stores the format string that will be passed to printf.
# pidin_output_argument is an array that lists which field should be printed
# in each column.
# pidin_output_headers is an array that contains the headers for each of the
# columns.
# The number of items in the two arrays will correspond to the number of
# items in the format string.
pidin_output_format=""
pidin_output_arguments=()
pidin_output_headers=()


# pidin_exit_on_error
#
# If the "-k" option is passed to pidin then it will exit immediately if an
# error is encountered. In general an error could occur if pidin tries to
# retrieve information regarding a process/thread that has terminated.
pidin_exit_on_error=0


# Load the show_proc / pidin library.
source `dirname $0`/sh_proc_lib


# pidin_prepare_format_string
#
# Uses the pidin_format_string variable to generate the "pidin_output_format",
# "pidin_output_arguments" and "pidin_output_headers variables".
# The function iterates over each token in the format string and adds the
# appropriate values to the output variables.
#
# Return: 0 if the string is successfully prepared, 1 if an error occurs
#
function pidin_prepare_format_string
{
    local ret_code=0
    
    local argument_length=""           # Stores the default length of fields
    local specified_argument_length="" # Stores the specified field length
    local argument_character=""        # Stores the formatting character
    local argument_name=""             # Stores the field name
    local argument_header=""           # Stores the field header
    local alignment=""                 # Used if a field needs alignment
    local add_column=1                 # Specifies whether a column should be
                                       # added for the field (only negated for
                                       # the run mask field).

    local output_format=()             # Stores the formatting string while
                                       # under construction.
    
    #Iterate over each token in the format string
    for format in $pidin_format_string; do
        alignment=""
        add_column=1

        # Extract the formatting character from the token
        # e.g "%60N" => 'N'
        argument_character=`echo $format | tr -d %[:digit:]`

        # Extract the field length (if any) from the token
        # e.g "%60N" => 60
        specified_argument_length=`echo $format | tr -d %[:alpha:]`

        # For each possible formatting character we set a field length, field
        # name and field header. In addition some formatting characters trigger
        # other options such as:
        # 1) Switching pidin to print per thread info not per process info.
        # 2) Setting alignment of the field.
        # 3) For the runmask ffields we need to track the number of occurences
        #    as it is printed differently.
        
        case "$argument_character" in
            "A")
                argument_length=""
                argument_name="args"
                argument_header="Arguments     "
                ;;
            "a")
                argument_length="5"
                argument_name="process_id"
                argument_header="pid"
                ;;
            "B")
                argument_length="10"
                argument_name="blocked_on"
                argument_header="Blocked"
                pidin_print_per_thread=1
                ;;
            "b")
                argument_length="5"
                argument_name="thread_id"
                argument_header="tid"
                pidin_print_per_thread=1
                ;;
            "c")
                argument_length="5"
                argument_name="code_size"
                argument_header="code"
                ;;
            "d")
                argument_length="5"
                argument_name="data_size"
                argument_header="data"
                ;;
            "E")
                argument_length=""
                argument_name="env"
                argument_header="Environment"
                ;;
            "e")
                argument_length="8"
                argument_name="ppid"
                argument_header="ppid"
                ;;
            "h")
                argument_length="15"
                argument_name="threadname"
                argument_header="thread name"
                pidin_print_per_thread=1
                alignment="-"
                ;;
            "I")
                argument_length="11"
                argument_name="process_and_thread_id"
                argument_header="pid-tid"
                pidin_print_per_thread=1
                ;;
            "i")
                let pidin_runmask_count+=1
                add_column=0
                ;;
            "J")
                argument_length="8"
                argument_name="thread_state"
                argument_header="STATE"
                pidin_print_per_thread=1
                ;;
            "l")
                argument_length="3"
                argument_name="last_cpu"
                argument_header="cpu"
                pidin_print_per_thread=1
                ;;
            "m")
                argument_length="5"
                argument_name="stack_size"
                argument_header="stack"
                pidin_print_per_thread=1
                ;;
            "N")
                argument_length="18"
                if [[ $specified_argument_length != "" ]]; then
                    argument_length=$specified_argument_length
                fi
                argument_name="name$argument_length"
                argument_header="name"
                alignment="-"
                ;;
            "n")
                argument_length=""
                argument_name="long_name"
                argument_header="name     "
                alignment="-"
                ;;
            "P")
                argument_length="5"
                argument_name="pgroup"
                argument_header="pgrp"
                ;;
            "p")
                argument_length="4"
                argument_name="priority"
                argument_header="prio"
                pidin_print_per_thread=1
                ;;
            "Q")
                argument_length="10"
                argument_name="session"
                argument_header="sid"
                ;;
            "q")
                argument_length="12"
                argument_name="procstarttime"
                argument_header="start time"
                ;;
            "R") 
                argument_length="7"
                argument_name="utime"
                argument_header="utime"
                ;;
            "r")
                argument_length="7"
                argument_name="stime"
                argument_header="stime"
                ;;
            "S")
                argument_length="16"
                argument_name="sig_ign"
                argument_header="signals ignored"
                pidin_print_per_thread=1
                ;;
            "s")
                argument_length="16"
                argument_name="sig_pend"
                argument_header="signals pending"
                pidin_print_per_thread=1
                ;;
            "T")
                argument_length="2"
                argument_name="n_threads"
                argument_header="nT"
                ;;
            "t")
                argument_length="7"
                argument_name="cutime"
                argument_header="cutime"
                ;;
            "U")
                argument_length="7"
                argument_name="cstime"
                argument_header="cstime"
                ;;
            "u")
                argument_length="12"
                argument_name="starttime"
                argument_header="thread start"
                ;;
            "V")
                argument_length="7"
                argument_name="sutime"
                argument_header="sutime"
                ;;
            "v")
                argument_length="8"
                argument_name="zero"
                argument_header="sibling"
                ;;
            "W")
                argument_length="8"
                argument_name="zero"
                argument_header="child"
                ;;

            "w")
                argument_length="6"
                argument_name="uid"
                argument_header="uid"
                ;;

            "X")
                argument_length="6"
                argument_name="gid"
                argument_header="gid"
                ;;

            "x")
                argument_length="6"
                argument_name="euid"
                argument_header="euid"
                ;;

            "Y")
                argument_length="6"
                argument_name="egid"
                argument_header="egid"
                ;;

            "y")
                argument_length="6"
                argument_name="suid"
                argument_header="suid"
                ;;

            "Z")
                argument_length="6"
                argument_name="sgid"
                argument_header="sgid"
                ;;
            *)
                # Unrecognised formatting character. This shouldn't occur as
                # the string has already been checked or was specified
                # internally.
                ret_code=1
                ;;
        esac

        if [ $add_column -eq 1 ]; then
            # If no length was specified in the format string then we use the
            # default length for that field.
            if [[ $specified_argument_length != "" ]]; then
                argument_length=$specified_argument_length
            fi

            # Add the appropriate text to the format array
            # e.g add "%N.Ns" where N is the length of the field.
            # Note: if the length is blank (i.e length is unbound) we just add "%s"
            if [[ $argument_length == "" ]]; then
                output_format=(${output_format[@]} "%$alignment""s")
            else
                output_format=(${output_format[@]} "%$alignment$argument_length.$argument_length""s")
            fi

            # Add the appropriate field name to the output_arguments array
            pidin_output_arguments=(${pidin_output_arguments[@]} "$argument_name")

            # Add the appropriate header to the output_headers array
            pidin_output_headers=("${pidin_output_headers[@]}" "$argument_header")
        fi
    done

    # Convert the format array into a format string.
    pidin_output_format="${output_format[@]}"

    return $ret_code
}


# pidin_check_format_string
# 
# Checks the "pidin_format_string" global variable to ensure it contains space
# separated tokens of the form "%(n)x" where n is an optional integer and x
# is one of the allowed formatting characters.
#
# Return: 0 if the string passes the check, 1 otherwise
#
function pidin_check_format_string
{
    local ret_code=0
    
    local tokens=($pidin_format_string)
    # Check the string contains at least 1 token.
    if [ ${#tokens} -eq 0 ]; then
        ret_code=1   
    else
        for format in $pidin_format_string; do
            # Check each token is of the form "$(n)x".
            if [[ $format =~ ^%[0-9]*[$PIDIN_FORMAT_CHARACTERS]$ ]]; then
                :
            else
                ret_code=1
            fi
        done
    fi

    return $ret_code
}


# pidin_check_format_string_contiguous
#
# Checks a contiguous format string (i.e one passed to the "-f" option). Checks
# each character is in the list of allowed characters and creates an
# apppropriate non-contiguous format string (i.e "-f abN" is converted to
# "%a %b %N"). This non-contiguous string can then be treated as if the user
# used the "-F" option.
#
# Return: 0 if the string passes the check, 1 otherwise
#
# Argument: format_string_contiguous
#    IN - The string to be checked.
#
function pidin_check_format_string_contiguous
{
    local format_string_contiguous="$1"

    local ret_code=0

    pidin_format_string=""
    local i=0
    while [[ $i -lt ${#format_string_contiguous} ]]; do
        if [[ ${format_string_contiguous:$i:1} =~ [$PIDIN_FORMAT_CHARACTERS] ]]; then
            pidin_format_string+="%"${format_string_contiguous:$i:1}" "
        else
            ret_code=1
        fi
        let "i += 1"
    done

    return $ret_code
}


# pidin_info
#
# Prints information about the system and then exists.
#
# Return: None
#
function pidin_info {
    # Get /proc/cpuinfo
    local cpu_info=`cat /proc/cpuinfo`

    # Format the CPU information
    local cpu_vendor=`echo "$cpu_info" | grep "^vendor_id" | cut -d':' -f 2 | head -n 1`
    local cpu_string="CPU:"$cpu_vendor

    # Get /proc/meminfo
    local mem_info=`cat /proc/meminfo`

    # Format the memory information
    local free_mem=`echo "$mem_info" | grep ^MemFree: | cut -d':' -f 2 | tr -d [:alpha:]`
    local buffer_mem=`echo "$mem_info" | grep ^Buffers: | cut -d':' -f 2 | tr -d [:alpha:]`
    local cached_mem=`echo "$mem_info" | grep ^Cached: | cut -d':' -f 2 | tr -d [:alpha:]`
    local actual_free_mem=0
    let actual_free_mem=(free_mem + buffer_mem + cached_mem)/1024

    local total_mem=`echo "$mem_info" | grep ^MemTotal: | cut -d':' -f 2 | tr -d [:alpha:]`
    let total_mem/=1024
    local mem_string="FreeMem:"$actual_free_mem"Mb/"$total_mem"Mb"    

    # Calculate the boot time of the system and format the information
    local uptime=`cat /proc/uptime | cut -d' ' -f 1`
    local boot_time=`date -d "-$uptime sec"`
    local boot_string="BootTime:$boot_time"
    
    # Print the CPU, memory and boot time information.
    printf "$cpu_string $mem_string $boot_string\n"

    # Loop over each processor, retrieving the appropriate information and
    # print it.
    local processors=(`echo "$cpu_info" | grep "^processor" | cut -d':' -f 2`)
    local oldIFS=$IFS
    IFS='\n'
    local models=("`echo "$cpu_info" | grep "^model name" | cut -d':' -f 2`")
    IFS=$oldIFS
    local speeds=("`echo "$cpu_info" | grep "^cpu MHz" | cut -d':' -f 2`")

    local index=0
    while [ $index -lt ${#processors[@]} ]; do
        printf "Processor${processors[$index]}: ${models[$index]} ${speeds[$index]} MHz\n"
        let index+=1
    done

    exit 0
}


# pidin_usage
#
# Prints a usage message and then exits.
#
function pidin_usage {
    printf "%s\n" "Displays information about active processes/threads or system information.

Usage is of the form: pidin (options) (arguments)

If no format string is specified using the \"-F\" or \"-f\" options and no argument
defines the information to be printed then the default behaviour is equivalent
to passing \"-f abNpJB\".

Available options are:
    -F 'format string'
        Specifies the information to be printed using a space separated format
        string such as \"-F '%a %b %25N'\". The width of each field may be
        specified using an optional number.
        See below for details of the format string characters.

    -f 'contiguous format string'
        Specifies the information to be printed using a contiguous format
        string such as \"-f abN\".
        See below for details of the format string characters.

    -h
        Prints this help message.

    -k
        Causes pidin to exit immediately if an error occurs. The most common
        error is a race condition in which a process that existed when listing
        the active processes does not exist when pidin atttempts to retrieve
        information regarding that process.

    -n <node>
        Runs the pidin command on the specified node.

    -o <priority>
        Runs the pidin command at the specific priority.

    -P <PID>
        Only displays processes/threads that are members of the specified
        process group.

    -p <PID>
        Only displays processes/threads that match the specified process ID.

Available arguments are:
    family      Displays the session ID, process group and parent process for
                each active process. Sibling and child process fields are also
                printed but these fields are not supported in the Linux
                implementation and are for formatting consistency only.

    info        Prints system information regarding CPU, memory and boot time.

    net         Prints system information for all nodes.

    rc          Prints information about processes on remote nodes that are
                connected to the current node.

    session     Sorts the results by session ID instead of process ID.

    times       Prints CPU time information for each process.

    ttimes      Prints time information for each thread.

    user        Prints information about the real, effective and saved user and
                group IDs for active processes.

    arg         Displays the arguments to each process.
                This argument is a synonym for \"pidin -f aA\".

    env         Displays the environment variable for each process.
                This argument is a synonym for \"pidin -f aNE\".

    pmem        Displays information about the memory usage of each
                process/thread.
                This argument is a synonym for \"pidin -f abNpJcdm\".

    rmasks      Displays the runamsk for each process.
                This argument is a synonym for \"pidin -f abNi\".

    sig/si      Displays the ignored and pending signal masks for each
                process/thread.
                This argument is a synonym for \"pidin -f abNSs\".

    threads     Displays information for each thread.
                This argument is a synonym for \"pidin -f aNhJB\".

Format string characters (for the \"-F\" and \"-f\" options) :
    A - The process arguments 
    a - Process ID
    B - What the process is blocked on
    b - Thread ID
    c - Code size
    d - Data size
    E - The environment variable
    e - Parent ID
    h - Thread name
    I - Process ID and Thread ID
    i - Runmask
    J - Thread state
    l - Last CPU the thread ran on
    m - Stack size
    N - Process name (long)
    n - Process name (short)
    P - Process group
    p - Priority
    S - Ignored signals mask
    s - Pending signals mask
    T - Number of threads"

    exit 1
}


# pidin_command_parser
#
# Parses the input to the pidin command. The parser uses getopts to interpret
# options that have been passed in. Once the options have been parsed the
# arguments are interpreted. As a result of this design all options must be
# passed in before arguments. If this is not done the program will exit.
#
function pidin_command_parser
{
    # First we process the options
    while getopts ":F:f:hkn:o:P:p:hd:lM:" Option
    do
        case "$Option" in
            F)
                pidin_format_string="$OPTARG"
                pidin_check_format_string
                if [ $? != 0 ]; then
                    printf "Error in format string: %s\n" "$OPTARG"
                    pidin_usage
                fi
                ;;
            f)
                pidin_check_format_string_contiguous "$OPTARG"
                if [ $? != 0 ]; then
                    printf "Error in format string: $OPTARG\n"
                    pidin_usage
                fi
                ;;
            h)
                pidin_usage
                ;;
            k)
                # Exit on error option, set the appropriate global variable.
                pidin_exit_on_error=1
                ;;
            n)
                # Specifies a remote node to use.
                if [[ $OPTARG =~ ^[0-9]*/[0-9]*/CPU[0-9]*$ ]]; then
                    sh_proc_location=$OPTARG
                else
                    printf "Incorrect node format\n"
                    pidin_usage
                fi
                ;;
            o)
                # Run at the specified priority, using the renice command
                # Check it is numeric
                if [[ $OPTARG =~ ^[0-9]*$ ]]; then
                    # Check it is within the QNX range of 1-63
                    if [ $OPTARG -lt 1 -o $OPTARG -gt 63 ]; then
                        printf "The specified priority was outside the range 1-63\n"
                        pidin_usage
                    fi
                else
                    printf "The specified priority was not numeric\n"
                    pidin_usage
                fi

                local priority
                
                # Convert the QNX priority to a Linux priority.
                let priority=21-$OPTARG/3

                # Renice the process.
                renice -n $priority -p $$ > /dev/null                
                ;;
            P)
                # Filter by process family
                if [[ $OPTARG =~ ^[0-9]*$ ]]; then
                    pidin_process_group_filter=$OPTARG
                else
                    printf "The argument passed to the -P options must be a numberic process group\n"
                    pidin_usage
                fi
                ;;
            p) 
                # Filter by process id
                if [[ $OPTARG =~ ^[0-9]*$ ]]; then
                    sh_proc_specific_pid=$OPTARG
                else
                    printf "The argument passed to the -p options must be a numberic process ID\n"
                    pidin_usage
                fi
                ;;

            ## Catch unsupported options
            "d" | "l" | "M")
                printf "The -$Option option is not supported by the Linux implementation of pidin\n"
                exit 0
                ;;
                
            ## Catch unrecognised options
            *)
                printf "Unrecognised options $Option\n"
                pidin_usage
                ;;
        esac
    done
    shift $(($OPTIND -1))

    # Now we process the arguments
    while [ $# -gt 0 ] ; do
        case "$1" in
            # Actual arguments (incl synonyms that use undocumented formatting
            # characters)
            "family") 
                pidin_format_string="%a %N %Q %P %e %v %W"
                ;;
            "info") 
                pidin_info
                ;;
            "net") 
                #TODO - Has external requirements
                printf "Remote node access is not yet implemented\n"
                exit 0
                ;;
            "rc") 
                #TODO - Implementing this argument has external requirements. 
                printf "Not yet implemented.\n"
                exit 0
                ;;
            "session") 
                pidin_sort_by_session=1
                ;;
            "times") 
                pidin_format_string="%a %N %Q %q %R %r %t %U"
                ;;
            "ttimes") 
                pidin_format_string="%a %b %N %J %q %u %V"
                ;;
            "user") 
                pidin_format_string="%a %N %w %X %x %Y %y %Z"
                ;;

            # Synonym arguments
            "arg") 
                pidin_format_string="%a %A"
                ;;
            "env") 
                pidin_format_string="%a %N %E"
                ;;
            "pmem") 
                pidin_format_string="%a %b %N %p %J %c %d %m"
                ;;
            "rmasks") 
                pidin_format_string="%a %b %N %i"
                ;;
            "sched") 
                pidin_format_string="%a %b %N %p %l %H %J"
                ;;
            "sig" | "si") 
                pidin_format_string="%a %b %N %S %s"
                ;;
            "threads") 
                pidin_format_string="%a %N %h %J %B"
                ;;

            # Unsupported arguments
            "extsched" | "flags" | "mem" | "regs" | "timers" | "fds" | "irqs" \
            | "ir" | "sched" | "timers") 
                printf "The $1 argument is not supported by the Linux implementation of pidin\n"
                exit 0
                ;;

            # Catch unrecognised arguments
            *)
                printf "Unrecognised argument $1\nNote: Arguments must be passed after options\n"
                pidin_usage
                ;;
        esac
        shift
    done
}


function main
{
    local node_list=""
    local process_list=""
    local thread_list=""

    local items=()

    pidin_prepare_format_string
    if [ $? -ne 0 ]; then
        printf "Error processing the format string\n"
        exit 2
    fi

    # Print the header.
    printf "$pidin_output_format\n" "${pidin_output_headers[@]}"

    # Generate a list of nodes.
    sh_proc_node_iterator
    node_list=("${sh_proc_node_iterator_out[@]}")

    for node in ${node_list[@]}; do
        if [ ${#node_list[@]} -gt 1 ]; then
            sh_proc_print_node_header $node
        fi

        sh_proc_set_proc_path $node

        # Generate a list of processes.
        sh_proc_process_iterator $node
        process_list=$sh_proc_process_iterator_out

        # If the sort by session argument has been passed we create a list of
        # PIDs and associated session IDs (SIDs). We sort the list
        # appropriately, remove the SIDs and use this as our new process list.
        if [[ $pidin_sort_by_session -eq 1 ]]; then
            local list=""
            for process in $process_list; do
                sh_proc_get_item $process "session"
                if [ $? -eq 0 ]; then
                    list+="$sh_proc_get_item_out"" ""$process""\n"
                fi
            done
            process_list="`echo -e $list | sort -k 1n -k 2n | cut -d' ' -f 2`"
        fi

        for process in $process_list; do
            # If the filer by process group option has been enabled we check
            # if the process is in the specified process group and if not we
            # jump to the next process.
            if [ $pidin_process_group_filter -ne -1 ]; then
                sh_proc_get_item $process "pgroup"
                if [[ $sh_proc_get_item_out -ne $pidin_process_group_filter ]]; then
                    continue
                fi
            fi

            # If information is being printed per process the list of threads
            # is just the process otherwise a list of threads is generated.
            if [ $pidin_print_per_thread -eq 0 ]; then
                thread_list=$process
            else
                sh_proc_thread_iterator $process
                thread_list=$sh_proc_thread_iterator_out
            fi
            
            for thread in $thread_list; do

                # For each thread the items specified in
                # "pidin_output_arguments" must be retrieved.
                items=()
                for item in "${pidin_output_arguments[@]}"; do
                    # Most item names can be passed straight to the library
                    # but a few special cases have to be caught.
                    case "$item" in
                        "process_id")
                            items=("${items[@]}" "$process")
                            ;;
                        "thread_id")
                            items=("${items[@]}" "$thread")
                            ;;
                        "process_and_thread_id")
                            items=("${items[@]}" "$process""-""$thread")
                            ;;
                        "zero")
                            items=("${items[@]}" "0")
                            ;;    
                        "procstarttime")
                            sh_proc_get_item $process "starttime"
                            items=("${items[@]}" "$sh_proc_get_item_out")
                            ;;              
                        *)
                            sh_proc_get_item $process"/task/"$thread "$item"
                            items=("${items[@]}" "$sh_proc_get_item_out")
                            ;;
                    esac            
                done

                # If the exit on error option has been actived we exit 
                # on error.
                if [ $pidin_exit_on_error -eq 1 -a $sh_proc_get_item_error -ne 0 ]; then
                    exit 1;
                fi

                # Print the retrieved items using the format string.
                printf "$pidin_output_format\n" "${items[@]}"

                # Print the runmask information the requested number of times.
                if [ $pidin_runmask_count -gt 0 ]; then
                    local i=0
                    sh_proc_get_item $thread "runmask"
                    
                    # If the exit on error option has been actived we exit 
                    # on error.
                    if [ $pidin_exit_on_error -eq 1 -a $sh_proc_get_item_error -ne 0 ]; then
                        exit 1;
                    fi

                    while [ $i -lt $pidin_runmask_count ]; do
                        printf "        Runmask     : 0x%s\n" "$sh_proc_get_item_out"
                        let i+=1
                    done
                fi

            done 
        done
        printf "\n"
    done
}


pidin_command_parser "$@"
main
