Code cleanup
[quassel.git] / data / scripts / inxi
index a7c2528..a444e66 100755 (executable)
-#!/usr/bin/env bash
-########################################################################
-####  Script Name: inxi
-####  Version: 2.1.1
-####  Date: 2014-03-14
-####  Patch Number: 00
-########################################################################
-####  SPECIAL THANKS
-########################################################################
-####  Special thanks to all those in #lsc and #smxi for their tireless 
-####  dedication helping test inxi modules.
-########################################################################
-####  ABOUT INXI
-########################################################################
-####  inxi is a fork of infobash 3.02, the original bash sys info tool by locsmif
-####  As time permits functionality improvements and recoding will occur.
-####
-####  inxi, the universal, portable, system information tool for irc.
-####  Tested with Irssi, Xchat, Konversation, BitchX, KSirc, ircII,
-####  Gaim/Pidgin, Weechat, KVIrc and Kopete.
-####  Original infobash author and copyright holder:
-####  Copyright (C) 2005-2007  Michiel de Boer a.k.a. locsmif
-####  inxi version: Copyright (C) 2008-2014 Scott Rogers & Harald Hope
-####  Further fixes (listed as known): Horst Tritremmel <hjt at sidux.com>
-####  Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch
-####  Jarett.Stevens - dmidecde -M patch for older systems with the /sys 
-####
-####  Current script home page/wiki/svn: http://inxi.googlecode.com
-####  Script forums: http://techpatterns.com/forums/forum-33.html
-####  IRC support: irc.oftc.net channel #smxi
-####
-####  This program is free software; you can redistribute it and/or modify
-####  it under the terms of the GNU General Public License as published by
-####  the Free Software Foundation; either version 3 of the License, or
-####  (at your option) any later version.
-####
-####  This program is distributed in the hope that it will be useful,
-####  but WITHOUT ANY WARRANTY; without even the implied warranty of
-####  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-####  GNU General Public License for more details.
-####
-####  You should have received a copy of the GNU General Public License
-####  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-####
-####  If you don't understand what Free Software is, please read (or reread)
-####  this page: http://www.gnu.org/philosophy/free-sw.html
-########################################################################
-####  * Package names in (...) are the Debian Squeeze package name. Check your 
-####    distro for proper package name by doing this: which <application> 
-####    then find what package owns that application file. Or run --recommends
-####    which shows package names for Debian/Ubuntu, Arch, and Fedora/Redhat/Suse
-####
-####  DEPENDENCIES
-####  * bash >=3.0 (bash); df, readlink, stty, tr, uname, wc (coreutils);
-####    gawk (gawk); grep (grep); lspci (pciutils);
-####    ps, uptime (procps); find (findutils)
-####  * Also the proc filesystem should be present and mounted
-####  * Some features, like -M and -d will not work, or will work incompletely,
-####    if /sys is missing
-####
-####   Apparently unpatched bash 3.0 has arrays broken; bug reports:
-####   http://ftp.gnu.org/gnu/bash/bash-3.0-patches/bash30-008
-####   http://lists.gnu.org/archive/html/bug-bash/2004-08/msg00144.html
-####  Bash 3.1 for proper array use
-####
-####   Arrays work in bash 2.05b, but "grep -Em" does not
-####
-####  RECOMMENDS (Needed to run certain features, listed by option)
-####  -A - for output of usb audio information: lsusb (usbutils)
-####  -Ax -Nx - for audio/network module version: modinfo (module-init-tools)
-####  -Dx - for hdd temp output (root only default): hddtemp (hddtemp)
-####       For user level hdd temp output: sudo (sudo)
-####       Note: requires user action for this feature to run as user (edit /etc/sudoers file)
-####  -G - full graphics output requires:  glxinfo (mesa-utils); xdpyinfo (X11-utils);
-####       xrandr (x11-xserver-utils)
-####  -i - IP information, local/wan - ip (iproute) legacy, not used if ip present: ifconfig (net-tools)
-####  -Ix - view current runlevel while not in X window system (or with -x): runlevel (sysvinit)
-####  -M - for older systems whose kernel does not have /sys data for machine, dmidecode (dmidecode)
-####  -o - for unmounted file system information in unmounted drives (root only default): file (file)
-####       Note: requires user action for this feature to run as user (edit /etc/sudoers file)
-####       For user level unmounted file system type output: sudo (sudo)
-####  -s   For any sensors output, fan, temp, etc: sensors (lm-sensors)
-####       Note: requires setup of lm-sensors (sensors-detect and adding modules/modprobe/reboot,
-####       and ideally, pwmconfig) prior to full output being available. 
-####  -S   For desktop environment, user must be in X and have xprop installed (in X11-utils)
-########################################################################
-####  BSD Adjustments
-####  * sed -i '' form supported by using SED_I="-i ''".
-####  * Note: New BSD sed supports using -r instead of -E for compatibility with gnu sed
-####    However, older, like FreeBSD 7.x, does not have -r so using SED_RX='-E' for this.
-####  * Gnu grep options can be used if the function component is only run in linux
-####    These are the options that bsd grep does not support that inxi uses: -m <number> -o 
-####    so make sure if you use those to have them in gnu/linux only sections.
-####    It appears that freebsd uses gnu grep but openbsd uses bsd grep, however.
-####  * BSD ps does not support --without-headers option, and also does not support --sort <option>
-####    Tests show that -m fails to sort memory as expected, but -r does sort cpu percentage.
-####  * BSD_TYPE is set with values null, debian-bsd (debian gnu/kfreebsd), bsd (all other bsds)
-####  * Subshell and array closing ) must not be on their own line unless you use an explicit \ 
-####    to indicate that logic continues to next line where closing ) or )) are located.
-########################################################################
-####  CONVENTIONS:
-####  * Character Encoding: UTF-8 - this file contains special characters that must be opened and saved as UTF8
-####  * Indentation: TABS
-####  * Do not use `....` (back quotes), those are totally non-reabable, use $(....).
-####  * Do not use one liner flow controls. 
-####    The ONLY time you should use ';' (semi-colon) is in this single case: if [[ condition ]];then.
-####    Never use compound 'if': ie, if [[ condition ]] && statement.
-####  * Note: [[ -n $something ]] - double brackets does not require quotes for variables: ie, "$something".
-####  * Always use quotes, double or single, for all string values.
-####  * All new code/methods must be in a function.
-####  * For all boolean tests, use 'true' / 'false'.
-####    !! Do NOT use 0 or 1 unless it's a function return. 
-####  * Avoid complicated tests in the if condition itself.
-####  * To 'return' a value in a function, use 'echo <var>'.
-####  * For gawk: use always if ( num_of_cores > 1 ) { hanging { starter for all blocks
-####    This lets us use one method for all gawk structures, including BEGIN/END, if, for, etc
-####
-####  VARIABLE/FUNCTION NAMING:
-####  * All functions should follow standard naming--verb adjective noun. 
-####     ie, get_cpu_data
-####  * All variables MUST be initialized / declared explicitly, either top of file, for Globals, or using local
-####  * All variables should clearly explain what they are, except counters like i, j.
-####  * Each word of Bash variable must be separated by '_' (underscore) (camel form), like: cpu_data
-####  * Each word of Gawk variable must be like this (first word lower, following start with upper): cpuData
-####  * Global variables are 'UPPER CASE', at top of this file.
-####     ie, SOME_VARIABLE=''
-####  * Local variables are 'lower case' and declared at the top of the function using local, always.
-####     ie: local some_variable=''
-####  * Locals that will be inherited by child functions have first char capitalized (so you know they are inherited).
-####     ie, Some_Variable 
-####  * Booleans should start with b_ (local) or B_ (global) and state clearly what is being tested.
-####  * Arrays should start with a_ (local) or A_ (global).
-####
-####  SPECIAL NOTES:
-####  * The color variable ${C2} must always be followed by a space unless you know what
-####    character is going to be next for certain. Otherwise irc color codes can be accidentally
-####    activated or altered.
-####  * For native script konversation support (check distro for correct konvi scripts path):
-####    ln -s <path to inxi> /usr/share/apps/konversation/scripts/inxi
-####    DCOP doesn't like \n, so avoid using it for most output unless required, as in error messages.
-####  * print_screen_output " " # requires space, not null, to avoid error in for example in irssi
-####  * For logging of array data, array must be placed into the temp_array, otherwise only the first key logs
-####  * In gawk search patterns, . is a wildcard EXCEPT in [0-9.] type containers, then it's a literal
-####    So outside of bracketed items, it must be escaped, \. but inside, no need. Outside of gawk it should 
-####    be escaped in search patterns if you are using it as a literal.
-####  
-####  PACKAGE MANAGER DATA (note, while inxi tries to avoid using package managers to get data, sometimes 
-####  it's the only way to get some data):
-####  * dpkg options: http://www.cyberciti.biz/howto/question/linux/dpkg-cheat-sheet.php
-####  * pacman options: https://wiki.archlinux.org/index.php/Pacman_Rosetta
-####
-####  As with all 'rules' there are acceptions, these are noted where used.
-###################################################################################
-####   KDE Konversation information.  Moving from dcop(qt3/KDE3) to dbus(qt4/KDE4)
-###################################################################################
-####  * dcop and dbus  -- these talk back to Konversation from this program
-####  * Scripting info -- http://konversation.berlios.de/docs/scripting.html
-####    -- http://www.kde.org.uk/apps/konversation/
-####  * dbus info      -- http://dbus.freedesktop.org/doc/dbus-tutorial.html
-####    view dbus info -- https://fedorahosted.org/d-feet/
-####    -- or run qdbus
-####  * Konvi dbus/usage-- qdbus org.kde.konversation /irc say <server> <target-channel> <output>
-####  * Python usage   -- http://wiki.python.org/moin/DbusExamples  (just in case)
-####
-####   Because webpages come and go, the above information needs to be moved to inxi's wiki
-########################################################################
-####  Valuable Resources
-####  CPU flags: http://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean
-####  Advanced Bash: http://wiki.bash-hackers.org/syntax/pe
-####  gawk arrays: http://www.math.utah.edu/docs/info/gawk_12.html
-####  raid mdstat: http://www-01.ibm.com/support/docview.wss?uid=isg3T1011259
-####               http://www.howtoforge.com/replacing_hard_disks_in_a_raid1_array
-####               https://raid.wiki.kernel.org/index.php/Mdstat
-########################################################################
-####  TESTING FLAGS
-####  inxi supports advanced testing triggers to do various things, using -! <arg>
-####  -! 1  - triggers default B_TESTING_1='true' to trigger some test or other
-####  -! 2  - triggers default B_TESTING_2='true' to trigger some test or other
-####  -! 3  - triggers B_TESTING_1='true' and B_TESTING_2='true'
-####  -! 10 - triggers an update from the primary dev download server instead of svn
-####  -! 11 - triggers an update from svn branch one - if present, of course
-####  -! 12 - triggers an update from svn branch two - if present, of course
-####  -! 13 - triggers an update from svn branch three - if present, of course
-####  -! 14 - triggers an update from svn branch four - if present, of course
-####  -! <http://......> - Triggers an update from whatever server you list.
-####  LOG FLAGS (logs to $HOME/.inxi/inxi.log with rotate 3 total)
-####  -@ 8  - Basic data logging of generated data / array values
-####  -@ 9  - Full logging of all data used, including cat of files and system data
-####  -@ 10 - Basic data logging plus color code logging
-########################################################################
-#### VARIABLES
-########################################################################
-
-## NOTE: we can use hwinfo if it's available in all systems, or most, to get
-## a lot more data and verbosity levels going
-
-### DISTRO MAINTAINER FLAGS ###
-# flag to allow distro maintainers to turn off update features. If false, turns off
-# -U and -! testing/advanced update options, as well as removing the -U help menu item
-# NOTE: Usually you want to create these in /etc/inxi.conf to avoid having to update each time
-B_ALLOW_UPDATE='true'
-B_ALLOW_WEATHER='true'
-
-### USER CONFIGS: SET IN inxi.conf file see wiki for directions ###
-# http://code.google.com/p/inxi/wiki/script_configuration_files
-# override in user config if desired, seems like less than .3 doesn't work as reliably
-CPU_SLEEP='0.3' 
-FILTER_STRING='<filter>'
-
-# for features like help/version will fit to terminal / console screen width. Console
-# widths will be dynamically set in main() based on cols in term/console
-COLS_MAX_CONSOLE='115'
-COLS_MAX_IRC='105'
-PS_COUNT=5
-# change to less, or more if you have very slow connection
-WGET_TIMEOUT=8
-### END USER CONFIGS ###
-
-### LOCALIZATION - DO NOT CHANGE! ###
-# set to default LANG to avoid locales errors with , or .
-LANG=C
-# Make sure every program speaks English.
-LC_ALL="C"
-export LC_ALL
-
-### ARRAYS ###
-## Prep
-# Clear nullglob, because it creates unpredictable situations with IFS=$'\n' ARR=($VAR) IFS="$ORIGINAL_IFS"
-# type constructs. Stuff like [rev a1] is now seen as a glob expansion pattern, and fails, and
-# therefore results in nothing.
-shopt -u nullglob
-## info on bash built in: $IFS - http://tldp.org/LDP/abs/html/internalvariables.html
-# Backup the current Internal Field Separator
-ORIGINAL_IFS="$IFS"
-
-## Initialize
-A_ALSA_DATA=''
-A_AUDIO_DATA=''
-A_CMDL=''
-A_CPU_CORE_DATA=''
-A_CPU_DATA=''
-A_CPU_TYPE_PCNT_CCNT=''
-A_DEBUG_BUFFER=''
-A_GCC_VERSIONS=''
-A_GLX_DATA=''
-A_GRAPHICS_CARD_DATA=''
-A_GRAPHIC_DRIVERS=''
-A_HDD_DATA=''
-A_INIT_DATA=''
-A_INTERFACES_DATA=''
-A_MACHINE_DATA=''
-A_NETWORK_DATA=''
-A_OPTICAL_DRIVE_DATA=''
-A_PARTITION_DATA=''
-A_PCICONF_DATA=''
-A_PS_DATA=''
-A_RAID_DATA=''
-A_SENSORS_DATA=''
-A_UNMOUNTED_PARTITION_DATA=''
-A_WEATHER_DATA=''
-A_DISPLAY_SERVER_DATA=''
-
-### BOOLEANS ###
-## standard boolean flags ##
-B_BSD_RAID='false'
-B_COLOR_SCHEME_SET='false'
-B_CONSOLE_IRC='false'
-# triggers full display of cpu flags
-B_CPU_FLAGS_FULL='false'
-# test for dbus irc client
-B_DBUS_CLIENT='false'
-# kde dcop
-B_DCOP='false'
-# Debug flood override: make 'true' to allow long debug output
-B_DEBUG_FLOOD='false'
-B_DMIDECODE_SET='false'
-# show extra output data
-B_EXTRA_DATA='false'
-# triggered by -xx
-B_EXTRA_EXTRA_DATA='false'
-B_ID_SET='false'
-# override certain errors due to currupted data
-B_HANDLE_CORRUPT_DATA='false'
-B_LABEL_SET='false'
-B_LSPCI='false'
-B_LOG_COLORS='false'
-B_LOG_FULL_DATA='false'
-B_MAPPER_SET='false'
-B_OUTPUT_FILTER='false'
-B_OVERRIDE_FILTER='false'
-B_PCICONF='false'
-B_PCICONF_SET='false'
-# kde qdbus
-B_QDBUS='false'
-B_PORTABLE='false'
-B_RAID_SET='false'
-B_ROOT='false'
-B_RUN_COLOR_SELECTOR='false'
-B_RUNNING_IN_DISPLAY='false' # in x type display server
-if tty >/dev/null;then
-       B_RUNNING_IN_SHELL='true'
-else
-       B_RUNNING_IN_SHELL='false'
-fi
-# this sets the debug buffer
-B_SCRIPT_UP='false'
-B_SHOW_ADVANCED_NETWORK='false'
-# Show sound card data
-B_SHOW_AUDIO='false'
-B_SHOW_BASIC_RAID='false'
-B_SHOW_BASIC_CPU='false'
-B_SHOW_BASIC_DISK='false'
-B_SHOW_BASIC_OPTICAL='false'
-B_SHOW_CPU='false'
-B_SHOW_DISPLAY_DATA='false'
-B_SHOW_DISK_TOTAL='false'
-B_SHOW_DISK='false'
-# Show full hard disk output
-B_SHOW_FULL_HDD='false'
-B_SHOW_FULL_OPTICAL='false'
-B_SHOW_GRAPHICS='false'
-# Set this to 'false' to avoid printing the hostname, can be set false now
-B_SHOW_HOST='true'
-B_SHOW_INFO='false'
-B_SHOW_IP='false'
-B_SHOW_LABELS='false'
-B_SHOW_MACHINE='false'
-B_SHOW_NETWORK='false'
-# either -v > 3 or -P will show partitions
-B_SHOW_PARTITIONS='false'
-B_SHOW_PARTITIONS_FULL='false'
-B_SHOW_PS_CPU_DATA='false'
-B_SHOW_PS_MEM_DATA='false'
-B_SHOW_RAID='false'
-# because many systems have no mdstat file, -b/-F should not show error if no raid file found
-B_SHOW_RAID_R='false' 
-B_SHOW_REPOS='false'
-B_SHOW_SENSORS='false'
-# triggers only short inxi output
-B_SHOW_SHORT_OUTPUT='false'
-B_SHOW_SYSTEM='false'
-B_SHOW_UNMOUNTED_PARTITIONS='false'
-B_SHOW_UUIDS='false'
-B_SHOW_WEATHER='false'
-B_SYSCTL='false'
-# triggers various debugging and new option testing
-B_TESTING_1='false'
-B_TESTING_2='false'
-B_UPLOAD_DEBUG_DATA='false'
-B_USB_NETWORKING='false'
-# set to true here for debug logging from script start
-B_USE_LOGGING='false'
-B_UUID_SET='false'
-B_XORG_LOG='false'
-
-## Directory/file exist flags; test as [[ $(boolean) ]] not [[ $boolean ]]
-B_ASOUND_DEVICE_FILE='false'
-B_ASOUND_VERSION_FILE='false'
-B_BASH_ARRAY='false'
-B_CPUINFO_FILE='false'
-B_DMESG_BOOT_FILE='false' # bsd only
-B_LSB_FILE='false'
-B_MDSTAT_FILE='false'
-B_MEMINFO_FILE='false'
-B_MODULES_FILE='false' #
-B_MOUNTS_FILE='false'
-B_OS_RELEASE_FILE='false' # new default distro id file? will this one work where lsb-release didn't?
-B_PARTITIONS_FILE='false' #
-B_PROC_DIR='false'
-B_SCSI_FILE='false'
-
-## app tested for and present, to avoid repeat tests
-B_FILE_TESTED='false'
-B_HDDTEMP_TESTED='false'
-B_MODINFO_TESTED='false'
-B_SUDO_TESTED='false'
-
-### CONSTANTS/INITIALIZE - SOME MAY BE RESET LATER ###
-DCOPOBJ="default"
-DEBUG=0 # Set debug levels from 1-10 (8-10 trigger logging levels)
-# Debug Buffer Index, index into a debug buffer storing debug messages until inxi is 'all up'
-DEBUG_BUFFER_INDEX=0
-## note: the debugger rerouting to /dev/null has been moved to the end of the get_parameters function
-## so -@[number] debug levels can be set if there is a failure, otherwise you can't even see the errors
-SED_I='-i' # for gnu sed, will be set to -i '' for bsd sed
-SED_RX='-r' # for gnu sed, will be set to -E for bsd sed for backward compatibility
-
-# default to false, no konversation found, 1 is native konvi (qt3/KDE3) script mode, 2 is /cmd inxi start,
-##     3 is Konversation > 1.2 (qt4/KDE4) 
-KONVI=0
-# NO_CPU_COUNT=0       # Wether or not the string "dual" or similar is found in cpuinfo output. If so, avoid dups.
-# This is a variable that controls how many parameters inxi will parse in a /proc/<pid>/cmdline file before stopping.
-PARAMETER_LIMIT=30
-SCHEME=0 # set default scheme - do not change this, it's set dynamically
-# this is set in user prefs file, to override dynamic temp1/temp2 determination of sensors output in case
-# cpu runs colder than mobo
-SENSORS_CPU_NO=''
-# SHOW_IRC=1 to avoid showing the irc client version number, or SHOW_IRC=0 to disable client information completely.
-SHOW_IRC=2
-# Verbosity level defaults to 0, this can also be set with -v0, -v2, -v3, etc as a parameter.
-VERBOSITY_LEVEL=0
-# Supported number of verbosity levels, including 0
-VERBOSITY_LEVELS=7
-
-### LOGGING ###
-## logging eval variables, start and end function: Insert to LOGFS LOGFE when debug level >= 8
-LOGFS_STRING='log_function_data fs $FUNCNAME "$( echo $@ )"'
-LOGFE_STRING='log_function_data fe $FUNCNAME'
-LOGFS=''
-LOGFE=''
-# uncomment for debugging from script start
-# LOGFS=$LOGFS_STRING
-# LOGFE=$LOGFE_STRING
-
-### FILE NAMES/PATHS/URLS - must be non root writable ###
-# File's used when present
-FILE_ASOUND_DEVICE='/proc/asound/cards'
-FILE_ASOUND_MODULES='/proc/asound/modules' # not used but maybe for -A?
-FILE_ASOUND_VERSION='/proc/asound/version'
-FILE_CPUINFO='/proc/cpuinfo'
-FILE_DMESG_BOOT='/var/run/dmesg.boot'
-FILE_LSB_RELEASE='/etc/lsb-release'
-FILE_MDSTAT='/proc/mdstat'
-FILE_MEMINFO='/proc/meminfo'
-FILE_MODULES='/proc/modules'
-FILE_MOUNTS='/proc/mounts'
-FILE_OS_RELEASE='/etc/os-release'
-FILE_PARTITIONS='/proc/partitions'
-FILE_SCSI='/proc/scsi/scsi'
-FILE_XORG_LOG='/var/log/Xorg.0.log' # if not found, search and replace with actual location
-
-FILE_PATH=''
-HDDTEMP_PATH=''
-MODINFO_PATH=''
-SUDO_PATH=''
-
-SCRIPT_DATA_DIR="$HOME/.inxi"
-ALTERNATE_FTP='' # for data uploads
-ALTERNATE_WEATHER_LOCATION='' # weather alternate location
-LOG_FILE="$SCRIPT_DATA_DIR/inxi.log"
-LOG_FILE_1="$SCRIPT_DATA_DIR/inxi.1.log"
-LOG_FILE_2="$SCRIPT_DATA_DIR/inxi.2.log"
-MAN_FILE_DOWNLOAD='http://inxi.googlecode.com/svn/trunk/inxi.1.gz'
-MAN_FILE_LOCATION='/usr/share/man/man1'
-SCRIPT_NAME='inxi'
-SCRIPT_PATCH_NUMBER=''
-SCRIPT_PATH='' #filled-in in Main
-SCRIPT_VERSION_NUMBER=""       #filled-in in Main
-SCRIPT_DOWNLOAD='http://inxi.googlecode.com/svn/trunk/'
-SCRIPT_DOWNLOAD_BRANCH_1='http://inxi.googlecode.com/svn/branches/one/'
-SCRIPT_DOWNLOAD_BRANCH_2='http://inxi.googlecode.com/svn/branches/two/'
-SCRIPT_DOWNLOAD_BRANCH_3='http://inxi.googlecode.com/svn/branches/three/'
-SCRIPT_DOWNLOAD_BRANCH_4='http://inxi.googlecode.com/svn/branches/four/'
-SCRIPT_DOWNLOAD_BRANCH_BSD='http://inxi.googlecode.com/svn/branches/bsd/'
-SCRIPT_DOWNLOAD_BRANCH_GNUBSD='http://inxi.googlecode.com/svn/branches/gnubsd/'
-SCRIPT_DOWNLOAD_DEV='http://smxi.org/test/'
-# note, you can use any ip url here as long as it's the only line on the output page.
-# Also the ip address must be the last thing on that line.
-WAN_IP_URL='http://smxi.org/opt/ip.php'
-KONVI_CFG="konversation/scripts/$SCRIPT_NAME.conf" # relative path to $(kde-config --path data)
-
-### INITIALIZE VARIABLES NULL ###
-BSD_TYPE=''
-BSD_VERSION=
-CMDL_MAX=''
-
-DEV_DISK_ID=''
-DEV_DISK_LABEL=''
-DEV_DISK_MAPPER=''
-DEV_DISK_UUID=''
-DMIDECODE_DATA=''
-IRC_CLIENT=''
-IRC_CLIENT_VERSION=''
-PS_THROTTLED=''
-REPO_DATA=''
-
-### LAYOUT ###
-# These two determine separators in single line output, to force irc clients not to break off sections
-SEP1='~'
-SEP2=' '
-# these will assign a separator to non irc states. Important! Using ':' can trigger stupid emoticon
-# behaviors in output on IRC, so do not use those.
-SEP3_IRC=''
-SEP3_CONSOLE=':'
-SEP3='' # do not set, will be set dynamically
-
-# Default indentation level. NOTE: actual indent is 1 greater to allow for spacing
-INDENT=10
-
-### COLUMN WIDTHS ###
-COLS_INNER='' ## for width minus INDENT
-COLS_MAX=''
-
-TERM_COLUMNS=80
-TERM_LINES=100
-
-# http://stackoverflow.com/questions/1780483/lines-and-columns-environmental-variables-lost-in-a-script
-if [[ -n $( type -p tput ) ]];then
-       TERM_COLUMNS=$(tput cols)
-       TERM_LINES=$(tput lines)
-fi
-# double check, just in case it's missing functionality or whatever
-if [[ -n ${TERM_COLUMNS##[0-9]*} ]];then
-       TERM_COLUMNS=80
-       TERM_LINES=100
-fi
-
-# Only for legacy user config files se we can test and convert the var name
-LINE_MAX_CONSOLE=''
-LINE_MAX_IRC=''
-
-### COLORS ###
-# Defaults to 2, make this 1 for normal, 0 for no colorcodes at all. Use following variables in config 
-# files to change defaults for each type, or global
-# Same as runtime parameter.
-DEFAULT_COLOR_SCHEME=2
-## color variables - set dynamically
-COLOR_SCHEME=''
-C1=''
-C2=''
-CN=''
-## Always leave these blank, these are only going to be set in inxi.conf files, that makes testing
-## for user changes easier after sourcing the files
-GLOBAL_COLOR_SCHEME=''
-IRC_COLOR_SCHEME=''
-IRC_CONS_COLOR_SCHEME=''
-IRC_X_TERM_COLOR_SCHEME=''
-CONSOLE_COLOR_SCHEME=''
-VIRT_TERM_COLOR_SCHEME=''
-
-## Output colors
-# A more elegant way to have a scheme that doesn't print color codes (neither ANSI nor mIRC) at all. See below.
-unset EMPTY
-#             DGREY   BLACK   RED     DRED    GREEN   DGREEN  YELLOW  DYELLOW
-ANSI_COLORS="\e[1;30m \e[0;30m \e[1;31m \e[0;31m \e[1;32m \e[0;32m \e[1;33m \e[0;33m"
-IRC_COLORS="  \x0314  \x0301  \x0304  \x0305  \x0309  \x0303  \x0308  \x0307"
-#                          BLUE    DBLUE   MAGENTA DMAGENTA CYAN   DCYAN   WHITE   GREY    NORMAL
-ANSI_COLORS="$ANSI_COLORS \e[1;34m \e[0;34m \e[1;35m \e[0;35m \e[1;36m \e[0;36m \e[1;37m \e[0;37m \e[0;37m"
-IRC_COLORS=" $IRC_COLORS    \x0312 \x0302  \x0313  \x0306  \x0311  \x0310  \x0300  \x0315  \x03"
-
-#ANSI_COLORS=($ANSI_COLORS); IRC_COLORS=($IRC_COLORS)
-A_COLORS_AVAILABLE=( DGREY BLACK RED DRED GREEN DGREEN YELLOW DYELLOW BLUE DBLUE MAGENTA DMAGENTA CYAN DCYAN WHITE GREY NORMAL )
-
-# See above for notes on EMPTY
-## note: group 1: 0, 1 are null/normal
-## Following: group 2: generic, light/dark or dark/light; group 3: dark on light; group 4 light on dark; 
-# this is the count of the first two groups, starting at zero
-SAFE_COLOR_COUNT=12
-A_COLOR_SCHEMES=( 
-EMPTY,EMPTY,EMPTY 
-NORMAL,NORMAL,NORMAL 
-
-BLUE,NORMAL,NORMAL
-BLUE,RED,NORMAL 
-CYAN,BLUE,NORMAL 
-DCYAN,NORMAL,NORMAL
-DCYAN,BLUE,NORMAL 
-DGREEN,NORMAL,NORMAL 
-DYELLOW,NORMAL,NORMAL 
-GREEN,DGREEN,NORMAL 
-GREEN,NORMAL,NORMAL 
-MAGENTA,NORMAL,NORMAL
-RED,NORMAL,NORMAL
-
-BLACK,DGREY,NORMAL
-DBLUE,DGREY,NORMAL 
-DBLUE,DMAGENTA,NORMAL
-DBLUE,DRED,NORMAL 
-DBLUE,BLACK,NORMAL
-DGREEN,DYELLOW,NORMAL 
-DYELLOW,BLACK,NORMAL
-DMAGENTA,BLACK,NORMAL
-DCYAN,DBLUE,NORMAL
-
-WHITE,GREY,NORMAL
-GREY,WHITE,NORMAL
-CYAN,GREY,NORMAL 
-GREEN,WHITE,NORMAL 
-GREEN,YELLOW,NORMAL 
-YELLOW,WHITE,NORMAL 
-MAGENTA,CYAN,NORMAL 
-MAGENTA,YELLOW,NORMAL
-RED,CYAN,NORMAL
-RED,WHITE,NORMAL 
-BLUE,WHITE,NORMAL
-)
-
-# WARNING: In the main part below (search for 'KONVI')
-# there's a check for Konversation-specific config files.
-# Any one of these can override the above if inxi is run
-# from Konversation!
-
-## DISTRO DATA/ID ##
-# In cases of derived distros where the version file of the base distro can also be found under /etc,
-# the derived distro's version file should go first. (Such as with Sabayon / Gentoo)
-DISTROS_DERIVED="antix-version aptosid-version kanotix-version knoppix-version mandrake-release pardus-release sabayon-release siduction-version sidux-version solusos-release turbolinux-release zenwalk-version"
-# debian_version excluded from DISTROS_PRIMARY so Debian can fall through to /etc/issue detection. Same goes for Ubuntu.
-DISTROS_EXCLUDE_LIST="debian_version ubuntu_version"
-DISTROS_PRIMARY="arch-release gentoo-release redhat-release slackware-version SuSE-release"
-DISTROS_LSB_GOOD="mandrake-release mandriva-release mandrakelinux-release"
-# this is being used both by core distros and derived distros now, eg, solusos 1 uses it for solusos id, while
-# debian, solusos base, uses it as well, so we have to know which it is.
-DISTROS_OS_RELEASE_GOOD="arch-release SuSE-release"
-## Distros with known problems
-# DSL (Bash 2.05b: grep -m doesn't work; arrays won't work) --> unusable output
-# Puppy Linux 4.1.2 (Bash 3.0: arrays won't work) --> works partially
-
-## OUTPUT FILTERS/SEARCH ##
-# Note that \<ltd\> bans only words, not parts of strings; in \<corp\> you can't use punctuation characters like . or ,
-# we're saving about 10+% of the total script exec time by hand building the ban lists here, using hard quotes.
-BAN_LIST_NORMAL='chipset|components|computing|computer|corporation|communications|electronics|electrical|electric|gmbh|group|industrial|international|revision|semiconductor|software|technologies|technology|ltd\.|\<ltd\>|inc\.|\<inc\>|intl\.|co\.|\<co\>|corp\.|\<corp\>|\(tm\)|\(r\)|®|\(rev ..\)'
-BAN_LIST_CPU='@|cpu deca|dual core|dual-core|tri core|tri-core|quad core|quad-core|ennea|genuine|hepta|hexa|multi|octa|penta|processor|single|triple|[0-9\.]+ *[MmGg][Hh][Zz]'
-
-SENSORS_GPU_SEARCH='intel|radeon|nouveau'
-
-### USB networking search string data, because some brands can have other products than
-### wifi/nic cards, they need further identifiers, with wildcards.
-### putting the most common and likely first, then the less common, then some specifics
-USB_NETWORK_SEARCH="Wi-Fi.*Adapter|Wireless.*Adapter|Ethernet.*Adapter|WLAN.*Adapter|Network.*Adapter|802\.11|Atheros|Atmel|D-Link.*Adapter|D-Link.*Wireless|Linksys|Netgea|Ralink|Realtek.*Network|Realtek.*Wireless|Realtek.*WLAN|Belkin.*Wireless|Belkin.*WLAN|Belkin.*Network"
-USB_NETWORK_SEARCH="$USB_NETWORK_SEARCH|Actiontec.*Wireless|Actiontec.*Network|AirLink.*Wireless|Asus.*Network|Asus.*Wireless|Buffalo.*Wireless|Davicom|DWA-.*RangeBooster|DWA-.*Wireless|ENUWI-.*Wireless|LG.*Wi-Fi|Rosewill.*Wireless|RNX-.*Wireless|Samsung.*LinkStick|Samsung.*Wireless|Sony.*Wireless|TEW-.*Wireless|TP-Link.*Wireless|WG[0-9][0-9][0-9].*Wireless|WNA[0-9][0-9][0-9]|WNDA[0-9][0-9][0-9]|Zonet.*ZEW.*Wireless|54 Mbps" 
-# then a few known hard to ID ones added 
-# belkin=050d; d-link=07d1; netgear=0846; ralink=148f; realtek=0bda; 
-USB_NETWORK_SEARCH="$USB_NETWORK_SEARCH|050d:935b|0bda:8189|0bda:8197"
-
-########################################################################
-#### MAIN: Where it all begins
-########################################################################
-main()
-{
-       eval $LOGFS
-       
-       local color_scheme=''
-       # this will be used by all functions following
-       local Ps_aux_Data="$( ps aux )"
-
-       # This function just initializes variables
-       initialize_data
-       
-       # Source global config overrides, needs to be here because some things
-       # can be reset that were set in initialize, but check_required_apps needs
-       if [[ -s /etc/$SCRIPT_NAME.conf ]];then
-               source /etc/$SCRIPT_NAME.conf
-       fi
-       # Source user config variables override /etc/inxi.conf variables
-       if [[ -s $HOME/.$SCRIPT_NAME/$SCRIPT_NAME.conf ]];then
-               source $HOME/.$SCRIPT_NAME/$SCRIPT_NAME.conf
-       fi
-       # Convert to new variable names if set in config files, legacy test
-       if [[ -n $LINE_MAX_CONSOLE ]];then
-               COLS_MAX_CONSOLE=$LINE_MAX_CONSOLE
-       fi
-       if [[ -n $LINE_MAX_IRC ]];then
-               COLS_MAX_IRC=$LINE_MAX_IRC
-       fi
-       # TERM_COLUMNS is set in top globals, using tput cols
-       if [[ $TERM_COLUMNS -lt $COLS_MAX_CONSOLE ]];then
-               COLS_MAX_CONSOLE=$TERM_COLUMNS
-       fi
-       # adjust, some terminals will wrap if output cols == term cols
-       COLS_MAX_CONSOLE=$(( $COLS_MAX_CONSOLE - 2 ))
-       
-       # comes after source for user set stuff
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               COLS_MAX=$COLS_MAX_CONSOLE
-               SEP3=$SEP3_CONSOLE
-       else
-               # too hard to read if no colors, so force that for users on irc
-               if [[ $SCHEME == 0 ]];then
-                       SEP3=$SEP3_CONSOLE
-               else
-                       SEP3=$SEP3_IRC
-               fi
-               COLS_MAX=$COLS_MAX_IRC
-       fi
-       COLS_INNER=$(( $COLS_MAX - $INDENT - 4 ))
-
-       # Check for dependencies BEFORE running ANYTHING else except above functions
-       # Not all distro's have these depends installed by default. Don't want to run
-       # this if the user is requesting to see this information in the first place
-       # Only continue if required apps tests ok
-       if [[ $1 != '--recommends' ]];then
-               check_required_apps
-               check_recommended_apps
-       fi
-
-       SCRIPT_VERSION_NUMBER=$( parse_version_data 'main' )
-       SCRIPT_PATCH_NUMBER=$( parse_version_data 'patch' )
-       
-       # previous source location, check for bugs
-
-       ## this needs to run before the KONVI stuff is set below
-       ## Konversation 1.2 apparently does not like the $PPID test in get_start_client
-       ## So far there is no known way to detect if qt4_konvi is the parent process
-       ## this method will infer qt4_konvi as parent
-       get_start_client
-
-       # note: this only works if it's run from inside konversation as a script builtin or something
-       # only do this if inxi has been started as a konversation script, otherwise bypass this 
-#      KONVI=3 ## for testing puroses
-       if [[ $KONVI -eq 1 || $KONVI -eq 3 ]];then
-               if [[ $KONVI -eq 1 ]]; then ## dcop Konversation (ie 1.x < 1.2(qt3))    
-                       DCPORT="$1"
-                       DCSERVER="$2"
-                       DCTARGET="$3"
-                       shift 3
-               elif [[ $KONVI -eq 3 ]]; then ## dbus Konversation (> 1.2 (qt4))
-                       DCSERVER="$1" ##dbus testing
-                       DCTARGET="$2" ##dbus testing
-                       shift 2
-               fi
-
-               # The section below is on request of Argonel from the Konversation developer team:
-               # it sources config files like $HOME/.kde/share/apps/konversation/scripts/inxi.conf
-               IFS=":"
-               for kde_config in $( kde-config --path data )
-               do
-                       if [[ -r ${kde_config}${KONVI_CFG} ]];then
-                               source "${kde_config}${KONVI_CFG}"
-                               break
-                       fi
-               done
-               IFS="$ORIGINAL_IFS"
-       fi
-
-       ## leave this for debugging dcop stuff if we get that working
-       #       print_screen_output "DCPORT: $DCPORT"
-       #       print_screen_output "DCSERVER: $DCSERVER"
-       #       print_screen_output "DCTARGET: $DCTARGET"
-       
-       # first init function must be set first for colors etc. Remember, no debugger
-       # stuff works on this function unless you set the debugging flag manually.
-       # Debugging flag -@ [number] will not work until get_parameters runs.
-       
-       # "$@" passes every parameter separately quoted, "$*" passes all parameters as one quoted parameter.
-       # must be here to allow debugger and other flags to be set.
-       get_parameters "$@"
-
-       # If no colorscheme was set in the parameter handling routine, then set the default scheme
-       if [[ $B_COLOR_SCHEME_SET != 'true' ]];then
-               # This let's user pick their color scheme. For IRC, only shows the color schemes, no interactive
-               # The override value only will be placed in user config files. /etc/inxi.conf can also override
-               if [[ $B_RUN_COLOR_SELECTOR == 'true' ]];then 
-                       select_default_color_scheme
-               else
-                       # set the default, then override as required
-                       color_scheme=$DEFAULT_COLOR_SCHEME
-                       if [[ -n $GLOBAL_COLOR_SCHEME ]];then
-                               color_scheme=$GLOBAL_COLOR_SCHEME
-                       else
-                               if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-                                       if [[ -n $CONSOLE_COLOR_SCHEME && -z $DISPLAY ]];then
-                                               color_scheme=$CONSOLE_COLOR_SCHEME
-                                       elif [[ -n $VIRT_TERM_COLOR_SCHEME ]];then
-                                               color_scheme=$VIRT_TERM_COLOR_SCHEME
-                                       fi
-                               else
-                                       if [[ -n $IRC_X_TERM_COLOR_SCHEME && $B_CONSOLE_IRC == 'true' && -n $B_RUNNING_IN_DISPLAY ]];then
-                                               color_scheme=$IRC_X_TERM_COLOR_SCHEME
-                                       elif [[ -n $IRC_CONS_COLOR_SCHEME && -z $DISPLAY ]];then
-                                               color_scheme=$IRC_CONS_COLOR_SCHEME
-                                       elif [[ -n $IRC_COLOR_SCHEME ]];then
-                                               color_scheme=$IRC_COLOR_SCHEME
-                                       fi
-                               fi
-                       fi
-                       set_color_scheme $color_scheme
-               fi
-       fi
-       
-       # all the pre-start stuff is in place now
-       B_SCRIPT_UP='true'
-       script_debugger "Debugger: $SCRIPT_NAME is up and running..."
-       
-       # then create the output
-       print_it_out
-
-       ## last steps
-       if [[ $B_RUNNING_IN_SHELL == 'true' && $SCHEME -gt 0 ]];then
-               echo -n "\e[0m"
-       fi
-       eval $LOGFE
-       # weechat's executor plugin forced me to do this, and rightfully so, because else the exit code
-       # from the last command is taken..
-       exit 0
-}
-
-#### -------------------------------------------------------------------
-#### basic tests: set script data, booleans, PATH, version numbers
-#### -------------------------------------------------------------------
-
-# Set PATH data so we can access all programs as user. Set BAN lists.
-# initialize some boleans, these directories are used throughout the script
-# some apps are used for extended functions any directory used, should be
-# checked here first.
-# No args taken.
-initialize_data()
-{
-       eval $LOGFS
-       BSD_VERSION=$( uname -s 2>/dev/null | tr '[A-Z]' '[a-z]' )
-       
-       # note: archbsd says they are a freebsd distro, so assuming it's the same as freebsd
-       if [[ -n $( grep 'bsd' <<< "$BSD_VERSION" ) ]];then
-               # GNU/kfreebsd will by definition have GNU tools like sed/grep
-               if [[ -n $( grep 'kfreebsd' <<< "$BSD_VERSION" ) ]];then
-                       BSD_TYPE='debian-bsd' # debian gnu bsd
-               else
-                       BSD_TYPE='bsd' # all other bsds
-                       SED_I="-i ''"
-                       SED_RX='-E'
-               fi
-       fi
-       # now set the script BOOLEANS for files required to run features
-       # note that freebsd has /proc but it's empty
-       if [[ -d "/proc/" && -z $BSD_TYPE ]];then
-               B_PROC_DIR='true'
-       elif [[ -n $BSD_TYPE ]];then
-               B_PROC_DIR='false'
-       else
-               error_handler 6
-       fi
-       
-       initialize_paths
-       
-       if [[ -n $BSD_TYPE ]];then
-               if [[ -e $FILE_DMESG_BOOT ]];then
-                       B_DMESG_BOOT_FILE='true'
-               fi
-       else
-               # found a case of battery existing but having nothing in it on desktop mobo
-               # not all laptops show the first, 
-               if [[ -n $( ls /proc/acpi/battery 2>/dev/null ) ]];then
-                       B_PORTABLE='true'
-               fi
-       fi
-       
-       
-       if [[ -e $FILE_CPUINFO ]]; then
-               B_CPUINFO_FILE='true'
-       fi
-
-       if [[ -e $FILE_MEMINFO ]];then
-               B_MEMINFO_FILE='true'
-       fi
-
-       if [[ -e $FILE_ASOUND_DEVICE ]];then
-               B_ASOUND_DEVICE_FILE='true'
-       fi
-
-       if [[ -e $FILE_ASOUND_VERSION ]];then
-               B_ASOUND_VERSION_FILE='true'
-       fi
-
-       if [[ -f $FILE_LSB_RELEASE ]];then
-               B_LSB_FILE='true'
-       fi
-       
-       if [[ -f $FILE_OS_RELEASE ]];then
-               B_OS_RELEASE_FILE='true'
-       fi
-
-       if [[ -e $FILE_SCSI ]];then
-               B_SCSI_FILE='true'
-       fi
-
-       if [[ -n $DISPLAY ]];then
-               B_SHOW_DISPLAY_DATA='true'
-               B_RUNNING_IN_DISPLAY='true'
-       fi
-       
-       if [[ -e $FILE_MDSTAT ]];then
-               B_MDSTAT_FILE='true'
-       fi
-
-       if [[ -e $FILE_MODULES ]];then
-               B_MODULES_FILE='true'
-       fi
-
-       if [[ -e $FILE_MOUNTS ]];then
-               B_MOUNTS_FILE='true'
-       fi
-
-       if [[ -e $FILE_PARTITIONS ]];then
-               B_PARTITIONS_FILE='true'
-       fi
-       # default to the normal location, then search for it
-       if [[ -e $FILE_XORG_LOG ]];then
-               B_XORG_LOG='true'
-       else
-               # Detect location of the Xorg log file
-               if [[ -n $( type -p xset ) ]]; then
-                       FILE_XORG_LOG=$( xset q 2>/dev/null | grep -i 'Log file' | gawk '{print $3}')
-                       if [[ -e $FILE_XORG_LOG ]];then
-                               B_XORG_LOG='true'
-                       fi
-               fi
-       fi
-       # gfx output will require this flag
-       if [[ $( whoami ) == 'root' ]];then
-               B_ROOT='true'
-       fi
-       eval $LOGFE
-}
-
-# arg: $1 - version number: main/patch/date
-parse_version_data()
-{
-       local version_data=''
-
-       # note, using ####[[:space:]]+ to avoid having this function also trip the version datas
-       case $1 in
-               date)
-                       version_data="$( gawk -F ': ' '
-                       /####[[:space:]]+Date:/ {
-                               print $NF
-                       }' $SCRIPT_PATH/$SCRIPT_NAME )"
-                       ;;
-               main)
-                       version_data="$( gawk '
-                       /####[[:space:]]+Version:/ {
-                               print $3
-                       }' $SCRIPT_PATH/$SCRIPT_NAME )"
-                       ;;
-               patch)
-                       version_data="$( gawk '
-                       /####[[:space:]]+Patch Number:/ {
-                               print $4
-                       }' $SCRIPT_PATH/$SCRIPT_NAME )"
-                       ;;
-       esac
-       echo $version_data
-}
-
-initialize_paths()
-{
-       local path='' added_path='' b_path_found='' sys_path=''
-       # Extra path variable to make execute failures less likely, merged below
-       local extra_paths="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
-       
-       # this needs to be set here because various options call the parent initialize function directly.
-       SCRIPT_PATH=$( dirname $0 )
-
-       # Fallback paths put into $extra_paths; This might, among others, help on gentoo.
-       # Now, create a difference of $PATH and $extra_paths and add that to $PATH:
-       IFS=":"
-       for path in $extra_paths
-       do
-               b_path_found='false'
-               for sys_path in $PATH
-               do
-                       if [[ $path == $sys_path ]];then
-                               b_path_found='true'
-                       fi
-               done
-               if [[ $b_path_found == 'false' ]];then
-                       added_path="$added_path:$path"
-               fi
-       done
-
-       IFS="$ORIGINAL_IFS"
-       PATH="${PATH}${added_path}"
-       ##echo "PATH='$PATH'"
-       ##/bin/sh -c 'echo "PATH in subshell=\"$PATH\""'
-}
-
-# No args taken.
-check_recommended_apps()
-{
-       eval $LOGFS
-       local bash_array_test=( "one" "two" )
-
-       # check for array ability of bash, this is only good for the warning at this time
-       # the boolean could be used later
-       # bash version 2.05b is used in DSL
-       # bash version 3.0 is used in Puppy Linux; it has a known array bug <reference to be placed here>
-       # versions older than 3.1 don't handle arrays
-       # distro's using below 2.05b are unknown, released in 2002
-       if [[ ${bash_array_test[1]} -eq "two" ]];then
-               B_BASH_ARRAY='true'
-       else
-               script_debugger "Suggestion: update to Bash v3.1 for optimal inxi output"
-       fi
-       # test for a few apps that bsds may not have after initial tests
-       if [[ -n $( type -p lspci ) ]];then
-               B_LSPCI='true'
-       fi
-       if [[ -n $BSD_TYPE ]];then
-               if [[ -n $( type -p sysctl ) ]];then
-                       B_SYSCTL='true'
-               fi
-               if [[ -n $( type -p pciconf ) ]];then
-                       B_PCICONF='true'
-               fi
-       fi
-       # now setting qdbus/dcop for first run, some systems can have both by the way
-       if [[ -n $( type -p qdbus ) ]];then
-               B_QDBUS='true'
-       fi
-       if [[ -n $( type -p dcop ) ]];then
-               B_DCOP='true'
-       fi
-       eval $LOGFE
-}
-
-# Determine if any of the absolutely necessary tools are absent
-# No args taken.
-check_required_apps()
-{
-       eval $LOGFS
-       local app_name='' app_path=''
-       # bc removed from deps for now
-       local depends="df gawk grep ps readlink tr uname uptime wc"
-       
-       if [[ -z $BSD_TYPE  ]];then
-               depends="$depends lspci"
-       elif [[ $BSD_TYPE == 'bsd' ]];then
-               depends="$depends sysctl"
-               # debian-bsd has lspci but you must be root to run it
-       elif [[ $BSD_TYPE == 'debian-bsd' ]];then
-               depends="$depends sysctl lspci"
-       fi
-       # no need to add xprop because it will just give N/A if not there, but if we expand use of xprop,
-       # should add that here as a test, then use the B_SHOW_DISPLAY_DATA flag to trigger the tests in de function
-       local x_apps="xrandr xdpyinfo glxinfo" 
-
-       if [[ $B_RUNNING_IN_DISPLAY == 'true' ]];then
-               for app_name in $x_apps
-               do
-                       app_path=$( type -p $app_name )
-                       if [[ -z $app_path ]];then
-                               script_debugger "Resuming in non X mode: $app_name not found. For package install advice run: $SCRIPT_NAME --recommends"
-                               B_SHOW_DISPLAY_DATA='false'
-                               break
-                       fi
-               done
-       fi
-
-       app_name=''
-
-       for app_name in $depends
-       do
-               app_path=$( type -p $app_name )
-               if [[ -z $app_path ]];then
-                       error_handler 5 "$app_name"
-               fi
-       done
-       eval $LOGFE
-}
-
-## note: this is now running inside each gawk sequence directly to avoid exiting gawk
-## looping in bash through arrays, then re-entering gawk to clean up, then writing back to array
-## in bash. For now I'll leave this here because there's still some interesting stuff to get re methods
-# Enforce boilerplate and buzzword filters
-# args: $1 - BAN_LIST_NORMAL/BAN_LIST_CPU; $2 - string to sanitize
-sanitize_characters()
-{
-       eval $LOGFS
-       # Cannot use strong quotes to unquote a string with pipes in it!
-       # bash will interpret the |'s as usual and try to run a subshell!
-       # Using weak quotes instead, or use '"..."'
-       echo "$2" | gawk "
-       BEGIN {
-               IGNORECASE=1
-       }
-       {
-               gsub(/${!1}/,\"\")
-               gsub(/ [ ]+/,\" \")    ## ([ ]+) with (space)
-               gsub(/^ +| +$/,\"\")   ## (pipe char) with (nothing)
-               print                  ## prints (returns) cleaned input
-       }"
-       eval $LOGFE
-}
-
-# Set the colorscheme
-# args: $1 = <scheme number>|<"none">
-set_color_scheme()
-{
-       eval $LOGFS
-       local i='' a_output_colors='' a_color_codes=''
-
-       if [[ $1 -ge ${#A_COLOR_SCHEMES[@]} ]];then
-               set -- 1
-       fi
-       # Set a global variable to allow checking for chosen scheme later
-       SCHEME="$1"
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               a_color_codes=( $ANSI_COLORS )
-       else
-               a_color_codes=( $IRC_COLORS )
-       fi
-       for (( i=0; i < ${#A_COLORS_AVAILABLE[@]}; i++ ))
-       do
-               eval "${A_COLORS_AVAILABLE[i]}=\"${a_color_codes[i]}\""
-       done
-       IFS=","
-       a_output_colors=( ${A_COLOR_SCHEMES[$1]} )
-       IFS="$ORIGINAL_IFS"
-       # then assign the colors globally
-       C1="${!a_output_colors[0]}"
-       C2="${!a_output_colors[1]}"
-       CN="${!a_output_colors[2]}"
-       # ((COLOR_SCHEME++)) ## note: why is this? ##
-       eval $LOGFE
-}
-
-select_default_color_scheme()
-{
-       eval $LOGFS
-       local spacer='  ' options='' user_selection='' config_variable=''
-       local config_file="$HOME/.$SCRIPT_NAME/$SCRIPT_NAME.conf"
-       local irc_clear="\e[0m" 
-       local irc_gui='Unset' irc_console='Unset' irc_x_term='Unset'
-       local console='Unset' virt_term='Unset' global='Unset' 
-       
-       if [[ -n $IRC_COLOR_SCHEME ]];then
-               irc_gui="Set: $IRC_COLOR_SCHEME"
-       fi
-       if [[ -n $IRC_CONS_COLOR_SCHEME ]];then
-               irc_console="Set: $IRC_CONS_COLOR_SCHEME"
-       fi
-       if [[ -n $IRC_X_TERM_COLOR_SCHEME ]];then
-               irc_x_term="Set: $IRC_X_TERM_COLOR_SCHEME"
-       fi
-       if [[ -n $VIRT_TERM_COLOR_SCHEME ]];then
-               virt_term="Set: $VIRT_TERM_COLOR_SCHEME"
-       fi
-       if [[ -n $CONSOLE_COLOR_SCHEME ]];then
-               console="Set: $CONSOLE_COLOR_SCHEME"
-       fi
-       if [[ -n $GLOBAL_COLOR_SCHEME ]];then
-               global="Set: $GLOBAL_COLOR_SCHEME"
-       fi
-       
-       # don't want these printing in irc since they show literally
-       if [[ $B_RUNNING_IN_SHELL != 'true' ]];then
-               irc_clear=''
-       fi
-       # first make output neutral so it's just plain default for console client
-       set_color_scheme "0"
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               print_screen_output "Welcome to $SCRIPT_NAME! Please select the default $COLOR_SELECTION color scheme."
-               # print_screen_output "You will see this message only one time per user account, unless you set preferences in: /etc/$SCRIPT_NAME.conf"
-               print_screen_output " "
-       fi
-       print_screen_output "Because there is no way to know your $COLOR_SELECTION foreground/background colors, you can"
-       print_screen_output "set your color preferences from color scheme option list below. 0 is no colors, 1 neutral."
-       print_screen_output "After these, there are 3 sets: 1-dark or light backgrounds; 2-light backgrounds; 3-dark backgrounds."
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               print_screen_output "Please note that this will set the $COLOR_SELECTION preferences only for user: $(whoami)"
-       fi
-       print_screen_output "------------------------------------------------------------------------------"
-       for (( i=0; i < ${#A_COLOR_SCHEMES[@]}; i++ ))
-       do
-               if [[ $i -gt 9 ]];then
-                       spacer=' '
-               fi
-               # only offer the safe universal defaults
-               case $COLOR_SELECTION in
-                       global|irc|irc-console|irc-virtual-terminal)
-                               if [[ $i -gt $SAFE_COLOR_COUNT ]];then
-                                       break
-                               fi
-                               ;;
-               esac
-               set_color_scheme $i
-               print_screen_output "$irc_clear $i)$spacer${C1}Card:${C2} nVidia G86 [GeForce 8400 GS] ${C1}X.Org${C2} 1.7.7"
-       done
-       set_color_scheme 0
-       
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               echo -n "\e[0m"
-               print_screen_output "$irc_clear $i)${spacer}Remove all color settings. Restore $SCRIPT_NAME default."
-               print_screen_output "$irc_clear $(($i+1)))${spacer}Continue, no changes or config file setting."
-               print_screen_output "$irc_clear $(($i+2)))${spacer}Exit, use another terminal, or set manually."
-               print_screen_output "------------------------------------------------------------------------------"
-               print_screen_output "Simply type the number for the color scheme that looks best to your eyes for your $COLOR_SELECTION settings"
-               print_screen_output "and hit ENTER. NOTE: You can bring this option list up by starting $SCRIPT_NAME with option: -c plus one of these numbers:"
-               print_screen_output "94 (console, no X - $console); 95 (terminal, X - $virt_term); 96 (irc, gui, X - $irc_gui);"
-               print_screen_output "97 (irc, X, in terminal - $irc_x_term); 98 (irc, no X - $irc_console); 99 (global - $global)"
-               print_screen_output "Your selection(s) will be stored here: $config_file"
-               print_screen_output "Global overrides all individual color schemes. Individual schemes remove the global setting."
-               print_screen_output "------------------------------------------------------------------------------"
-               read user_selection
-               if [[ -n $( grep -Es '^([0-9]+)$' <<< "$user_selection" ) && $user_selection -lt $i ]];then
-                       case $COLOR_SELECTION in
-                               irc)
-                                       config_variable='IRC_COLOR_SCHEME'
-                                       ;;
-                               irc-console)
-                                       config_variable='IRC_CONS_COLOR_SCHEME'
-                                       ;;
-                               irc-virtual-terminal)
-                                       config_variable='IRC_X_TERM_COLOR_SCHEME'
-                                       ;;
-                               console)
-                                       config_variable='CONSOLE_COLOR_SCHEME'
-                                       ;;
-                               virtual-terminal)
-                                       config_variable='VIRT_TERM_COLOR_SCHEME'
-                                       ;;
-                               global)
-                                       config_variable='GLOBAL_COLOR_SCHEME'
-                                       ;;
-                       esac
-                       set_color_scheme $user_selection
-                       # make file/directory first if missing
-                       if [[ ! -f $config_file ]];then
-                               if [[ ! -d $HOME/.$SCRIPT_NAME ]];then
-                                       mkdir $HOME/.$SCRIPT_NAME
-                               fi
-                               touch $config_file
-                       fi
-                       if [[ -z $( grep -s "$config_variable=" $config_file ) ]];then
-                               print_screen_output "Creating and updating config file for $COLOR_SELECTION color scheme now..."
-                               echo "$config_variable=$user_selection" >> $config_file
-                       else
-                               print_screen_output "Updating config file for $COLOR_SELECTION color scheme now..."
-                               sed $SED_I "s/$config_variable=.*/$config_variable=$user_selection/" $config_file
-                       fi
-                       # file exists now so we can go on to cleanup
-                       case $COLOR_SELECTION in
-                               irc|irc-console|irc-virtual-terminal|console|virtual-terminal)
-                                       sed $SED_I '/GLOBAL_COLOR_SCHEME=/d' $config_file
-                                       ;;
-                               global)
-                                       sed $SED_I -e '/VIRT_TERM_COLOR_SCHEME=/d' -e '/CONSOLE_COLOR_SCHEME=/d' -e '/IRC_COLOR_SCHEME=/d' \
-                                       -e '/IRC_CONS_COLOR_SCHEME=/d' -e '/IRC_X_TERM_COLOR_SCHEME=/d' $config_file
-                                       ;;
-                       esac
-               elif [[ $user_selection == $i ]];then
-                       print_screen_output "Removing all color settings from config file now..."
-                       sed $SED_I -e '/VIRT_TERM_COLOR_SCHEME=/d' -e '/GLOBAL_COLOR_SCHEME=/d' -e '/CONSOLE_COLOR_SCHEME=/d' \
-                       -e '/IRC_COLOR_SCHEME=/d' -e '/IRC_CONS_COLOR_SCHEME=/d' -e '/IRC_X_TERM_COLOR_SCHEME=/d' $config_file
-                       set_color_scheme $DEFAULT_COLOR_SCHEME
-               elif [[ $user_selection == $(( $i+1 )) ]];then
-                       print_screen_output "Ok, continuing $SCRIPT_NAME unchanged. You can set the colors anytime by starting with: -c 95 to 99"
-                       if [[ -n $CONSOLE_COLOR_SCHEME && -z $DISPLAY ]];then
-                               set_color_scheme $CONSOLE_COLOR_SCHEME
-                       elif [[ -n $VIRT_TERM_COLOR_SCHEME ]];then
-                               set_color_scheme $VIRT_TERM_COLOR_SCHEME
-                       else
-                               set_color_scheme $DEFAULT_COLOR_SCHEME
-                       fi
-               elif [[ $user_selection == $(( $i+2 )) ]];then
-                       set_color_scheme $DEFAULT_COLOR_SCHEME
-                       print_screen_output "Ok, exiting $SCRIPT_NAME now. You can set the colors later."
-                       exit 0
-               else
-                       print_screen_output "Error - Invalid Selection. You entered this: $user_selection"
-                       print_screen_output " "
-                       select_default_color_scheme
-               fi
-       else
-               print_screen_output "------------------------------------------------------------------------------"
-               print_screen_output "After finding the scheme number you like, simply run this again in a terminal to set the configuration"
-               print_screen_output "data file for your irc client. You can set color schemes for the following: start inxi with -c plus:"
-               print_screen_output "94 (console, no X - $console); 95 (terminal, X - $virt_term); 96 (irc, gui, X - $irc_gui);"
-               print_screen_output "97 (irc, X, in terminal - $irc_x_term); 98 (irc, no X - $irc_console); 99 (global - $global)"
-               exit 0
-       fi
-
-       eval $LOGFE
+#!/usr/bin/env perl
+## infobash: Copyright (C) 2005-2007  Michiel de Boer aka locsmif
+## inxi: Copyright (C) 2008-2018 Harald Hope
+##       Additional features (C) Scott Rogers - kde, cpu info
+## Further fixes (listed as known): Horst Tritremmel <hjt at sidux.com>
+## Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch
+## Jarett.Stevens - dmidecode -M patch for older systems with the /sys
+##
+## License: GNU GPL v3 or greater
+##
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+## If you don't understand what Free Software is, please read (or reread)
+## this page: http://www.gnu.org/philosophy/free-sw.html
+
+use strict;
+use warnings;
+# use diagnostics;
+use 5.008;
+
+use Cwd qw(abs_path); # qw(abs_path);#abs_path realpath getcwd
+use Data::Dumper qw(Dumper); # print_r
+use File::Find;
+use Getopt::Long qw(GetOptions);
+# Note: default auto_abbrev is enabled, that's fine
+Getopt::Long::Configure ('bundling', 'no_ignore_case', 
+'no_getopt_compat', 'no_auto_abbrev','pass_through');
+use POSIX qw(uname strftime ttyname);
+# use feature qw(state);
+
+## INXI INFO ##
+my $self_name='inxi';
+my $self_version='3.0.18';
+my $self_date='2018-07-16';
+my $self_patch='00';
+## END INXI INFO ##
+
+### INITIALIZE VARIABLES ###
+
+## Self data
+my ($self_path, $user_config_dir, $user_config_file,$user_data_dir);
+
+## Debuggers
+my $debug=0;
+my (@t0,$end,$start,$fh_l,$log_file); # log file handle, file
+my ($b_hires,$t1,$t2,$t3) = (0,0,0,0);
+# NOTE: redhat removed HiRes from Perl Core Modules. 
+if (eval {require Time::HiRes}){
+       Time::HiRes->import('gettimeofday','tv_interval','usleep');
+       $b_hires = 1;
 }
+@t0 = eval 'Time::HiRes::gettimeofday()' if $b_hires; # let's start it right away
+## Hashes
+my ( %alerts,%client,%colors,%dl,%files,%rows,%system_files,%use );
+
+## Arrays
+# ps_aux is full output, ps_cmd is only the last 10 columns to last
+my (@app,@dmesg_boot,@dmi,@gpudata,@ifs,@ifs_bsd,@paths,@pci,@ps_aux,
+@ps_cmd,@ps_gui,@sysctl,@sysctl_battery,@sysctl_sensors,@sysctl_machine,
+@uname,@usb);
+## Disk arrays 
+my (@dm_boot_disk,@dm_boot_optical,@glabel,@gpart,@hardware_raid,@labels,
+@lsblk,@partitions,@raid,@sysctl_disks,@uuids);
+my @test = (0,0,0,0,0);
+
+## Booleans
+my ($b_admin,$b_arm,$b_console_irc,$b_debug_gz,$b_debug_timers,
+$b_display,$b_dmesg_boot_check,
+$b_dmi,$b_dmidecode_force,$b_fake_bsd,$b_fake_dboot,$b_fake_pciconf,
+$b_fake_sysctl,$b_fake_usbdevs,$b_force_display,$b_gpudata,$b_irc,
+$b_log,$b_log_colors,$b_log_full,$b_man,$b_mem,$b_mips,$b_pci,$b_pci_tool,
+$b_proc_debug,$b_ps_gui,$b_root,$b_running_in_display,$b_slot_tool,
+$b_soc_audio,$b_soc_gfx,$b_soc_net,$b_sudo,$b_sysctl,$b_usb_check,$b_wmctrl);
+## Disk checks
+my ($b_dm_boot_disk,$b_dm_boot_optical,$b_glabel,$b_hardware_raid,
+$b_label_uuid,$b_lsblk,$b_partitions,$b_raid);
+my ($b_sysctl_disk,$b_update,$b_weather) = (1,1,1);
+
+## System
+my ($bsd_type,$language,$os) = ('','','');
+my ($bits_sys,$cpu_arch);
+my ($cpu_sleep,$dl_timeout,$limit,$ps_count,$usb_level) = (0.35,4,10,5,0);
+my $sensors_cpu_nu = 0;
+my $weather_unit='mi';
+
+## Tools
+my ($display,$ftp_alt,$tty_session);
+my ($display_opt,$sudo) = ('','');
+
+## Output
+my $extra = 0;# supported values: 0-3
+my $filter_string = '<filter>';
+my $line1 = "----------------------------------------------------------------------\n";
+my $line2 = "======================================================================\n";
+my $line3 = "----------------------------------------\n";
+my ($output_file,$output_type) = ('','screen');
+my $prefix = 0; # for the primiary row hash key prefix
+
+# these will assign a separator to non irc states. Important! Using ':' can 
+# trigger stupid emoticon. Note: SEP1/SEP2 from short form not used anymore.
+# behaviors in output on IRC, so do not use those.
+my %sep = ( 
+'s1-irc' => ':',
+'s1-console' => ':',
+'s2-irc' => '',
+'s2-console' => ':',
+);
+
+my %show = ('host' => 1);
+
+my %size = (
+'console' => 115,
+# Default indentation level. NOTE: actual indent is 1 greater to allow for 
+# spacing
+'indent' => 11,
+'indent-min' => 90,
+'irc' => 100, # shorter because IRC clients have nick  lists etc
+'max' => 0,
+'no-display' => 130,
+# these will be set dynamically in set_display_width()
+'term' => 80,
+'term-lines' => 100,
+);
+
+## debug temp tools
+$client{'test-konvi'} = 0;
 
 ########################################################################
-#### UTILITY FUNCTIONS
+#### STARTUP
 ########################################################################
 
 #### -------------------------------------------------------------------
-#### error handler, debugger, script updater
+#### MAIN
 #### -------------------------------------------------------------------
 
-# Error handling
-# args: $1 - error number; $2 - optional, extra information; $3 - optional extra info
-error_handler()
-{
-       eval $LOGFS
-       local error_message=''
-
-       # assemble the error message
-       case $1 in
-               2)      error_message="large flood danger, debug buffer full!"
-                       ;;
-               3)      error_message="unsupported color scheme number: $2"
-                       ;;
-               4)      error_message="unsupported verbosity level: $2"
-                       ;;
-               5)      error_message="dependency not met: $2 not found in path.\nFor distribution installation package names and missing apps information, run: $SCRIPT_NAME --recommends"
-                       ;;
-               6)      error_message="/proc not found! Quitting..."
-                       ;;
-               7)      error_message="One of the options you entered in your script parameters: $2\nis not supported.The option may require extra arguments to work.\nFor supported options (and their arguments), check the help menu: $SCRIPT_NAME -h"
-                       ;;
-               8)      error_message="the self-updater failed, wget exited with error: $2.\nYou probably need to be root.\nHint, to make for easy updates without being root, do: chown <user name> $SCRIPT_PATH/$SCRIPT_NAME"
-                       ;;
-               9)      error_message="unsupported debugging level: $2"
-                       ;;
-               10)
-                       error_message="the alt download url you provided: $2\nappears to be wrong, download aborted. Please note, the url\nneeds to end in /, without $SCRIPT_NAME, like: http://yoursite.com/downloads/"
-                       ;;
-               11)
-                       error_message="unsupported testing option argument: -! $2"
-                       ;;
-               12)
-                       error_message="the svn branch download url: $2\nappears to be empty currently. Make sure there is an actual svn branch version\nactive before you try this again. Check http://code.google.com/p/inxi\nto verify the branch status."
-                       ;;
-               13)
-                       error_message="The -t option requires the following extra arguments (no spaces between letters/numbers):\nc m cm [required], for example: -t cm8 OR -t cm OR -t c9\n(numbers: 1-20, > 5 throttled to 5 in irc clients) You entered: $2"
-                       ;;
-               14)
-                       error_message="failed to write correctly downloaded $SCRIPT_NAME to location $SCRIPT_PATH.\nThis usually means you don't have permission to write to that location, maybe you need to be root?\nThe operation failed with error: $2"
-                       ;;
-               15)
-                       error_message="failed set execute permissions on $SCRIPT_NAME at location $SCRIPT_PATH.\nThis usually means you don't have permission to set permissions on files there, maybe you need to be root?\nThe operation failed with error: $2"
-                       ;;
-               16)
-                       error_message="$SCRIPT_NAME downloaded but the file data is corrupted. Purged data and using current version."
-                       ;;
-               17)
-                       error_message="All $SCRIPT_NAME self updater features have been disabled by the distribution\npackage maintainer. This includes the option you used: $2"
-                       ;;
-               18)
-                       error_message="The argument you provided for $2 does not have supported syntax.\nPlease use the following formatting:\n$3"
-                       ;;
-               19)
-                       error_message="The option $2 has been deprecated. Please use $3 instead.\nSee -h for instructions and syntax."
-                       ;;
-               20)
-                       error_message="The option you selected has been deprecated. $2\nSee the -h (help) menu for currently supported options."
-                       ;;
-               *)      error_message="error unknown: $@"
-                       set -- 99
-                       ;;
-       esac
-       # then print it and exit
-       print_screen_output "Error $1: $error_message"
-       eval $LOGFE
-       exit $1
-}
-
-# prior to script up set, pack the data into an array
-# then we'll print it out later.
-# args: $1 - $@ debugging string text
-script_debugger()
-{
-       eval $LOGFS
-       if [[ $B_SCRIPT_UP == 'true' ]];then
-               # only return if debugger is off and no pre start up errors have occured
-               if [[ $DEBUG -eq 0 && $DEBUG_BUFFER_INDEX -eq 0 ]];then
-                       return 0
-               # print out the stored debugging information if errors occured
-               elif [[ $DEBUG_BUFFER_INDEX -gt 0 ]];then
-                       for (( DEBUG_BUFFER_INDEX=0; DEBUG_BUFFER_INDEX < ${#A_DEBUG_BUFFER[@]}; DEBUG_BUFFER_INDEX++ ))
-                       do
-                               print_screen_output "${A_DEBUG_BUFFER[$DEBUG_BUFFER_INDEX]}"
-                       done
-                       DEBUG_BUFFER_INDEX=0
-               fi
-               # or print out normal debugger messages if debugger is on
-               if [[ $DEBUG -gt 0 ]];then
-                       print_screen_output "$1"
-               fi
-       else
-               if [[ $B_DEBUG_FLOOD == 'true' && $DEBUG_BUFFER_INDEX -gt 10 ]];then
-                       error_handler 2
-               # this case stores the data for later printout, will print out only
-               # at B_SCRIPT_UP == 'true' if array index > 0
-               else
-                       A_DEBUG_BUFFER[$DEBUG_BUFFER_INDEX]="$1"
-                       # increment count for next pre script up debugging error
-                       (( DEBUG_BUFFER_INDEX++ ))
-               fi
-       fi
-       eval $LOGFE
-}
-
-# NOTE: no logging available until get_parameters is run, since that's what sets logging
-# in order to trigger earlier logging manually set B_USE_LOGGING to true in top variables.
-# $1 alone: logs data; $2 with or without $3 logs func start/end.
-# $1 type (fs/fe/cat/raw) or logged data; [$2 is $FUNCNAME; [$3 - function args]]
-log_function_data()
-{
-       if [ "$B_USE_LOGGING" == 'true' ];then
-               local logged_data='' spacer='   ' line='----------------------------------------'
-               case $1 in
-                       fs)
-                               logged_data="Function: $2 - Primary: Start"
-                               if [ -n "$3" ];then
-                                       logged_data="$logged_data\n${spacer}Args: $3"
-                               fi
-                               spacer=''
-                               ;;
-                       fe)
-                               logged_data="Function: $2 - Primary: End"
-                               spacer=''
-                               ;;
-                       cat)
-                               if [[ $B_LOG_FULL_DATA == 'true' ]];then
-                                       logged_data="\n$line\nFull file data: cat $2\n\n$( cat $2 )\n$line\n"
-                                       spacer=''
-                               fi
-                               ;;
-                       raw)
-                               if [[ $B_LOG_FULL_DATA == 'true' ]];then
-                                       logged_data="\n$line\nRaw system data:\n\n$2\n$line\n"
-                                       spacer=''
-                               fi
-                               ;;
-                       *)
-                               logged_data="$1"
-                               ;;
-               esac
-               # Create any required line breaks and strip out escape color code, either ansi (case 1)or irc (case 2).
-               # This pattern doesn't work for irc colors, if we need that someone can figure it out
-               if [[ -n $logged_data ]];then
-                       if [[ $B_LOG_COLORS != 'true' ]];then
-                               echo -e "${spacer}$logged_data" | sed $SED_RX 's/\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m//g' >> $LOG_FILE
-                       else
-                               echo -e "${spacer}$logged_data" >> $LOG_FILE
-                       fi
-               fi
-       fi
-}
-
-# called in the initial -@ 10 script args setting so we can get logging as soon as possible
-# will have max 3 files, inxi.log, inxi.1.log, inxi.2.log
-create_rotate_logfiles()
-{
-       if [[ ! -d $SCRIPT_DATA_DIR ]];then
-               mkdir $SCRIPT_DATA_DIR
-       fi
-       # do the rotation if logfile exists
-       if [[ -f $LOG_FILE ]];then
-               # copy if present second to third
-               if [[ -f $LOG_FILE_1 ]];then
-                       mv -f $LOG_FILE_1 $LOG_FILE_2
-               fi
-               # then copy initial to second
-               mv -f $LOG_FILE $LOG_FILE_1
-       fi
-       # now create the logfile
-       touch $LOG_FILE
-       # and echo the start data
-       echo "=========================================================" >> $LOG_FILE
-       echo "START $SCRIPT_NAME LOGGING:"                               >> $LOG_FILE
-       echo "Script started: $( date +%Y-%m-%d-%H:%M:%S )"              >> $LOG_FILE
-       echo "=========================================================" >> $LOG_FILE
-}
-
-# args: $1 - download url, not including file name; $2 - string to print out
-# $3 - update type option
-# note that $1 must end in / to properly construct the url path
-script_self_updater()
-{
-       eval $LOGFS
-       local wget_error=0 file_contents='' wget_man_error=0 
-       local man_file_path="$MAN_FILE_LOCATION/inxi.1.gz"
-       
-       if [[ $B_RUNNING_IN_SHELL != 'true' ]];then
-               print_screen_output "Sorry, you can't run the $SCRIPT_NAME self updater option (-$3) in an IRC client."
-               exit 1
-       fi
-
-       print_screen_output "Starting $SCRIPT_NAME self updater."
-       print_screen_output "Currently running $SCRIPT_NAME version number: $SCRIPT_VERSION_NUMBER"
-       print_screen_output "Current version patch number: $SCRIPT_PATCH_NUMBER"
-       print_screen_output "Updating $SCRIPT_NAME in $SCRIPT_PATH using $2 as download source..."
-
-       file_contents="$( wget -q -O - $1$SCRIPT_NAME )" || wget_error=$?
-       # then do the actual download
-       if [[  $wget_error -eq 0 ]];then
-               # make sure the whole file got downloaded and is in the variable
-               if [[ -n $( grep '###\*\*EOF\*\*###' <<< "$file_contents" ) ]];then
-                       echo "$file_contents" > $SCRIPT_PATH/$SCRIPT_NAME || error_handler 14 "$?"
-                       chmod +x $SCRIPT_PATH/$SCRIPT_NAME || error_handler 15 "$?"
-                       SCRIPT_VERSION_NUMBER=$( parse_version_data 'main' )
-                       SCRIPT_PATCH_NUMBER=$( parse_version_data 'patch' )
-                       print_screen_output "Successfully updated to $2 version: $SCRIPT_VERSION_NUMBER"
-                       print_screen_output "New $2 version patch number: $SCRIPT_PATCH_NUMBER"
-                       print_screen_output "To run the new version, just start $SCRIPT_NAME again."
-                       print_screen_output "----------------------------------------"
-                       print_screen_output "Starting download of man page file now."
-                       if [[ ! -d $MAN_FILE_LOCATION ]];then
-                               print_screen_output "The required man directory was not detected on your system, unable to continue: $MAN_FILE_LOCATION"
-                       else
-                               if [[ $B_ROOT == 'true' ]];then
-                                       print_screen_output "Checking Man page download URL..."
-                                       if [[ -f /usr/share/man/man8/inxi.8.gz ]];then
-                                               print_screen_output "Updating man page location to man1."
-                                               mv -f /usr/share/man/man8/inxi.8.gz /usr/share/man/man1/inxi.1.gz 
-                                               if [[ -n $( type -p mandb ) ]];then
-                                                       exec $( type -p mandb ) -q 
-                                               fi
-                                       fi
-                                       wget -q --spider $MAN_FILE_DOWNLOAD || wget_man_error=$?
-                                       if [[ $wget_man_error -eq 0 ]];then
-                                               print_screen_output "Man file download URL verified: $MAN_FILE_DOWNLOAD"
-                                               print_screen_output "Downloading Man page file now."
-                                               wget -q -O $man_file_path $MAN_FILE_DOWNLOAD || wget_man_error=$?
-                                               if [[ $wget_man_error -gt 0 ]];then
-                                                       print_screen_output "Oh no! Something went wrong downloading the Man gz file at: $MAN_FILE_DOWNLOAD"
-                                                       print_screen_output "Check the error messages for what happened. Error: $wget_man_error"
-                                               else
-                                                       print_screen_output "Download/install of man page successful. Check to make sure it works: man inxi"
-                                               fi
-                                       else
-                                               print_screen_output "Man file download URL failed, unable to continue: $MAN_FILE_DOWNLOAD"
-                                       fi
-                               else
-                                       print_screen_output "Updating / Installing the Man page requires root user, writing to: $MAN_FILE_LOCATION"
-                                       print_screen_output "If you want the man page, you'll have to run $SCRIPT_NAME -$3 as root."
-                               fi
-                       fi
-                       exit 0
-               else
-                       error_handler 16
-               fi
-       # now run the error handlers on any wget failure
-       else
-               if [[ $2 == 'svn server' ]];then
-                       error_handler 8 "$wget_error"
-               elif [[ $2 == 'alt server' ]];then
-                       error_handler 10 "$1"
-               else
-                       error_handler 12 "$1"
-               fi
-       fi
-       eval $LOGFS
-}
-
-# args: $1 - debug data type: sys|xorg|disk
-debug_data_collector()
-{
-       local xiin_app='' xiin_data_file='' xiin_download='' error='' b_run_xiin='false'
-       local debug_data_dir='' bsd_string=''
-       local completed_gz_file='' xiin_file='xiin.py' ftp_upload='ftp.techpatterns.com/incoming'
-       local Line='-------------------------'
-       local start_directory=$( pwd )
-       
-       if [[ -n $BSD_TYPE ]];then
-               bsd_string="$BSD_TYPE-"
-       fi
-       
-       debug_data_dir="inxi-$bsd_string$(tr ' ' '-' <<< $HOSTNAME | tr '[A-Z]' '[a-z]' )-$1-$(date +%Y%m%d)" 
-       
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               if [[ -n $ALTERNATE_FTP ]];then
-                       ftp_upload=$ALTERNATE_FTP
-               fi
-               echo "Starting debugging data collection type: $1"
-               echo -n "Checking/creating required directories... "
-               if [[ ! -d $SCRIPT_DATA_DIR ]];then
-                       mkdir $SCRIPT_DATA_DIR
-               fi
-               echo 'completed'
-               cd $SCRIPT_DATA_DIR
-               if [[ -d $SCRIPT_DATA_DIR/$debug_data_dir ]];then
-                       echo 'Deleting previous xiin data directory...'
-                       rm -rf $SCRIPT_DATA_DIR/$debug_data_dir
-               fi
-               mkdir $SCRIPT_DATA_DIR/$debug_data_dir
-               if [[ -f $SCRIPT_DATA_DIR/$debug_data_dir.tar.gz ]];then
-                       echo 'Deleting previous tar.gz file...'
-                       rm -f $SCRIPT_DATA_DIR/$debug_data_dir.tar.gz
-               fi
-               
-               echo 'Collecting system info: sensors, lsusb, lspci, lspci -v data, plus /proc data'
-               echo 'also checking for dmidecode data: note, you must be root to have dmidecode work.'
-               echo "Data going into: $SCRIPT_DATA_DIR/$debug_data_dir"
-               # bsd tools
-               pciconf -vl &> $debug_data_dir/bsd-pciconf-vl.txt
-               sysctl -a &> $debug_data_dir/bsd-sysctl-a.txt
-               
-               dmidecode &> $debug_data_dir/dmidecode.txt
-               
-               lscpu &> $debug_data_dir/lscpu.txt
-               lspci &> $debug_data_dir/lspci.txt
-               lspci -n &> $debug_data_dir/lspci-n.txt
-               lspci -v &> $debug_data_dir/lspci-v.txt
-               lsusb &> $debug_data_dir/lsusb.txt
-               ps aux &> $debug_data_dir/ps-aux.txt
-               runlevel &> $debug_data_dir/runlevel.txt
-               systemctl list-units &> $debug_data_dir/systemctl-list-units.txt
-               systemctl list-units --type=target &> $debug_data_dir/systemctl-list-units-target.txt
-               initctl list &> $debug_data_dir/initctl-list.txt
-               sensors &> $debug_data_dir/sensors.txt
-               strings --version  &> $debug_data_dir/strings.txt
-               nvidia-smi -q &> $debug_data_dir/nvidia-smi-q.txt
-               nvidia-smi -q -x &> $debug_data_dir/nvidia-smi-xq.txt
-               
-               ls /usr/bin/gcc* &> $debug_data_dir/gcc-sys-versions.txt
-               gcc --version &> $debug_data_dir/gcc-version.txt
-               cat /etc/issue &> $debug_data_dir/etc-issue.txt
-               cat $FILE_LSB_RELEASE &> $debug_data_dir/lsb-release.txt
-               cat $FILE_OS_RELEASE &> $debug_data_dir/os-release.txt
-               cat $FILE_ASOUND_DEVICE &> $debug_data_dir/proc-asound-device.txt
-               cat $FILE_ASOUND_VERSION &> $debug_data_dir/proc-asound-version.txt
-               cat $FILE_CPUINFO &> $debug_data_dir/proc-cpu-info.txt
-               cat $FILE_MEMINFO &> $debug_data_dir/proc-meminfo.txt
-               cat $FILE_MODULES &> $debug_data_dir/proc-modules.txt
-               cat /proc/net/arp &> $debug_data_dir/proc-net-arp.txt 
-               # bsd data
-               cat /var/run/dmesg.boot &> $debug_data_dir/bsd-var-run-dmesg.boot.txt 
-               
-               check_recommends_user_output &> $debug_data_dir/check-recommends-user-output.txt
-               # first download and verify xiin
-               if [[ $B_UPLOAD_DEBUG_DATA == 'true' || $1 == 'disk' || $1 == 'sys' || $1 == 'all' ]];then
-                       touch $SCRIPT_DATA_DIR/$debug_data_dir/xiin-error.txt
-                       echo "Downloading required tree traverse tool $xiin_file..."
-                       if [[ -f xiin && ! -f $xiin_file ]];then
-                               mv -f xiin $xiin_file
-                       fi
-                       # -Nc is creating really weird download anomolies, so using -O instead
-                       xiin_download="$( wget -q -O - http://inxi.googlecode.com/svn/branches/xiin/$xiin_file )"
-                       # if nothing got downloaded kick out error, otherwise we'll use an older version
-                       if [[ $? -gt 0 && ! -f $xiin_file ]];then
-                               echo -e "ERROR: Failed to download required file: $xiin_file\nMaybe the remote site is down or your networking is broken?"
-                               echo "Continuing with incomplete data collection."
-                               echo "$xiin_file download failed and no existing $xiin_file" >> $debug_data_dir/xiin-error.txt
-                       elif [[ -n $( grep -s '# EOF' <<< "$xiin_download" ) || -f $xiin_file ]];then
-                               if [[ -n $( grep -s '# EOF' <<< "$xiin_download" ) ]];then
-                                       echo "Updating $xiin_file from remote location"
-                                       echo "$xiin_download" > $xiin_file
-                               else
-                                       echo "Using local $xiin_file due to download failure"
-                               fi
-                               b_run_xiin='true'
-                       else
-                               echo -e "ERROR: $xiin_file downloaded but the program file data is corrupted.\nContinuing with incomplete data collection."
-                               echo "$xiin_file downloaded but the program file data is corrupted." >> $debug_data_dir/xiin-error.txt
-                       fi
-               fi
-               # note, only bash 4> supports ;;& for case, so using if/then here
-               if [[ $1 == 'disk' || $1 == 'sys' || $1 == 'all' ]];then
-                       xiin_data_file=$SCRIPT_DATA_DIR/$debug_data_dir/xiin-sys.txt
-                       echo 'Collecting networking data...'
-                       ifconfig &> $debug_data_dir/ifconfig.txt
-                       ip addr &> $debug_data_dir/ip-addr.txt
-                       if [[ $b_run_xiin == 'true' && -z $BSD_TYPE ]];then
-                               echo $Line
-                               echo "Running $xiin_file tool now on /sys..."
-                               echo "Using Python version:" && python --version
-                               python --version &> $debug_data_dir/python-version.txt
-                               python ./$xiin_file -d /sys -f $xiin_data_file
-                               if [[ $? -ne 0 ]];then
-                                       error=$?
-                                       echo -e "ERROR: $xiin_file exited with error $error - removing data file.\nContinuing with incomplete data collection."
-                                       echo "Continuing with incomplete data collection."
-                                       rm -f $xiin_data_file
-                                       echo "$xiin_file data generation failed with python error $error" >> $debug_data_dir/xiin-error.txt
-                               fi
-                               echo $Line
-                       fi
-               fi
-               if [[ $1 == 'xorg' || $1 == 'all' ]];then
-                       if [[ $B_RUNNING_IN_DISPLAY != 'true' ]];then
-                               echo 'Warning: only some of the data collection can occur if you are not in X'
-                               touch $debug_data_dir/warning-user-not-in-x
-                       fi
-                       if [[ $B_ROOT == 'true' ]];then
-                               echo 'Warning: only some of the data collection can occur if you are running as Root user'
-                               touch $debug_data_dir/warning-root-user
-                       fi
-                       echo 'Collecting Xorg log and xorg.conf files'
-                       if [[ -e $FILE_XORG_LOG ]];then
-                               cat $FILE_XORG_LOG &> $debug_data_dir/xorg-log-file.txt
-                       else
-                               touch $debug_data_dir/no-xorg-log-file
-                       fi
-                       if [[ -e /etc/X11/xorg.conf ]];then
-                               cp /etc/X11/xorg.conf $SCRIPT_DATA_DIR/$debug_data_dir
-                       else
-                               touch $debug_data_dir/no-xorg-conf-file
-                       fi
-                       if [[ -n $( ls /etc/X11/xorg.conf.d/ 2>/dev/null ) ]];then
-                               ls /etc/X11/xorg.conf.d &> $debug_data_dir/ls-etc-x11-xorg-conf-d.txt
-                               cp /etc/X11/xorg.conf.d $SCRIPT_DATA_DIR/$debug_data_dir
-                       else
-                               touch $debug_data_dir/no-xorg-conf-d-files
-                       fi
-                       echo 'Collecting X, xprop, glxinfo, xrandr, xdpyinfo data...'
-                       xprop -root &> $debug_data_dir/xprop_root.txt
-                       glxinfo &> $debug_data_dir/glxinfo.txt
-                       xdpyinfo &> $debug_data_dir/xdpyinfo.txt
-                       xrandr &> $debug_data_dir/xrandr.txt
-                       X -version &> $debug_data_dir/x-version.txt
-                       Xorg -version &> $debug_data_dir/xorg-version.txt
-                       echo $GNOME_DESKTOP_SESSION_ID &> $debug_data_dir/gnome-desktop-session-id.txt
-                       # kde 3 id
-                       echo $KDE_FULL_SESSION &> $debug_data_dir/kde3-ful-session.txt
-                       echo $KDE_SESSION_VERSION &> $debug_data_dir/kde456-session-version.txt
-                       echo "$(kded$KDE_SESSION_VERSION --version )" &> $debug_data_dir/kde-version-data.txt
-                       echo $XDG_CURRENT_DESKTOP &> $debug_data_dir/xdg-current-desktop.txt
-               fi
-               if [[ $1 == 'disk' || $1 == 'all' ]];then
-                       echo 'Collecting dev, label, disk, uuid data, df...'
-                       ls -l /dev &> $debug_data_dir/dev-data.txt
-                       ls -l /dev/disk &> $debug_data_dir/dev-disk-data.txt
-                       ls -l /dev/disk/by-id &> $debug_data_dir/dev-disk-id-data.txt
-                       ls -l /dev/disk/by-label &> $debug_data_dir/dev-disk-label-data.txt
-                       ls -l /dev/disk/by-uuid &> $debug_data_dir/dev-disk-uuid-data.txt
-                       ls -l /dev/disk/by-path &> $debug_data_dir/dev-disk-path-data.txt
-                       ls -l /dev/mapper &> $debug_data_dir/dev-disk-mapper-data.txt
-                       readlink /dev/root &> $debug_data_dir/dev-root.txt
-                       df -h -T -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 --exclude-type=devfs --exclude-type=linprocfs --exclude-type=sysfs --exclude-type=fdescfs &> $debug_data_dir/df-h-T-P-excludes.txt
-                       df -T -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 --exclude-type=devfs --exclude-type=linprocfs --exclude-type=sysfs --exclude-type=fdescfs &> $debug_data_dir/df-T-P-excludes.txt
-                       df -H -T &> $debug_data_dir/bsd-df-H-T-no-excludes.txt
-                       df -H &> $debug_data_dir/bsd-df-H-no-excludes.txt
-                       # bsd tool
-                       mount &> $debug_data_dir/mount.txt
-                       gpart list &> $debug_data_dir/bsd-gpart-list.txt
-                       gpart show &> $debug_data_dir/bsd-gpart-show.txt
-                       gpart status &> $debug_data_dir/bsd-gpart-status.txt
-                       swapctl -l &> $debug_data_dir/bsd-swapctl-l.txt
-                       swapon -s &> $debug_data_dir/swapon-s.txt
-                       sysctl -b kern.geom.conftxt  &> $debug_data_dir/bsd-sysctl-b-kern.geom.conftxt.txt
-                       sysctl -b kern.geom.confxml &> $debug_data_dir/bsd-sysctl-b-kern.geom.confxml.txt
-                       zfs list &> $debug_data_dir/bsd-zfs-list.txt
-                       zpool list &> $debug_data_dir/bsd-zpool-list.txt
-                       zpool list -v &> $debug_data_dir/bsd-zpool-list-v.txt
-                       df -P --exclude-type=aufs --exclude-type=squashfs --exclude-type=unionfs --exclude-type=devtmpfs --exclude-type=tmpfs --exclude-type=iso9660 &> $debug_data_dir/df-P-excludes.txt
-                       df -P &> $debug_data_dir/bsd-df-P-no-excludes.txt
-                       cat /proc/mdstat &> $debug_data_dir/proc-mdstat.txt
-                       cat $FILE_PARTITIONS &> $debug_data_dir/proc-partitions.txt
-                       cat $FILE_SCSI &> $debug_data_dir/proc-scsi.txt
-                       cat $FILE_MOUNTS &> $debug_data_dir/proc-mounts.txt
-                       cat /proc/sys/dev/cdrom/info &> $debug_data_dir/proc-cdrom-info.txt
-                       ls /proc/ide/ &> $debug_data_dir/proc-ide.txt
-                       cat /proc/ide/*/* &> $debug_data_dir/proc-ide-hdx-cat.txt
-                       cat /etc/fstab &> $debug_data_dir/etc-fstab.txt
-                       cat /etc/mtab &> $debug_data_dir/etc-mtab.txt
-               fi
-               echo 'Creating inxi output file now. This can take a few seconds...'
-               echo "Starting $SCRIPT_NAME from: $start_directory"
-               cd $start_directory
-               $SCRIPT_PATH/$SCRIPT_NAME -FRploudxxx -c 0 -@ 8 > $SCRIPT_DATA_DIR/$debug_data_dir/inxi-FRploudxxx.txt
-               cp $LOG_FILE $SCRIPT_DATA_DIR/$debug_data_dir
-               if [[ -f $SCRIPT_DATA_DIR/$debug_data_dir.tar.gz ]];then
-                       echo "Found and removing previous tar.gz data file: $debug_data_dir.tar.gz"
-                       rm -f $SCRIPT_DATA_DIR/$debug_data_dir.tar.gz
-               fi
-               cd $SCRIPT_DATA_DIR
-               echo 'Creating tar.gz compressed file of this material now. Contents:'
-               echo $Line
-               tar -cvzf $debug_data_dir.tar.gz $debug_data_dir
-               echo $Line
-               echo 'Cleaning up leftovers...'
-               rm -rf $debug_data_dir
-               echo 'Testing gzip file integrity...'
-               gzip -t $debug_data_dir.tar.gz
-               if [[ $? -gt 0 ]];then
-                       echo 'Data in gz is corrupted, removing gzip file, try running data collector again.'
-                       rm -f $debug_data_dir.tar.gz
-                       echo "Data in gz is corrupted, removed gzip file" >> $debug_data_dir/gzip-error.txt
-               else
-                       echo 'All done, you can find your data gzipped directory here:'
-                       completed_gz_file=$SCRIPT_DATA_DIR/$debug_data_dir.tar.gz
-                       echo $completed_gz_file
-                       if [[ $B_UPLOAD_DEBUG_DATA == 'true' ]];then
-                               echo $Line
-                               if [[ $b_run_xiin == 'true' ]];then
-                                       echo "Running automatic upload of data to remote server $ftp_upload now..."
-                                       python ./$xiin_file --version
-                                       python ./$xiin_file -u $completed_gz_file $ftp_upload
-                                       if [[ $? -gt 0 ]];then
-                                               echo $Line
-                                               echo "Error: looks like the ftp upload failed. Error number: $?"
-                                               echo "The ftp upload failed. Error number: $?" >> $debug_data_dir/xiin-error.txt
-                                       fi
-                               else
-                                       echo 'Unable to run the automoatic ftp upload because of an error with the xiin download.'
-                                       echo "Unable to run the automoatic ftp upload because of an error with the xiin download" >> $debug_data_dir/xiin-error.txt
-                               fi
-                       else
-                               echo 'You can upload this here using most file managers: ftp.techpatterns.com/incoming'
-                               echo 'then let a maintainer know it is uploaded.'
-                       fi
-               fi
-       else
-               echo 'This feature only available in console or shell client! Exiting now.'
-       fi
-       exit 0
-}
-
-check_recommends_user_output()
-{
-       local Line='-----------------------------------------------------------------------------------------'
-       local gawk_version='N/A' sed_version='N/A' sudo_version='N/A' python_version='N/A'
-       
-       if [[ $B_RUNNING_IN_SHELL != 'true' ]];then
-               print_screen_output "Sorry, you can't run this option in an IRC client."
-               exit 1
-       fi
-       
-       initialize_paths
-       
-       echo "$SCRIPT_NAME will now begin checking for the programs it needs to operate. First a check of"
-       echo "the main languages and tools $SCRIPT_NAME uses. Python is only for debugging data collection."
-       echo $Line
-       echo "Bash version: $( bash --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^GNU bash/ {print $4}' )"
-       if [[ -n $( type -p gawk ) ]];then
-               gawk_version=$( gawk --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^GNU Awk/ {print $3}' )
-       fi
-       if [[ -n $( type -p sed ) ]];then
-               sed_version=$( sed --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^GNU sed version/ {print $4}' )
-               if [[ -z $sed_version ]];then
-                       # note: bsd sed shows error with --version flag
-                       sed_version=$( sed --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^sed: illegal option/ {print "BSD sed"}' )
-               fi
-       fi
-       if [[ -n $( type -p sudo ) ]];then
-               sudo_version=$( sudo -V 2>&1 | awk 'BEGIN {IGNORECASE=1} /^Sudo version/ {print $3}' )
-       fi
-       if [[ -n $( type -p python ) ]];then
-               python_version=$( python --version 2>&1 | awk 'BEGIN {IGNORECASE=1} /^Python/ {print $2}' )
-       fi
-       echo "Gawk version: $gawk_version"
-       echo "Sed version: $sed_version"
-       echo "Sudo version: $sudo_version"
-       echo "Python version: $python_version"
-       echo $Line
-       echo "Test One: Required System Directories."
-       echo "If one of these system directories is missing, $SCRIPT_NAME cannot operate:"
-       echo 
-       check_recommends_items 'required-dirs'
-       echo "Test Two: Required Core Applications."
-       echo "If one of these applications is missing, $SCRIPT_NAME cannot operate:"
-       echo 
-       check_recommends_items 'required-apps'
-       echo 'Test Three: Script Recommends for Graphics Features. If you do not use X these do not matter.'
-       echo "If one of these applications is missing, $SCRIPT_NAME will have incomplete output:"
-       echo 
-       check_recommends_items 'recommended-x-apps'
-       echo 'Test Four: Script Recommends for Remaining Features.' 
-       echo "If one of these applications is missing, $SCRIPT_NAME will have incomplete output:"
-       echo 
-       check_recommends_items 'recommended-apps'
-       echo 'Test Five: System Directories for Various Information.' 
-       echo "If one of these directories is missing, $SCRIPT_NAME will have incomplete output:"
-       echo 
-       check_recommends_items 'system-dirs'
-       echo 'All tests completed.' 
-}
-# args: $1 - check item
-check_recommends_items()
-{
-       local item='' item_list='' item_string='' missing_items='' missing_string=''
-       local package='' application='' feature='' type='' starter='' finisher=''
-       local package_deb='' package_pacman='' package_rpm='' 
-       local print_string='' separator=''
-       local required_dirs='/proc /sys'
-       # package-owner: 1 - debian/ubuntu; 2 - arch; 3 - yum/rpm
-       # pardus: pisi sf -q /usr/bin/package
-       local required_apps='
-       df:coreutils~coreutils~coreutils~:partition_data 
-       gawk:gawk~gawk~gawk~:core_tool
-       grep:grep~grep~grep~:string_search 
-       lspci:pciutils~pciutils~pciutils~:hardware_data 
-       ps:procps~procps~procps~:process_data 
-       readlink:coreutils~coreutils~coreutils~: 
-       sed:sed~sed~sed~:string_replace 
-       tr:coreutils~coreutils~coreutils~:character_replace 
-       uname:uname~coreutils~coreutils~:kernel_data 
-       uptime:procps~procps~procps~: 
-       wc:coreutils~coreutils~coreutils~:word_character_count
-       '
-       local x_recommends='
-       glxinfo:mesa-utils~mesa-demos~glx-utils_(openSUSE_12.3_and_later_Mesa-demo-x)~:-G_glx_info 
-       xdpyinfo:X11-utils~xorg-xdpyinfo~xorg-x11-utils~:-G_multi_screen_resolution 
-       xprop:X11-utils~xorg-xprop~x11-utils~:-S_desktop_data 
-       xrandr:x11-xserver-utils~xrandr~x11-server-utils~:-G_single_screen_resolution
-       '
-       local recommended_apps='
-       dmidecode:dmidecode~dmidecode~dmidecode~:-M_if_no_sys_machine_data 
-       file:file~file~file~:-o_unmounted_file_system
-       hddtemp:hddtemp~hddtemp~hddtemp~:-Dx_show_hdd_temp 
-       ifconfig:net-tools~net-tools~net-tools~:-i_ip_lan-deprecated
-       ip:iproute~iproute2~iproute~:-i_ip_lan
-       sensors:lm-sensors~lm_sensors~lm-sensors~:-s_sensors_output
-       strings:binutils~~~:-I_sysvinit_version
-       lsusb:usbutils~usbutils~usbutils~:-A_usb_audio;-N_usb_networking 
-       modinfo:module-init-tools~module-init-tools~module-init-tools~:-Ax,-Nx_module_version 
-       runlevel:sysvinit~sysvinit~systemd~:-I_runlevel
-       sudo:sudo~sudo~sudo~:-Dx_hddtemp-user;-o_file-user
-       '
-       local recommended_dirs='
-       /sys/class/dmi/id:-M_system,_motherboard,_bios
-       /dev:-l,-u,-o,-p,-P,-D_disk_partition_data
-       /dev/disk/by-label:-l,-o,-p,-P_partition_labels
-       /dev/disk/by-uuid:-u,-o,-p,-P_partition_uuid
-       /var/run/dmesg.boot:-C,-f_(BSD_only)
-       '
-       
-       case $1 in
-               required-dirs)
-                       item_list=$required_dirs
-                       item_string='Required file system'
-                       missing_string='system directories'
-                       type='directories'
-                       ;;
-               required-apps)
-                       item_list=$required_apps
-                       item_string='Required application'
-                       missing_string='applications, and their corresponding packages,'
-                       type='applications'
-                       ;;
-               recommended-x-apps)
-                       item_list=$x_recommends
-                       item_string='Recommended X application'
-                       missing_string='applications, and their corresponding packages,'
-                       type='applications'
-                       ;;
-               recommended-apps)
-                       item_list=$recommended_apps
-                       item_string='Recommended application'
-                       missing_string='applications, and their corresponding packages,'
-                       type='applications'
-                       ;;
-               system-dirs)
-                       item_list=$recommended_dirs
-                       item_string='System directory'
-                       missing_string='system directories'
-                       type='directories'
-                       ;;
-       esac
-       # great trick from: http://ideatrash.net/2011/01/bash-string-padding-with-sed.html
-       # left pad: sed -e :a -e 's/^.\{1,80\}$/& /;ta'
-       # right pad: sed -e :a -e 's/^.\{1,80\}$/ &/;ta'
-       # center pad: sed -e :a -e 's/^.\{1,80\}$/ & /;ta'
-       
-       for item in $item_list
-       do
-               if [[ $( awk -F ":" '{print NF-1}' <<< $item ) -eq 0 ]];then
-                       application=$item
-                       package=''
-                       feature=''
-                       location=''
-               elif [[ $( awk -F ":" '{print NF-1}' <<< $item ) -eq 1 ]];then
-                       application=$( cut -d ':' -f 1 <<< $item )
-                       package=''
-                       feature=$( cut -d ':' -f 2 <<< $item )
-                       location=''
-               else
-                       application=$( cut -d ':' -f 1 <<< $item )
-                       package=$( cut -d ':' -f 2 <<< $item )
-                       location=$( type -p $application )
-                       if [[ $( awk -F ":" '{print NF-1}' <<< $item ) -eq 2 ]];then
-                               feature=$( cut -d ':' -f 3 <<< $item )
-                       else
-                               feature=''
-                       fi
-               fi
-               if [[ -n $feature ]];then
-                       print_string="$item_string: $application (info: $( sed 's/_/ /g' <<< $feature ))"
-               else
-                       print_string="$item_string: $application"
-               fi
-               
-               starter="$( sed -e :a -e 's/^.\{1,75\}$/&./;ta' <<< $print_string )"
-               if [[ -z $( grep '^/' <<< $application ) && -n $location ]] || [[ -d $application ]];then
-                       if [[ -n $location ]];then
-                               finisher=" $location"
-                       else
-                               finisher=" Present"
-                       fi
-               else
-                       finisher=" Missing"
-                       missing_items="$missing_items$separator$application:$package"
-                       separator=' '
-               fi
-               
-               echo "$starter$finisher"
-       done
-       echo 
-       if [[ -n $missing_items ]];then
-               echo "The following $type are missing from your system:"
-               for item in $missing_items
-               do
-                       application=$( cut -d ':' -f 1 <<< $item )
-                       if [[ $type == 'applications' ]];then
-                               # echo '--------------------------------------------------------'
-                               echo
-                               package=$( cut -d ':' -f 2 <<< $item )
-                               package_deb=$( cut -d '~' -f 1 <<< $package )
-                               package_pacman=$( cut -d '~' -f 2 <<< $package )
-                               package_rpm=$( cut -d '~' -f 3 <<< $package )
-                               echo "Application: $application"
-                               echo "To add to your system, install the proper distribution package for your system:"
-                               echo "Debian/Ubuntu: $package_deb :: Arch Linux: $package_pacman :: Redhat/Fedora/Suse: $package_rpm"
-                       else
-                               echo "Directory: $application"
-                       fi
-               done
-               if [[ $item_string == 'System directory' ]];then
-                       echo "These directories are created by the kernel, so don't worry if they are not present."
-               fi
-       else
-               echo "All the $( cut -d ' ' -f 1 <<< $item_string | sed -e 's/Re/re/' -e 's/Sy/sy/' ) $type are present."
-       fi
-       echo $Line
-}
-
-#### -------------------------------------------------------------------
-#### print / output cleaners
-#### -------------------------------------------------------------------
-
-# inxi speaks through here. When run by Konversation script alias mode, uses DCOP
-# for dcop to work, must use 'say' operator, AND colors must be evaluated by echo -e
-# note: dcop does not seem able to handle \n so that's being stripped out and replaced with space.
-print_screen_output()
-{
-       eval $LOGFS
-       # the double quotes are needed to avoid losing whitespace in data when certain output types are used
-       local print_data="$( echo -e "$1" )"
-
-       # just using basic debugger stuff so you can tell which thing is printing out the data. This
-       # should help debug kde 4 konvi issues when that is released into sid, we'll see. Turning off
-       # the redundant debugger output which as far as I can tell does exactly nothing to help debugging.
-       if [[ $DEBUG -gt 5 ]];then
-               if [[ $KONVI -eq 1 ]];then
-                       # konvi doesn't seem to like \n characters, it just prints them literally
-                       # print_data="$( tr '\n' ' ' <<< "$print_data" )"
-                       # dcop "$DCPORT" "$DCOPOBJ" say "$DCSERVER" "$DCTARGET" "konvi='$KONVI' saying : '$print_data'"
-                       print_data="KP-$KONVI: $print_data"
-               elif [[ $KONVI -eq 2 ]];then
-                       # echo "konvi='$KONVI' saying : '$print_data'"
-                       print_data="KP-$KONVI: $print_data"
-               else
-                       # echo "printing out: '$print_data'"
-                       print_data="P: $print_data"
-               fi
-       fi
-
-       if [[ $KONVI -eq 1 && $B_DCOP == 'true' ]]; then ## dcop Konversation (<= 1.1 (qt3))
-               # konvi doesn't seem to like \n characters, it just prints them literally
-               $print_data="$( tr '\n' ' ' <<< "$print_data" )"
-               dcop "$DCPORT" "$DCOPOBJ" say "$DCSERVER" "$DCTARGET" "$print_data"
-
-       elif [[ $KONVI -eq 3 && $B_QDBUS == 'true' ]]; then ## dbus Konversation (> 1.2 (qt4))
-               qdbus org.kde.konversation /irc say "$DCSERVER" "$DCTARGET" "$print_data"
-
-#      elif [[ $IRC_CLIENT == 'X-Chat' ]]; then
-#              qdbus org.xchat.service print "$print_data\n"
-
-       else
-               # the -n is needed to avoid double spacing of output in terminal
-               echo -ne "$print_data\n"
-       fi
-       eval $LOGFE
-}
-
-## this handles all verbose line construction with indentation/line starter
-## args: $1 - null (, actually: " ") or line starter; $2 - line content
-create_print_line()
-{
-       eval $LOGFS
-       printf "${C1}%-${INDENT}s${C2} %s" "$1" "$2"
-       eval $LOGFE
-}
-
-# this removes newline and pipes.
-# args: $1 - string to clean
-remove_erroneous_chars()
-{
-       eval $LOGFS
-       ## RS is input record separator
-       ## gsub is substitute;
-       gawk '
-       BEGIN {
-               RS=""
-       }
-       {
-               gsub(/\n$/,"")         ## (newline; end of string) with (nothing)
-               gsub(/\n/," ");        ## (newline) with (space)
-               gsub(/^ *| *$/, "")    ## (pipe char) with (nothing)
-               gsub(/  +/, " ")       ## ( +) with (space)
-               gsub(/ [ ]+/, " ")     ## ([ ]+) with (space)
-               gsub(/^ +| +$/, "")    ## (pipe char) with (nothing)
-               printf $0
-       }' "$1"      ## prints (returns) cleaned input
-       eval $LOGFE
+sub main {
+#      print Dumper \@ARGV;
+       eval $start if $b_log;
+       initialize();
+       ## use for start client debugging
+       # $debug = 3; # 3 prints timers
+       # set_debugger(); # for debugging of konvi issues
+       #my $ob_start = StartClient->new();
+       #$ob_start->get_client_data();
+       StartClient::get_client_data();
+       # print_line( Dumper \%client);
+       get_options();
+       set_debugger(); # right after so it's set
+       check_tools();
+       set_colors();
+       set_sep();
+       # print download_file('stdout','https://') . "\n";
+       generate_lines();
+       eval $end if $b_log;
+       cleanup();
+       # weechat's executor plugin forced me to do this, and rightfully so, 
+       # because else the exit code from the last command is taken..
+       exit 0;
 }
 
 #### -------------------------------------------------------------------
-#### parameter handling, print usage functions.
+#### INITIALIZE
 #### -------------------------------------------------------------------
 
-# Get the parameters. Note: standard options should be lower case, advanced or testing, upper
-# args: $1 - full script startup args: $@
-get_parameters()
-{
-       eval $LOGFS
-       local opt='' wget_test='' debug_data_type='' weather_flag='wW:' 
-       local use_short='true' # this is needed to trigger short output, every v/d/F/line trigger sets this false
-
-       # if distro maintainers don't want the weather feature disable it
-       if [[ $B_ALLOW_WEATHER == 'false' ]];then
-               weather_flag=''
-               
-       fi
-       if [[ $1 == '--version' ]];then
-               print_version_info
-               exit 0
-       elif [[ $1 == '--help' ]];then
-               show_options
-               exit 0
-       elif [[ $1 == '--recommends' ]];then
-               check_recommends_user_output
-               exit 0
-       # the short form only runs if no args output args are used
-       # no need to run through these if there are no args
-       # reserved for future use: -g for extra Graphics; -m for extra Machine; -d for extra Disk
-       elif [[ -n $1 ]];then
-               while getopts Abc:CdDfFGhHiIlMnNopPrRsSt:uUv:V${weather_flag}xzZ%@:!: opt
-               do
-                       case $opt in
-                       A)      B_SHOW_AUDIO='true'
-                               use_short='false'
-                               ;;
-                       b)      use_short='false'
-                               B_SHOW_BASIC_CPU='true'
-                               B_SHOW_BASIC_RAID='true'
-                               B_SHOW_DISK_TOTAL='true'
-                               B_SHOW_GRAPHICS='true'
-                               B_SHOW_INFO='true'
-                               B_SHOW_MACHINE='true'
-                               B_SHOW_NETWORK='true'
-                               B_SHOW_SYSTEM='true'
-                               ;;
-                       c)      if [[ -n $( grep -E '^[0-9][0-9]?$' <<< $OPTARG ) ]];then
-                                       case $OPTARG in
-                                               99)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='global'
-                                                       ;;
-                                               98)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='irc-console'
-                                                       ;;
-                                               97)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='irc-virtual-terminal'
-                                                       ;;
-                                               96)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='irc'
-                                                       ;;
-                                               95)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='virtual-terminal'
-                                                       ;;
-                                               94)
-                                                       B_RUN_COLOR_SELECTOR='true'
-                                                       COLOR_SELECTION='console'
-                                                       ;;
-                                               *)      
-                                                       B_COLOR_SCHEME_SET='true'
-                                                       ## note: not sure about this, you'd think user values should be overridden, but
-                                                       ## we'll leave this for now
-                                                       if [[ -z $COLOR_SCHEME ]];then
-                                                               set_color_scheme "$OPTARG"
-                                                       fi
-                                                       ;;
-                                       esac
-                               else
-                                       error_handler 3 "$OPTARG"
-                               fi
-                               ;;
-                       C)      B_SHOW_CPU='true'
-                               use_short='false'
-                               ;;
-                       d)      B_SHOW_DISK='true'
-                               B_SHOW_FULL_OPTICAL='true'
-                               use_short='false'
-                               # error_handler 20 "-d has been replaced by -b"
-                               ;;
-                       D)      B_SHOW_DISK='true'
-                               use_short='false'
-                               ;;
-                       f)      B_SHOW_CPU='true'
-                               B_CPU_FLAGS_FULL='true'
-                               use_short='false'
-                               ;;
-                       F)      # B_EXTRA_DATA='true'
-                               B_SHOW_ADVANCED_NETWORK='true'
-                               B_SHOW_AUDIO='true'
-                               # B_SHOW_BASIC_OPTICAL='true'
-                               B_SHOW_CPU='true'
-                               B_SHOW_DISK='true'
-                               B_SHOW_GRAPHICS='true'
-                               B_SHOW_INFO='true'
-                               B_SHOW_MACHINE='true'
-                               B_SHOW_NETWORK='true'
-                               B_SHOW_PARTITIONS='true'
-                               B_SHOW_RAID='true'
-                               B_SHOW_SENSORS='true'
-                               B_SHOW_SYSTEM='true'
-                               use_short='false'
-                               ;;
-                       G)      B_SHOW_GRAPHICS='true'
-                               use_short='false'
-                               ;;
-                       i)      B_SHOW_IP='true'
-                               B_SHOW_NETWORK='true'
-                               B_SHOW_ADVANCED_NETWORK='true'
-                               use_short='false'
-                               ;;
-                       I)      B_SHOW_INFO='true'
-                               use_short='false'
-                               ;;
-                       l)      B_SHOW_LABELS='true'
-                               B_SHOW_PARTITIONS='true'
-                               use_short='false'
-                               ;;
-                       M)      B_SHOW_MACHINE='true'
-                               use_short='false'
-                               ;;
-                       n)      B_SHOW_ADVANCED_NETWORK='true'
-                               B_SHOW_NETWORK='true'
-                               use_short='false'
-                               ;;
-                       N)      B_SHOW_NETWORK='true'
-                               use_short='false'
-                               ;;
-                       o)      B_SHOW_UNMOUNTED_PARTITIONS='true'
-                               use_short='false'
-                               ;;
-                       p)      B_SHOW_PARTITIONS_FULL='true'
-                               B_SHOW_PARTITIONS='true'
-                               use_short='false'
-                               ;;
-                       P)      B_SHOW_PARTITIONS='true'
-                               use_short='false'
-                               ;;
-                       r)      B_SHOW_REPOS='true'
-                               use_short='false'
-                               ;;
-                       R)      B_SHOW_RAID='true'
-                               # it turns out only users with mdraid software installed will have raid,
-                               # so unless -R is explicitly called, blank -b/-F/-v6 and less output will not show
-                               # error if file is missing.
-                               B_SHOW_RAID_R='true'
-                               use_short='false'
-                               ;;
-                       s)      B_SHOW_SENSORS='true'
-                               use_short='false'
-                               ;;
-                       S)      B_SHOW_SYSTEM='true'
-                               use_short='false'
-                               ;;
-                       t)      if [[ -n $( grep -E '^(c|m|cm|mc)([1-9]|1[0-9]|20)?$' <<< $OPTARG ) ]];then
-                                       use_short='false'
-                                       if [[ -n $( grep -E '[0-9]+' <<< $OPTARG ) ]];then
-                                               PS_COUNT=$( sed 's/[^0-9]//g' <<< $OPTARG )
-                                       fi
-                                       if [[ -n $( grep 'c' <<< $OPTARG ) ]];then
-                                               B_SHOW_PS_CPU_DATA='true'
-                                       fi
-                                       if [[ -n $( grep 'm' <<< $OPTARG ) ]];then
-                                               B_SHOW_PS_MEM_DATA='true'
-                                       fi
-                               else
-                                       error_handler 13 "$OPTARG"
-                               fi
-                               ;;
-                       u)      B_SHOW_UUIDS='true'
-                               B_SHOW_PARTITIONS='true'
-                               use_short='false'
-                               ;;
-                       v)      if [[ -n $( grep -E "^[0-9][0-9]?$" <<< $OPTARG ) && $OPTARG -le $VERBOSITY_LEVELS ]];then
-                                       if [[ $OPTARG -ge 1 ]];then
-                                               use_short='false'
-                                               B_SHOW_BASIC_CPU='true'
-                                               B_SHOW_DISK_TOTAL='true'
-                                               B_SHOW_GRAPHICS='true'
-                                               B_SHOW_INFO='true'
-                                               B_SHOW_SYSTEM='true'
-                                       fi
-                                       if [[ $OPTARG -ge 2 ]];then
-                                               B_SHOW_BASIC_DISK='true'
-                                               B_SHOW_BASIC_RAID='true'
-                                               B_SHOW_MACHINE='true'
-                                               B_SHOW_NETWORK='true'
-                                       fi
-                                       if [[ $OPTARG -ge 3 ]];then
-                                               B_SHOW_ADVANCED_NETWORK='true'
-                                               B_SHOW_CPU='true'
-                                               B_EXTRA_DATA='true'
-                                       fi
-                                       if [[ $OPTARG -ge 4 ]];then
-                                               B_SHOW_DISK='true'
-                                               B_SHOW_PARTITIONS='true'
-                                       fi
-                                       if [[ $OPTARG -ge 5 ]];then
-                                               B_SHOW_AUDIO='true'
-                                               B_SHOW_BASIC_OPTICAL='true'
-                                               B_SHOW_SENSORS='true'
-                                               B_SHOW_LABELS='true'
-                                               B_SHOW_UUIDS='true'
-                                               B_SHOW_RAID='true'
-                                       fi
-                                       if [[ $OPTARG -ge 6 ]];then
-                                               B_SHOW_FULL_OPTICAL='true'
-                                               B_SHOW_PARTITIONS_FULL='true'
-                                               B_SHOW_UNMOUNTED_PARTITIONS='true'
-                                               B_EXTRA_EXTRA_DATA='true'
-                                       fi
-                                       if [[ $OPTARG -ge 7 ]];then
-                                               B_EXTRA_EXTRA_EXTRA_DATA='true'
-                                               B_SHOW_IP='true'
-                                               B_SHOW_RAID_R='true'
-                                       fi
-                               else
-                                       error_handler 4 "$OPTARG"
-                               fi
-                               ;;
-                       U)      if [[ $B_ALLOW_UPDATE == 'true' ]];then
-                                       script_self_updater "$SCRIPT_DOWNLOAD" 'svn server' "$opt"
-                               else
-                                       error_handler 17 "-$opt"
-                               fi
-                               ;;
-                       V)      print_version_info
-                               exit 0
-                               ;;
-                       w)      B_SHOW_WEATHER=true
-                               use_short='false'
-                               ;;
-                       W)      ALTERNATE_WEATHER_LOCATION=$( sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'  <<< $OPTARG )
-                               if [[ -n $( grep -Esi '([^,]+,.+|[0-9-]+)' <<< $ALTERNATE_WEATHER_LOCATION ) ]];then
-                                       B_SHOW_WEATHER=true
-                                       use_short='false'
-                               else
-                                       error_handler 18 "-$opt: '$OPTARG'" "city,state OR latitude,longitude OR postal/zip code."
-                               fi
-                               ;;
-                       # this will trigger either with x, xx, xxx or with Fx but not with xF
-                       x)      if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                       B_EXTRA_EXTRA_EXTRA_DATA='true'
-                               elif [[ $B_EXTRA_DATA == 'true' ]];then
-                                       B_EXTRA_EXTRA_DATA='true'
-                               else
-                                       B_EXTRA_DATA='true'
-                               fi
-                               ;;
-                       z)      B_OUTPUT_FILTER='true'
-                               ;;
-                       Z)      B_OVERRIDE_FILTER='true'
-                               ;;
-                       h)      show_options
-                               exit 0
-                               ;;
-                       H)      show_options 'full'
-                               exit 0
-                               ;;
-                       ## debuggers and testing tools
-                       %)      B_HANDLE_CORRUPT_DATA='true'
-                               ;;
-                       @)      if [[ -n $( grep -E "^([1-9]|1[0-4])$" <<< $OPTARG ) ]];then
-                                       DEBUG=$OPTARG
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               B_UPLOAD_DEBUG_DATA='true'
-                                       fi
-                                       exec 2>&1
-                                       # switch on logging only for -@ 8-10
-                                       case $OPTARG in
-                                               8|9|10)
-                                                       if [[ $OPTARG -eq 10 ]];then
-                                                               B_LOG_COLORS='true'
-                                                       elif [[ $OPTARG -eq 9 ]];then           
-                                                               B_LOG_FULL_DATA='true'
-                                                       fi
-                                                       B_USE_LOGGING='true'
-                                                       # pack the logging data for evals function start/end
-                                                       LOGFS=$LOGFS_STRING
-                                                       LOGFE=$LOGFE_STRING
-                                                       create_rotate_logfiles # create/rotate logfiles before we do anything else
-                                                       ;;
-                                               11|12|13|14)
-                                                       case $OPTARG in
-                                                               11)
-                                                                       debug_data_type='sys'
-                                                                       ;;
-                                                               12)
-                                                                       debug_data_type='xorg'
-                                                                       ;;
-                                                               13)
-                                                                       debug_data_type='disk'
-                                                                       ;;
-                                                               14)
-                                                                       debug_data_type='all'
-                                                                       ;;
-                                                       esac
-                                                       initialize_data
-                                                       debug_data_collector $debug_data_type
-                                                       ;;
-                                       esac
-                               else
-                                       error_handler 9 "$OPTARG"
-                               fi
-                               ;;
-                       !)      # test for various supported methods
-                               case $OPTARG in
-                                       1)      B_TESTING_1='true'
-                                               ;;
-                                       2)      B_TESTING_2='true'
-                                               ;;
-                                       3)      B_TESTING_1='true'
-                                               B_TESTING_2='true'
-                                               ;;
-                                       1[0-6]|http*)
-                                               if [[ $B_ALLOW_UPDATE == 'true' ]];then
-                                                       case $OPTARG in
-                                                               10)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_DEV" 'dev server' "$opt $OPTARG"
-                                                                       ;;
-                                                               11)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_1" 'svn: branch one server' "$opt $OPTARG"
-                                                                       ;;
-                                                               12)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_2" 'svn: branch two server' "$opt $OPTARG"
-                                                                       ;;
-                                                               13)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_3" 'svn: branch three server' "$opt $OPTARG"
-                                                                       ;;
-                                                               14)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_4" 'svn: branch four server' "$opt $OPTARG"
-                                                                       ;;
-                                                               15)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_BSD" 'svn: branch bsd server' "$opt $OPTARG"
-                                                                       ;;
-                                                               16)
-                                                                       script_self_updater "$SCRIPT_DOWNLOAD_BRANCH_GNUBSD" 'svn: branch gnubsd server' "$opt $OPTARG"
-                                                                       ;;
-                                                               http*)
-                                                                       script_self_updater "$OPTARG" 'alt server' "$opt <http...>"
-                                                                       ;;
-                                                       esac
-                                               else
-                                                       error_handler 17 "-$opt $OPTARG"
-                                               fi
-                                               ;;
-                                       30)
-                                               B_RUNNING_IN_SHELL='true'
-                                               ;;
-                                       31)
-                                               B_SHOW_HOST='false'
-                                               ;;
-                                       32)
-                                               B_SHOW_HOST='true'
-                                               ;;
-                                       ftp*)
-                                               ALTERNATE_FTP="$OPTARG"
-                                               ;;
-                                       # for weather function, allows user to set an alternate weather location
-                                       location=*)
-                                               error_handler 19 "-$opt location=" "-W"
-                                               ;;
-                                       *)      error_handler 11 "$OPTARG"
-                                               ;;
-                               esac
-                               ;;
-                       *)      error_handler 7 "$1"
-                               ;;
-                       esac
-               done
-       fi
-       ## this must occur here so you can use the debugging flag to show errors
-       ## Reroute all error messages to the bitbucket (if not debugging)
-       if [[ $DEBUG -eq 0 ]];then
-               exec 2>/dev/null
-       fi
-       #((DEBUG)) && exec 2>&1 # This is for debugging konversation
-
-       # after all the args have been processed, if no long output args used, run short output
-       if [[ $use_short == 'true' ]];then
-               B_SHOW_SHORT_OUTPUT='true'
-       fi
-       # just in case someone insists on using -zZ
-       if [[ $B_OVERRIDE_FILTER == 'true' ]];then
-               B_OUTPUT_FILTER='false'
-       fi
-       # change basic to full if user requested it or if arg overrides it
-       if [[ $B_SHOW_RAID == 'true' && $B_SHOW_BASIC_RAID == 'true' ]];then
-               B_SHOW_BASIC_RAID='false'
-       fi
-       
-       
-       eval $LOGFE
-}
-
-## print out help menu, not including Testing or Debugger stuff because it's not needed
-show_options()
-{
-       local color_scheme_count=$(( ${#A_COLOR_SCHEMES[@]} - 1 ))
-       local partition_string='partition' partition_string_u='Partition'
-       
-       if [[ $B_RUNNING_IN_SHELL != 'true' ]];then
-               print_screen_output "Sorry, you can't run the help option in an IRC client."
-               exit 1
-       fi
-       if [[ -n $BSD_TYPE ]];then
-               partition_string='slice'
-               partition_string_u='Slice'
-       fi
-       # print_lines_basic "0" "" ""
-       # print_lines_basic "1" "" ""
-       # print_lines_basic "2" "" ""
-       # print_screen_output " "
-       print_lines_basic "0" "" "$SCRIPT_NAME supports the following options. You can combine them, or list them one by one. Examples: $SCRIPT_NAME^-v4^-c6 OR $SCRIPT_NAME^-bDc^6. If you start $SCRIPT_NAME with no arguments, it will show the short form."
-       print_screen_output " "
-       print_lines_basic "0" "" "The following options if used without -F, -b, or -v will show just option line(s): A, C, D, G, I, M, N, P, R, S, f, i, n, o, p, l, u, r, s, t - you can use these alone or together to show just the line(s) you want to see. If you use them with -v [level], -b or -F, it will show the full output for that line along with the output for the chosen verbosity level."
-       
-       print_screen_output "- - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
-       print_screen_output "Output Control Options:"
-       print_lines_basic "1" "-A" "Audio/sound card information."
-       print_lines_basic "1" "-b" "Basic output, short form. Like $SCRIPT_NAME^-v^2, only minus hard disk names."
-       print_lines_basic "1" "-c" "Color schemes. Scheme number is required. Color selectors run a color selector option prior to $SCRIPT_NAME starting which lets you set the config file value for the selection."
-       print_lines_basic "1" "" "Supported color schemes: 0-$color_scheme_count Example: $SCRIPT_NAME^-c^11"
-       print_lines_basic "1" "" "Color selectors for each type display (NOTE: irc and global only show safe color set):"
-#      print_screen_output "    Supported color schemes: 0-$color_scheme_count Example: $SCRIPT_NAME -c 11"
-#      print_screen_output "    Color selectors for each type display (NOTE: irc and global only show safe color set):"
-       print_lines_basic "2" "94" "Console, out of X"
-       print_lines_basic "2" "95" "Terminal, running in X - like xTerm"
-       print_lines_basic "2" "96" "Gui IRC, running in X - like Xchat, Quassel, Konversation etc."
-       print_lines_basic "2" "97" "Console IRC running in X - like irssi in xTerm"
-       print_lines_basic "2" "98" "Console IRC not in  X"
-       print_lines_basic "2" "99" "Global - Overrides/removes all settings. Setting specific removes global."
-       print_lines_basic "1" "-C" "CPU output, including per CPU clockspeed (if available)."
-       print_lines_basic "1" "-d" "Optical drive data. Same as -Dd. See also -x and -xx."
-       print_lines_basic "1" "-D" "Full hard Disk info, not only model, ie: /dev/sda ST380817AS 80.0GB. See also -x and -xx."
-       print_lines_basic "1" "-f" "All cpu flags, triggers -C. Not shown with -F to avoid spamming. ARM cpus show 'features'."
-       print_lines_basic "1" "-F" "Full output for $SCRIPT_NAME. Includes all Upper Case line letters, plus -s and -n. Does not show extra verbose options like -x -d -f -u -l -o -p -t -r"
-       print_lines_basic "1" "-G" "Graphic card information (card, display server type/version, resolution, glx renderer, version)."
-       print_lines_basic "1" "-i" "Wan IP address, and shows local interfaces (requires ifconfig network tool). Same as -Nni. Not shown with -F for user security reasons, you shouldn't paste your local/wan IP."
-       print_lines_basic "1" "-I" "Information: processes, uptime, memory, irc client (or shell type), $SCRIPT_NAME version."
-       print_lines_basic "1" "-l" "${partition_string_u} labels. Default: short ${partition_string} -P. For full -p output, use: -pl (or -plu)."
-       print_lines_basic "1" "-M" "Machine data. Motherboard, Bios, and if present, System Builder (Like Lenovo). Older systems/kernels without the required /sys data can use dmidecode instead, run as root."
-       print_lines_basic "1" "-n" "Advanced Network card information. Same as -Nn. Shows interface, speed, mac id, state, etc."
-       print_lines_basic "1" "-N" "Network card information. With -x, shows PCI BusID, Port number."
-       print_lines_basic "1" "-o" "Unmounted ${partition_string} information (includes UUID and LABEL if available). Shows file system type if you have file installed, if you are root OR if you have added to /etc/sudoers (sudo v. 1.7 or newer) Example:^<username>^ALL^=^NOPASSWD:^/usr/bin/file^"
-       print_lines_basic "1" "-p" "Full ${partition_string} information (-P plus all other detected ${partition_string}s)."
-       print_lines_basic "1" "-P" "Basic ${partition_string} information (shows what -v 4 would show, but without extra data). Shows, if detected: / /boot /home /tmp /usr /var. Use -p to see all mounted ${partition_string}s."
-       print_lines_basic "1" "-r" "Distro repository data. Supported repo types: APT; PACMAN; PISI; YUM; URPMQ; Ports."
-       print_lines_basic "1" "-R" "RAID data. Shows RAID devices, states, levels, and components, and extra data with -x/-xx. md-raid: If device is resyncing, shows resync progress line as well."
-       print_lines_basic "1" "-s" "Sensors output (if sensors installed/configured): mobo/cpu/gpu temp; detected fan speeds. Gpu temp only for Fglrx/Nvidia drivers. Nvidia shows screen number for > 1 screens."
-       print_lines_basic "1" "-S" "System information: host name, kernel, desktop environment (if in X), distro"
-       print_lines_basic "1" "-t" "Processes. Requires extra options: c (cpu) m (memory) cm (cpu+memory). If followed by numbers 1-20, shows that number of processes for each type (default:^$PS_COUNT; if in irc, max:^5): -t^cm10"
-       print_lines_basic "1" "" "Make sure to have no space between letters and numbers (-t^cm10 - right, -t^cm^10 - wrong)."
-       print_lines_basic "1" "-u" "${partition_string_u} UUIDs. Default: short ${partition_string} -P. For full -p output, use: -pu (or -plu)."
-       print_lines_basic "1" "-v" "Script verbosity levels. Verbosity level number is required. Should not be used with -b or -F"
-       print_lines_basic "1" "" "Supported levels: 0-${VERBOSITY_LEVELS} Example: $SCRIPT_NAME^-v^4"
-       print_lines_basic "2" "0" "Short output, same as: $SCRIPT_NAME"
-       print_lines_basic "2" "1" "Basic verbose, -S + basic CPU + -G + basic Disk + -I."
-       print_lines_basic "2" "2" "Networking card (-N), Machine (-M) data, shows basic hard disk data (names only), and, if present, basic raid (devices only, and if inactive, notes that). similar to: $SCRIPT_NAME^-b"
-       print_lines_basic "2" "3" "Advanced CPU (-C), network (-n) data, and switches on -x advanced data option."
-       print_lines_basic "2" "4" "${partition_string_u} size/filled data (-P) for (if present):/, /home, /var/, /boot. Shows full disk data (-D)."
-       print_lines_basic "2" "5" "Audio card (-A); sensors (-s), ${partition_string} label (-l) and UUID (-u), short form of optical drives, standard raid data (-R)."
-       print_lines_basic "2" "6" "Full ${partition_string} (-p), unmounted ${partition_string} (-o), optical drive (-d), full raid; triggers -xx."
-       print_lines_basic "2" "7" "Network IP data (-i); triggers -xxx."
-       
-       # if distro maintainers don't want the weather feature disable it
-       if [[ $B_ALLOW_WEATHER == 'true' ]];then
-               print_lines_basic "1" "-w" "Local weather data/time. To check an alternate location, see: -W^<location>. For extra weather data options see -x, -xx, and -xxx."
-               print_lines_basic "1" "-W" "<location> Supported options for <location>: postal code; city, state/country; latitude/longitude. Only use if you want the weather somewhere other than the machine running $SCRIPT_NAME. Use only ascii characters, replace spaces in city/state/country names with '+'. Example:^$SCRIPT_NAME^-W^new+york,ny"
-       fi
-       print_lines_basic "1" "-x" "Adds the following extra data (only works with verbose or line output, not short form):"
-       print_lines_basic "2" "-C" "CPU Flags, Bogomips on Cpu;"
-       print_lines_basic "2" "-d" "Extra optical drive data; adds rev version to optical drive."
-       print_lines_basic "2" "-D" "Hdd temp with disk data if you have hddtemp installed, if you are root OR if you have added to /etc/sudoers (sudo v. 1.7 or newer) Example:^<username>^ALL^=^NOPASSWD:^/usr/sbin/hddtemp"
-       print_lines_basic "2" "-G" "Direct rendering status for Graphics (in X)."
-       print_lines_basic "2" "-G" "(for single gpu, nvidia driver) screen number gpu is running on."
-       print_lines_basic "2" "-i" "IPv6 as well for LAN interface (IF) devices."
-       print_lines_basic "2" "-I" "System GCC, default. With -xx, also show other installed GCC versions. If running in console, not in IRC client, shows shell version number, if detected. Init/RC Type and runlevel (if available)."
-       print_lines_basic "2" "-N -A" "Version/port(s)/driver version (if available) for Network/Audio;"
-       print_lines_basic "2" "-N -A -G" "Network, audio, graphics, shows PCI Bus ID/Usb ID number of card."
-       print_lines_basic "2" "-R" "md-raid: Shows component raid id. Adds second RAID Info line: raid level; report on drives (like 5/5); blocks; chunk size; bitmap (if present). Resync line, shows blocks synced/total blocks. zfs-raid: Shows raid array full size; available size; portion allocated to RAID"
-       print_lines_basic "2" "-S" "Desktop toolkit if avaliable (GNOME/XFCE/KDE only); Kernel gcc version"
-       print_lines_basic "2" "-t" "Memory use output to cpu (-xt c), and cpu use to memory (-xt m)."
-       if [[ $B_ALLOW_WEATHER == 'true' ]];then
-               print_lines_basic "2" "-w -W" "Wind speed and time zone (-w only)."
-       fi
-       print_lines_basic "1" "-xx" "Show extra, extra data (only works with verbose or line output, not short form):"
-       print_lines_basic "2" "-A" "Chip vendor:product ID for each audio device."
-       print_lines_basic "2" "-D" "Disk serial number."
-       print_lines_basic "2" "-G" "Chip vendor:product ID for each video card."
-       print_lines_basic "2" "-I" "Other detected installed gcc versions (if present). System default runlevel. Adds parent program (or tty) for shell info if not in IRC (like Konsole or Gterm). Adds Init/RC (if found) version number."
-       print_lines_basic "2" "-M" "Chassis information, bios rom size (dmidecode only), if data for either is available."
-       print_lines_basic "2" "-N" "Chip vendor:product ID for each nic."
-       print_lines_basic "2" "-R" "md-raid: Superblock (if present); algorythm, U data. Adds system info line (kernel support,read ahead, raid events). If present, adds unused device line. Resync line, shows progress bar."
-       print_lines_basic "2" "-S" "Display manager (dm) in desktop output, if in X (like kdm, gdm3, lightdm)."
-       if [[ $B_ALLOW_WEATHER == 'true' ]];then
-               print_lines_basic "2" "-w -W" "Humidity, barometric pressure."
-       fi
-       print_lines_basic "2" "-@ 11-14" "Automatically uploads debugger data tar.gz file to ftp.techpatterns.com. EG: $SCRIPT_NAME^-xx@14"
-       print_lines_basic "1" "-xxx" "Show extra, extra, extra data (only works with verbose or line output, not short form):"
-       print_lines_basic "2" "-S" "Panel/shell information in desktop output, if in X (like gnome-shell, cinnamon, mate-panel)."
-       if [[ $B_ALLOW_WEATHER == 'true' ]];then
-               print_lines_basic "2" "-w -W" "Location (uses -z/irc filter), weather observation time, wind chill, heat index, dew point (shows extra lines for data where relevant)."
-       fi
-       print_lines_basic "1" "-z" "Security filters for IP/Mac addresses, location, user home directory name. Default on for irc clients."
-       print_lines_basic "1" "-Z" "Absolute override for output filters. Useful for debugging networking issues in irc for example."
-       print_screen_output " "
-       print_screen_output "Additional Options:"
-       print_lines_basic "4" "-h --help" "This help menu."
-       print_lines_basic "4" "-H" "This help menu, plus developer options. Do not use dev options in normal operation!"
-       print_lines_basic "4" "--recommends" "Checks $SCRIPT_NAME application dependencies + recommends, and directories, then shows what package(s) you need to install to add support for that feature. "
-       if [[ $B_ALLOW_UPDATE == 'true' ]];then
-               print_lines_basic "4" "-U" "Auto-update script. Will also install/update man page. Note: if you installed as root, you must be root to update, otherwise user is fine. Man page installs require root user mode."
-       fi
-       print_lines_basic "4" "-V --version" "$SCRIPT_NAME version information. Prints information then exits."
-       print_screen_output " "
-       print_screen_output "Debugging Options:"
-       print_lines_basic "1" "-%" "Overrides defective or corrupted data."
-       print_lines_basic "1" "-@" "Triggers debugger output. Requires debugging level 1-14 (8-10 - logging of data). Less than 8 just triggers $SCRIPT_NAME debugger output on screen."
-       print_lines_basic "2" "1-7" "On screen debugger output"
-       print_lines_basic "2" "8" "Basic logging"
-       print_lines_basic "2" "9" "Full file/sys info logging"
-       print_lines_basic "2" "10" "Color logging."
-       print_lines_basic "1" "" "The following create a tar.gz file of system data, plus collecting the inxi output to file. To automatically upload debugger data tar.gz file to ftp.techpatterns.com: inxi^-xx@^<11-14>"
-       print_lines_basic "1" "" "For alternate ftp upload locations: Example: inxi^-!^ftp.yourserver.com/incoming^-xx@^14"
-       print_lines_basic "2" "11" "With data file of xiin read of /sys."
-       print_lines_basic "2" "12" "With xorg conf and log data, xrandr, xprop, xdpyinfo, glxinfo etc."
-       print_lines_basic "2" "13" "With data from dev, disks, ${partition_string}s, etc., plus xiin data file."
-       print_lines_basic "2" "14" "Everything, full data collection."
-       print_screen_output " "
-       print_screen_output "Advanced Options:"
-       print_lines_basic "1" "-! 31" "Turns off hostname in output. Useful if showing output from servers etc."
-       print_lines_basic "1" "-! 32" "Turns on hostname in output. Overrides global B_SHOW_HOST='false'"
-       
-       if [[ $1 == 'full' ]];then
-               print_screen_output " "
-               print_screen_output "Developer and Testing Options (Advanced):"
-               print_lines_basic "1" "-! 1" "Sets testing flag B_TESTING_1='true' to trigger testing condition 1."
-               print_lines_basic "1" "-! 2" "Sets testing flag B_TESTING_2='true' to trigger testing condition 2."
-               print_lines_basic "1" "-! 3" "Sets flags B_TESTING_1='true' and B_TESTING_2='true'."
-               if [[ $B_ALLOW_UPDATE == 'true' ]];then
-                       print_lines_basic "1" "-! 10" "Triggers an update from the primary dev download server instead of svn."
-                       print_lines_basic "1" "-! 11" "Triggers an update from svn branch one - if present, of course."
-                       print_lines_basic "1" "-! 12" "Triggers an update from svn branch two - if present, of course."
-                       print_lines_basic "1" "-! 13" "Triggers an update from svn branch three - if present, of course."
-                       print_lines_basic "1" "-! 14" "Triggers an update from svn branch four - if present, of course."
-                       print_lines_basic "1" "-! 15" "Triggers an update from svn branch BSD - if present, of course."
-                       print_lines_basic "1" "-! 16" "Triggers an update from svn branch GNUBSD - if present, of course."
-                       print_lines_basic "1" "-! <http://......>" "Triggers an update from whatever server you list."
-               fi
-               print_lines_basic "1" "-! <ftp.......>" "Changes debugging data ftp upload location to whatever you enter here. Only used together with -xx@^11-14, and must be used in front of that. "
-               print_lines_basic "1" "" "Example: inxi^-!^ftp.yourserver.com/incoming^-xx@^14"
-       fi
-       print_screen_output " "
-}
-
-# uses $TERM_COLUMNS to set width using $COLS_MAX as max width
-# IMPORTANT: minimize use of subshells here or the output is too slow
-# args: $1 - 0 1 2 3 4 for indentation level; $2 -line starter, like -m; $3 - content of block.
-print_lines_basic()
-{
-       local line_width=$COLS_MAX
-       local print_string='' indent_inner='' indent_full='' indent_x='' 
-       local indent_working='' indent_working_full=''
-       local line_starter='' line_1_starter='' line_x_starter='' 
-       # note: to create a padded string below
-       local fake_string=' ' temp_count='' line_count='' spacer=''
-       local indent_main=6 indent_x='' b_indent_x='true' 
-       
-       case $1 in
-               # for no options, start at left edge
-               0)      indent_full=0
-                       line_1_starter=''
-                       line_x_starter=''
-                       b_indent_x='false'
-                       ;;
-               1)      indent_full=$indent_main
-                       temp_count=${#2}
-                       if [[ $temp_count -le $indent_full ]];then
-                               indent_working=$indent_full
-                       else
-                               indent_working=$temp_count #$(( $temp_count + 1 ))
-                       fi
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )"
-                       ;;
-               # first left pad 2 and 3, then right pad them
-               2)      indent_full=$(( $indent_main + 6 ))
-                       indent_inner=3
-                       temp_count=${#2}
-                       if [[ $temp_count -le $indent_inner ]];then
-                               indent_working=$indent_inner
-                               #indent_working_full=$indent_full
-                       else
-                               indent_working=$(( $temp_count + 1 ))
-                               #indent_working_full=$(( $indent_full - $indent_inner - 1 ))
-                       fi
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )"
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_full\}$/ &/;ta" <<< "$line_1_starter" )"
-                       ;;
-               3)      indent_full=$(( $indent_main + 8 ))
-                       indent_inner=3
-                       temp_count=${#2}
-                       if [[ $temp_count -le $indent_inner ]];then
-                               indent_working=$indent_inner
-                       else
-                               indent_working=$(( $temp_count + 1 ))
-                       fi
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )"
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_full\}$/ &/;ta" <<< "$line_1_starter" )"
-                       ;;
-               # for long options
-               4)      indent_full=$(( $indent_main + 8 ))
-                       temp_count=${#2}
-                       if [[ $temp_count -lt $indent_full ]];then
-                               indent_working=$indent_full
-                       else
-                               indent_working=$temp_count #$(( $temp_count + 1 ))
-                       fi
-                       line_1_starter="$( sed -e :a -e "s/^.\{1,$indent_working\}$/& /;ta" <<< $2 )"
-                       ;;
-       esac
-       
-       if [[ $b_indent_x == 'true' ]];then
-               indent_x=$(( $indent_full + 1 ))
-               line_x_starter="$(printf "%${indent_x}s" '')"
-       fi
-       
-       line_count=$(( $line_width - $indent_full ))
-       
-       # bash loop is slow, only run this if required
-       if [[ ${#3} -gt $line_count ]];then
-               for word in $3
-               do
-                       temp_string="$print_string$spacer$word"
-                       spacer=' '
-                       if [[ ${#temp_string} -lt $line_count ]];then
-                               print_string=$temp_string # lose any white space start/end
-                               # echo -n $(( $line_width - $indent_full ))
-                       else
-                               if [[ -n $line_1_starter ]];then
-                                       line_starter="$line_1_starter"
-                                       line_1_starter=''
-                               else
-                                       line_starter="$line_x_starter"
-                               fi
-                               # clean up forced connections, ie, stuff we don't want wrapping
-                               print_string=${print_string//\^/ }
-                               print_screen_output "$line_starter$print_string"
-                               print_string="$word$spacer" # needed to handle second word on new line
-                               temp_string=''
-                               spacer=''
-                       fi
-               done
-       else
-               # echo no loop
-               print_string=$3
-       fi
-       # print anything left over
-       if [[ -n $print_string ]];then
-               if [[ -n $line_1_starter ]];then
-                       line_starter="$line_1_starter"
-                       line_1_starter=''
-               else
-                       line_starter="$line_x_starter"
-               fi
-               print_string=${print_string//\^/ }
-               print_screen_output "$line_starter$print_string"
-       fi
-}
-# print_lines_basic '1' '-m' 'let us teest this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now'
-# print_lines_basic '2' '7' 'and its substring this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now'
-# print_lines_basic '2' '12' 'and its sss substring'
-# print_lines_basic '3' '12' 'and its sss substring this string and lots more and stuff and more stuff and x is wy and z is x and fred is dead and gus is alive an yes we have to go now'
-# exit
-
-## print out version information for -V/--version
-print_version_info()
-{
-       # if not in PATH could be either . or directory name, no slash starting
-       local script_path=$SCRIPT_PATH script_symbolic_start=''
-       if [[ $script_path == '.' ]];then
-               script_path=$( pwd )
-       elif [[ -z $( grep '^/' <<< "$script_path" ) ]];then
-               script_path="$( pwd )/$script_path"
-       fi
-       # handle if it's a symbolic link, rare, but can happen with script directories in irc clients
-       # which would only matter if user starts inxi with -! 30 override in irc client
-       if [[ -L $script_path/$SCRIPT_NAME ]];then
-               script_symbolic_start=$script_path/$SCRIPT_NAME
-               script_path=$( readlink $script_path/$SCRIPT_NAME )
-               script_path=$( dirname $script_path )
-       fi
-       local last_modified=$( parse_version_data 'date' ) 
-       local year_modified=$( gawk '{print $NF}' <<< "$last_modified" )
-       
-       print_screen_output "$SCRIPT_NAME $SCRIPT_VERSION_NUMBER-$SCRIPT_PATCH_NUMBER ($last_modified)"
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               print_screen_output "Program Location: $script_path"
-               if [[ -n $script_symbolic_start ]];then
-                       print_screen_output "Started via symbolic link: $script_symbolic_start"
-               fi
-               print_lines_basic "0" "" "Website:^http://inxi.goooglecode.com"
-               print_lines_basic "0" "" "IRC:^irc.oftc.net channel:^#smxi"
-               print_lines_basic "0" "" "Forums:^http://techpatterns.com/forums/forum-33.html"
-               print_screen_output " "
-               print_lines_basic "0" "" "$SCRIPT_NAME - the universal, portable, system information tool for console and irc."
-               print_screen_output " "
-               print_lines_basic "0" "" "This program started life as a fork of Infobash 3.02: Copyright (C) 2005-2007  Michiel de Boer a.k.a. locsmif."
-               print_lines_basic "0" "" "Subsequent changes and modifications (after Infobash 3.02): Copyright (C) 2008-$year_modified Scott Rogers, Harald Hope, aka trash80 & h2"
-               print_screen_output " "
-               print_lines_basic "0" "" "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. (http://www.gnu.org/licenses/gpl.html)"
-       fi
+sub initialize {
+       set_os();
+       set_path();
+       set_user_paths();
+       set_basics();
+       system_files('set');
+       get_configs();
+       # set_downloader();
+       set_display_width('live');
 }
 
-########################################################################
-#### MAIN FUNCTIONS
-########################################################################
-
-#### -------------------------------------------------------------------
-#### initial startup stuff
-#### -------------------------------------------------------------------
-
-# Determine where inxi was run from, set IRC_CLIENT and IRC_CLIENT_VERSION
-get_start_client()
-{
-       eval $LOGFS
-       local Irc_Client_Path='' irc_client_path_lower='' non_native_konvi='' i=''
-       local B_Non_Native_App='false' pppid='' App_Working_Name=''
-       local b_qt4_konvi='false' ps_parent=''
-
-       if [[ $B_RUNNING_IN_SHELL == 'true' ]];then
-               IRC_CLIENT='Shell'
-               unset IRC_CLIENT_VERSION
-       # elif [[ -n $PPID ]];then
-       elif [[ -n $PPID && -f /proc/$PPID/exe ]];then
-               if [[ $B_OVERRIDE_FILTER != 'true' ]];then
-                       B_OUTPUT_FILTER='true'
-               fi
-               Irc_Client_Path=$( readlink /proc/$PPID/exe )
-               # Irc_Client_Path=$( ps -p $PPID | gawk '!/[[:space:]]*PID/ {print $5}'  )
-               # echo $( ps -p $PPID )
-               irc_client_path_lower=$( tr '[:upper:]' '[:lower:]' <<< $Irc_Client_Path )
-               App_Working_Name=$( basename $irc_client_path_lower )
-               # handles the xchat/sh/bash/dash cases, and the konversation/perl cases, where clients
-               # report themselves as perl or unknown shell. IE:  when konversation starts inxi
-               # from inside itself, as a script, the parent is konversation/xchat, not perl/bash etc
-               # note: perl can report as: perl5.10.0, so it needs wildcard handling
-               case $App_Working_Name in
-                       # bsd will never use this section
-                       bash|dash|sh|python*|perl*)     # We want to know who wrapped it into the shell or perl.
-                               pppid="$( ps -p $PPID -o ppid --no-headers | sed 's/[[:space:]]//g' )"
-                               if [[ -n $pppid && -f /proc/$pppid/exe ]];then
-                                       Irc_Client_Path="$( readlink /proc/$pppid/exe )"
-                                       irc_client_path_lower="$( tr '[:upper:]' '[:lower:]' <<< $Irc_Client_Path )"
-                                       App_Working_Name=$( basename $irc_client_path_lower )
-                                       B_Non_Native_App='true'
-                               fi
-                               ;;
-               esac
-               # sets version number if it can find it
-               get_irc_client_version
-       else
-               ## lets look to see if qt4_konvi is the parent.  There is no direct way to tell, so lets infer it.
-               ## because $PPID does not work with qt4_konvi, the above case does not work
-               if [[ $B_OVERRIDE_FILTER != 'true' ]];then
-                       B_OUTPUT_FILTER='true'
-               fi
-               b_qt4_konvi=$( is_this_qt4_konvi )
-               if [[ $b_qt4_konvi == 'true' ]];then
-                       KONVI=3
-                       IRC_CLIENT='Konversation'
-                       IRC_CLIENT_VERSION=" $( konversation -v | gawk '
-                               /Konversation:/ {
-                                       for ( i=2; i<=NF; i++ ) {
-                                               if (i == NF) {
-                                                       print $i
+sub check_tools {
+       my ($action,$program,$message,@data,%commands,%hash);
+       if ( $b_dmi ){
+               $action = 'use';
+               if ($program = check_program('dmidecode')) {
+                       @data = grabber("$program -t chassis -t baseboard -t processor 2>&1");
+                       if (scalar @data < 15){
+                               if ($b_root) {
+                                       foreach (@data){
+                                               if ($_ =~ /No SMBIOS/i){
+                                                       $action = 'smbios';
+                                                       last;
+                                               }
+                                               elsif ($_ =~ /^\/dev\/mem: Operation/i){
+                                                       $action = 'no-data';
+                                                       last;
                                                }
                                                else {
-                                                       printf $i" "
+                                                       $action = 'unknown-error';
+                                                       last;
                                                }
                                        }
-                                       exit
-                               }' )"
-               else
-                       # this should handle certain cases where it's ssh or some other startup tool
-                       # that falls through all the other tests
-                       if [[ $BSD_TYPE != 'bsd' ]];then
-                               App_Working_Name=$(ps -p $PPID --no-headers 2>/dev/null | gawk '{print $NF}' )
-                       else
-                               # without --no-headers we need the second line
-                               App_Working_Name=$(ps -p $PPID 2>/dev/null | gawk '/^[0-9]+/ {print $5}' )
-                       fi
-                       
-                       if [[ -n $App_Working_Name ]];then
-                               Irc_Client_Path=$App_Working_Name
-                               B_Non_Native_App='false'
-                               get_irc_client_version
-                               if [[ -z $IRC_CLIENT ]];then
-                                       IRC_CLIENT=$App_Working_Name
-                               fi
-                       else
-                               IRC_CLIENT="PPID=\"$PPID\" - empty?"
-                               unset IRC_CLIENT_VERSION
-                       fi
-               fi
-       fi
-
-       log_function_data "IRC_CLIENT: $IRC_CLIENT :: IRC_CLIENT_VERSION: $IRC_CLIENT_VERSION :: PPID: $PPID"
-       eval $LOGFE
-}
-# note: all variables set in caller so no need to pass
-get_irc_client_version()
-{
-       local file_data=''
-       # replacing loose detection with tight detection, bugs will be handled with app names
-       # as they appear.
-       case $App_Working_Name in
-               # check for shell first
-               bash|dash|sh)
-                       unset IRC_CLIENT_VERSION
-                       IRC_CLIENT="Shell wrapper"
-                       ;;
-               # now start on irc clients, alphabetically
-               bitchx)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk '
-                       /Version/ {
-                               a=tolower($2)
-                               gsub(/[()]|bitchx-/,"",a)
-                               print a
-                               exit
-                       }
-                       $2 == "version" {
-                               a=tolower($3)
-                               sub(/bitchx-/,"",a)
-                               print a
-                               exit
-                       }' )"
-                       B_CONSOLE_IRC='true'
-                       IRC_CLIENT="BitchX"
-                       ;;
-               finch)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       B_CONSOLE_IRC='true'
-                       IRC_CLIENT="Finch"
-                       ;;
-               gaim)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       IRC_CLIENT="Gaim"
-                       ;;
-               hexchat)
-                       # the hexchat author decided to make --version/-v return a gtk dialogue box, lol...
-                       # so we need to read the actual config file for hexchat. Note that older hexchats
-                       # used xchat config file, so test first for default, then legacy. Because it's possible
-                       # for this file to be use edited, doing some extra checks here.
-                       if [[ -f ~/.config/hexchat/hexchat.conf ]];then
-                               file_data="$( cat ~/.config/hexchat/hexchat.conf )"
-                       elif [[ -f  ~/.config/hexchat/xchat.conf ]];then
-                               file_data="$( cat ~/.config/hexchat/xchat.conf )"
-                       fi
-                       if [[ -n $file_data ]];then
-                               IRC_CLIENT_VERSION=$( gawk '
-                               BEGIN {
-                                       IGNORECASE=1
-                                       FS="="
-                               }
-                               /^[[:space:]]*version/ {
-                                       # get rid of the space if present
-                                       gsub(/[[:space:]]*/, "", $2 )
-                                       print $2
-                                       exit # usually this is the first line, no point in continuing
-                               }' <<< "$file_data" )
-                               
-                               IRC_CLIENT_VERSION=" $IRC_CLIENT_VERSION"
-                       else
-                               IRC_CLIENT_VERSION=' N/A'
-                       fi
-                       IRC_CLIENT="HexChat"
-                       ;;
-               ircii)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $3
-                       }' )"
-                       B_CONSOLE_IRC='true'
-                       IRC_CLIENT="ircII"
-                       ;;
-               irssi-text|irssi)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       B_CONSOLE_IRC='true'
-                       IRC_CLIENT="Irssi"
-                       ;;
-               konversation) ## konvi < 1.2 (qt4)
-                       # this is necessary to avoid the dcop errors from starting inxi as a /cmd started script
-                       if [[ $B_Non_Native_App == 'true' ]];then  ## true negative is confusing
-                               KONVI=2
-                       else # if native app
-                               KONVI=1
-                       fi
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk '
-                       /Konversation:/ {
-                               for ( i=2; i<=NF; i++ ) {
-                                       if (i == NF) {
-                                               print $i
-                                       }
-                                       else {
-                                               printf $i" "
-                                       }
                                }
-                               exit
-                       }' )"
-
-                       T=($IRC_CLIENT_VERSION)
-                       if [[ ${T[0]} == *+* ]];then
-                               # < Sho_> locsmif: The version numbers of SVN versions look like this:
-                               #         "<version number of last release>+ #<build number", i.e. "1.0+ #3177" ...
-                               #         for releases we remove the + and build number, i.e. "1.0" or soon "1.0.1"
-                               IRC_CLIENT_VERSION=" CVS $IRC_CLIENT_VERSION"
-                               T2="${T[0]/+/}"
-                       else
-                               IRC_CLIENT_VERSION=" ${T[0]}"
-                               T2="${T[0]}"
-                       fi
-                       # Remove any dots except the first, and make sure there are no trailing zeroes,
-                       T2=$( echo "$T2" | gawk '{
-                               sub(/\./, " ")
-                               gsub(/\./, "")
-                               sub(/ /, ".")
-                               printf("%g\n", $0)
-                       }' )
-                       # Since Konversation 1.0, the DCOP interface has changed a bit: dcop "$DCPORT" Konversation ..etc
-                       # becomes : dcop "$DCPORT" default ... or dcop "$DCPORT" irc ..etc. So we check for versions smaller
-                       # than 1 and change the DCOP parameter/object accordingly.
-                       if [[ ${T2} -lt 1 ]];then
-                               DCOPOBJ="Konversation"
-                       fi
-                       IRC_CLIENT="Konversation"
-                       ;;
-               kopete)
-                       IRC_CLIENT_VERSION=" $( kopete -v | gawk '
-                       /Kopete:/ {
-                               print $2
-                               exit
-                       }' )"
-                       IRC_CLIENT="Kopete"
-                       ;;
-               kvirc)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v 2>&1 | gawk '{
-                               for ( i=2; i<=NF; i++) {
-                                       if ( i == NF ) {
-                                               print $i
+                               else {
+                                       if (grep { $_ =~ /^\/dev\/mem: Permission/i } @data){
+                                               $action = 'permissions';
                                        }
                                        else {
-                                               printf $i" "
+                                               $action = 'unknown-error';
                                        }
                                }
-                               exit
-                               }' )"
-                       IRC_CLIENT="KVIrc"
-                       ;;
-               pidgin)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       IRC_CLIENT="Pidgin"
-                       ;;
-               quassel*)
-                       # sample: quassel -v
-                       # Qt: 4.5.0
-                       # KDE: 4.2.65 (KDE 4.2.65 (KDE 4.3 >= 20090226))
-                       # Quassel IRC: v0.4.0 [+60] (git-22effe5)
-                       # note: early < 0.4.1 quassels do not have -v
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v 2>/dev/null | gawk -F ': ' '
-                       BEGIN {
-                               IGNORECASE=1
-                               clientVersion=""
-                       }
-                       /Quassel IRC/ {
-                               clientVersion = $2
-                       }
-                       END {
-                               # this handles pre 0.4.1 cases with no -v
-                               if ( clientVersion == "" ) {
-                                       clientVersion = "(pre v0.4.1)"
-                               }
-                               print clientVersion
-                       }' )"
-                       # now handle primary, client, and core. quasselcore doesn't actually
-                       # handle scripts with exec, but it's here just to be complete
-                       case $App_Working_Name in
-                               quassel)
-                                       IRC_CLIENT="Quassel [M]"
-                                       ;;
-                               quasselclient)
-                                       IRC_CLIENT="Quassel"
-                                       ;;
-                               quasselcore)
-                                       IRC_CLIENT="Quassel (core)"
-                                       ;;
-                       esac
-                       ;;
-               weechat-curses)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v ) "
-                       B_CONSOLE_IRC='true'
-                       IRC_CLIENT="Weechat"
-                       ;;
-               xchat-gnome)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       IRC_CLIENT="X-Chat-Gnome"
-                       ;;
-               xchat)
-                       IRC_CLIENT_VERSION=" $( $Irc_Client_Path -v | gawk 'NR == 1 {
-                               print $2
-                       }' )"
-                       IRC_CLIENT="X-Chat"
-                       ;;
-               # then do some perl type searches, do this last since it's a wildcard search
-               perl*|ksirc|dsirc)
-                       unset IRC_CLIENT_VERSION
-                       # KSirc is one of the possibilities now. KSirc is a wrapper around dsirc, a perl client
-                       get_cmdline $PPID
-                       for (( i=0; i <= $CMDL_MAX; i++ ))
-                       do
-                               case ${A_CMDL[i]} in
-                                       *dsirc*)
-                                       IRC_CLIENT="KSirc"
-                                       # Dynamic runpath detection is too complex with KSirc, because KSirc is started from
-                                       # kdeinit. /proc/<pid of the grandparent of this process>/exe is a link to /usr/bin/kdeinit
-                                       # with one parameter which contains parameters separated by spaces(??), first param being KSirc.
-                                       # Then, KSirc runs dsirc as the perl irc script and wraps around it. When /exec is executed,
-                                       # dsirc is the program that runs inxi, therefore that is the parent process that we see.
-                                       # You can imagine how hosed I am if I try to make inxi find out dynamically with which path
-                                       # KSirc was run by browsing up the process tree in /proc. That alone is straightjacket material.
-                                       # (KSirc sucks anyway ;)
-                                       IRC_CLIENT_VERSION=" $( ksirc -v | gawk '
-                                       /KSirc:/ {
-                                               print $2
-                                               exit
-                                       }' )"
-                                       break
-                                       ;;
-                               esac
-                       done
-                       B_CONSOLE_IRC='true'
-                       set_perl_python_konvi "$App_Working_Name"
-                       ;;
-               python*)
-                       # B_CONSOLE_IRC='true' # are there even any python type console irc clients? check.
-                       set_perl_python_konvi "$App_Working_Name"
-                       ;;
-               # then unset, set unknown data
-               *)      
-                       IRC_CLIENT="Unknown : ${Irc_Client_Path##*/}"
-                       unset IRC_CLIENT_VERSION
-                       ;;
-       esac
-       if [[ $SHOW_IRC -lt 2 ]];then
-               unset IRC_CLIENT_VERSION
-       fi
-}
-
-# args: $1 - App_Working_Name
-set_perl_python_konvi()
-{
-       if [[ -z $IRC_CLIENT_VERSION ]];then
-               # this is a hack to try to show konversation if inxi is running but started via /cmd
-               if [[ -n $( grep -i 'konversation' <<< "$Ps_aux_Data" | grep -v 'grep' ) && $B_RUNNING_IN_DISPLAY == 'true' ]];then
-                       IRC_CLIENT='Konversation'
-                       IRC_CLIENT_VERSION=" $( konversation --version 2>/dev/null | gawk '/^Konversation/ {print $2}' )"
-                       B_CONSOLE_IRC='false'
-               else
-                       IRC_CLIENT="Unknown $1 client"
-               fi
-       fi
-}
-
-## try to infer the use of Konversation >= 1.2, which shows $PPID improperly
-## no known method of finding Kovni >= 1.2 as parent process, so we look to see if it is running,
-## and all other irc clients are not running.  
-is_this_qt4_konvi()
-{
-       local konvi_qt4_client='' konvi_dbus_exist='' konvi_pid='' konvi_home_dir='' 
-       local konvi='' konvi_qt4_ver='' b_is_qt4=''
-       
-       # fringe cases can throw error, always if untested app, use 2>/dev/null after testing if present
-       if [[ $B_QDBUS == 'true' ]];then
-               konvi_dbus_exist=$( qdbus 2>/dev/null | grep "org.kde.konversation" )
-       fi
-       # sabayon uses /usr/share/apps/konversation as path
-       if [[ -n $konvi_dbus_exist ]] && [[ -e /usr/share/kde4/apps/konversation || -e  /usr/share/apps/konversation ]]; then
-               konvi_pid=$( ps -A | gawk 'BEGIN{IGNORECASE=1} /konversation/ { print $1 }' ) 
-               konvi_home_dir=$( readlink /proc/$konvi_pid/exe )
-               konvi=$( echo $konvi_home_dir | sed "s/\// /g" )
-               konvi=($konvi)
-
-               if [[ ${konvi[2]} == 'konversation' ]];then     
-                       konvi_qt4_ver=$( konversation -v | grep -i 'konversation' )
-                       # note: we need to change this back to a single dot number, like 1.3, not 1.3.2
-                       konvi_qt4_client=$( echo "$konvi_qt4_ver" | gawk '{ print $2 }' | cut -d '.' -f 1,2 )
-
-                       if [[ $konvi_qt4_client > 1.1 ]]; then
-                               b_is_qt4='true'
-                       fi
-               fi
-       else
-               konvi_qt4="qt3"
-               b_is_qt4='false'
-       fi
-       log_function_data "b_is_qt4: $b_is_qt4"
-       echo $b_is_qt4
-       ## for testing this module
-       #qdbus org.kde.konversation /irc say $1 $2 "getpid_dir: $konvi_qt4  qt4_konvi: $konvi_qt4_ver   verNum: $konvi_qt4_ver_num  pid: $konvi_pid ppid: $PPID  konvi_home_dir: ${konvi[2]}"
-}
-
-# This needs some cleanup and comments, not quite understanding what is happening, although generally output is known
-# Parse the null separated commandline under /proc/<pid passed in $1>/cmdline
-# args: $1 - $PPID
-get_cmdline()
-{
-       eval $LOGFS
-       local i=0 ppid=$1
-
-       if [[ ! -e /proc/$ppid/cmdline ]];then
-               echo 0
-               return
-       fi
-       ##print_screen_output "Marker"
-       ##print_screen_output "\$ppid='$ppid' -=- $(< /proc/$ppid/cmdline)"
-       unset A_CMDL
-       ## note: need to figure this one out, and ideally clean it up and make it readable
-       while read -d $'\0' L && [[ $i -lt 32 ]]
-       do
-               A_CMDL[i++]="$L" ## note: make sure this is valid - What does L mean? ##
-       done < /proc/$ppid/cmdline
-       ##print_screen_output "\$i='$i'"
-       if [[ $i -eq 0 ]];then
-               A_CMDL[0]=$(< /proc/$ppid/cmdline)
-               if [[ -n ${A_CMDL[0]} ]];then
-                       i=1
-               fi
-       fi
-       CMDL_MAX=$i
-       log_function_data "CMDL_MAX: $CMDL_MAX"
-       eval $LOGFE
-}
-
-#### -------------------------------------------------------------------
-#### get data types
-#### -------------------------------------------------------------------
-## create array of sound cards installed on system, and if found, use asound data as well
-get_audio_data()
-{
-       eval $LOGFS
-       local i='' alsa_data='' audio_driver='' device_count='' temp_array=''
-
-       IFS=$'\n'
-       # this first step handles the drivers for cases where the second step fails to find one
-       device_count=$( echo "$Lspci_v_Data" | grep -iEc '(multimedia audio controller|audio device)' )
-       if [[ $device_count -eq 1 ]] && [[ $B_ASOUND_DEVICE_FILE == 'true' ]];then
-               audio_driver=$( gawk -F ']: ' '
-               BEGIN {
-                       IGNORECASE=1
+                       }
                }
-               # filtering out modems and usb devices like webcams, this might get a
-               # usb audio card as well, this will take some trial and error
-               $0 !~ /modem|usb|webcam/ {
-                       driver=gensub( /^(.+)( - )(.+)$/, "\\1", 1, $2 )
-                       gsub(/^ +| +$/,"",driver)
-                       if ( driver != "" ){
-                               print driver
-                       }
-               }' $FILE_ASOUND_DEVICE ) 
-               log_function_data 'cat' "$FILE_ASOUND_DEVICE"
-       fi
-
-       # this is to safeguard against line breaks from results > 1, which if inserted into following
-       # array will create a false array entry. This is a hack, not a permanent solution.
-       audio_driver=$( echo $audio_driver )
-       # now we'll build the main audio data, card name, driver, and port. If no driver is found,
-       # and if the first method above is not null, and one card is found, it will use that instead.
-       A_AUDIO_DATA=( $( echo "$Lspci_v_Data" | gawk -F ': ' -v audioDriver="$audio_driver" '
-       BEGIN {
-               IGNORECASE=1
-       }
-       /multimedia audio controller|audio device/ {
-               audioCard=gensub(/^[0-9a-f:\.]+ [^:]+: (.+)$/,"\\1","g",$0)
-               # The doublequotes are necessary because of the pipes in the variable.
-               gsub(/'"$BAN_LIST_NORMAL"'/, "", audioCard)
-               gsub(/,/, " ", audioCard)
-               gsub(/^ +| +$/, "", audioCard)
-               gsub(/ [ \t]+/, " ", audioCard)
-               aPciBusId[audioCard] = gensub(/(^[0-9a-f:\.]+) [^:]+: .+$/,"\\1","g",$0)
-               cards[audioCard]++
-
-               # loop until you get to the end of the data block
-               while (getline && !/^$/) {
-                       gsub( /,/, "", $0 )
-                       if (/driver in use/) {
-                               drivers[audioCard] = drivers[audioCard] gensub( /(.*): (.*)/ ,"\\2", "g" ,$0 ) ""
-                       }
-                       else if (/kernel modules:/) {
-                               modules[audioCard] = modules[audioCard] gensub( /(.*): (.*)/ ,"\\2" ,"g" ,$0 ) ""
-                       }
-                       else if (/I\/O/) {
-                               portsTemp = gensub(/\t*I\/O ports at (.*) \[.*\]/,"\\1","g",$0)
-                               ports[audioCard] = ports[audioCard] portsTemp " "
-                       }
-               }
-       }
-
-       END {
-               j=0
-               for (i in cards) {
-                       useDrivers=""
-                       useModules=""
-                       usePorts=""
-                       usePciBusId=""
-                        
-                       if (cards[i]>1) {
-                               a[j]=cards[i]"x "i
-                               if (drivers[i] != "") {
-                                       useDrivers=drivers[i]
-                               }
+               else {
+                       $action = 'missing';
+               }
+               %hash = (
+               'dmidecode' => {
+               'action' => $action,
+               'missing' => 'Required program dmidecode not available',
+               'permissions' => 'Unable to run dmidecode. Are you root?',
+               'smbios' => 'No SMBIOS data for dmidecode to process',
+               'no-data' => 'dmidecode is not allowed to read /dev/mem',
+               'unknown-error' => 'dmidecode was unable to generate data',
+               },
+               );
+               %alerts = (%alerts, %hash);
+       }
+       # note: gnu/linux has sysctl so it may be used that for something if present
+       # there is lspci for bsds so doesn't hurt to check it
+       if ($b_pci || $b_sysctl){
+               if (!$bsd_type){
+                       if ($b_pci ){
+                               %hash = ('lspci' => '-n',);
+                               %commands = (%commands,%hash);
                        }
-                       else {
-                               a[j]=i
-                               # little trick here to try to catch the driver if there is
-                               # only one card and it was null, from the first test of asound/cards
-                               if (drivers[i] != "") {
-                                       useDrivers=drivers[i]
-                               }
-                               else if ( audioDriver != "" ) {
-                                       useDrivers=audioDriver
-                               }
+               }
+               else {
+                       if ($b_pci ){
+                               %hash = ('pciconf' => '-l',);
+                               %commands = (%commands,%hash);
                        }
-                       if (ports[i] != "") {
-                               usePorts = ports[i]
+                       if ($b_sysctl ){
+                               # note: there is a case of kernel.osrelease but it's a linux distro
+                               %hash = ('sysctl' => 'kern.osrelease',);
+                               %commands = (%commands,%hash);
                        }
-                       if (modules[i] != "" ) {
-                               useModules = modules[i]
+               }
+               foreach ( keys %commands ){
+                       $action = 'use';
+                       if ($program = check_program($_)) {
+                               # > 0 means error in shell
+                               #my $cmd = "$program $commands{$_} >/dev/null";
+                               #print "$cmd\n";
+                               $action = 'permissions' if system("$program $commands{$_} >/dev/null 2>&1");
                        }
-                       if ( aPciBusId[i] != "" ) {
-                               usePciBusId = aPciBusId[i]
+                       else {
+                               $action = 'missing';
                        }
-                       # create array primary item for master array
-                       sub( / $/, "", usePorts ) # clean off trailing whitespace
-                       print a[j] "," useDrivers "," usePorts "," useModules "," usePciBusId
-                       j++
+                       %hash = (
+                       $_ => {
+                       'action' => $action,
+                       'missing' => "Missing system tool: $_. Output will be incomplete",
+                       'permissions' => "Unable to run $_. Root required?",
+                       },
+                       );
+                       %alerts = (%alerts, %hash);
                }
-       }') )
-
-       # in case of failure of first check do this instead
-       if [[ ${#A_AUDIO_DATA[@]} -eq 0 ]] && [[ $B_ASOUND_DEVICE_FILE == 'true' ]];then
-               A_AUDIO_DATA=( $( gawk -F ']: ' '
-               BEGIN {
-                       IGNORECASE=1
+       }
+       %commands = ();
+       if ( $show{'sensor'} ){
+               %commands = ('sensors' => 'linux',);
+       }
+       # note: lsusb ships in FreeBSD ports sysutils/usbutils
+       if ( $usb_level ){
+               %hash = ('lsusb' => 'all',);
+               %commands = (%commands,%hash);
+               %hash = ('usbdevs' => 'bsd',);
+               %commands = (%commands,%hash);
+       }
+       if ($show{'ip'} || ($bsd_type && $show{'network-advanced'})){
+               %hash = (
+               'ip' => 'linux',
+               'ifconfig' => 'all',
+               );
+               %commands = (%commands,%hash);
+       }
+       foreach ( keys %commands ){
+               $action = 'use';
+               $message = 'Present and working';
+               if ( ($commands{$_} eq 'linux' && $os ne 'linux' ) || ($commands{$_} eq 'bsd' && $os eq 'linux' ) ){
+                       $message = "No " . ucfirst($os) . " support. Is a comparable $_ tool available?";
+                       $action = 'platform';
                }
-               $1 !~ /modem/ && $2 !~ /modem/ {
-                       card=gensub( /^(.+)( - )(.+)$/, "\\3", 1, $2 )
-                       driver=gensub( /^(.+)( - )(.+)$/, "\\1", 1, $2 )
-                       if ( card != "" ){
-                               print card","driver
-                       }
-               }' $FILE_ASOUND_DEVICE ) )
-       fi
-       IFS="$ORIGINAL_IFS"
-       get_audio_usb_data
-       # handle cases where card detection fails, like in PS3, where lspci gives no output, or headless boxes..
-       if [[ ${#A_AUDIO_DATA[@]} -eq 0 ]];then
-               A_AUDIO_DATA[0]='Failed to Detect Sound Card!'
-       fi
-       temp_array=${A_AUDIO_DATA[@]}
-       log_function_data "A_AUDIO_DATA: $temp_array"
-
-       eval $LOGFE
-}
-# alsa usb detection by damentz
-
-get_audio_usb_data()
-{
-       eval $LOGFS
-       local usb_proc_file='' array_count='' usb_data='' usb_id='' lsusb_path='' lsusb_data=''
-       local temp_array=''
-       
-       IFS=$'\n'
-       lsusb_path=$( type -p lsusb )
-       if [[ -n $lsusb_path ]];then
-               lsusb_data=$( $lsusb_path 2>/dev/null )
-       fi
-       log_function_data 'raw' "usb_data:\n$lsusb_data"
-       if [[ -n $lsusb_data ]];then
-               # for every sound card symlink in /proc/asound - display information about it
-               for usb_proc_file in /proc/asound/*
-               do
-                       # If the file is a symlink, and contains an important usb exclusive file: continue
-                       if [[ -L $usb_proc_file && -e $usb_proc_file/usbid  ]]; then
-                               # find the contents of usbid in lsusb and print everything after the 7th word on the
-                               # corresponding line. Finally, strip out commas as they will change the driver :)
-                               usb_id=$( cat $usb_proc_file/usbid )
-                               usb_data=$( grep "$usb_id" <<< "$lsusb_data" )
-                               if [[ -n $usb_data && -n $usb_id ]];then
-                                       usb_data=$( gawk '
-                                       BEGIN {
-                                               IGNORECASE=1
-                                               string=""
-                                               separator=""
-                                       }
-                                       {
-                                               gsub( /,/, " ", $0 )
-                                               gsub(/'"$BAN_LIST_NORMAL"'/, "", $0)
-                                               gsub(/ [ \t]+/, " ", $0)
-                                               for ( i=7; i<= NF; i++ ) {
-                                                       string = string separator $i
-                                                       separator = " "
-                                               }
-                                               if ( $2 != "" ){
-                                                       sub(/:/,"", $4)
-                                                       print string ",USB Audio,,," $2 "-" $4 "," $6
-                                               }
-                                       }' <<< "$usb_data" )
-                               fi
-                               # this method is interesting, it shouldn't work but it does
-                               #A_AUDIO_DATA=( "${A_AUDIO_DATA[@]}" "$usb_data,USB Audio,," )
-                               # but until we learn why the above worked, I'm using this one, which is safer
-                               if [[ -n $usb_data ]];then
-                                       array_count=${#A_AUDIO_DATA[@]}
-                                       A_AUDIO_DATA[$array_count]="$usb_data"
-                               fi
-                       fi
-               done
-       fi
-       IFS="$ORIGINAL_IFS"
-       temp_array=${A_AUDIO_DATA[@]}
-       log_function_data "A_AUDIO_DATA: $temp_array"
-       
-       eval $LOGFE
+               elsif (!check_program($_)){
+                       $message = "Required tool $_ not installed. Check --recommends";
+                       $action = 'missing';
+               }
+               %hash = (
+               $_ => {
+               'action' => $action,
+               'missing' => $message,
+               'platform' => $message,
+               },
+               );
+               %alerts = (%alerts, %hash);
+       }
+       # print Dumper \%alerts;
+       # only use sudo if not root, -n option requires sudo -V 1.7 or greater. 
+       # for some reason sudo -n with < 1.7 in Perl does not print to stderr
+       # sudo will just error out which is the safest course here for now,
+       # otherwise that interactive sudo password thing is too annoying
+       # important: -n makes it non interactive, no prompt for password
+       if (!$b_root && $b_sudo && (my $path = main::check_program('sudo') )) {
+               my @data = program_values('sudo');
+               my $version = program_version($path,$data[0],$data[1],$data[2],$data[5]);
+               $version =~ s/^([0-9]+\.[0-9]+).*/$1/;
+               $sudo = "$path -n " if $version >= 1.7;
+       }
+       set_fake_tools() if $b_fake_bsd;
 }
 
-get_audio_alsa_data()
-{
-       eval $LOGFS
-       local alsa_data='' temp_array=''
-
-       # now we'll get the alsa data if the file exists
-       if [[ $B_ASOUND_VERSION_FILE == 'true' ]];then
-               IFS=","
-               A_ALSA_DATA=( $( 
-               gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                               alsa=""
-                               version=""
-                       }
-                       # some alsa strings have the build date in (...)
-                       # remove trailing . and remove possible second line if compiled by user
-                       $0 !~ /compile/ {
-                               gsub( /Driver | [(].*[)]|\.$/,"",$0 )
-                               gsub(/,/, " ", $0)
-                               gsub(/^ +| +$/, "", $0)
-                               gsub(/ [ \t]+/, " ", $0)
-                               sub(/Advanced Linux Sound Architecture/, "ALSA", $0)
-                               if ( $1 == "ALSA" ){
-                                       alsa=$1
-                               }
-                               version=$NF
-                               print alsa "," version
-                       }' $FILE_ASOUND_VERSION ) )
-               IFS="$ORIGINAL_IFS"
-               log_function_data 'cat' "$FILE_ASOUND_VERSION"
-       fi
-       temp_array=${A_ALSA_DATA[@]}
-       log_function_data "A_ALSA_DATA: $temp_array"
-       eval $LOGFE
-}
-
-## create A_CPU_CORE_DATA, currently with two values: integer core count; core string text
-## return value cpu core count string, this helps resolve the multi redundant lines of old style output
-get_cpu_core_count()
-{
-       eval $LOGFS
-       local cpu_physical_count='' cpu_core_count='' cpu_type='' cpu_alpha_count=''
-       if [[ $B_CPUINFO_FILE == 'true' ]]; then
-               # load the A_CPU_TYPE_PCNT_CCNT core data array
-               get_cpu_ht_multicore_smp_data
-               ## Because of the upcoming release of cpus with core counts over 6, a count of cores is given after Deca (10)
-               # count the number of processors given
-               cpu_physical_count=${A_CPU_TYPE_PCNT_CCNT[1]}
-               cpu_core_count=${A_CPU_TYPE_PCNT_CCNT[2]}
-               cpu_type=${A_CPU_TYPE_PCNT_CCNT[0]}
-
-               # match the numberic value to an alpha value
-               cpu_alpha_count=$( get_cpu_core_count_alpha "$cpu_core_count" )
-               
-               # create array, core count integer; core count string
-               # A_CPU_CORE_DATA=( "$cpu_core_count" "$cpu_alpha_count Core$cpu_type" )
-               A_CPU_CORE_DATA=( "$cpu_physical_count" "$cpu_alpha_count" "$cpu_type" "$cpu_core_count" )
-       elif [[ -n $BSD_TYPE ]];then
-               local gawk_fs=': '
-       
-               if [[ $BSD_VERSION == 'openbsd' ]];then
-                       gawk_fs='='
-               fi
-               cpu_core_count=$( gawk -F "$gawk_fs" '
-               # note: on openbsd can also be hw.ncpufound so exit after first
-               /^hw.ncpu/ {
-                       print $NF
-                       exit
-               }' <<< "$Sysctl_a_Data" )
-               if [[ -n $( grep -E '^[0-9]+$' <<< "$cpu_core_count" ) ]];then
-                       cpu_alpha_count=$( get_cpu_core_count_alpha "$cpu_core_count" )
-                       if [[ $cpu_core_count -gt 1 ]];then
-                               cpu_type='SMP'
-                       fi
-               fi
-               cpu_physical_count=1
-               A_CPU_CORE_DATA=( "$cpu_physical_count" "$cpu_alpha_count" "$cpu_type" "$cpu_core_count" )
-       fi
-       temp_array=${A_CPU_CORE_DATA[@]}
-       log_function_data "A_CPU_CORE_DATA: $temp_array"
-       eval $LOGFE
-}
-
-# args: $1 - integer core count
-get_cpu_core_count_alpha()
-{
-       eval $LOGFS
-       local cpu_alpha_count=''
-       
-       case $1 in
-               1) cpu_alpha_count='Single';;
-               2) cpu_alpha_count='Dual';;
-               3) cpu_alpha_count='Triple';;
-               4) cpu_alpha_count='Quad';;
-               5) cpu_alpha_count='Penta';;
-               6) cpu_alpha_count='Hexa';;
-               7) cpu_alpha_count='Hepta';;
-               8) cpu_alpha_count='Octa';;
-               9) cpu_alpha_count='Ennea';;
-               10) cpu_alpha_count='Deca';;
-               *) cpu_alpha_count='Multi';;
-       esac
-       
-       echo $cpu_alpha_count
-       
-       eval $LOGFE
+# args: 1 - desktop/app command for --version; 2 - search string; 
+# 3 - space print number; 4 - [optional] version arg: -v, version, etc
+# 5 - [optional] exit first find 0/1; 6 - [optional] 0/1 stderr output
+sub set_basics {
+       ### LOCALIZATION - DO NOT CHANGE! ###
+       # set to default LANG to avoid locales errors with , or .
+       # Make sure every program speaks English.
+       $ENV{'LANG'}='C';
+       $ENV{'LC_ALL'}='C';
+       # remember, perl uses the opposite t/f return as shell!!!
+       $b_irc = ( system('tty >/dev/null') ) ? 1 : 0;
+       # print "birc: $b_irc\n";
+       $b_display = ( $ENV{'DISPLAY'} ) ? 1 : 0;
+       $b_root = ( $ENV{'HOME'} eq '/root' ) ? 1 : 0;
+       $dl{'dl'} = 'curl';
+       $dl{'curl'} = 1;
+       $dl{'tiny'} = 1; # note: two modules needed, tested for in set_downloader
+       $dl{'wget'} = 1;
+       $dl{'fetch'} = 1;
+       $client{'console-irc'} = 0;
+       $client{'dcop'} = (check_program('dcop')) ? 1 : 0;
+       $client{'qdbus'} = (check_program('qdbus')) ? 1 : 0;
+       $client{'konvi'} = 0;
+       $client{'name'} = '';
+       $client{'name-print'} = '';
+       $client{'su-start'} = ''; # shows sudo/su
+       $client{'version'} = '';
+       $colors{'default'} = 2;
 }
 
-## main cpu data collector
-get_cpu_data()
-{
-       eval $LOGFS
-       local i='' j='' cpu_array_nu='' a_cpu_working='' multi_cpu='' bits='' temp_array=''
-       local bsd_cpu_flags=''
-
-       if [[ $B_CPUINFO_FILE == 'true' ]];then
-               # stop script for a bit to let cpu slow down before parsing cpu /proc file
-               sleep $CPU_SLEEP
-               IFS=$'\n'
-               A_CPU_DATA=( $( 
-               gawk -F': ' '
-               BEGIN {
-                       IGNORECASE=1
-                       # need to prime nr for arm cpus, which do not have processor number output in some cases
-                       nr = 0
-                       count = 0
-                       bArm = "false"
-               }
-               # TAKE STRONGER NOTE: \t+ does NOT always work, MUST be [ \t]+
-               # TAKE NOTE: \t+ will work for $FILE_CPUINFO, but SOME ARBITRARY FILE used for TESTING might contain SPACES!
-               # Therefore PATCH to use [ \t]+ when TESTING!
-               /^processor[ \t]+:/ {
-                       gsub(/,/, " ", $NF)
-                       gsub(/^ +| +$/, "", $NF)
-                       if ( $NF ~ "^[0-9]+$" ) {
-                               nr = $NF
+# args: $1 - default OR override default cols max integer count. $_[0]
+# is the display width override.
+sub set_display_width {
+       my ($width) = @_;
+       if ( $width eq 'live' ){
+               ## sometimes tput will trigger an error (mageia) if irc client
+               if ( ! $b_irc ){
+                       if ( check_program('tput') ) {
+                               # trips error if use qx()...
+                               chomp($size{'term'}=qx{tput cols});
+                               chomp($size{'term-lines'}=qx{tput lines});
+                               $size{'term-cols'} = $size{'term'};
                        }
-                       else {
-                               if ( $NF ~ "^ARM" ) {
-                                       bArm = "true"
-                               }
-                               count += 1
-                               nr = count - 1
-                               cpu[nr, "model"] = $NF
+                       # print "tc: $size{'term'} cmc: $size{'console'}\n";
+                       # double check, just in case it's missing functionality or whatever
+                       if ( $size{'term'} == 0 || $size{'term'} !~ /\d/ ){ 
+                               $size{'term'}=80;
+                               # we'll be using this for terminal dimensions later so don't set default.
+                               # $size{'term-lines'}=100;
                        }
                }
-
-               /^model name|^cpu\t+:/ {
-                       gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF )
-                       gsub(/'"$BAN_LIST_CPU"'/, "", $NF )
-                       gsub(/,/, " ", $NF)
-                       gsub(/^ +| +$/, "", $NF)
-                       gsub(/ [ \t]+/, " ", $NF)
-                       cpu[nr, "model"] = $NF
-                       if ( $NF ~ "^ARM" ) {
-                               bArm = "true"
-                       }
+               # this lets you set different size for in or out of display server
+               # if ( ! $b_running_in_display && $configs{'COLS_MAX_NO_DISPLAY'} != 0 ){
+               #       $size{'console'}=$configs{'COLS_MAX_NO_DISPLAY'};
+               # }
+               # term_cols is set in top globals, using tput cols
+               # print "tc: $size{'term'} cmc: $size{'console'}\n";
+               if ( $size{'term'} < $size{'console'} ){
+                       $size{'console'}=$size{'term'};
                }
+               # adjust, some terminals will wrap if output cols == term cols
+               $size{'console'}=( $size{'console'} - 2 );
+               # echo cmc: $size{'console'}
+               # comes after source for user set stuff
+               if ( ! $b_irc ){
+                       $size{'max'}=$size{'console'};
+               }
+               else {
+                       $size{'max'}=$size{'irc'};
+               }
+       }
+       else {
+               $size{'max'}=$width;
+       }
+       # print "tc: $size{'term'} cmc: $size{'console'} cm: $size{'max'}\n";
+}
 
-               /^cpu MHz|^clock\t+:/ {
-                       if (!min) {
-                               min = $NF
-                       }
-                       else {
-                               if ($NF < min) {
-                                       min = $NF
-                               }
-                       }
+# only for dev/debugging BSD 
+sub set_fake_tools {
+       $system_files{'dmesg-boot'} = '/var/run/dmesg.boot' if $b_fake_dboot;
+       $alerts{'pciconf'} = ({'action' => 'use'}) if $b_fake_pciconf;
+       $alerts{'sysctl'} = ({'action' => 'use'}) if $b_fake_sysctl;
+       if ($b_fake_usbdevs ){
+               $alerts{'usbdevs'} = ({'action' => 'use'});
+               $alerts{'lsusb'} = ({
+               'action' => 'missing',
+               'missing' => 'Required program lsusb not available',
+               });
+       }
+}
 
-                       if ($NF > max) {
-                               max = $NF
-                       }
-                       gsub(/MHZ/,"",$NF) ## clears out for cell cpu
-                       gsub(/.00[0]+$/,".00",$NF) ## clears out excessive zeros
-                       cpu[nr, "speed"] = $NF
+# NOTE: most tests internally are against !$bsd_type
+sub set_os {
+       @uname = uname();
+       $os = lc($uname[0]);
+       $cpu_arch = lc($uname[-1]);
+       if ($cpu_arch =~ /arm|aarch/){$b_arm = 1}
+       elsif ($cpu_arch =~ /mips/) {$b_mips = 1}
+       # aarch32 mips32 intel/amd handled in cpu
+       if ($cpu_arch =~ /(armv[1-7]|32|sparc_v9)/){
+               $bits_sys = 32;
+       }
+       elsif ($cpu_arch =~ /(alpha|64)/){
+               $bits_sys = 64;
+       }
+       if ( $os =~ /(bsd|dragonfly|darwin)/ ){
+               if ( $os =~ /openbsd/ ){
+                       $os = 'openbsd';
                }
-
-               /^cache size/ {
-                       cpu[nr, "cache"] = $NF
+               elsif ($os =~ /darwin/){
+                       $os = 'darwin';
                }
-
-               /^flags|^features/ {
-                       cpu[nr, "flags"] = $NF
-                       # not all ARM cpus show ARM in model name
-                       if ( $1 ~ /^features/ ) {
-                               bArm = "true"
-                       }
+               if ($os =~ /kfreebsd/){
+                       $bsd_type = 'debian-bsd';
                }
-
-               /^bogomips/ {
-                       cpu[nr, "bogomips"] = $NF
+               else {
+                       $bsd_type = $os;
                }
+       }
+}
 
-               /vendor_id/ {
-                       gsub(/genuine|authentic/,"",$NF)
-                       cpu[nr, "vendor"] = tolower( $NF )
+# This data is hard set top of program but due to a specific project's
+# foolish idea that ignoring the FSH totally is somehow a positive step
+# forwards for free software, we also have to padd the results with PATH.
+sub set_path {
+       # Extra path variable to make execute failures less likely, merged below
+       my (@path);
+       @paths = qw(/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin /usr/X11R6/bin);
+       @path = split /:/, $ENV{'PATH'} if $ENV{'PATH'};
+       # print "paths: @paths\nPATH: $ENV{'PATH'}\n";
+       # Create a difference of $PATH and $extra_paths and add that to $PATH:
+       foreach my $id (@path) {
+               if ( !(grep { /^$id$/ } @paths) && $id !~ /(game)/ ){
+                       push @paths, $id;
                }
+       }
+       # print "paths: @paths\n";
+}
 
-               END {
-                       #if (!nr) { print ",,,"; exit } # <- should this be necessary or should bash handle that
-                       for ( i = 0; i <= nr; i++ ) {
-                               # note: assuming bogomips for arm at 1 x clock
-                               # http://en.wikipedia.org/wiki/BogoMips ARM could change so watch this
-                               # maybe add:  && bArm == "true" but I think most of the bogomips roughly equal cpu speed if not amd/intel
-                               if ( cpu[i, "bogomips"] != "" && cpu[i, "speed"] == "" ) {
-                                       cpu[i, "speed"] = cpu[i, "bogomips"]
-                               }
-                               print cpu[i, "model"] "," cpu[i, "speed"] "," cpu[i, "cache"] "," cpu[i, "flags"] "," cpu[i, "bogomips"] ","  cpu[nr, "vendor"] "," bArm
-                       }
-                       # this is / was used in inxi short output only, but when it is N/A, need to use the previous array
-                       # value, from above, the actual speed that is, for short output, key 0.
-                       if (!min) {
-                               print "N/A"
-                               exit
-                       }
-                       else {
-                               if (min != max) {
-                                       printf("Min:%s%s Max:%s%s\n", min, "Mhz", max, "Mhz")
-                               }
-                               else {
-                                       printf("%s %s\n", max, "Mhz")
-                               }
-                       }
+sub set_sep {
+       if ( $b_irc ){
+               # too hard to read if no colors, so force that for users on irc
+               if ($colors{'scheme'} == 0 ){
+                       $sep{'s1'} = $sep{'s1-console'};
+                       $sep{'s2'} = $sep{'s2-console'};
                }
-               ' $FILE_CPUINFO ) )
-               IFS="$ORIGINAL_IFS"
-               log_function_data 'cat' "$FILE_CPUINFO"
-       elif [[ -n $BSD_TYPE ]];then
-               get_cpu_data_bsd
-       fi
-       
-       temp_array=${A_CPU_DATA[@]}
-       log_function_data "A_CPU_DATA: $temp_array"
-#      echo ta: ${temp_array[@]}
-       eval $LOGFE
-#      echo getMainCpu: ${[@]}
+               else {
+                       $sep{'s1'} = $sep{'s1-irc'};
+                       $sep{'s2'} = $sep{'s2-irc'};
+               }
+       }
+       else {
+               $sep{'s1'} = $sep{'s1-console'};
+               $sep{'s2'} = $sep{'s2-console'};
+       }
 }
 
-get_cpu_data_bsd()
-{
-       eval $LOGFS
-
-       local bsd_cpu_flags=$( get_cpu_flags_bsd )
-       local gawk_fs=': '
-       
-       if [[ $BSD_VERSION == 'openbsd' ]];then
-               gawk_fs='='
-       fi
-               
-       IFS=$'\n'
-       A_CPU_DATA=( $( 
-       gawk -F "$gawk_fs" -v cpuFlags="$bsd_cpu_flags" '
-       BEGIN {
-               IGNORECASE=1
-               cpuModel=""
-               cpuClock=""
-               cpuCache=""
-               cpuBogomips=""
-               cpuVendor=""
-       }
-       /^hw.model/ {
-               gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF )
-               gsub(/'"$BAN_LIST_CPU"'/, "", $NF )
-               sub(//,"",$NF)
-               sub(/[a-z]+-core/, "", $NF )
-               gsub(/^ +| +$|\"/, "", $NF)
-               gsub(/ [ \t]+/, " ", $NF)
-               cpuModel=$NF
-               if ( cpuClock != "" ) {
-                       exit
-               }
-       }
-       /^hw.(clock|cpuspeed)/ {
-               cpuClock=$NF
-               if ( cpuModel != "" ) {
-                       exit
-               }
-       }
-       END {
-               print cpuModel "," cpuClock "," cpuCache "," cpuFlags "," cpuBogomips ","  cpuVendor
-               print "N/A"
-       }' <<< "$Sysctl_a_Data" ) )
-       IFS="$ORIGINAL_IFS"
+sub set_user_paths {
+       my ( $b_conf, $b_data );
+       # this needs to be set here because various options call the parent 
+       # initialize function directly.
+       $self_path = $0;
+       $self_path =~ s/[^\/]+$//;
+       # print "0: $0 sp: $self_path\n";
        
-       eval $LOGFE
+       if ( defined $ENV{'XDG_CONFIG_HOME'} && $ENV{'XDG_CONFIG_HOME'} ){
+               $user_config_dir=$ENV{'XDG_CONFIG_HOME'};
+               $b_conf=1;
+       }
+       elsif ( -d "$ENV{'HOME'}/.config" ){
+               $user_config_dir="$ENV{'HOME'}/.config";
+               $b_conf=1;
+       }
+       else {
+               $user_config_dir="$ENV{'HOME'}/.$self_name";
+       }
+       if ( defined $ENV{'XDG_DATA_HOME'} && $ENV{'XDG_DATA_HOME'} ){
+               $user_data_dir="$ENV{'XDG_DATA_HOME'}/$self_name";
+               $b_data=1;
+       }
+       elsif ( -d "$ENV{'HOME'}/.local/share" ){
+               $user_data_dir="$ENV{'HOME'}/.local/share/$self_name";
+               $b_data=1;
+       }
+       else {
+               $user_data_dir="$ENV{'HOME'}/.$self_name";
+       }
+       # note, this used to be created/checked in specific instance, but we'll just do it
+       # universally so it's done at script start.
+       if ( ! -d $user_data_dir ){
+               mkdir $user_data_dir;
+               # system "echo", "Made: $user_data_dir";
+       }
+       if ( $b_conf && -f "$ENV{'HOME'}/.$self_name/$self_name.conf" ){
+               #system 'mv', "-f $ENV{'HOME'}/.$self_name/$self_name.conf", $user_config_dir;
+               # print "WOULD: Moved $self_name.conf from $ENV{'HOME'}/.$self_name to $user_config_dir\n";
+       }
+       if ( $b_data && -d "$ENV{'HOME'}/.$self_name" ){
+               #system 'mv', '-f', "$ENV{'HOME'}/.$self_name/*", $user_data_dir;
+               #system 'rm', '-Rf', "$ENV{'HOME'}/.$self_name";
+               # print "WOULD: Moved data dir $ENV{'HOME'}/.$self_name to $user_data_dir\n";
+       }
+       $log_file="$user_data_dir/$self_name.log";
+       #system 'echo', "$ENV{'HOME'}/.$self_name/* $user_data_dir";
+       # print "scd: $user_config_dir sdd: $user_data_dir \n";
 }
 
-get_cpu_flags_bsd()
-{
-       eval $LOGFS
-       
-       local cpu_flags=$( gawk -F '=' '
-       BEGIN {
-               IGNORECASE=1
-               cpuFlags=""
-       }
-       /^CPU:/ {
-               while ( getline && !/memory/  ) {
-                       if ( $1 ~ /Features/ ) {
-                               # clean up odd stuff like <b23>
-                               gsub(/<[a-z0-9]+>/,"", $2)
-                               # all the flags are contained within < ... > on freebsd at least
-                               gsub(/.*<|>.*/,"", $2)
-                               gsub(/,/," ", $2)
-                               cpuFlags = cpuFlags " " $2
-                       }
-               }
-               cpuFlags=tolower(cpuFlags)
-               print cpuFlags
-               exit
-       }' <<< "$Dmesg_Boot_Data" )
-       
-       echo $cpu_flags
-       log_function_data "$cpu_flags"
-       eval $LOGFE
+# args: 1: set|hash key to return either null or path
+sub system_files {
+       my ($file) = @_;
+       if ( $file eq 'set'){
+               %files = (
+               'asound-cards' => '/proc/asound/cards',
+               'asound-modules' => '/proc/asound/modules',
+               'asound-version' => '/proc/asound/version',
+               'cpuinfo' => '/proc/cpuinfo',
+               'dmesg-boot' => '/var/run/dmesg.boot',
+               'lsb-release' => '/etc/lsb-release',
+               'mdstat' => '/proc/mdstat',
+               'meminfo' => '/proc/meminfo',
+               'modules' => '/proc/modules',
+               'mounts' => '/proc/mounts',
+               'os-release' => '/etc/os-release',
+               'partitions' => '/proc/partitions',
+               'scsi' => '/proc/scsi/scsi',
+               'version' => '/proc/version',
+               'xorg-log' => '/var/log/Xorg.0.log'
+               );
+               foreach ( keys %files ){
+                       $system_files{$_} = ( -e $files{$_} ) ? $files{$_} : '';
+               }
+               if ( ! $system_files{'xorg-log'} && check_program('xset') ){
+                       my $data = qx(xset q 2>/dev/null);
+                       foreach ( split /\n/, $data){
+                               if ($_ =~ /Log file/i){
+                                       $system_files{'xorg-log'} = get_piece($_,3);
+                                       last;
+                               }
+                       }
+               }
+       }
+       else {
+               return $system_files{$file};
+       }
 }
 
-## this is for counting processors and finding HT types
-get_cpu_ht_multicore_smp_data()
-{
-       eval $LOGFS
-       # in /proc/cpuinfo
-       local temp_array=''
+########################################################################
+#### UTILITIES
+########################################################################
+
+#### -------------------------------------------------------------------
+#### COLORS
+#### -------------------------------------------------------------------
+
+## arg: 1 - the type of action, either integer, count, or full
+sub get_color_scheme {
+       my ($type) = @_;
+       eval $start if $b_log;
+       my @color_schemes = (
+       [qw(EMPTY EMPTY EMPTY )],
+       [qw(NORMAL NORMAL NORMAL )],
+       # for dark OR light backgrounds
+       [qw(BLUE NORMAL NORMAL)],
+       [qw(BLUE RED NORMAL )],
+       [qw(CYAN BLUE NORMAL )],
+       [qw(DCYAN NORMAL NORMAL)],
+       [qw(DCYAN BLUE NORMAL )],
+       [qw(DGREEN NORMAL NORMAL )],
+       [qw(DYELLOW NORMAL NORMAL )],
+       [qw(GREEN DGREEN NORMAL )],
+       [qw(GREEN NORMAL NORMAL )],
+       [qw(MAGENTA NORMAL NORMAL)],
+       [qw(RED NORMAL NORMAL)],
+       # for light backgrounds
+       [qw(BLACK DGREY NORMAL)],
+       [qw(DBLUE DGREY NORMAL )],
+       [qw(DBLUE DMAGENTA NORMAL)],
+       [qw(DBLUE DRED NORMAL )],
+       [qw(DBLUE BLACK NORMAL)],
+       [qw(DGREEN DYELLOW NORMAL )],
+       [qw(DYELLOW BLACK NORMAL)],
+       [qw(DMAGENTA BLACK NORMAL)],
+       [qw(DCYAN DBLUE NORMAL)],
+       # for dark backgrounds
+       [qw(WHITE GREY NORMAL)],
+       [qw(GREY WHITE NORMAL)],
+       [qw(CYAN GREY NORMAL )],
+       [qw(GREEN WHITE NORMAL )],
+       [qw(GREEN YELLOW NORMAL )],
+       [qw(YELLOW WHITE NORMAL )],
+       [qw(MAGENTA CYAN NORMAL )],
+       [qw(MAGENTA YELLOW NORMAL)],
+       [qw(RED CYAN NORMAL)],
+       [qw(RED WHITE NORMAL )],
+       [qw(BLUE WHITE NORMAL)],
+       # miscellaneous
+       [qw(RED BLUE NORMAL )],
+       [qw(RED DBLUE NORMAL)],
+       [qw(BLACK BLUE NORMAL)],
+       [qw(BLACK DBLUE NORMAL)],
+       [qw(NORMAL BLUE NORMAL)],
+       [qw(BLUE MAGENTA NORMAL)],
+       [qw(DBLUE MAGENTA NORMAL)],
+       [qw(BLACK MAGENTA NORMAL)],
+       [qw(MAGENTA BLUE NORMAL)],
+       [qw(MAGENTA DBLUE NORMAL)],
+       );
+       if ($type eq 'count' ){
+               return scalar @color_schemes;
+       }
+       if ($type eq 'full' ){
+               return @color_schemes;
+       }
+       else {
+               return @{$color_schemes[$type]};
+               # print Dumper $color_schemes[$scheme_nu];
+       }
+       eval $end if $b_log;
+}
+
+sub set_color_scheme {
+       eval $start if $b_log;
+       my ($scheme) = @_;
+       $colors{'scheme'} = $scheme;
+       my $index = ( $b_irc ) ? 1 : 0; # defaults to non irc
        
-       # note: known bug with xeon intel, they show a_core_id/physical_id as 0 for ht 4 core
-       if [[ $B_CPUINFO_FILE == 'true' ]]; then
-               A_CPU_TYPE_PCNT_CCNT=( $(
-               gawk '
-               BEGIN {
-                       FS=": "
-                       IGNORECASE = 1
-                       num_of_cores = 0
-                       num_of_processors = 0
-                       num_of_physical_cpus = 0
-                       cpu_core_count = 0
-                       siblings = 0
-                       # these 3 arrays cannot be declared because that sets the first element
-                       # but leaving this here so that we avoid doing that in the future
-                       # a_core_id = ""
-                       # a_processor_id = ""
-                       # a_physical_id = ""
-                       cpu_type = "-"
-                       # note: we need separate iterators because some cpuinfo data has only
-                       # processor, no core id or phys id
-                       proc_iter = 0
-                       core_iter = "" # set from actual NF data
-                       phys_iter = "" # set from actual NF data
-                       # needed to handle arm cpu, no processor number cases
-                       arm_count = 0
-                       nr = 0
-                       bArm = "false"
-                       bXeon = "false"
-               }
-               # hack to handle xeons which can have buggy /proc/cpuinfo files
-               /^model name/ && ( $0 ~ /Xeon/ ) {
-                       bXeon = "true"
-               }
-               # only do this once since sibling count does not change. 
-               /^siblings/ && ( bXeon == "true" ) && ( siblings == 0 ) {
-                       gsub(/[^0-9]/,"",$NF)
-                       if ( $NF != "" ) {
-                               siblings = $NF
-                       }
-               }
-               # array of logical processors, both HT and physical
-               
-               /^processor/ {
-                       gsub(/,/, " ", $NF)
-                       gsub(/^ +| +$/, "", $NF)
-                       if ( $NF ~ "^[0-9]+$" ) {
-                               a_processor_id[proc_iter] = $NF
-                               proc_iter++
+       # NOTE: qw(...) kills the escape, it is NOT the same as using 
+       # Literal "..", ".." despite docs saying it is.
+       my %color_palette = (
+       'EMPTY' => [ '', '' ],
+       'DGREY' => [ "\e[1;30m", "\x0314" ],
+       'BLACK' => [ "\e[0;30m", "\x0301" ],
+       'RED' => [ "\e[1;31m", "\x0304" ],
+       'DRED' => [ "\e[0;31m", "\x0305" ],
+       'GREEN' => [ "\e[1;32m", "\x0309" ],
+       'DGREEN' => [ "\e[0;32m", "\x0303" ],
+       'YELLOW' => [ "\e[1;33m", "\x0308" ],
+       'DYELLOW' => [ "\e[0;33m", "\x0307" ],
+       'BLUE' => [ "\e[1;34m", "\x0312" ],
+       'DBLUE' => [ "\e[0;34m", "\x0302" ],
+       'MAGENTA' => [ "\e[1;35m", "\x0313" ],
+       'DMAGENTA' => [ "\e[0;35m", "\x0306" ],
+       'CYAN' => [ "\e[1;36m", "\x0311" ],
+       'DCYAN' => [ "\e[0;36m", "\x0310" ],
+       'WHITE' => [ "\e[1;37m", "\x0300" ],
+       'GREY' => [ "\e[0;37m", "\x0315" ],
+       'NORMAL' => [ "\e[0m", "\x03" ],
+       );
+       my @scheme = get_color_scheme($colors{'scheme'});
+       $colors{'c1'} = $color_palette{$scheme[0]}[$index];
+       $colors{'c2'} = $color_palette{$scheme[1]}[$index];
+       $colors{'cn'} = $color_palette{$scheme[2]}[$index];
+       # print Dumper \@scheme;
+       # print "$colors{'c1'}here$colors{'c2'} we are!$colors{'cn'}\n";
+       eval $end if $b_log;
+}
+
+sub set_colors {
+       eval $start if $b_log;
+       # it's already been set with -c 0-43
+       if ( exists $colors{'c1'} ){
+               return 1;
+       }
+       # This let's user pick their color scheme. For IRC, only shows the color schemes, 
+       # no interactive. The override value only will be placed in user config files. 
+       # /etc/inxi.conf can also override
+       if (exists $colors{'selector'}){
+               my $ob_selector = SelectColors->new($colors{'selector'});
+               $ob_selector->select_schema();
+               return 1;
+       }
+       # set the default, then override as required
+       my $color_scheme = $colors{'default'};
+       # these are set in user configs
+       if (defined $colors{'global'}){
+               $color_scheme = $colors{'global'};
+       }
+       else {
+               if ( $b_irc ){
+                       if (defined $colors{'irc-virt-term'} && $b_display && $client{'console-irc'}){
+                               $color_scheme = $colors{'irc-virt-term'};
                        }
-                       else {
-                               # note, for dual core, this can be off by one because the first
-                               # line says: Processor : Arm.. but subsequent say: processor : 0 and so on as usual
-                               if ( $NF ~ "^ARM" ) {
-                                       bArm = "true"
-                               }
-                               arm_count += 1
-                               nr = arm_count - 1
-                               # note: do not iterate because new ARM syntax puts cpu in processsor : 0 syntax
-                               a_processor_id[proc_iter] = nr
+                       elsif (defined $colors{'irc-console'} && !$b_display){
+                               $color_scheme = $colors{'irc-console'};
+                       }
+                       elsif ( defined $colors{'irc-gui'}) {
+                               $color_scheme = $colors{'irc-gui'};
                        }
                }
-               
-               # array of physical cpu ids, note, this will be unset for vm cpus in many cases
-               # because they have no physical cpu, so we cannot assume this will be here.
-               /^physical/ {
-                       phys_iter = $NF
-                       a_physical_id[phys_iter] = $NF
-               }
-               # array of core ids, again, here we may have HT, so we need to create an array of the
-               # actual core ids. As With physical, we cannot assume this will be here in a vm
-               /^core id/ {
-                       core_iter = $NF
-                       a_core_id[core_iter] = $NF
-               }
-               # this will be used to fix an intel glitch if needed, cause, intel
-               # sometimes reports core id as the same number for each core, 
-               # so if cpu cores shows greater value than number of cores, use this. 
-               /^cpu cores/ {
-                       cpu_core_count = $NF
-               }
-               END {
-                       ##      Look thru the array and filter same numbers.
-                       ##      only unique numbers required
-                       ##      this is to get an accurate count
-                       ##      we are only concerned with array length
-                       i = 0
-                       ## count unique processors ##
-                       # note, this fails for intel cpus at times
-                       for ( i in a_processor_id ) {
-                               num_of_processors++
-                       }
-                       i = 0
-                       ## count unique physical cpus ##
-                       for ( i in a_physical_id ) {
-                               num_of_physical_cpus++
+               else {
+                       if (defined $colors{'console'} && !$b_display){
+                               $color_scheme = $colors{'console'};
+                       }
+                       elsif (defined $colors{'virt-term'}){
+                               $color_scheme = $colors{'virt-term'};
                        }
-                       
-                       i = 0
-                       ## count unique cores ##
-                       for ( i in a_core_id ) {
-                               num_of_cores++
-                       }
-                       # xeon may show wrong core / physical id count, if it does, fix it. A xeon
-                       # may show a repeated core id : 0 which gives a fake num_of_cores=1
-                       if ( bXeon == "true" && num_of_cores == 1 && siblings > 1 ) {
-                               num_of_cores = siblings/2
-                       }
-                       # final check, override the num of cores value if it clearly is wrong
-                       # and use the raw core count and synthesize the total instead of real count
-                       if ( ( num_of_cores == 0 ) && ( cpu_core_count * num_of_physical_cpus > 1 ) ) {
-                               num_of_cores = cpu_core_count * num_of_physical_cpus
-                       }
-                       # last check, seeing some intel cpus and vms with intel cpus that do not show any
-                       # core id data at all, or siblings.
-                       if ( num_of_cores == 0 && num_of_processors > 0 ) {
-                               num_of_cores = num_of_processors
-                       }
-                       # ARM/vm cpu fix, if no physical or core found, use count of 1 instead
-                       if ( num_of_physical_cpus == 0 ) {
-                               num_of_physical_cpus = 1
-                       }
-#                      print "NoCpu: " num_of_physical_cpus
-#                      print "NoCores: " num_of_cores
-#                      print "NoProc:" num_of_processors
-#                      print "CpuCoreCount:" cpu_core_count
-                       ####################################################################
-                       #                               algorithm
-                       # if > 1 processor && processor id (physical id) == core id then Hyperthreaded (HT)
-                       # if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP)
-                       # if > 1 processor && processor ids (physical id) > 1 then Multiple Processors (SMP)
-                       # if = 1 processor then single core/processor Uni-Processor (UP)
-                       if ( num_of_processors > 1 || ( bXeon == "true" && siblings > 0 ) ) {
-                               # non-multicore HT
-                               if ( num_of_processors == (num_of_cores * 2) ) {
-                                       cpu_type = cpu_type "HT-"
-                               }
-                               else if ( bXeon == "true" && siblings > 1 ) {
-                                       cpu_type = cpu_type "HT-"
-                               }
-                               # non-HT multi-core or HT multi-core
-                               if (( num_of_processors == num_of_cores) || ( num_of_physical_cpus < num_of_cores)) {
-                                       cpu_type = cpu_type "MCP-"
-                               }
-                               # >1 cpu sockets active
-                               if ( num_of_physical_cpus > 1 ) {
-                                       cpu_type = cpu_type "SMP-"
-                               }
-                       } 
-                       else {
-                               cpu_type = cpu_type "UP-"
-                       }                       
-                       
-                       print cpu_type " " num_of_physical_cpus " " num_of_cores 
                }
-               ' $FILE_CPUINFO ) )
-       fi
-       temp_array=${A_CPU_TYPE_PCNT_CCNT[@]}
-       log_function_data "A_CPU_TYPE_PCNT_CCNT: $temp_array"
-       eval $LOGFE
+       }
+       # force 0 for | or > output, all others prints to irc or screen
+       if (!$b_irc && ! -t STDOUT ){
+               $color_scheme = 0;
+       }
+       set_color_scheme($color_scheme);
+       eval $end if $b_log;
 }
 
-# Detect desktop environment in use, initial rough logic from: compiz-check
-# http://forlong.blogage.de/entries/pages/Compiz-Check
-# NOTE $XDG_CURRENT_DESKTOP envvar is not reliable, but it shows certain desktops better.
-# most desktops are not using it as of 2014-01-13 (KDE, UNITY, LXDE. Not Gnome)
-get_desktop_environment()
+## SelectColors
 {
-       eval $LOGFS
-       
-       # set the default, this function only runs in X, if null, don't print data out
-       local desktop_environment='' xprop_root=''
-       local version='' version_data='' toolkit=''
+package SelectColors;
+
+# use warnings;
+# use strict;
+# use diagnostics;
+# use 5.008;
+
+my (@data,@rows,%configs,%status);
+my ($type,$w_fh);
+my $safe_color_count = 12; # null/normal + default color group
+my $count = 0;
+
+# args: 1 - type
+sub new {
+       my $class = shift;
+       ($type) = @_;
+       my $self = {};
+       return bless $self, $class;
+}
+sub select_schema {
+       eval $start if $b_log;
+       assign_selectors();
+       main::set_color_scheme(0);
+       set_status();
+       start_selector();
+       create_color_selections();
+       if (! $b_irc ){
+               main::check_config_file();
+               get_selection();
+       }
+       else {
+               print_irc_message();
+       }
+       eval $end if $b_log;
+}
 
-       # works on 4, assume 5 will id the same, why not, no need to update in future
-       # KDE_SESSION_VERSION is the integer version of the desktop
-       if [[ $XDG_CURRENT_DESKTOP == 'KDE' || -n $KDE_SESSION_VERSION ]]; then
-               # note the command is actually like, kded4 --version, so we construct it
-               version_data=$( kded$KDE_SESSION_VERSION --version 2>/dev/null )
-               version=$( grep -si '^KDE Development Platform:' <<< "$version_data" | gawk '{print $4}' )
-               if [[ -z $version ]];then
-                       version=$KDE_SESSION_VERSION
-               fi
-               if [[ $B_EXTRA_DATA == 'true' ]];then
-                       toolkit=$( grep -si '^Qt:' <<< "$version_data" | gawk '{print $2}' )
-                       if [[ -n $toolkit ]];then
-                               version="$version (Qt $toolkit)"
-                       fi
-               fi
-               desktop_environment="KDE"
-       # KDE_FULL_SESSION property is only available since KDE 3.5.5.
-       # src: http://humanreadable.nfshost.com/files/startkde
-       elif [[ $KDE_FULL_SESSION == 'true' ]]; then
-               version_data=$( kded --version 2>/dev/null )
-               version=$( grep -si '^KDE:' <<< "$version_data" | gawk '{print $2}' )
-               # version=$( get_de_app_version 'kded' '^KDE:' '2' )
-               if [[ -z $version ]];then
-                       version='3.5'
-               fi
-               if [[ $B_EXTRA_DATA == 'true' ]];then
-                       toolkit=$( grep -si '^Qt:' <<< "$version_data" | gawk '{print $2}' )
-                       if [[ -n $toolkit ]];then
-                               version="$version (Qt $toolkit)"
-                       fi
-               fi
-               desktop_environment="KDE"
-       elif [[ $XDG_CURRENT_DESKTOP == 'Unity' ]];then
-               version=$( get_de_app_version 'unity' '^unity' '2' )
-               # not certain cinn will always have version, so keep output right if not
-               if [[ -n $version ]];then
-                       version="$version "
-               fi
-               if [[ $B_EXTRA_DATA == 'true' ]];then
-                       toolkit=$( get_de_gtk_data )
-                       if [[ -n $toolkit ]];then
-                               version="${version}(Gtk ${toolkit})"
-                       fi
-               fi
-               desktop_environment="Unity"
-       fi
-       # did we find it? If not, start the xprop tests
-       if [[ -z $desktop_environment ]];then
-               if [[ -n $( type -p xprop ) ]];then
-                       xprop_root="$( xprop -root 2>/dev/null )"
-               fi
-               # note that cinnamon split from gnome, and and can now be id'ed via xprop,
-               # but it will still trigger the next gnome true case, so this needs to go before gnome test
-               # eventually this needs to be better organized so all the xprop tests are in the same
-               # section, but this is good enough for now.
-               if [[ -n $xprop_root && -n $( grep -is '^_MUFFIN' <<< "$xprop_root" ) ]];then
-                       version=$( get_de_app_version 'cinnamon' '^cinnamon' '2' )
-                       # not certain cinn will always have version, so keep output right if not
-                       if [[ -n $version ]];then
-                               version="$version "
-                       fi
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                               toolkit=$( get_de_gtk_data )
-                               if [[ -n $toolkit ]];then
-                                       version="${version}(Gtk ${toolkit})"
-                               fi
-                       fi
-                       desktop_environment="Cinnamon"
-               elif [[ -n $xprop_root && -n $( grep -is '^_MARCO' <<< "$xprop_root" ) ]];then
-                       version=$( get_de_app_version 'mate-about' '^MATE[[:space:]]DESKTOP' 'NF' )
-                       # not certain cinn/mate will always have version, so keep output right if not
-                       if [[ -n $version ]];then
-                               version="$version "
-                       fi
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                               toolkit=$( get_de_gtk_data )
-                               if [[ -n $toolkit ]];then
-                                       version="${version}(Gtk ${toolkit})"
-                               fi
-                       fi
-                       desktop_environment="MATE"
-               # note, GNOME_DESKTOP_SESSION_ID is deprecated so we'll see how that works out
-               # https://bugzilla.gnome.org/show_bug.cgi?id=542880
-               elif [[ -n $GNOME_DESKTOP_SESSION_ID ]]; then
-                       if [[ -n $( type -p gnome-shell ) ]];then
-                               version=$( get_de_app_version 'gnome-shell' 'gnome' '3' )
-                       elif [[ -n $( type -p gnome-about ) ]];then
-                               version=$( get_de_app_version 'gnome-about' 'gnome' '3' )
-                       fi
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                               toolkit=$( get_de_gtk_data )
-                               if [[ -n $toolkit ]];then
-                                       version="$version (Gtk $toolkit)"
-                               fi
-                       fi
-                       desktop_environment="Gnome"
-               fi
-               if [[ -z $desktop_environment ]];then
-               # now that the primary ones have been handled, next is to find the ones with unique
-               # xprop detections possible
-                       if [[ -n $xprop_root ]];then
-                               # String: "This is xfdesktop version 4.2.12"
-                               # alternate: xfce4-about --version > xfce4-about 4.10.0 (Xfce 4.10)
-                               if [[ -n $( grep -Eis '\"xfce4\"' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'xfdesktop' 'xfdesktop[[:space:]]version' '5' )
-                                       # arch linux reports null, so use alternate if null
-                                       if [[ -z $version ]];then
-                                               version=$( get_de_app_version 'xfce4-panel' '^xfce4-panel' '2' )
-                                               if [[ -z $version ]];then
-                                                       version='4'
-                                               fi
-                                       fi
-                                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                                               toolkit=$( get_de_app_version 'xfdesktop' 'Built[[:space:]]with[[:space:]]GTK' '4' )
-                                               if [[ -n $toolkit ]];then
-                                                       version="$version (Gtk $toolkit)"
-                                               fi
-                                       fi
-                                       desktop_environment="Xfce"
-                               # when 5 is released, the string may need updating
-                               elif [[ -n $( grep -is '\"xfce5\"' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'xfdesktop' 'xfdesktop[[:space:]]version' '5' )
-                                       # arch linux reports null, so use alternate if null
-                                       if [[ -z $version ]];then
-                                               version=$( get_de_app_version 'xfce5-panel' '^xfce5-panel' '2' )
-                                               if [[ -z $version ]];then
-                                                       version='5'
-                                               fi
-                                       fi
-                                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                                               toolkit=$( get_de_app_version 'xfdesktop' 'Built[[:space:]]with[[:space:]]GTK' '4' )
-                                               if [[ -n $toolkit ]];then
-                                                       version="$version (Gtk $toolkit)"
-                                               fi
-                                       fi
-                                       desktop_environment="Xfce"
-                               elif [[ -n $( grep -is 'BLACKBOX_PID' <<< "$xprop_root" ) ]];then
-                                       if [[ -n $( grep -is 'fluxbox' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                               version=$( get_de_app_version 'fluxbox' '^fluxbox' '2' )
-                                               desktop_environment='Fluxbox'
-                                       else
-                                               desktop_environment='Blackbox'
-                                       fi
-                               elif [[ -n $( grep -is 'OPENBOX_PID' <<< "$xprop_root" ) ]];then
-                                       # note: openbox-lxde --version may be present, but returns openbox data
-                                       version=$( get_de_app_version 'openbox' '^openbox' '2' )
-                                       if [[ $XDG_CURRENT_DESKTOP == 'LXDE' || \
-                                                       -n $( grep -is 'lxde' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                               if [[ -n $version ]];then
-                                                       version="(Openbox $version)"
-                                               fi
-                                               desktop_environment='LXDE'
-                                       elif [[ -n $( grep -is 'razor-desktop' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                               if [[ -n $version ]];then
-                                                       version="(Openbox $version)"
-                                               fi
-                                               desktop_environment='Razor-QT'
-                                       else
-                                               desktop_environment='Openbox'
-                                       fi
-                               elif [[ -n $( grep -is 'ICEWM' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'icewm' '^icewm' '2' )
-                                       desktop_environment='IceWM'
-                               elif [[ -n $( grep -is 'ENLIGHTENMENT' <<< "$xprop_root" ) ]];then
-                                       # no -v or --version but version is in xprop -root
-                                       # ENLIGHTENMENT_VERSION(STRING) = "Enlightenment 0.16.999.49898"
-                                       version=$( grep -is 'ENLIGHTENMENT_VERSION' <<< "$xprop_root" | cut -d '"' -f 2 | gawk '{print $2}' )
-                                       desktop_environment='Enlightenment'
-                               elif [[ -n $( grep -is '^I3_' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'i3' '^i3' '3' )
-                                       desktop_environment='i3'
-                               elif [[ -n $( grep -is 'WINDOWMAKER' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'wmaker' '^Window[[:space:]]*Maker' 'NF' )
-                                       if [[ -n $version ]];then
-                                               version="$version "
-                                       fi
-                                       desktop_environment="WindowMaker"
-                               elif [[ -n $( grep -is '^_WM2' <<< "$xprop_root" ) ]];then
-                                       # note; there isn't actually a wm2 version available but error handling should cover it and return null
-                                       # maybe one day they will add it?
-                                       version=$( get_de_app_version 'wm2' '^wm2' 'NF' )
-                                       # not certain will always have version, so keep output right if not
-                                       if [[ -n $version ]];then
-                                               version="$version "
-                                       fi
-                                       desktop_environment="WM2"
-                               elif [[ -n $( grep -is 'herbstluftwm' <<< "$xprop_root" ) ]];then
-                                       version=$( get_de_app_version 'herbstluftwm' '^herbstluftwm' 'NF' )
-                                       if [[ -n $version ]];then
-                                               version="$version "
-                                       fi
-                                       desktop_environment="herbstluftwm"
-                               fi
-                       fi
-                       # a few manual hacks for things that don't id with xprop, these are just good guesses
-                       # note that gawk is going to exit after first occurance of search string, so no need for extra
-                       if [[ -z $desktop_environment ]];then
-                               if [[ -n $( grep -is 'fvwm-crystal'  <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'fvwm' '^fvwm' '2' )
-                                       desktop_environment='FVWM-Crystal'
-                               elif [[ -n $( grep -is 'fvwm'  <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'fvwm' '^fvwm' '2' )
-                                       desktop_environment='FVWM'
-                               elif [[ -n $( grep -is 'pekwm'  <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'pekwm' '^pekwm' '3' )
-                                       desktop_environment='pekwm'
-                               elif [[ -n $( grep -is 'awesome' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'awesome' '^awesome' '2' )
-                                       desktop_environment='Awesome'
-                               elif [[ -n $( grep -is 'scrotwm' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'scrotwm' '^welcome.*scrotwm' '4' )
-                                       desktop_environment='Scrotwm' # no --version for this one
-                               elif [[ -n $( grep -is 'spectrwm' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'spectrwm' '^spectrwm.*welcome.*spectrwm' '5' )
-                                       desktop_environment='Spectrwm' # no --version for this one
-                               elif [[ -n $( grep -Eis '([[:space:]]|/)twm' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       desktop_environment='Twm' # no --version for this one
-                               elif [[ -n $( grep -Eis '([[:space:]]|/)dwm' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'dwm' '^dwm' '1' )
-                                       desktop_environment='dwm'
-                               elif [[ -n $( grep -is 'wmii2' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'wmii2' '^wmii2' '1' )
-                                       desktop_environment='wmii2'
-                               # note: in debian at least, wmii is actuall wmii3
-                               elif [[ -n $( grep -is 'wmii' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'wmii' '^wmii' '1' )
-                                       desktop_environment='wmii'
-                               elif [[ -n $( grep -Eis '([[:space:]]|/)jwm' <<< "$Ps_aux_Data" | grep -v 'grep' ) ]];then
-                                       version=$( get_de_app_version 'jwm' '^jwm' '2' )
-                                       desktop_environment='JWM'
-                               fi
-                       fi
-               fi
-       fi
-       if [[ -n $version ]];then
-               version=" $version"
-       fi
-       echo "$desktop_environment${version}"
-       eval $LOGFE
-}
-
-# note: gawk doesn't support white spaces in search string, gave errors, so use [[:space:]] instead
-# args: $1 - desktop/app command for --version; $2 - search string; $3 - gawk print number
-get_de_app_version()
-{
-       local version_data='' version='' get_version='--version' 
-       
-       # mate-about -v = MATE Desktop Environment 1.4.0
-       case $1 in
-               dwm|jwm|mate-about|wmii|wmii2)
-                       get_version='-v'
-                       ;;
-               epoch)
-                       get_version='version'
-                       ;;
-       esac
-       
-       case $1 in
-               # note, some wm/apps send version info to stderr instead of stdout
-               dwm|ksh|scrotwm|spectrwm)
-                       version_data="$( $1 $get_version 2>&1 )"
-                       ;;
-               csh)
-                       version_data="$( tcsh $get_version 2>/dev/null )"
-                       ;;
-               # quick debian/buntu hack until I find a universal way to get version for these
-               dash)
-                       if [[ -n $( type -p dpkg ) ]];then
-                               version_data="$( dpkg -l $1 2>/dev/null )"
-                       fi
-                       ;;
-               *)
-                       version_data="$( $1 $get_version 2>/dev/null )"
-                       ;;
-       esac
-       
-       if [[ -n $version_data ]];then
-               version=$( gawk '
-               BEGIN {
-                       IGNORECASE=1
-               }
-               /'$2'/ {
-                       # sample: dwm-5.8.2, ©.. etc, why no space? who knows. Also get rid of v in number string
-                       # xfce, and other, output has , in it, so dump all commas and parentheses
-                       gsub(/(,|dwm-|wmii2-|wmii-|v|V|\(|\))/, "",$'$3') 
-                       print $'$3'
-                       exit # quit after first match prints
-               }' <<< "$version_data" )
-       fi
-       echo $version
-}
-
-get_desktop_extra_data()
-{
-       eval $LOGFS
-       local de_data=$( ps -A | gawk '
-       BEGIN {
-               IGNORECASE=1
-               desktops=""
-               separator=""
-       }
-       /(gnome-shell|gnome-panel|kicker|lxpanel|mate-panel|plasma-desktop|xfce4-panel)$/ {
-               # only one entry per type, can be multiple
-               if ( desktops !~ $NF ) {
-                       desktops = desktops separator $NF
-                       separator = ","
+sub set_status {
+       $status{'console'} = (defined $colors{'console'}) ? "Set: $colors{'console'}" : 'Not Set';
+       $status{'virt-term'} = (defined $colors{'virt-term'}) ? "Set: $colors{'virt-term'}" : 'Not Set';
+       $status{'irc-console'} = (defined $colors{'irc-console'}) ? "Set: $colors{'irc-console'}" : 'Not Set';
+       $status{'irc-gui'} = (defined $colors{'irc-gui'}) ? "Set: $colors{'irc-gui'}" : 'Not Set';
+       $status{'irc-virt-term'} = (defined $colors{'irc-virt-term'}) ? "Set: $colors{'irc-virt-term'}" : 'Not Set';
+       $status{'global'} = (defined $colors{'global'}) ? "Set: $colors{'global'}" : 'Not Set';
+}
+
+sub assign_selectors {
+       if ($type == 94){
+               $configs{'variable'} = 'CONSOLE_COLOR_SCHEME';
+               $configs{'selection'} = 'console';
+       }
+       elsif ($type == 95){
+               $configs{'variable'} = 'VIRT_TERM_COLOR_SCHEME';
+               $configs{'selection'} = 'virt-term';
+       }
+       elsif ($type == 96){
+               $configs{'variable'} = 'IRC_COLOR_SCHEME';
+               $configs{'selection'} = 'irc-gui';
+       }
+       elsif ($type == 97){
+               $configs{'variable'} = 'IRC_X_TERM_COLOR_SCHEME';
+               $configs{'selection'} = 'irc-virt-term';
+       }
+       elsif ($type == 98){
+               $configs{'variable'} = 'IRC_CONS_COLOR_SCHEME';
+               $configs{'selection'} = 'irc-console';
+       }
+       elsif ($type == 99){
+               $configs{'variable'} = 'GLOBAL_COLOR_SCHEME';
+               $configs{'selection'} = 'global';
+       }
+}
+sub start_selector {
+       my $whoami = getpwuid($<) || "unknown???";
+       if ( ! $b_irc ){
+               @data = (
+               [ 0, '', '', "Welcome to $self_name! Please select the default 
+               $configs{'selection'} color scheme."],
+               );
+       }
+       @rows = (
+       [ 0, '', '', "Because there is no way to know your $configs{'selection'}
+       foreground/background colors, you can set your color preferences from 
+       color scheme option list below:"],
+       [ 0, '', '', "0 is no colors; 1 is neutral."],
+       [ 0, '', '', "After these, there are 4 sets:"],
+       [ 0, '', '', "1-dark^or^light^backgrounds; 2-light^backgrounds; 
+       3-dark^backgrounds; 4-miscellaneous"],
+       [ 0, '', '', ""],
+       );
+       push @data, @rows;
+       if ( ! $b_irc ){
+               @rows = (
+               [ 0, '', '', "Please note that this will set the $configs{'selection'} 
+               preferences only for user: $whoami"],
+               );
+               push @data, @rows;
+       }
+       @rows = (
+       [ 0, '', '', "$line1"],
+       );
+       push @data, @rows;
+       main::print_basic(@data); 
+       @data = ();
+}
+sub create_color_selections {
+       my $spacer = '^^'; # printer removes double spaces, but replaces ^ with ' '
+       $count = ( main::get_color_scheme('count') - 1 );
+       for my $i (0 .. $count){
+               if ($i > 9){
+                       $spacer = '^';
                }
+               if ($configs{'selection'} =~ /^global|irc-gui|irc-console|irc-virt-term$/ && $i > $safe_color_count ){
+                       last;
+               }
+               main::set_color_scheme($i);
+               @rows = (
+               [0, '', '', "$i)$spacer$colors{'c1'}Card:$colors{'c2'}^nVidia^GT218 
+               $colors{'c1'}Display^Server$colors{'c2'}^x11^(X.Org^1.7.7)$colors{'cn'}"],
+               );
+               push @data, @rows;
+       }
+       main::print_basic(@data); 
+       @data = ();
+       main::set_color_scheme(0);
+}
+sub get_selection {
+       my $number = $count + 1;
+       @data = (
+       [0, '', '', ($number++) . ")^Remove all color settings. Restore $self_name default."],
+       [0, '', '', ($number++) . ")^Continue, no changes or config file setting."],
+       [0, '', '', ($number++) . ")^Exit, use another terminal, or set manually."],
+       [0, '', '', "$line1"],
+       [0, '', '', "Simply type the number for the color scheme that looks best to your 
+       eyes for your $configs{'selection'} settings and hit <ENTER>. NOTE: You can bring this 
+       option list up by starting $self_name with option: -c plus one of these numbers:"],
+       [0, '', '', "94^-^console,^not^in^desktop^-^$status{'console'}"],
+       [0, '', '', "95^-^terminal,^desktop^-^$status{'virt-term'}"],
+       [0, '', '', "96^-^irc,^gui,^desktop^-^$status{'irc-gui'}"],
+       [0, '', '', "97^-^irc,^desktop,^in^terminal^-^$status{'irc-virt-term'}"],
+       [0, '', '', "98^-^irc,^not^in^desktop^-^$status{'irc-console'}"],
+       [0, '', '', "99^-^global^-^$status{'global'}"],
+       [0, '', '',  ""],
+       [0, '', '', "Your selection(s) will be stored here: $user_config_file"],
+       [0, '', '', "Global overrides all individual color schemes. Individual 
+       schemes remove the global setting."],
+       [0, '', '', "$line1"],
+       );
+       main::print_basic(@data); 
+       @data = ();
+       my $response = <STDIN>;
+       chomp $response;
+       if ($response =~ /([^0-9]|^$)/ || ( $response =~ /^[0-9]+$/ && $response > ($count + 3) )){
+               @data = (
+               [0, '', '', "Error - Invalid Selection. You entered this: $response. Hit <ENTER> to continue."],
+               [0, '', '',  "$line1"],
+               );
+               main::print_basic(@data); 
+               my $response = <STDIN>;
+               start_selector();
+               create_color_selections();
+               get_selection();
        }
-       END {
-               print desktops
+       else {
+               process_selection($response);
        }
-       ' )
-       echo $de_data
-       
-       eval $LOGFE
+}
+sub process_selection {
+       my $response = shift;
+       if ($response == ($count + 3) ){
+               @data = ([0, '', '', "Ok, exiting $self_name now. You can set the colors later."],);
+               main::print_basic(@data); 
+               exit 1;
+       }
+       elsif ($response == ($count + 2)){
+               @data = (
+               [0, '', '', "Ok, continuing $self_name unchanged."],
+               [0, '', '',  "$line1"],
+               );
+               main::print_basic(@data); 
+               if ( defined $colors{'console'} && !$b_display ){
+                       main::set_color_scheme($colors{'console'});
+               }
+               if ( defined $colors{'virt-term'} ){
+                       main::set_color_scheme($colors{'virt-term'});
+               }
+               else {
+                       main::set_color_scheme($colors{'default'});
+               }
+       }
+       elsif ($response == ($count + 1)){
+               @data = (
+               [0, '', '', "Removing all color settings from config file now..."],
+               [0, '', '',  "$line1"],
+               );
+               main::print_basic(@data); 
+               delete_all_config_colors();
+               main::set_color_scheme($colors{'default'});
+       }
+       else {
+               main::set_color_scheme($response);
+               @data = (
+               [0, '', '', "Updating config file for $configs{'selection'} color scheme now..."],
+               [0, '', '',  "$line1"],
+               );
+               main::print_basic(@data); 
+               if ($configs{'selection'} eq 'global'){
+                       delete_all_config_colors();
+               }
+               set_config_color_scheme($response);
+       }
+}
+sub delete_all_config_colors {
+       my @file_lines = main::reader( $user_config_file );
+       open( $w_fh, '>', $user_config_file ) or error_handler('open', $user_config_file, $!);
+       foreach ( @file_lines ) { 
+               if ( $_ !~ /^(CONSOLE_COLOR_SCHEME|GLOBAL_COLOR_SCHEME|IRC_COLOR_SCHEME|IRC_CONS_COLOR_SCHEME|IRC_X_TERM_COLOR_SCHEME|VIRT_TERM_COLOR_SCHEME)/){
+                       print {$w_fh} "$_"; 
+               }
+       } 
+       close $w_fh;
+}
+sub set_config_color_scheme {
+       my $value = shift;
+       my @file_lines = main::reader( $user_config_file );
+       my $b_found = 0;
+       open( $w_fh, '>', $user_config_file ) or error_handler('open', $user_config_file, $!);
+       foreach ( @file_lines ) { 
+               if ( $_ =~ /^$configs{'variable'}/ ){
+                       $_ = "$configs{'variable'}=$value";
+                       $b_found = 1;
+               }
+               print $w_fh "$_\n";
+       }
+       if (! $b_found ){
+               print $w_fh "$configs{'variable'}=$value\n";
+       }
+       close $w_fh;
 }
 
-get_de_gtk_data()
-{
-       eval $LOGFS
-       
-       local toolkit=''
-       
-       # this is a hack, and has to be changed with every toolkit version change, and only dev systems
-       # have this installed, but it's a cross distro command so let's test it first
-       if [[ -n $( type -p pkg-config ) ]];then
-               toolkit=$( pkg-config --modversion gtk+-4.0 2>/dev/null )
-               # note: opensuse gets null output here, we need the command to get version and output sample
-               if [[ -z $toolkit ]];then
-                       toolkit=$( pkg-config --modversion gtk+-3.0 2>/dev/null )
-               fi
-               if [[ -z $toolkit ]];then
-                       toolkit=$( pkg-config --modversion gtk+-2.0 2>/dev/null )
-               fi
-       fi
-       # now let's go to more specific version tests, this will never cover everything and that's fine.
-       if [[ -z $toolkit ]];then
-               # we'll try some known package managers next. dpkg will handle a lot of distros 
-               # this is the most likely order as of: 2014-01-13. Not going to try to support all package managers
-               # too much work, just the very biggest ones.
-               if [[ -n $( type -p dpkg ) ]];then
-                       toolkit=$( dpkg -s libgtk-3-0 2>/dev/null | gawk -F ':' '/^Version/ {print $2}' )
-                       if [[ -z $toolkit ]];then
-                               toolkit=$( dpkg -s libgtk2.0-0 2>/dev/null | gawk -F ':' '/^Version/ {print $2}' )
-                       fi
-                       # just guessing on gkt 4 package name
-                       if [[ -z $toolkit ]];then
-                               toolkit=$( dpkg -s libgtk-4-0 2>/dev/null | gawk -F ':' '/^Version/ {print $2}' )
-                       fi
-               elif [[ -n $( type -p pacman ) ]];then
-                       toolkit=$(  pacman -Qi gtk3 2>/dev/null | gawk -F ':' '/^Version/ {print $2}' )
-                       if [[ -z $toolkit ]];then
-                               toolkit=$( pacman -Qi gtk2 2>/dev/null | gawk -F ':' '/^Version/ {print $2}' )
-                       fi
-               fi
-       fi
-       echo $toolkit
-       
-       eval $LOGFE
+sub print_irc_message {
+       @data = (
+       [ 0, '', '', "$line1"],
+       [ 0, '', '', "After finding the scheme number you like, simply run this again
+       in a terminal to set the configuration data file for your irc client. You can 
+       set color schemes for the following: start inxi with -c plus:"],
+       [ 0, '', '', "94 (console,^not^in^desktop^-^$status{'console'})"],
+       [ 0, '', '', "95 (terminal, desktop^-^$status{'virt-term'})"],
+       [ 0, '', '', "96 (irc,^gui,^desktop^-^$status{'irc-gui'})"],
+       [ 0, '', '', "97 (irc,^desktop,^in terminal^-^$status{'irc-virt-term'})"],
+       [ 0, '', '', "98 (irc,^not^in^desktop^-^$status{'irc-console'})"],
+       [ 0, '', '', "99 (global^-^$status{'global'})"]
+       );
+       main::print_basic(@data); 
+       exit 1;
 }
 
-# see which dm has started if any
-get_display_manager()
-{
-       eval $LOGFS
-       # ldm - LTSP display manager
-       local dm_id_list='entranced.pid entrance/entranced.pid gdm.pid gdm3.pid kdm.pid ldm.pid lightdm.pid lxdm.pid mdm.pid nodm.pid slim.lock tint2.pid wdm.pid xdm.pid' 
-       local dm_id='' dm='' separator=''
-       # note we don't need to filter grep if we do it this way
-       local x_is_running=$( grep '/usr.*/X' <<< "$Ps_aux_Data" | grep -iv '/Xprt' )
-
-       for dm_id in $dm_id_list
-       do
-               if [[ -e /var/run/$dm_id || -e /run/$dm_id ]];then
-                       # just on the off chance that two dms are running, good info to have in that case, if possible
-                       dm=$dm$separator$( basename $dm_id | cut -d '.' -f 1 )
-                       separator=','
-               fi
-       done
-       # might add this in, but the rate of new dm's makes it more likely it's an unknown dm, so
-       # we'll keep output to N/A
-       if [[ -n $x_is_running && -z $dm ]];then
-               if [[ -n $( grep 'startx$' <<< "$Ps_aux_Data" ) ]];then
-                       dm='(startx)'
-               fi
-       fi
-       echo $dm
-
-       log_function_data "display manager: $dm"
-
-       eval $LOGFE
-}
-
-# for more on distro id, please reference this python thread: http://bugs.python.org/issue1322
-## return distro name/id if found
-get_distro_data()
-{
-       eval $LOGFS
-       local i='' j='' distro='' distro_file='' a_distro_glob='' temp_array=''
-       
-       # may need modification if archbsd / debian can be id'ed with /etc files
-       if [[ -n $BSD_TYPE ]];then
-               distro=$( uname -sr )
-               echo "$distro"
-               log_function_data "distro: $distro"
-               eval $LOGFE
-               return 0
-       fi
-
-       # get the wild carded array of release/version /etc files if present
-       shopt -s nullglob
-       cd /etc
-       # note: always exceptions, so wild card after release/version: /etc/lsb-release-crunchbang
-       # wait to handle since crunchbang file is one of the few in the world that uses this method
-       a_distro_glob=(*[-_]{release,version})
-       cd "$OLDPWD"
-       shopt -u nullglob
-       
-       temp_array=${a_distro_glob[@]}
-       log_function_data "A_GLX_DATA: $temp_array"
-
-       if [[ ${#a_distro_glob[@]} -eq 1 ]];then
-               distro_file="${a_distro_glob}"
-       # use the file if it's in the known good lists
-       elif [[ ${#a_distro_glob[@]} -gt 1 ]];then
-               for i in $DISTROS_DERIVED $DISTROS_PRIMARY
-               do
-                       # Only echo works with ${var[@]}, not print_screen_output() or script_debugger()
-                       # This is a known bug, search for the word "strange" inside comments
-                       # echo "i='$i' a_distro_glob[@]='${a_distro_glob[@]}'"
-                       if [[ " ${a_distro_glob[@]} " == *" $i "* ]];then
-                               # Now lets see if the distro file is in the known-good working-lsb-list
-                               # if so, use lsb-release, if not, then just use the found file
-                               # this is for only those distro's with self named release/version files
-                               # because Mint does not use such, it must be done as below 
-                               ## this if statement requires the spaces and * as it is, else it won't work
-                               ##
-                               if [[ " $DISTROS_LSB_GOOD " == *" ${i} "* ]] && [[ $B_LSB_FILE == 'true' ]];then
-                                       distro_file='lsb-release'
-                               elif [[ " $DISTROS_OS_RELEASE_GOOD " == *" ${i} "* ]] && [[ $B_OS_RELEASE_FILE == 'true' ]];then
-                                       distro_file='os-release'
-                               else
-                                       distro_file="${i}"
-                               fi
-                               break
-                       fi
-               done
-       fi
-       log_function_data "distro_file: $distro_file"
-       # first test for the legacy antiX distro id file
-       if [[ -e /etc/antiX ]];then
-               distro="$( grep -Eoi 'antix.*\.iso' <<< $( remove_erroneous_chars '/etc/antiX' ) | sed 's/\.iso//' )"
-       # this handles case where only one release/version file was found, and it's lsb-release. This would
-       # never apply for ubuntu or debian, which will filter down to the following conditions. In general
-       # if there's a specific distro release file available, that's to be preferred, but this is a good backup.
-       elif [[ -n $distro_file && $B_LSB_FILE == 'true' && " $DISTROS_LSB_GOOD" == *" $distro_file "* ]];then
-               distro=$( get_distro_lsb_os_release_data 'lsb-file' )
-       elif [[ $distro_file == 'lsb-release' ]];then
-               distro=$( get_distro_lsb_os_release_data 'lsb-file' )
-       elif [[ $distro_file == 'os-release' ]];then
-               distro=$( get_distro_lsb_os_release_data 'os-release-file' )
-       # then if the distro id file was found and it's not in the exluded primary distro file list, read it
-       elif [[ -n $distro_file && -s /etc/$distro_file && " $DISTROS_EXCLUDE_LIST " != *" $distro_file "* ]];then
-               # new opensuse uses os-release, but older ones may have a similar syntax, so just use the first line
-               if [[ $distro_file == 'SuSE-release' ]];then
-                       # leaving off extra data since all new suse have it, in os-release, this file has line breaks, like os-release
-                       # but in case we  want it, it's: CODENAME = Mantis  | VERSION = 12.2 
-                       # for now, just take first occurance, which should be the first line, which does not use a variable type format
-                       distro=$( grep -i -m 1 'suse' /etc/$distro_file )
-               else
-                       distro=$( remove_erroneous_chars "/etc/$distro_file" )
-               fi
-       # otherwise try  the default debian/ubuntu /etc/issue file
-       elif [[ -f /etc/issue ]];then
-               # lsb gives more manageable and accurate output than issue, but mint should use issue for now
-               # some bashism, boolean must be in parenthesis to work correctly, ie [[ $(boolean) ]] not [[ $boolean ]]
-               if [[ $B_LSB_FILE == 'true' ]] && [[ -z $( grep -i 'mint' /etc/issue ) ]];then
-                       distro=$( get_distro_lsb_os_release_data 'lsb-file' )
-               else
-                       distro=$( gawk '
-                       BEGIN {
-                               RS=""
-                       }
-                       {
-                               gsub(/\\[a-z]/, "")
-                               gsub(/,/, " ")
-                               gsub(/^ +| +$/, "")
-                               gsub(/ [ \t]+/, " ")
-                               print
-                       }' /etc/issue )
-                       
-                       # this handles an arch bug where /etc/arch-release is empty and /etc/issue is corrupted
-                       # only older arch installs that have not been updated should have this fallback required, new ones use
-                       # os-release
-                       if [[ -n $( grep -i 'arch linux' <<< $distro ) ]];then
-                               distro='Arch Linux'
-                       fi
-               fi
-       fi
-       
-       if [[ ${#distro} -gt 80 ]] && [[ $B_HANDLE_CORRUPT_DATA != 'true' ]];then
-               distro="${RED}/etc/${distro_file} corrupted, use -% to override${NORMAL}"
-       fi
-       ## note: would like to actually understand the method even if it's not used
-       # : ${distro:=Unknown distro o_O}
-       ## test for /etc/lsb-release as a backup in case of failure, in cases where > one version/release file
-       ## were found but the above resulted in null distro value
-       if [[ -z $distro ]] && [[ $B_LSB_FILE == 'true' ]];then
-               distro=$( get_distro_lsb_os_release_data 'lsb-file' )
-       fi
-       if [[ -z $distro ]] && [[ $B_OS_RELEASE_FILE == 'true' ]];then
-               distro=$( get_distro_lsb_os_release_data 'os-release-file' )
-       fi
-       # now some final null tries
-       if [[ -z $distro ]];then
-               # if the file was null but present, which can happen in some cases, then use the file name itself to 
-               # set the distro value. Why say unknown if we have a pretty good idea, after all?
-               if [[ -n $distro_file ]] && [[ " $DISTROS_DERIVED $DISTROS_PRIMARY " == *" $distro_file "* ]];then
-                       distro=$( sed $SED_RX -e 's/[-_]//' -e 's/(release|version)//' <<< $distro_file | sed $SED_RX 's/^([a-z])/\u\1/' )
-               fi
-               ## finally, if all else has failed, give up
-               if [[ -z $distro ]];then
-                       distro='unknown'
-               fi
-       fi
-       # final step cleanup of unwanted information
-       # opensuse has the x86 etc type string in names, not needed as redundant since -S already shows that
-       distro=$( gawk '
-       BEGIN {
-               IGNORECASE=1
-       }
-       {
-               sub(/ *\(*(x86_64|i486|i586|i686|686|586|486)\)*/, "", $0)
-               print $0
-       }' <<< $distro )
-       echo "$distro"
-       log_function_data "distro: $distro"
-       eval $LOGFE
-}
-
-# args: $1 - lsb-file/lsb-app/os-release-file
-get_distro_lsb_os_release_data()
-{
-       eval $LOGFS
-       local distro=''
-       
-       case $1 in
-               lsb-file)
-                       if [[ $B_LSB_FILE == 'true' ]];then
-                               distro=$( gawk -F '=' '
-                               BEGIN {
-                                       IGNORECASE=1
-                               }
-                               # clean out unwanted characters
-                               { 
-                                       gsub(/\\|\"|[:\47]/,"", $0 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 )
-                               }
-                               # note: adding the spacing directly to variable to make sure distro output is null if not found
-                               /^DISTRIB_ID/ {
-                                       # this is needed because grep for "arch" is too loose to be safe
-                                       if ( $2 == "arch" ) {
-                                               distroId = "Arch Linux"
-                                       }
-                                       else if ( $2 != "n/a" ) {
-                                               distroId = $2 " "
-                                       }
-                               }
-                               /^DISTRIB_RELEASE/ {
-                                       if ( $2 != "n/a" ) {
-                                               distroRelease = $2 " "
-                                       }
-                               }
-                               /^DISTRIB_CODENAME/ {
-                                       if ( $2 != "n/a" ) {
-                                               distroCodename = $2 " "
-                                       }
-                               }
-                               # sometimes some distros cannot do their lsb-release files correctly, so here is
-                               # one last chance to get it right.
-                               /^DISTRIB_DESCRIPTION/ {
-                                       if ( $2 != "n/a" ) {
-                                               distroDescription = $2
-                                       }
-                               }
-                               END {
-                                       fullString=""
-                                       if ( distroId == "" && distroRelease == "" && distroCodename == "" && distroDescription != "" ){
-                                               fullString = distroDescription
-                                       }
-                                       else {
-                                               fullString = distroId distroRelease distroCodename
-                                       }
-                                       print fullString
-                               }
-                               ' $FILE_LSB_RELEASE )
-                               log_function_data 'cat' "$FILE_LSB_RELEASE"
-                       fi
-                       ;;
-               lsb-app)
-                       # this is HORRIBLY slow, not using
-                       if [[  -n $( type -p lsb_release ) ]];then
-                               distro=$( echo "$( lsb_release -irc )" | gawk -F ':' '
-                               BEGIN { 
-                                       IGNORECASE=1 
-                               }
-                               # clean out unwanted characters
-                               { 
-                                       gsub(/\\|\"|[:\47]/,"", $0 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 )
-                               }
-                               /^Distributor ID/ {
-                                       distroId = $2
-                               }
-                               /^Release/ {
-                                       distroRelease = $2
-                               }
-                               /^Codename/ {
-                                       distroCodename = $2
-                               }
-                               END {
-                                       print distroId " " distroRelease " (" distroCodename ")"
-                               }' )
-                       fi
-                       ;;
-               os-release-file)
-                       if [[ $B_OS_RELEASE_FILE == 'true' ]];then
-                               distro=$( gawk -F '=' '
-                               BEGIN {
-                                       IGNORECASE=1
-                                       prettyName=""
-                                       regularName=""
-                                       versionName=""
-                                       versionId=""
-                                       distroName=""
-                               }
-                               # clean out unwanted characters
-                               { 
-                                       gsub(/\\|\"|[:\47]/,"", $0 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2 )
-                                       gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1 )
-                               }
-                               # note: adding the spacing directly to variable to make sure distro output is null if not found
-                               /^PRETTY_NAME/ {
-                                       if ( $2 != "n/a" ) {
-                                               prettyName = $2
-                                       }
-                               }
-                               /^NAME/ {
-                                       if ( $2 != "n/a" ) {
-                                               regularName = $2
-                                       }
-                               }
-                               /^VERSION/ {
-                                       if ( $2 != "n/a" && $1 == "VERSION" ) {
-                                               versionName = $2
-                                       }
-                                       else if ( $2 != "n/a" && $1 == "VERSION_ID" ) {
-                                               versionId = $2
-                                       }
-                               }
-                               END {
-                                       if ( prettyName != "" ) {
-                                               distroName = prettyName
-                                       }
-                                       else if ( regularName != "" ) {
-                                               distroName = regularName
-                                               if ( versionName != "" ) {
-                                                       distroName = distroName " " versionName
-                                               }
-                                               else if ( versionId != "" ) {
-                                                       distroName = distroName " " versionId
-                                               }
-                                               
-                                       }
-                                       print distroName
-                               }
-                               ' $FILE_OS_RELEASE )
-                               log_function_data 'cat' "$FILE_OS_RELEASE"
-                       fi
-                       ;;
-       esac
-       echo $distro
-       log_function_data "distro: $distro"
-       eval $LOGFE
 }
 
-get_dmidecode_data()
-{
-       eval $LOGFS
-       
-       local dmidecodePath=''
-
-       if [[ $B_DMIDECODE_SET != 'true' ]];then
-               dmidecodePath=$( type -p dmidecode 2>/dev/null )
-               if [[ -n $dmidecodePath ]];then
-                       # note stripping out these lines: Handle 0x0016, DMI type 17, 27 bytes
-                       # but NOT deleting them, in case the dmidecode data is missing empty lines which will be
-                       # used to separate results. Then we remove the doubled empty lines to keep it clean and
-                       # strip out all the stuff we don't want to see in the results.
-                       DMIDECODE_DATA="$( $dmidecodePath 2>/dev/null \
-                       | gawk -F ':' '
-                       BEGIN {
-                               IGNORECASE=1
-                               cutExtraTab="false"
-                               twoData=""
-                               oneData=""
-                       }
-                       {
-                               # no idea why, but freebsd gawk does not do this right
-                               oneData=$1
-                               twoData=$2
-                               if ( twoData != "" ) {
-                                       twoHolder="true"
-                               }
-                               else {
-                                       twoHolder="false"
-                               }
-                               if ( $0 ~ /^\tDMI type/ ) {
-                                       sub(/^\tDMI type.*/, "", $0)
-                                       cutExtraTab="true"
-                               }
-                               gsub(/'"$BAN_LIST_NORMAL"'/, "", twoData)
-                               gsub(/,/, " ", $0)
-                               # clean out Handle line
-                               sub(/^Handle.*/,"", $0)
-                               sub(/^[[:space:]]*Inactive.*/,"",$0)
-                               # yes, there is a typo in a user data set, unknow
-                               # Base Board Version|Base Board Serial Number
-                               # Chassis Manufacturer|Chassis Version|Chassis Serial Number
-                               # System manufacturer|System Product Name|System Version
-                               # To Be Filled By O.E.M.
-                               # strip out starting white space so that the following stuff will clear properly
-                               sub(/^[[:space:]]+/, "", twoData)
-                               sub(/^Base Board .*|^Chassis .*|.*O\.E\.M\..*|.*OEM.*|^Not .*|^System .*|.*unknow.*|.*N\/A.*|none|^To be filled.*/, "", twoData) 
-                               gsub(/bios|acpi/, "", twoData)
-                               sub(/http:\/\/www.abit.com.tw\//, "Abit", twoData)
-                               
-                               # for double indented values replace with ~ so later can test for it, we are trusting that
-                               # indentation will be tabbed in this case
-                               # special case, dmidecode 2.2 has an extra tab and a DMI type line
-                               if ( cutExtraTab == "true" ) {
-                                       sub(/^\t\t\t+/, "~", oneData)
-                               }
-                               else {
-                                       sub(/^\t\t+/, "~", oneData)
-                               }
-                               
-                               gsub(/^[[:space:]]+|[[:space:]]+$/, "", twoData)
-                               gsub(/^[[:space:]]+|[[:space:]]+$/, "", oneData)
-                               gsub(/ [ \t]+/, " ", twoData)
-                               # reconstructing the line for processing so gawk can use -F : again
-                               if ( oneData != "" && twoHolder == "true" ) {
-                                       print oneData ":" twoData
-                               }
-                               else {
-                                       print $0
-                               }
-                       }' \
-                       | sed '/^$/{
-N
-/^\n$/D
-}' \
-                       )"
-               fi
-               B_DMIDECODE_SET='true'
-               log_function_data "DMIDECODE_DATA: $DMIDECODE_DATA"
-       fi
+#### -------------------------------------------------------------------
+#### CONFIGS
+#### -------------------------------------------------------------------
 
-       eval $LOGFE
+sub check_config_file {
+       $user_config_file = "$user_config_dir/$self_name.conf";
+       if ( ! -f $user_config_file ){
+               open( my $fh, '>', $user_config_file ) or error_handler('create', $user_config_file, $!);
+       }
 }
-# get_dmidecode_data;echo "$DMIDECODE_DATA";exit
 
-# BSD only
-get_dmesg_boot_data()
-{
-       eval $LOGFS
-       
-       local dmsg_boot_data=''
-       
-       if [[ $B_DMESG_BOOT_FILE == 'true' ]];then
-               # replace all indented items with ~ so we can id them easily while processing
-               dmsg_boot_data="$( cat $FILE_DMESG_BOOT | sed $SED_RX 's/"//g' )"
-       fi
-       echo "$dmsg_boot_data"
-       # log_function_data "$dmsg_boot_data"
-       eval $LOGFE
+sub get_configs {
+       my (@configs) = @_;
+       my ($key, $val,@config_files);
+       if (!@configs){
+               @config_files = (
+               qq(/etc/$self_name.conf), 
+               qq($user_config_dir/$self_name.conf)
+               );
+       }
+       else {
+               @config_files = (@configs);
+       }
+       # Config files should be passed in an array as a param to this function.
+       # Default intended use: global @CONFIGS;
+       foreach (@config_files) {
+               next unless open (my $fh, '<', "$_");
+               while (<$fh>) {
+                       chomp;
+                       s/#.*//;
+                       s/^\s+//;
+                       s/\s+$//;
+                       s/'|"//g;
+                       s/true/1/; # switch to 1/0 perl boolean
+                       s/false/0/; # switch to 1/0 perl boolean
+                       next unless length;
+                       ($key, $val) = split(/\s*=\s*/, $_, 2);
+                       get_config_item($key,$val);
+                       # print "f: $file key: $key val: $val\n";
+               }
+               close $fh;
+       }
 }
 
-get_gcc_kernel_version()
-{
-       # note that we use gawk to get the last part because beta, alpha, git versions can be non-numeric
-       local gccVersion=''
-       
-       if [[ -e /proc/version ]];then
-               gccVersion=$( grep -Eio 'gcc[[:space:]]*version[[:space:]]*([^ \t]*)' /proc/version 2>/dev/null | gawk '{print $3}' )
-       fi
-       echo $gccVersion
+# args: 0: key; 1: value
+sub get_config_item {
+       my ($key,$val) = @_;
+       if ($key eq 'ALLOW_UPDATE' || $key eq 'B_ALLOW_UPDATE') {$b_update = int($val)}
+       elsif ($key eq 'ALLOW_WEATHER' || $key eq 'B_ALLOW_WEATHER') {$b_weather = int($val)}
+       elsif ($key eq 'CPU_SLEEP') {$cpu_sleep = $val if $val =~ /^[0-9\.]$/}
+       elsif ($key eq 'DL_TIMEOUT') {$dl_timeout = int($val)}
+       elsif ($key eq 'DOWNLOADER') {
+               if ($val =~ /^(curl|fetch|ftp|perl|wget)$/){
+                       # this dumps all the other data and resets %dl for only the
+                       # desired downloader.
+                       $val = set_perl_downloader($val);
+                       %dl = ('dl' => $val, $val => 1);
+               }}
+       elsif ($key eq 'FILTER_STRING') {$filter_string = $val}
+       elsif ($key eq 'LANGUAGE') {$language = $val if $val =~ /^(en)$/}
+       elsif ($key eq 'LIMIT') {$limit = int($val)}
+       elsif ($key eq 'OUTPUT_TYPE') {$output_type = $val if $val =~ /^json|screen|xml/}
+       elsif ($key eq 'PS_COUNT') {$ps_count = int($val) }
+       elsif ($key eq 'SENSORS_CPU_NO') {$sensors_cpu_nu = int($val)}
+       elsif ($key eq 'SHOW_HOST' || $key eq 'B_SHOW_HOST') { $show{'host'} = int($val)}
+       elsif ($key eq 'WEATHER_UNIT') { 
+               $val = lc($val) if $val;
+               if ($val && $val =~ /^(c|f|cf|fc|i|m|im|mi)$/){
+                       my %units = ('c'=>'m','f'=>'i','cf'=>'mi','fc'=>'im');
+                       $val = $units{$val} if defined $units{$val};
+                       $weather_unit = $val;
+               }
+       }
+       # layout
+       elsif ($key eq 'CONSOLE_COLOR_SCHEME') {$colors{'console'} = int($val)}
+       elsif ($key eq 'GLOBAL_COLOR_SCHEME') {$colors{'global'} = int($val)}
+       elsif ($key eq 'IRC_COLOR_SCHEME') {$colors{'irc-gui'} = int($val)}
+       elsif ($key eq 'IRC_CONS_COLOR_SCHEME') {$colors{'irc-console'} = int($val)}
+       elsif ($key eq 'IRC_X_TERM_COLOR_SCHEME') {$colors{'irc-virt-term'} = int($val)}
+       elsif ($key eq 'VIRT_TERM_COLOR_SCHEME') {$colors{'virt-term'} = int($val)}
+       # note: not using the old short SEP1/SEP2
+       elsif ($key eq 'SEP1_IRC') {$sep{'s1-irc'} = $val}
+       elsif ($key eq 'SEP1_CONSOLE') {$sep{'s1-console'} = $val}
+       elsif ($key eq 'SEP[23]_IRC') {$sep{'s2-irc'} = $val}
+       elsif ($key eq 'SEP[23]_CONSOLE') {$sep{'s2-console'} = $val}
+       # size
+       elsif ($key eq 'COLS_MAX_CONSOLE') {$size{'console'} = int($val)}
+       elsif ($key eq 'COLS_MAX_IRC') {$size{'irc'} = int($val)}
+       elsif ($key eq 'COLS_MAX_NO_DISPLAY') {$size{'no-display'} = int($val)}
+       elsif ($key eq 'INDENT') {$size{'indent'} = int($val)}
+       elsif ($key eq 'INDENT_MIN') {$size{'indent-min'} = int($val)}
+       #  print "mc: key: $key val: $val\n";
+       # print Dumper (keys %size) . "\n";
 }
 
-get_gcc_system_version()
-{
-       eval $LOGFS
-       local separator='' gcc_installed='' gcc_list='' gcc_others='' temp_array=''
-       local gcc_version=$( 
-       gcc --version 2>/dev/null | sed $SED_RX 's/\([^\)]*\)//g' | gawk '
-       BEGIN {
-               IGNORECASE=1
-       }
-       /^gcc/ {
-               print $2
-               exit
-       }'      )
-
-       # can't use xargs -L basename because not all systems support thats
-       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-               gcc_others=$( ls /usr/bin/gcc-* 2>/dev/null )
-               if [[ -n $gcc_others ]];then
-                       for item in $gcc_others
-                       do
-                               gcc_installed=$( basename $item | gawk -F '-' '
-                               $2 ~ /^[0-9\.]+$/ {
-                                       print $2
-                               }' )
-                               if [[ -n $gcc_installed && -z $( grep "^$gcc_installed" <<< $gcc_version ) ]];then
-                                       gcc_list=$gcc_list$separator$gcc_installed
-                                       separator=','
-                               fi
-                       done
-               fi
-       fi
-       if [[ -n $gcc_version ]];then
-               A_GCC_VERSIONS=( "$gcc_version" $gcc_list )
-       fi
-       temp_array=${A_GCC_VERSIONS[@]}
-       log_function_data "A_GCC_VERSIONS: $temp_array"
-       eval $LOGFE
-}
-get_gpu_temp_data()
-{
-       local gpu_temp='' gpu_fan='' screens='' screen_nu='' gpu_temp_looper=''
+#### -------------------------------------------------------------------
+#### DEBUGGERS
+#### -------------------------------------------------------------------
 
-       # we'll try for nvidia/ati, then add if more are shown
-       if [[ -n $( type -p nvidia-settings ) ]];then
-               # first get the number of screens
-               screens=$( nvidia-settings -q screens | gawk '
-               /:[0-9]\.[0-9]/ {
-                       screens=screens gensub(/(.*)(:[0-9]\.[0-9])(.*)/, "\\2", "1", $0) " "
-               }
-               END {
-                       print screens
+# called in the initial -@ 10 program args setting so we can get logging 
+# as soon as possible # will have max 3 files, inxi.log, inxi.1.log, 
+# inxi.2.log
+sub begin_logging {
+       return 1 if $fh_l; # if we want to start logging for testing before options
+       my $log_file_2="$user_data_dir/$self_name.1.log";
+       my $log_file_3="$user_data_dir/$self_name.2.log";
+       my $data = '';
+       $end='main::log_data("fe", (caller(1))[3], "");';
+       $start='main::log_data("fs", (caller(1))[3], \@_);';
+       #$t3 = tv_interval ($t0, [gettimeofday]);
+       $t3 = eval 'Time::HiRes::tv_interval (\@t0, [Time::HiRes::gettimeofday()]);' if $b_hires;
+       #print Dumper $@;
+       my $now = strftime "%Y-%m-%d %H:%M:%S", localtime;
+       return if $b_debug_timers;
+       # do the rotation if logfile exists
+       if ( -f $log_file ){
+               # copy if present second to third
+               if ( -f $log_file_2 ){
+                       rename $log_file_2, $log_file_3 or error_handler('rename', "$log_file_2 -> $log_file_3", "$!");
                }
-               ' )
-               # now we'll get the gpu temp for each screen discovered. The print out function
-               # will handle removing screen data for single gpu systems
-               for screen_nu in $screens
-               do
-                       gpu_temp_looper=$( nvidia-settings -c $screen_nu -q GPUCoreTemp | gawk -F ': ' '
-                       BEGIN {
-                               IGNORECASE=1
-                               gpuTemp=""
-                               gpuTempWorking=""
-                       }
-                       /Attribute (.*)[0-9]+\.$/ {
-                               gsub(/\./, "", $2)
-                               if ( $2 ~ /^[0-9]+$/ ) {
-                                       gpuTemp=gpuTemp $2 "C "
-                               }
-                       }
-                       END {
-                               print gpuTemp
-                       }'      )
-                       screen_nu=$( cut -d ':' -f 2 <<< $screen_nu )
-                       gpu_temp="$gpu_temp$screen_nu:$gpu_temp_looper "
-               done
-       elif [[ -n $( type -p aticonfig ) ]];then
-#              gpu_temp=$( aticonfig --adapter=0 --od-gettemperature | gawk -F ': ' '
-               gpu_temp=$( aticonfig --adapter=all --od-gettemperature | gawk -F ': ' '
-               BEGIN {
-                       IGNORECASE=1
-                       gpuTemp=""
-                       gpuTempWorking=""
-               }
-               /Sensor (.*)[0-9\.]+ / {
-                       gpuTempWorking=gensub(/(.*) ([0-9\.]+) (.*)/, "\\2", "1", $2)
-                       if ( gpuTempWorking ~ /^[0-9\.]+$/ ) {
-                               gpuTemp=gpuTemp gpuTempWorking "C "
-                       }
-               }
-               END {
-                       print gpuTemp
-               }'      )
-       # this handles some newer cases of free driver temp readouts, will require modifications as
-       # more user data appears.
-       elif [[ -n $Sensors_Data ]];then
-               gpu_temp=$( 
-               gawk '
-               BEGIN {
-                       IGNORECASE=1
-                       gpuTemp=""
-                       separator=""
-               }
-               /^('"$SENSORS_GPU_SEARCH"')-pci/ {
-                       while ( getline && !/^$/  ) {
-                               if ( /^temp/ ) {
-                                       sub(/^[[:alnum:]]*.*:/, "", $0 ) # clear out everything to the :
-                                       gsub(/[\+ \t°]/, "", $1) # ° is a special case, like a space for gawk
-                                       gpuTemp=gpuTemp separator $1
-                                       separator=","
-                               }       
-                       }
-               }
-               END {
-                       print gpuTemp
-               }' <<< "$Sensors_Data" )
-       fi
-       
-       if [[ -n $gpu_temp ]];then
-               echo $gpu_temp
-       fi
-}
-
-## for possible future data, not currently used
-get_graphics_agp_data()
-{
-       eval $LOGFS
-       local agp_module=''
-
-       if [[ $B_MODULES_FILE == 'true' ]];then
-               ## not used currently
-               agp_module=$( gawk '
-               /agp/ && !/agpgart/ && $3 > 0 {
-                       print(gensub(/(.*)_agp.*/,"\\1","g",$1))
-               }' $FILE_MODULES )
-               log_function_data 'cat' "$FILE_MODULES"
-       fi
-       log_function_data "agp_module: $agp_module"
-       eval $LOGFE
-}
-
-## create array of gfx cards installed on system
-get_graphics_card_data()
-{
-       eval $LOGFS
-       local i='' temp_array=''
-
-       IFS=$'\n'
-       A_GRAPHICS_CARD_DATA=( $( gawk -F': ' '
-       BEGIN {
-               IGNORECASE=1
-               busId=""
-       }
-       /vga compatible controller/ {
-               gsub(/'"$BAN_LIST_NORMAL"'/, "", $NF)
-               gsub(/,/, " ", $NF)
-               gsub(/^ +| +$/, "", $NF)
-               gsub(/ [ \t]+/, " ", $NF)
-               busId=gensub(/^([0-9a-f:\.]+) (.+)$/,"\\1","",$1)
-               print $NF "," busId
-       }' <<< "$Lspci_v_Data" ) )
-       IFS="$ORIGINAL_IFS"
-#      for (( i=0; i < ${#A_GRAPHICS_CARD_DATA[@]}; i++ ))
-#      do
-#              A_GRAPHICS_CARD_DATA[i]=$( sanitize_characters BAN_LIST_NORMAL "${A_GRAPHICS_CARD_DATA[i]}" )
-#      done
-
-       # GFXMEM is UNUSED at the moment, because it shows AGP aperture size, which is not necessarily equal to GFX memory..
-       # GFXMEM="size=[$(echo "$Lspci_v_Data" | gawk '/VGA/{while (!/^$/) {getline;if (/size=[0-9][0-9]*M/) {size2=gensub(/.*\[size=([0-9]+)M\].*/,"\\1","g",$0);if (size<size2){size=size2}}}}END{print size2}')M]"
-       temp_array=${A_GRAPHICS_CARD_DATA[@]}
-       log_function_data "A_GRAPHICS_CARD_DATA: $temp_array"
-       eval $LOGFE
-}
-
-get_graphics_driver()
-{
-       eval $LOGFS
-       
-       # list is from sgfxi plus non-free drivers
-       local driver_list='apm|ark|ati|chips|cirrus|cyrix|fbdev|fglrx|glint|i128|i740|intel|i810|imstt|mach64|mga|neomagic|nsc|nvidia|nv|openchrome|nouveau|radeon|radeonhd|rendition|s3virge|s3|savage|siliconmotion|sisusb|sis|tdfx|tga|trident|tseng|unichrome|vboxvideo|vesa|vga|via|voodoo|vmware|v4l'
-       local driver='' driver_string='' xorg_log_data='' status='' temp_array=''
-
-       if [[ $B_XORG_LOG == 'true' ]];then
-               A_GRAPHIC_DRIVERS=( $(
-               gawk '
-               BEGIN {
-                       driver=""
-                       bLoaded="false"
-                       IGNORECASE=1
-               }
-               # note that in file names, driver is always lower case
-               /[[:space:]]Loading.*('"$driver_list"')_drv.so$/ {
-                       driver=gensub(/.*[[:space:]]Loading.*('"$driver_list"')_drv.so/, "\\1", 1, $0 )
-                       # we get all the actually loaded drivers first, we will use this to compare the
-                       # failed/unloaded, which have not always actually been truly loaded
-                       aDrivers[driver]="loaded" 
-               }
-               /Unloading[[:space:]].*('"$driver_list"')(|_drv.so)$/ {
-                       driver=gensub(/(.*)Unloading[[:space:]].*('"$driver_list"')(|_drv.so)$/, "\\2", 1, $0 )
-                       # we need to make sure that the driver has already been truly loaded, not just discussed
-                       if ( driver in aDrivers ) {
-                               aDrivers[driver]="unloaded"
-                       }
-               }
-               /Failed.*('"$driver_list"')_drv.so|Failed.*\"('"$driver_list"')\"/ {
-                       driver=gensub(/(.*)Failed.*('"$driver_list"')_drv.so/, "\\2", 1, $0 )
-                       if ( driver == $0 ) {
-                               driver=gensub(/(.*)Failed.*\"('"$driver_list"')\".*|fred/, "\\2", 1, $0 )
-                       }
-                       # we need to make sure that the driver has already been truly loaded, not just discussed
-                       if ( driver != $0 && driver in aDrivers ) {
-                               aDrivers[driver]="failed"
-                       }
-               }
-               # verify that the driver actually started the desktop, even with false failed messages which can occur
-               # this is the driver that is actually driving the display
-               /.*\([0-9]+\):[[:space:]]Depth.*framebuffer/ {
-                       driver=gensub(/.*('"$driver_list"')\([0-9]+\):[[:space:]]Depth.*framebuffer.*/, "\\1", 1, $0 )
-                       # we need to make sure that the driver has already been truly loaded, not just discussed, also
-                       # set driver to lower case because sometimes it will show as RADEON or NVIDIA in the actual x start
-                       driver=tolower(driver)
-                       if ( driver != $0 && driver in aDrivers ) {
-                               aDrivers[driver]="loaded"
-                       }
-               }
-               END {
-                       for ( driver in aDrivers ) {
-                               print driver "," aDrivers[driver]
-                       }
-               }' < $FILE_XORG_LOG ) )
-       fi
-       temp_array=${A_GRAPHIC_DRIVERS[@]}
-       log_function_data "A_GRAPHIC_DRIVERS: $temp_array"
+               # then copy initial to second
+               rename $log_file, $log_file_2 or error_handler('rename', "$log_file -> $log_file_2", "$!");
+       }
+       # now create the logfile
+       # print "Opening log file for reading: $log_file\n";
+       open $fh_l, '>', $log_file or error_handler(4, $log_file, "$!");
+       # and echo the start data
+       $data = $line2;
+       $data .= "START $self_name LOGGING:\n";
+       $data .= "NOTE: HiRes timer not available.\n" if !$b_hires;
+       $data .= "$now\n";
+       $data .= "Elapsed since start: $t3\n";
+       $data .= "n: $self_name v: $self_version p: $self_patch d: $self_date\n";
+       $data .= '@paths:' . joiner(\@paths, '::', 'unset') . "\n";
+       $data .= $line2;
        
-       eval $LOGFE
+       print $fh_l $data;
 }
 
-## create array of glx data
-get_graphics_glx_data()
-{
-       eval $LOGFS
-       local temp_array=''
-       if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then
-               IFS=$'\n'
-               A_GLX_DATA=( $( glxinfo | gawk -F ': ' '
-               # note: function declarations go before BEGIN? It appears so, confirm.
-               # the real question here though is why this function is even here, seems
-               # just to be a complicated way to pack/print a variable, but maybe the
-               # original idea was to handle > 1 cases of detections I guess
-               function join( arr, sep ) {
-                       s=""
-                       i=flag=0
-                       for ( i in arr ) {
-                               if ( flag++ ) {
-                                       s = s sep
-                               }
-                               s = s i
-                       }
-                       return s
-               }
-
-               BEGIN {
-                       IGNORECASE=1
-               }
-               /opengl renderer/ {
-                       gsub(/'"$BAN_LIST_NORMAL"'/, "", $2)
-                       gsub(/ [ \t]+/, " ", $2) # get rid of the created white spaces
-                       gsub(/^ +| +$/, "", $2)
-                       if ( $2 ~ /mesa/ ) {
-                               # Allow all mesas
-#                              if ( $2 ~ / r[3-9][0-9][0-9] / ) {
-                                       a[$2]
-                                       # this counter failed in one case, a bug, and is not needed now
-#                                      f++
-#                              }
-                               next
+# NOTE: no logging available until get_parameters is run, since that's what 
+# sets logging # in order to trigger earlier logging manually set $b_log
+# to true in top variables.
+# args: $1 - type [fs|fe|cat|dump|raw] OR data to log
+# arg: $2 - 
+# arg: $one type (fs/fe/cat/dump/raw) or logged data; 
+# [$two is function name; [$three - function args]]
+sub log_data {
+       return if ! $b_log;
+       my ($one, $two, $three) = @_;
+       my ($args,$data,$timer) = ('','','');
+       my $spacer = '   ';
+       # print "1: $one 2: $two 3: $three\n";
+       if ($one eq 'fs') {
+               if (ref $three eq 'ARRAY'){
+                       my @temp = @$three;
+                       # print Data::Dumper::Dumper \@$three;
+                       $args = "\n${spacer}Args: " . joiner($three, '; ', 'unset');
+               }
+               else {
+                       $args = "\n${spacer}Args: None";
+               }
+               # $t1 = [gettimeofday];
+               #$t3 = tv_interval ($t0, [gettimeofday]);
+               $t3 = eval 'Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()])' if $b_hires;
+               #print Dumper $@;
+               $data = "Start: Function: $two$args\n${spacer}Elapsed: $t3\n";
+               $spacer='';
+               $timer = $data if $b_debug_timers;
+       }
+       elsif ( $one eq 'fe') {
+               # print 'timer:', Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()]),"\n";
+               #$t3 = tv_interval ($t0, [gettimeofday]);
+               eval '$t3 = Time::HiRes::tv_interval(\@t0, [Time::HiRes::gettimeofday()])' if $b_hires;
+               #print Dumper $t3;
+               $data = "${spacer}Elapsed: $t3\nEnd: Function: $two\n";
+               $spacer='';
+               $timer = $data if $b_debug_timers;
+       }
+       elsif ( $one eq 'cat') {
+               if ( $b_log_full ){
+                       for my $file ($two){
+                               my $contents = do { local( @ARGV, $/ ) = $file; <> }; # or: qx(cat $file)
+                               $data = "$data${line3}Full file data: $file\n\n$contents\n$line3\n";
                        }
-                       
-                       $2 && a[$2]
-               }
-               # dropping all conditions from this test to just show full mesa information
-               # there is a user case where not f and mesa apply, atom mobo
-               # /opengl version/ && ( f || $2 !~ /mesa/ ) {
-               /opengl version/ {
-                       # fglrx started appearing with this extra string, does not appear to communicate anything of value
-                       sub(/Compatibility Profile Context/, "- CPC", $2 )
-                       gsub(/ [ \t]+/, " ", $2) # get rid of the created white spaces
-                       gsub(/^ +| +$/, "", $2)
-                       $2 && b[$2]
-               }
-               /direct rendering/ {
-                       $2 && c[$2]
-               }
-               END {
-                       printf( "%s\n%s\n%s\n", join( a, ", " ), join( b, ", " ), join( c, ", " ) )
-               }' ) )
-               IFS="$ORIGINAL_IFS"
-
-               # GLXR=$(glxinfo | gawk -F ': ' 'BEGIN {IGNORECASE=1} /opengl renderer/ && $2 !~ /mesa/ {seen[$2]++} END {for (i in seen) {printf("%s ",i)}}')
-               #    GLXV=$(glxinfo | gawk -F ': ' 'BEGIN {IGNORECASE=1} /opengl version/ && $2 !~ /mesa/ {seen[$2]++} END {for (i in seen) {printf("%s ",i)}}')
-       fi
-       temp_array=${A_GLX_DATA[@]}
-       log_function_data "A_GLX_DATA: $temp_array"
-       eval $LOGFE
-}
-
-## return screen resolution / tty resolution
-get_graphics_res_data()
-{
-       eval $LOGFS
-       local screen_resolution='' xdpy_data='' screens_count=0 tty_session=''
-
-       if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then
-               # Added the two ?'s , because the resolution is now reported without spaces around the 'x', as in
-               # 1400x1050 instead of 1400 x 1050. Change as of X.org version 1.3.0
-               xdpy_data="$( xdpyinfo )"
-               xdpy_count=$( grep -c 'dimensions' <<< "$xdpy_data" )
-               # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle
-               # multiple screens from different video cards
-               if [[ $xdpy_count -eq 1 ]];then
-                       screen_resolution=$( xrandr | gawk '
-                       /\*/ {
-                               res[++m] = gensub(/^.* ([0-9]+) ?x ?([0-9]+)[_ ].* ([0-9\.]+)\*.*$/,"\\1x\\2@\\3hz","g",$0)
-                       }
-                       END {
-                               for (n in res) {
-                                       if (res[n] ~ /^[[:digit:]]+x[[:digit:]]+/) {
-                                               line = line ? line ", " res[n] : res[n]
-                                       }
-                               }
-                               if (line) {
-                                       print(line)
-                               }
-                       }' )
-               fi
-               if [[ -z $screen_resolution || $xdpy_count -gt 1 ]];then
-                       screen_resolution=$( gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                               screens = ""
-                               separator = ""
-                       }
-                       /dimensions/ {
-                               screens = screens separator # first time, this is null, next, has comma last
-                               screens = screens $2 # then tack on the new value for nice comma list
-                               separator = ", "
-                       }
-                       END {
-                               print screens
-                       }' <<< "$xdpy_data" )
-               fi
-       else
-               if [[ $B_PROC_DIR == 'true' && -z $BSD_TYPE ]];then
-                       screen_resolution=$( stty -F $( readlink /proc/$PPID/fd/0 ) size | gawk '{
-                               print $2"x"$1
-                       }' )
-               # note: this works fine for all systems but keeping the above for now since
-               # the above is probably more accurate for linux systems.
-               else
-                       if [[ $B_CONSOLE_IRC != 'true' ]];then
-                               screen_resolution=$( stty -a | gawk -F ';' '
-                                       /^speed/ {
-                                               gsub(/[[:space:]]*(rows|columns)[[:space:]]*/,"",$0)
-                                               gsub(/[[:space:]]*/,"",$2)
-                                               gsub(/[[:space:]]*/,"",$3)
-                                               print $3"x"$2
-                                       }' )
-                       else
-                               if [[ -n $BSD_TYPE ]];then
-                                       tty_session=$( get_tty_console_irc )
-                                       # getting information for tty that owns the irc client
-                                       screen_resolution="$( stty -f /dev/pts/$tty_session size | gawk '{print $2"x"$1}' )"
-                               fi
-                       fi
-               fi
-       fi
-       echo "$screen_resolution"
-       log_function_data "screen_resolution: $screen_resolution"
-       eval $LOGFE
-}
-
-## create array of display server vendor/version data
-get_graphics_display_server_data()
-{
-       eval $LOGFS
-       local vendor='' version='' temp_array='' xdpy_info='' a_display_vendor_working=''
-
-       if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then
-               # X vendor and version detection.
-               # new method added since radeon and X.org and the disappearance of <X server name> version : ...etc
-               # Later on, the normal textual version string returned, e.g. like: X.Org version: 6.8.2
-               # A failover mechanism is in place. (if $version is empty, the release number is parsed instead)
-               # xdpy_info="$( xdpyinfo )"
-               IFS=","
-               a_display_vendor_working=( $( xdpyinfo | gawk -F': +' '
-               BEGIN {
-                       IGNORECASE=1
-                       vendorString=""
-                       version=""
-                       vendorRelease=""
-               }
-               /vendor string/ {
-                       gsub(/the|inc|foundation|project|corporation/, "", $2)
-                       gsub(/,/, " ", $2)
-                       gsub(/^ +| +$/, "", $2)
-                       gsub(/ [ \t]+/, " ", $2)
-                       vendorString = $2
-               }
-               /version:/ {
-                       version = $NF
-               }
-               /vendor release number/ {
-                       gsub(/0+$/, "", $2)
-                       gsub(/0+/, ".", $2)
-                       vendorRelease = $2
-               }
-               END {
-                       print vendorString "," version "," vendorRelease
-               }' ) )
-               vendor=${a_display_vendor_working[0]}
-               version=${a_display_vendor_working[1]}
-
-               # this gives better output than the failure last case, which would only show:
-               # for example: X.org: 1.9 instead of: X.org: 1.9.0
-               if [[ -z $version ]];then
-                       version=$( get_graphics_display_server_version )
-               fi
-               if [[ -z $version ]];then
-                       version=${a_display_vendor_working[2]}
-               fi
-               
-               # some distros, like fedora, report themselves as the xorg vendor, so quick check
-               # here to make sure the vendor string includes Xorg in string
-               if [[ -z $( grep -E '(X|xorg|x\.org)' <<< $vendor ) ]];then
-                       vendor="$vendor X.org"
-               fi
-               IFS="$ORIGINAL_IFS"
-               A_DISPLAY_SERVER_DATA[0]="$vendor"
-               A_DISPLAY_SERVER_DATA[1]="$version"
-       else
-               version=$( get_graphics_display_server_version )
-               if [[ -n $version ]];then
-                       vendor='X.org'
-                       A_DISPLAY_SERVER_DATA[0]="$vendor"
-                       A_DISPLAY_SERVER_DATA[1]="$version"
-               fi
-       fi
-       temp_array=${A_DISPLAY_SERVER_DATA[@]}
-       log_function_data "A_DISPLAY_SERVER_DATA: $temp_array"
-       eval $LOGFE
-}
-
-# if other tests fail, try this one, this works for root, out of X also
-get_graphics_display_server_version()
-{
-       eval $LOGFS
-       local version='' x_data=''
-       # note that some users can have /usr/bin/Xorg but not /usr/bin/X
-       if [[ -n $( type -p X ) ]];then
-               # note: MUST be this syntax: X -version 2>&1
-               # otherwise X -version overrides everything and this comes out null.
-               # two knowns id strings: X.Org X Server 1.7.5 AND X Window System Version 1.7.5
-               #X -version 2>&1 | gawk '/^X Window System Version/ { print $5 }'
-               x_data="$( X -version 2>&1 )"
-       elif [[ -n $( type -p Xorg ) ]];then
-               x_data="$( Xorg -version 2>&1)"
-       fi
-       if [[ -n $x_data ]];then
-               version=$( 
-               gawk '
-               BEGIN {
-                       IGNORECASE=1
-               }
-               /^x.org x server/ {
-                       print $4
-                       exit
-               }
-               /^X Window System Version/ {
-                       print $5
-                       exit
-               }' <<< "$x_data" )
-       fi
-       echo $version
-       log_function_data " version: $version"
-       eval $LOGFE
-}
-
-# this gets just the raw data, total space/percent used and disk/name/per disk capacity
-get_hdd_data_basic()
-{
-       eval $LOGFS
-       local hdd_used='' temp_array='' df_string=''
-       local hdd_data='' df_test=''
-       
-       if [[ -z $BSD_TYPE ]];then
-               df_string='df -P -T --exclude-type=aufs --exclude-type=devfs --exclude-type=devtmpfs 
-               --exclude-type=fdescfs --exclude-type=iso9660 --exclude-type=linprocfs --exclude-type=procfs
-               --exclude-type=squashfs --exclude-type=sysfs --exclude-type=tmpfs --exclude-type=unionfs'
-       else
-               # default size is 512, so use -k for 1024
-               df_string='df -T -k'
-               # default size is 512, -H only for size in human readable format
-               # older bsds don't support -T, pain, so we'll use partial output there
-               df_test=$( df -H -T 2>/dev/null )
-               if [[ -n $df_test ]];then
-                       df_string='df -k -T'
-               else
-                       df_string='df -k'
-               fi
-       fi
-       hdd_data="$( eval $df_string )"
-       log_function_data 'raw' "hdd_data:\n$hdd_data"
-       hdd_used=$(     echo "$hdd_data" | gawk -v bsdType=$BSD_TYPE '
-       BEGIN {
-               # this is used for specific cases where bind, or incorrect multiple mounts to same partitions,
-               # is present. The value is searched for an earlier appearance of that partition and if it is 
-               # present, the data is not added into the partition used size.
-               partitionsSet=""
-               # this handles a case where the same dev item is mounted twice to different points
-               devSet=""
-               devWorking=""
-               mountWorking=""
-       }
-       # using $1, not $2, because older bsd df do not have -T, filesystem type
-       ( bsdType != "" ) && $1 ~ /^(aufs|devfs|devtmpfs|fdescfs|filesystem|iso9660|linprocfs|procfs|squashfs|sysfs|tmpfs|type|unionfs)$/ {
-               # note use next, not getline or it does not work right
-               next 
-       }
-       # also handles odd dm-1 type, from lvm, and mdraid, and some other bsd partition syntax
-       # note that linux 3.2.45-grsec-9th types kernels have this type of partition name: /dev/xvdc (no number, letter)
-       /^\/dev\/(mapper\/|[hsv]d[a-z][0-9]+|dm[-]?[0-9]+|ada[0-9]+p[0-9]+.*|md[0-9]+|[aw]d[0-9]+s.*|xvd[a-z])/ {
-               # this handles the case where the first item is too long
-               # and makes df wrap output to next line, so here we advance
-               # it to the next line for that single case. Using df -P should
-               # make this unneeded but leave it in just in case
-               if ( NF < 6 && $0 !~ /.*%/ ) {
-                       devSet = devSet "~" $1 "~"
-                       getline
-               }
-               # if the first item caused a wrap, use one less than standard
-               # testing for the field with % in it, ie: 34%, then go down from there
-               # this also protects against cases where the mount point has a space in the
-               # file name, thus breaking going down from $NF directly.
-               # some bsds will also have only 6 items
-               if ( $5 ~ /.*%/ ) {
-                       devWorking="~" $1 "~"
-                       mountWorking="~" $6 "~"
-                       if ( partitionsSet !~ mountWorking && devSet !~ devWorking ) {
-                               used += $3
-                       }
-                       partitionsSet = partitionsSet mountWorking
-                       # make sure to only include bsd real lines here, ie, short df output
-                       if ( $1 ~ /^\/dev\// ) {
-                               devSet = devSet devWorking
-                       }
-               }
-               # otherwise use standard
-               else if ( $6 ~ /.*%/ ) {
-                       devWorking="~" $1 "~"
-                       mountWorking="~" $7 "~"
-                       if ( partitionsSet !~ mountWorking && devSet !~ devWorking ) {
-                               used += $4
-                       }
-                       partitionsSet = partitionsSet mountWorking
-                       devSet = devSet devWorking
-               }
-               # and if this is not detected, give up, we need user data to debug
+                       $spacer='';
+               }
+       }
+       elsif ($one eq 'cmd'){
+               $data = "Command: $two\n";
+               $data .= qx($two);
+       }
+       elsif ($one eq 'data'){
+               $data = "$two\n";
+       }
+       elsif ( $one eq 'dump') {
+               $data = "$two:\n";
+               if (ref $three eq 'HASH'){
+                       $data .= Data::Dumper::Dumper \%$three;
+               }
+               elsif (ref $three eq 'ARRAY'){
+                       # print Data::Dumper::Dumper \@$three;
+                       $data .= Data::Dumper::Dumper \@$three;
+               }
                else {
-                       next
-               }
-       }
-       END {
-               print used
-       }' )
-
-       if [[ -z $hdd_used ]];then
-               hdd_used='na'
-       fi
-       log_function_data "hdd_used: $hdd_used"
-       # create the initial array strings:
-       # disk-dev, capacity, name, usb or not
-       # final item is the total of the disk
-       IFS=$'\n'
-
-       if [[ $B_PARTITIONS_FILE == 'true' ]];then
-               A_HDD_DATA=( $(
-               gawk -v hddused="$hdd_used" '
-               /[hsv]d[a-z]$/ {
-                       driveSize = $(NF - 1)*1024/1000**3
-                       gsub(/,/, " ", driveSize)
-                       gsub(/^ +| +$/, "", driveSize)
-                       printf( $NF",%.1fGB,,\n", driveSize )
+                       $data .= Data::Dumper::Dumper $three;
                }
-               # See http://lanana.org/docs/device-list/devices-2.6+.txt for major numbers used below
-               # $1 ~ /^(3|22|33|8)$/ && $2 % 16 == 0  {
-               #       size += $3
-               # }
-               # special case from this data: 8     0  156290904 sda
-               # note: vm has 252/253/254 known starter, grsec has 202
-               $1 ~ /^(3|8|22|33|202|252|253|254)$/ && $NF ~ /[hsv]d[a-z]$/ && ( $2 % 16 == 0 || $2 % 16 == 8 ) {
-                       size += $3
+               $data .= "\n";
+               # print $data;
+       }
+       elsif ( $one eq 'raw') {
+               if ( $b_log_full ){
+                       $data = "\n${line3}Raw System Data:\n\n$two\n$line3";
+                       $spacer='';
                }
-
-               END {
-                       size = size*1024/1000**3                   # calculate size in GB size
-                       workingUsed = hddused*1024/1000**3         # calculate workingUsed in GB used
-                       # this handles a special case with livecds where no hdd_used is detected
-                       if ( size > 0 && hddused == "na" ) {
-                               size = sprintf( "%.1f", size )
-                               print size "GB,-"
-                       }
-                       else if ( size > 0 && workingUsed > 0 ) {
-                               diskUsed = workingUsed*100/size  # calculate used percentage
-                               diskUsed = sprintf( "%.1f", diskUsed )
-                               size = sprintf( "%.1f", size )
-                               print size "GB," diskUsed "% used"
-                       }
-                       else {
-                               print "NA,-" # print an empty array, this will be further handled in the print out function
-                       }
-               }' $FILE_PARTITIONS ) )
-               log_function_data 'cat' "$FILE_PARTITIONS"
-       fi
-       IFS="$ORIGINAL_IFS"
-       temp_array=${A_HDD_DATA[@]}
-       log_function_data "A_HDD_DATA: $temp_array"
-       eval $LOGFE
+       }
+       else {
+               $data = "$two\n";
+       }
+       if ($b_debug_timers){
+               print $timer if $timer;
+       }
+       #print "d: $data";
+       elsif ($data){
+               print $fh_l "$spacer$data";
+       }
 }
 
-## fills out the A_HDD_DATA array with disk names
-get_hard_drive_data_advanced()
-{
-       eval $LOGFS
-       local a_temp_working='' a_temp_scsi='' temp_holder='' temp_name='' i='' j=''
-       local sd_ls_by_id='' ls_disk_by_id='' usb_exists='' temp_array=''
+sub set_debugger {
+       if ( $debug >= 20){
+               error_handler('not-in-irc', 'debug data generator') if $b_irc;
+               my $option = ( $debug > 22 ) ? 'main-full' : 'main';
+               $b_debug_gz = 1 if ($debug == 22 || $debug == 24);
+               my $ob_sys = SystemDebugger->new($option);
+               $ob_sys->run_debugger();
+               $ob_sys->upload_file($ftp_alt) if $debug > 20;
+               exit 0;
+       }
+       elsif ($debug >= 10 && $debug <= 12){
+               $b_log = 1;
+               if ($debug == 11){
+                       $b_log_full = 1;
+               }
+               elsif ($debug == 12){
+                       $b_log_colors = 1;
+               }
+               begin_logging();
+       }
+       elsif ($debug <= 3){
+               if ($debug == 3){
+                       $b_log = 1;
+                       $b_debug_timers = 1;
+                       begin_logging();
+               }
+               else {
+                       $end = '';
+                       $start = '';
+               }
+       }
+}
 
-       ## check for all ide type drives, non libata, only do it if hdx is in array
-       ## this is now being updated for new /sys type paths, this may handle that ok too
-       if [[ -n $( grep -E 'hd[a-z]' <<< ${A_HDD_DATA[@]} ) ]];then
-               # remember, we're using the last array item to store the total size of disks
-               for (( i=0; i < ${#A_HDD_DATA[@]} - 1; i++ ))
-               do
-                       IFS=","
-                       a_temp_working=( ${A_HDD_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       if [[ -n $( grep -E '^hd[a-z]' <<< ${a_temp_working[0]} ) ]];then
-                               if [[ -e /proc/ide/${a_temp_working[0]}/model ]];then
-                                       a_temp_working[2]="$( remove_erroneous_chars /proc/ide/${a_temp_working[0]}/model )"
-                               else
-                                       a_temp_working[2]="Name n/a"
-                               fi
-                               # these loops are to easily extend the cpu array created in the gawk script above with more fields per cpu.
-                               for (( j=0; j < ${#a_temp_working[@]}; j++ ))
-                               do
-                                       if [[ $j -gt 0 ]];then
-                                               A_HDD_DATA[i]="${A_HDD_DATA[i]},${a_temp_working[$j]}"
-                                       else
-                                               A_HDD_DATA[i]="${a_temp_working[$j]}"
-                                       fi
-                               done
-                       fi
-               done
-       fi
-
-       ## then handle libata names
-       # first get the ata device names, put them into an array
-       IFS=$'\n'
-       if [[ $B_SCSI_FILE == 'true' ]]; then
-               a_temp_scsi=( $( gawk  '
-               BEGIN {
-                       IGNORECASE=1
-               }
-               /host/ {
-                       getline a[$0]
-                       getline b[$0]
-               }
-               END {
-                       for (i in a) {
-                               if (b[i] ~ / *type: *direct-access.*/) {
-                                       #c=gensub(/^ *vendor: (.+) +model: (.+) +rev: (.+)$/,"\\1 \\2 \\3","g",a[i])
-                                       #c=gensub( /^ *vendor: (.+) +model: (.+) +rev:.*$/,"\\1 \\2","g",a[i] )
-                                       # the vendor: string is useless, and is a bug, ATA is not a vendor for example
-                                       c=gensub( /^ *vendor: (.+) +model: (.+) +rev:.*$/, "\\2", "g", a[i] )
-                                       gsub(/,/, " ", c)
-                                       gsub(/^ +| +$/, "", c)
-                                       gsub(/ [ \t]+/, " ", c)
-                                       #print a[i]
-                                       # we actually want this data, so leaving this off for now
-#                                      if (c ~ /\<flash\>|\<pendrive\>|memory stick|memory card/) {
-#                                              continue
-#                                      }
-                                       print c
-                               }
-                       }
-               }' $FILE_SCSI ) )
-               log_function_data 'cat' "$FILE_SCSI"
-       fi
-       IFS="$ORIGINAL_IFS"
-
-       ## then we'll loop through that array looking for matches.
-       if [[ -n $( grep -E 'sd[a-z]' <<< ${A_HDD_DATA[@]} ) ]];then
-               # first pack the main ls variable so we don't have to keep using ls /dev...
-               # not all systems have /dev/disk/by-id
-               ls_disk_by_id="$( ls -l /dev/disk/by-id 2>/dev/null )"
-               for (( i=0; i < ${#A_HDD_DATA[@]} - 1; i++ ))
-               do
-                       if [[ -n $( grep -E '^sd[a-z]' <<< ${A_HDD_DATA[$i]} ) ]];then
-                               IFS=","
-                               a_temp_working=( ${A_HDD_DATA[$i]} )
-                               IFS="$ORIGINAL_IFS"
-                               # /sys/block/[sda,hda]/device/model
-                               # this is handles the new /sys data types first
-                               if [[ -e /sys/block/${a_temp_working[0]}/device/model ]];then
-                                       temp_name="$( remove_erroneous_chars /sys/block/${a_temp_working[0]}/device/model )"
-                                       temp_name=$( tr ' ' '_' <<< $temp_name | cut -d '-' -f 1 )
-                               elif [[ ${#a_temp_scsi[@]} -gt 0 ]];then
-                                       for (( j=0; j < ${#a_temp_scsi[@]}; j++ ))
-                                       do
-                                               ## ok, ok, it's incomprehensible, search /dev/disk/by-id for a line that contains the
-                                               # discovered disk name AND ends with the correct identifier, sdx
-                                               # get rid of whitespace for some drive names and ids, and extra data after - in name
-                                               temp_name=$( tr ' ' '_' <<< ${a_temp_scsi[$j]} | cut -d '-' -f 1 )
-                                               sd_ls_by_id=$( grep -Em1 ".*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" )
-
-                                               if [[ -n $sd_ls_by_id ]];then
-                                                       temp_name=${a_temp_scsi[$j]}
-                                                       break
-                                               else
-                                                       # test to see if we can get a better name output when null
-                                                       if [[ -n $temp_name ]];then
-                                                               temp_name=$temp_name
-                                                       fi
-                                               fi
-                                       done
-                               fi
-                               
-                               if [[ -z $temp_name ]];then
-                                       temp_name="Name n/a"
-                               else 
-                                       usb_exists=$( grep -Em1 "usb-.*$temp_name.*${a_temp_working[0]}$" <<< "$ls_disk_by_id" )
-                                       if [[ -n $usb_exists ]];then
-                                               a_temp_working[3]='USB'
-                                       fi
-                               fi
-                               a_temp_working[2]=$temp_name
-                               # these loops are to easily extend the cpu array created in the gawk script above with more fields per cpu.
-                               for (( j=0; j < ${#a_temp_working[@]}; j++ ))
-                               do
-                                       if [[ $j -gt 0 ]];then
-                                               A_HDD_DATA[i]="${A_HDD_DATA[i]},${a_temp_working[$j]}"
-                                       else
-                                               A_HDD_DATA[i]="${a_temp_working[$j]}"
-                                       fi
-                               done
-                       fi
-               done
-               unset ls_disk_by_id # and then let's dump the data we don't need
-       fi
-       temp_array=${A_HDD_DATA[@]}
-       log_function_data "A_HDD_DATA: $temp_array"
-       eval $LOGFE
-}
-
-# args: $1 - which drive to get serial number of
-get_hdd_serial_number()
+## SystemDebugger
 {
-       eval $LOGFS
-       
-       local hdd_serial=''
-       
-       get_partition_dev_data 'id'
-       
-       # lrwxrwxrwx 1 root root  9 Apr 26 09:32 scsi-SATA_ST3160827AS_5MT2HMH6 -> ../../sdc
-       # exit on the first instance
-       hdd_serial=$( gawk '
-       /'$1'$/ {
-               serial=gensub( /^(.+)_([^_]+)$/, "\\2", 1, $9 )
-               print serial
-               exit
-       }' <<< "$DEV_DISK_ID" )
-       
-       echo $hdd_serial
-       log_function_data "hdd serial: $hdd_serial"
-       eval $LOGFE
+package SystemDebugger;
+
+# use File::Find q(find);
+#no warnings 'File::Find';
+# use File::Spec::Functions;
+#use File::Copy;
+#use POSIX qw(strftime);
+
+my $option = 'main';
+my ($data_dir,$debug_dir,$debug_gz,$parse_src,$upload) = ('','','','','');
+my @content = (); 
+my $b_debug = 0;
+my $b_delete_dir = 1;
+# args: 1 - type
+# args: 2 - upload
+sub new {
+       my $class = shift;
+       ($option) = @_;
+       my $self = {};
+       # print "$f\n";
+       # print "$option\n";
+       return bless $self, $class;
 }
 
-# a few notes, normally hddtemp requires root, but you can set user rights in /etc/sudoers.
-# args: $1 - /dev/<disk> to be tested for
-get_hdd_temp_data()
-{
-       eval $LOGFS
-       local hdd_temp='' sudo_command='' 
-
-       if [[ $B_HDDTEMP_TESTED != 'true' ]];then
-               B_HDDTEMP_TESTED='true'
-               HDDTEMP_PATH=$( type -p hddtemp )
-       fi
-       if [[ $B_SUDO_TESTED != 'true' ]];then
-               B_SUDO_TESTED='true'
-               SUDO_PATH=$( type -p sudo )
-       fi
+sub run_debugger {
+       require File::Copy;
+       import File::Copy;
+       require File::Spec::Functions;
+       import File::Spec::Functions;
        
-       if [[ -n $HDDTEMP_PATH && -n $1 ]];then
-               # only use sudo if not root, -n option requires sudo -V 1.7 or greater. sudo will just error out
-               # which is the safest course here for now, otherwise that interactive sudo password thing is too annoying
-               # important: -n makes it non interactive, no prompt for password
-               if [[ $B_ROOT != 'true' && -n $SUDO_PATH ]];then
-                       sudo_command='sudo -n '
-               fi
-               # this will fail if regular user and no sudo present, but that's fine, it will just return null
-               hdd_temp=$( eval $sudo_command $HDDTEMP_PATH -nq -u C $1 )
-               if [[ -n $hdd_temp && -n $( grep -E '^([0-9\.]+)$' <<< $hdd_temp ) ]];then
-                       echo $hdd_temp
-               fi
-       fi
-       eval $LOGFE
+       print "Starting $self_name debugging data collector...\n";
+       create_debug_directory();
+       print "Note: for dmidecode data you must be root.\n" if !$b_root;
+       print $line3;
+       if (!$b_debug){
+               audio_data();
+               disk_data();
+               display_data();
+               network_data();
+               perl_modules();
+               system_data();
+       }
+       system_files();
+       print $line3;
+       if (!$b_debug){
+               if ( -d '/sys' && main::count_dir_files('/sys') ){
+                       build_tree('sys');
+                       sys_traverse_data();
+               }
+               else {
+                       print "Skipping /sys data collection. /sys not present, or empty.\n";
+               }
+               print $line3;
+               # note: proc has some files that are apparently kernel processes, I've tried 
+               # filtering them out but more keep appearing, so only run proc debugger if not root
+               if ( (!$b_root || $b_proc_debug ) && -d '/proc' && main::count_dir_files('/proc') ){
+                       build_tree('proc');
+                       proc_traverse_data();
+               }
+               else {
+                       print "Skipping /proc data collection. /proc not present, or empty.\n";
+               }
+               print $line3;
+       }
+       run_self();
+       print $line3;
+       compress_dir();
 }
 
-get_init_data()
-{
-       eval $LOGFS
-       
-       local init_type='' init_version='' rc_type='' rc_version='' temp_array=''
-       local ls_run='' strings_init_version=''
-       local runlevel=$( get_runlevel_data )
-       local default_runlevel=$( get_runlevel_default )
-       
-       # this test is pretty solid, if pid 1 is owned by systemd, it is systemd
-       # otherwise that is 'init', which covers the rest of the init systems, I think anyway.
-       # more data may be needed for other init systems.
-       if [[ -e /proc/1/comm && -n $( grep -s 'systemd' /proc/1/comm ) ]];then
-               init_type='systemd'
-               if [[ -n $( type -p systemd ) ]];then
-                       init_version=$( get_de_app_version 'systemd' '^systemd' '2' )
-               fi
-               if [[ -z $init_version && -n $( type -p systemctl ) ]];then
-                       init_version=$( get_de_app_version 'systemctl' '^systemd' '2' )
-               fi
-       else
-               ls_run=$(ls /run)
-               # note: upstart-file-bridge.pid upstart-socket-bridge.pid upstart-udev-bridge.pid
-               if [[ -n $( /sbin/init --version 2>/dev/null | grep 'upstart' ) ]];then
-                       init_type='Upstart'
-                       # /sbin/init --version == init (upstart 1.12.1)
-                       init_version=$( get_de_app_version 'init' 'upstart' '3' )
-               elif [[ -n $( type -p epoch ) ]];then
-                       init_type='Epoch'
-                       # epoch version == Epoch Init System 1.0.1 "Sage"
-                       init_version=$( get_de_app_version 'epoch' '^Epoch' '4' )
-               # missing data: 
-               # http://smarden.org/runit/sv.8.html
-               elif [[ -e /sbin/runit-init || -e /etc/runit || -n $( type -p sv ) ]];then
-                       init_type='runit' # lower case
-                       # no data on version yet
-               elif [[ -f /etc/inittab ]];then
-                       init_type='SysVinit'
-                       if [[ -n $( type -p strings ) ]];then
-                               strings_init_version="$( strings /sbin/init | grep -E 'version[[:space:]]+[0-9]' )"
-                       fi
-                       if [[ -n $strings_init_version ]];then
-                               init_version=$( gawk '{print $2}' <<< "$strings_init_version" )
-                       fi
-               # freebsd at least
-               elif [[ -f /etc/ttys ]];then
-                       init_type='init (bsd)'
-               fi
-               
-               if [[ -n $( grep 'openrc' <<< "$ls_run" ) ]];then
-                       rc_type='OpenRC'
-                       # /sbin/openrc --version == openrc (OpenRC) 0.13
-                       if [[ -n $( type -p openrc ) ]];then
-                               rc_version=$( get_de_app_version 'openrc' '^openrc' '3' )
-                       # /sbin/rc --version == rc (OpenRC) 0.11.8 (Gentoo Linux)
-                       elif [[ -n $( type -p rc ) ]];then
-                               rc_version=$( get_de_app_version 'rc' '^rc' '3' )
-                       fi
-               ## assume sysvrc, but this data is too buggy and weird and inconsistent to have meaning
-               # leaving this off for now
-#              elif [[ -f /etc/inittab ]];then
-#                      rc_type='SysVrc'
-#                      # this is a guess that rc and init are same versions, may need updates / fixes
-#                      rc_version=$init_version
-               fi
-       fi
-       
-       IFS=$'\n'
+sub create_debug_directory {
+       my $host = main::get_hostname();
+       $host =~ s/ /-/g;
+       $host = 'no-host' if !$host || $host eq 'N/A';
+       my ($alt_string,$bsd_string,$root_string) = ('','','');
+       # note: Time::Piece was introduced in perl 5.9.5
+       my ($sec,$min,$hour,$mday,$mon,$year) = localtime;
+       $year = $year+1900;
+       $mon += 1;
+       if (length($sec)  == 1) {$sec = "0$sec";}
+       if (length($min)  == 1) {$min = "0$min";}
+       if (length($hour) == 1) {$hour = "0$hour";}
+       if (length($mon)  == 1) {$mon = "0$mon";}
+       if (length($mday) == 1) {$mday = "0$mday";}
        
-       A_INIT_DATA=( 
-       "$init_type"
-       "$init_version"
-       "$rc_type"
-       "$rc_version"
-       "$runlevel"
-       "$default_runlevel" )
-       
-       IFS="$ORIGINAL_IFS"
-       
-       temp_array=${A_INIT_DATA[@]}
-       log_function_data "A_INIT_DATA: $temp_array"
-       
-       eval $LOGFE
+       my $today = "$year-$mon-${mday}_$hour$min$sec";
+       # my $date = strftime "-%Y-%m-%d_", localtime;
+       if ($b_root){
+               $root_string = '-root';
+       }
+       $bsd_string = "-BSD-$bsd_type" if $bsd_type;
+       if ($b_arm ){$alt_string = '-ARM'}
+       elsif ($b_mips) {$alt_string = '-MIPS'}
+       $debug_dir = "$self_name$alt_string$bsd_string-$host-$today$root_string-$self_version";
+       $debug_gz = "$debug_dir.tar.gz";
+       $data_dir = "$user_data_dir/$debug_dir";
+       if ( -d $data_dir ){
+               unlink $data_dir or main::error_handler('remove', "$data_dir", "$!");
+       }
+       mkdir $data_dir or main::error_handler('mkdir', "$data_dir", "$!");
+       if ( -e "$user_data_dir/$debug_gz" ){
+               #rmdir "$user_data_dir$debug_gz" or main::error_handler('remove', "$user_data_dir/$debug_gz", "$!");
+               print "Failed removing leftover directory:\n$user_data_dir$debug_gz error: $?" if system('rm','-rf',"$user_data_dir$debug_gz");
+       }
+       print "Data going into:\n$data_dir\n";
 }
-
-get_kernel_version()
-{
-       eval $LOGFS
-       
-       local kernel_version='' ksplice_kernel_version=''
-       
-       kernel_version=$( uname -rm )
-       
-       if [[ -n $( type -p uptrack-uname ) && -n $kernel_version ]];then
-               ksplice_kernel_version=$( uptrack-uname -rm )
-               if [[ $kernel_version != $ksplice_kernel_version ]];then
-                       kernel_version="$ksplice_kernel_version (ksplice)"
-               fi
-       fi
-       log_function_data "kernel_version: $kernel_version - ksplice_kernel_version: $ksplice_kernel_version"
-       
-       echo $kernel_version
-       
-       eval $LOGFE
+sub compress_dir {
+       print "Creating tar.gz compressed file of this material...\n";
+       print "File: $debug_gz\n";
+       system("cd $user_data_dir; tar -czf $debug_gz $debug_dir");
+       print "Removing $data_dir...\n";
+       #rmdir $data_dir or print "failed removing: $data_dir error: $!\n";
+       return 1 if !$b_delete_dir;
+       if (system('rm','-rf',$data_dir) ){
+               print "Failed removing: $data_dir\nError: $?\n";
+       }
+       else {
+               print "Directory removed.\n";
+       }
 }
-
-# args: $1 - v/n 
-get_lspci_data()
-{
-       eval $LOGFS
-       local lspci_data=''
-
-       if [[ $B_LSPCI == 'true' ]];then
-               lspci_data="$( lspci -$1 | gawk '{
-                       gsub(/\(prog-if[^)]*\)/,"")
-                       sub(/^0000:/, "", $0) # seen case where the 0000: is prepended, rare, but happens
-                       print
-               }' )"
-       fi
+# NOTE: incomplete, don't know how to ever find out 
+# what sound server is actually running, and is in control
+sub audio_data {
+       my (%data,@files,@files2);
+       print "Collecting audio data...\n";
+       my @cmds = (
+       ['aplay', '-l'], # alsa
+       ['pactl', 'list'], # pulseaudio
+       );
+       run_commands(\@cmds,'audio');
+       @files = main::globber('/proc/asound/card*/codec*');
+       if (@files){
+               my $asound = qx(head -n 1 /proc/asound/card*/codec* 2>&1);
+               $data{'proc-asound-codecs'} = $asound;
+       }
+       else {
+               $data{'proc-asound-codecs'} = undef;
+       }
        
-       echo "$lspci_data"
-       log_function_data 'raw' "lspci_data $1:\n$lspci_data"
-       eval $LOGFE
+       write_data(\%data,'audio');
+       @files = (
+       '/proc/asound/cards',
+       '/proc/asound/version',
+       );
+       @files2 = main::globber('/proc/asound/*/usbid');
+       @files = (@files,@files2) if @files2;
+       copy_files(\@files,'audio');
 }
-
-# args: $1 - busid
-get_lspci_chip_id()
-{
-       eval $LOGFS
-       
-       local chip_id=''
-       
-       chip_id=$( gawk '
-       /^'$1'/ {
-               if ( $3 != "" ) {
-                       print $3
-               }
-       }' <<< "$Lspci_n_Data" )
-       
-       echo $chip_id
-       
-       eval $LOGFE
+## NOTE: >/dev/null 2>&1 is sh, and &>/dev/null is bash, fix this
+# ls -w 1 /sysrs > tester 2>&1
+sub disk_data {
+       my (%data,@files,@files2);
+       print "Collecting dev, label, disk, uuid data, df...\n";
+       @files = (
+       '/etc/fstab',
+       '/etc/mtab',
+       '/proc/mdstat',
+       '/proc/mounts',
+       '/proc/partitions',
+       '/proc/scsi/scsi',
+       '/proc/sys/dev/cdrom/info',
+       );
+       # very old systems
+       if (-d '/proc/ide/'){
+               my @ides = main::globber('/proc/ide/*/*');
+               @files = (@files, @ides) if @ides;
+       }
+       else {
+               push (@files, '/proc-ide-directory');
+       }
+       copy_files(\@files, 'disk');
+       my @cmds = (
+       ['btrfs', 'filesystem show'],
+       ['btrfs', 'filesystem show --mounted'],
+       # ['btrfs', 'filesystem show --all-devices'],
+       ['df', '-h -T'],
+       ['df', '-h'],
+       ['df', '-k'],
+       ['df', '-k -T'],
+       ['df', '-k -T -P'],
+       ['df', '-P'],
+       ['lsblk', '-fs'],
+       ['lsblk', '-fsr'],
+       ['lsblk', '-fsP'],
+       ['lsblk', '-a'],
+       ['lsblk', '-aP'],
+       ['lsblk', '-ar'],
+       ['lsblk', '-p'],
+       ['lsblk', '-pr'],
+       ['lsblk', '-pP'],
+       ['lsblk', '-r'],
+       ['lsblk', '-r --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,MOUNTPOINT'],
+       ['lsblk', '-rb --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,MOUNTPOINT'],
+       ['lsblk', '-Pb --output NAME,PKNAME,TYPE,RM,FSTYPE,SIZE'],
+       ['lsblk', '-Pb --output NAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,SERIAL,MOUNTPOINT'],
+       ['gpart', 'list'],
+       ['gpart', 'show'],
+       ['gpart', 'status'],
+       ['ls', '-l /dev'],
+       ['ls', '-l /dev/disk'],
+       ['ls', '-l /dev/disk/by-id'],
+       ['ls', '-l /dev/disk/by-label'],
+       ['ls', '-l /dev/disk/by-uuid'],
+       # http://comments.gmane.org/gmane.linux.file-systems.zfs.user/2032
+       ['ls', '-l /dev/disk/by-wwn'],
+       ['ls', '-l /dev/disk/by-path'],
+       ['ls', '-l /dev/mapper'],
+       # LSI raid https://hwraid.le-vert.net/wiki/LSIMegaRAIDSAS
+       ['megacli', '-AdpAllInfo -aAll'],
+       ['megacli', '-LDInfo -L0 -a0'],
+       ['megacli', '-PDList -a0'],
+       ['megaclisas-status', ''],
+       ['megaraidsas-status', ''],
+       ['megasasctl', ''],
+       ['mount', ''],
+       ['nvme', 'present'],
+       ['readlink', '/dev/root'],
+       ['swapon', '-s'],
+       # 3ware-raid
+       ['tw-cli', 'info'],
+       ['zfs', 'list'],
+       ['zpool', 'list'],
+       ['zpool', 'list -v'],
+       );
+       run_commands(\@cmds,'disk');
+       @cmds = (
+       ['atacontrol', 'list'],
+       ['camcontrol', 'devlist'], 
+       ['glabel', 'status'], 
+       ['swapctl', '-l -k'],
+       ['swapctl', '-l -k'],
+       ['vmstat', '-H'],
+       );
+       run_commands(\@cmds,'disk-bsd');
 }
-
-get_machine_data()
-{
-       eval $LOGFS
-       local temp_array='' separator='' id_file='' file_data='' array_string=''
-       local id_dir='/sys/class/dmi/id/' dmi_data='' 
-       local machine_files="
-       sys_vendor product_name product_version product_serial product_uuid 
-       board_vendor board_name board_version board_serial 
-       bios_vendor bios_version bios_date 
-       "
-
-       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-               machine_files="$machine_files
-               chassis_vendor chassis_type chassis_version chassis_serial
-               "
-       fi
-       if [[ -d $id_dir ]];then
-               for id_file in $machine_files
-               do
-                       file_data=''
-                       if [[ -r $id_dir$id_file ]];then
-                               file_data=$( gawk '
-                               BEGIN {
-                                       IGNORECASE=1
-                               }
-                               {
-                                       gsub(/'"$BAN_LIST_NORMAL"'/, "", $0)
-                                       gsub(/,/, " ", $0)
-                                       # yes, there is a typo in a user data set, unknow
-                                       # Base Board Version|Base Board Serial Number
-                                       # Chassis Manufacturer|Chassis Version|Chassis Serial Number
-                                       # System manufacturer|System Product Name|System Version
-                                       # To Be Filled By O.E.M.
-                                       sub(/^Base Board .*|^Chassis .*|.*O\.E\.M\..*|.*OEM.*|^Not .*|^System .*|.*unknow.*|.*N\/A.*|none|^To be filled.*/, "", $0) 
-                                       gsub(/bios|acpi/, "", $0)
-                                       sub(/http:\/\/www.abit.com.tw\//, "Abit", $0)
-                                       gsub(/^ +| +$/, "", $0)
-                                       gsub(/ [ \t]+/, " ", $0)
-                                       print $0
-                               }' < $id_dir$id_file )
-                       fi
-                       array_string="$array_string$separator$file_data"
-                       separator=','
-               done
-       else
-               get_dmidecode_data
-               if [[ -n $DMIDECODE_DATA ]];then
-                       if [[ $B_ROOT == 'true' ]];then
-                               # this handles very old systems, like Lenny 2.6.26, with dmidecode, but no data
-                               if [[ -n $( grep -i 'no smbios ' <<< "$DMIDECODE_DATA" ) ]];then
-                                       array_string='dmidecode-no-smbios-dmi-data'
-                               # please note: only dmidecode version 2.11 or newer supports consistently the -s flag
-                               else
-                                       array_string=$( gawk -F ':' '
-                                       BEGIN {
-                                               IGNORECASE=1
-                                               baseboardManufacturer=""
-                                               baseboardProductName=""
-                                               baseboardSerialNumber=""
-                                               baseboardVersion=""
-                                               biosReleaseDate=""
-                                               biosRevision="" # only available from dmidecode
-                                               biosRomSize="" # only available from dmidecode
-                                               biosVendor=""
-                                               biosVersion=""
-                                               chassisManufacturer=""
-                                               chassisSerialNumber=""
-                                               chassisType=""
-                                               chassisVersion=""
-                                               systemManufacturer=""
-                                               systemProductName=""
-                                               systemVersion=""
-                                               systemSerialNumber=""
-                                               systemUuid=""
-                                               bItemFound="" # we will only output if at least one item was found
-                                               fullString=""
-                                               testString=""
-                                               bSys=""
-                                               bCha=""
-                                               bBio=""
-                                               bBas=""
-                                       }
-                                       /^Bios Information/ {
-                                               while ( getline && !/^$/ ) {
-                                                       if ( $1 ~ /^Release Date/ ) { biosReleaseDate=$2 }
-                                                       if ( $1 ~ /^BIOS Revision/ ) { biosRevision=$2 }
-                                                       if ( $1 ~ /^ROM Size/ ) { biosRomSize=$2 }
-                                                       if ( $1 ~ /^Vendor/ ) { biosVendor=$2 }
-                                                       if ( $1 ~ /^Version/ ) { biosVersion=$2 }
-                                               }
-                                               testString=biosReleaseDate biosRevision biosRomSize biosVendor biosVersion
-                                               if ( testString != ""  ) {
-                                                       bItemFound="true"
-                                               }
-                                               bBio="true"
-                                       }
-                                       /^Base Board Information/ {
-                                               while ( getline && !/^$/ ) {
-                                                       if ( $1 ~ /^Manufacturer/ ) { baseboardManufacturer=$2 }
-                                                       if ( $1 ~ /^Product Name/ ) { baseboardProductName=$2 }
-                                                       if ( $1 ~ /^Serial Number/ ) { baseboardSerialNumber=$2 }
-                                               }
-                                               testString=baseboardManufacturer baseboardProductName baseboardSerialNumber
-                                               if ( testString != ""  ) {
-                                                       bItemFound="true"
-                                               }
-                                               bBas="true"
-                                       }
-                                       /^Chassis Information/ {
-                                               while ( getline && !/^$/ ) {
-                                                       if ( $1 ~ /^Manufacturer/ ) { chassisManufacturer=$2 }
-                                                       if ( $1 ~ /^Serial Number/ ) { chassisSerialNumber=$2 }
-                                                       if ( $1 ~ /^Type/ ) { chassisType=$2 }
-                                                       if ( $1 ~ /^Version/ ) { chassisVersion=$2 }
-                                               }
-                                               testString=chassisManufacturer chassisSerialNumber chassisType chassisVersion
-                                               if ( testString != ""  ) {
-                                                       bItemFound="true"
-                                               }
-                                               bCha="true"
-                                       }
-                                       /^System Information/ {
-                                               while ( getline && !/^$/ ) {
-                                                       if ( $1 ~ /^Manufacturer/ ) { systemManufacturer=$2 }
-                                                       if ( $1 ~ /^Product Name/ ) { systemProductName=$2 }
-                                                       if ( $1 ~ /^Version/ ) { systemVersion=$2 }
-                                                       if ( $1 ~ /^Serial Number/ ) { systemSerialNumber=$2 }
-                                                       if ( $1 ~ /^UUID/ ) { systemUuid=$2 }
-                                               }
-                                               testString=systemManufacturer systemProductName systemVersion systemSerialNumber systemUuid
-                                               if ( testString != ""  ) {
-                                                       bItemFound="true"
-                                               }
-                                               bSys="true"
-                                       }
-                                       ( bSys == "true" && bCha="true" && bBio == "true" && bBas == "true" ) {
-                                               exit # stop the loop
-                                       }
-                                       END {
-                                               if ( bItemFound == "true" ) {
-                                                       fullString = systemManufacturer "," systemProductName "," systemVersion "," systemSerialNumber 
-                                                       fullString = fullString "," systemUuid "," baseboardManufacturer "," baseboardProductName 
-                                                       fullString = fullString "," baseboardVersion "," baseboardSerialNumber "," biosVendor
-                                                       fullString = fullString "," biosVersion "," biosReleaseDate "," chassisManufacturer
-                                                       fullString = fullString "," chassisType "," chassisVersion "," chassisSerialNumber 
-                                                       fullString = fullString ","  biosRevision "," biosRomSize
-                                                       
-                                                       print fullString
-                                               }
-                                       }' <<< "$DMIDECODE_DATA" )
-                               fi
-                       else
-                               array_string='dmidecode-non-root-user'
-                       fi
-               fi
-       fi
-       IFS=','
-       A_MACHINE_DATA=( $array_string )
-       IFS="$ORIGINAL_IFS"
-       temp_array=${A_MACHINE_DATA[@]}
-#      echo ${temp_array[@]}
-       log_function_data "A_MACHINE_DATA: $temp_array"
-       eval $LOGFE
-}
-# B_ROOT='true';get_machine_data;exit
-## return memory used/installed
-get_memory_data()
-{
-       eval $LOGFS
-       local memory='' memory_full='' 
-       if [[ $B_MEMINFO_FILE == 'true' ]];then
-               memory=$( gawk '
-               /^MemTotal:/ {
-                       tot = $2
-               }
-               /^(MemFree|Buffers|Cached):/ {
-                       notused+=$2
-               }
-               END {
-                       used = tot - notused
-                       printf("%.1f/%.1fMB\n", used/1024, tot/1024)
-               }' $FILE_MEMINFO )
-               log_function_data 'cat' "$FILE_MEMINFO"
-       elif [[ $B_SYSCTL == 'true' && -n $Sysctl_a_Data ]];then
-               local gawk_fs=': '
-       
-               if [[ $BSD_VERSION == 'openbsd' ]];then
-                       gawk_fs='='
-               fi
-               memory=$( grep -i 'mem' <<< "$Sysctl_a_Data" | gawk -F "$gawk_fs"  '
-               BEGIN {
-                       realMemory=""
-                       freeMemory=""
-               }
-               # freebsd seems to use bytes here
-               /^hw.physmem/ {
-                       gsub(/^[^0-9]+|[^0-9]+$/,"",$2)
-                       realMemory = $2/1024
-                       if ( freeMemory != "" ) {
-                               exit
-                       }
-               }
-               # But, it uses K here. Openbsd does not seem to have this item
-               # this can be either: Free Memory OR Free Memory Pages
-               $1 ~ /^Free Memory/ {
-                       gsub(/[^0-9]/,"",$NF)
-                       freeMemory = $NF
-                       if ( realMemory != "" ) {
-                               exit
-                       }
-               }
-               END {
-                       # hack: temp fix for openbsd: in case no free mem was detected but we have physmem
-                       if ( freeMemory == "" && realMemory != "" ) {
-                               printf("NA/%.1fMB\n", realMemory/1024)
-                       }
-                       else if ( freeMemory != "" && realMemory != "" ) {
-                               used = realMemory - freeMemory
-                               printf("%.1f/%.1fMB\n", used/1024, realMemory/1024)
-                       }
-               }' )
-       fi
-       echo "$memory"
-       log_function_data "memory: $memory"
-       eval $LOGFE
-}
-
-# process and return module version data
-get_module_version_number()
-{
-       eval $LOGFS
-       local module_version=''
-       
-       if [[ $B_MODINFO_TESTED != 'true' ]];then
-               B_MODINFO_TESTED='true'
-               MODINFO_PATH=$( type -p modinfo )
-       fi
-
-       if [[ -n $MODINFO_PATH ]];then
-               module_version=$( $MODINFO_PATH $1 2>/dev/null | gawk '
-               BEGIN {
-                       IGNORECASE=1
+sub display_data {
+       my (%data,@files,@files2);
+       my $working = '';
+       if ( ! $b_display ){
+               print "Warning: only some of the data collection can occur if you are not in X\n";
+               main::toucher("$data_dir/display-data-warning-user-not-in-x");
+       }
+       if ( $b_root ){
+               print "Warning: only some of the data collection can occur if you are running as Root user\n";
+               main::toucher("$data_dir/display-data-warning-root-user");
+       }
+       print "Collecting Xorg log and xorg.conf files...\n";
+       if ( -d "/etc/X11/xorg.conf.d/" ){
+               @files = main::globber("/etc/X11/xorg.conf.d/*");
+       }
+       else {
+               @files = ('/xorg-conf-d');
+       }
+       push (@files, $files{'xorg-log'});
+       push (@files, '/etc/X11/xorg.conf');
+       copy_files(\@files,'display-xorg');
+       print "Collecting X, xprop, glxinfo, xrandr, xdpyinfo data, wayland, weston...\n";
+       %data = (
+       'desktop-session' => $ENV{'DESKTOP_SESSION'},
+       'gdmsession' => $ENV{'GDMSESSION'},
+       'gnome-desktop-session-id' => $ENV{'GNOME_DESKTOP_SESSION_ID'},
+       'kde-full-session' => $ENV{'KDE_FULL_SESSION'},
+       'kde-session-version' => $ENV{'KDE_SESSION_VERSION'},
+       'vdpau-driver' => $ENV{'VDPAU_DRIVER'},
+       'xdg-current-desktop' => $ENV{'XDG_CURRENT_DESKTOP'},
+       'xdg-session-desktop' => $ENV{'XDG_SESSION_DESKTOP'},
+       'xdg-vtnr' => $ENV{'XDG_VTNR'},
+       # wayland data collectors:
+       'xdg-session-type' => $ENV{'XDG_SESSION_TYPE'},
+       'wayland-display' =>  $ENV{'WAYLAND_DISPLAY'},
+       'gdk-backend' => $ENV{'GDK_BACKEND'},
+       'qt-qpa-platform' => $ENV{'QT_QPA_PLATFORM'},
+       'clutter-backend' => $ENV{'CLUTTER_BACKEND'},
+       'sdl-videodriver' => $ENV{'SDL_VIDEODRIVER'},
+       # program display values
+       'size-indent' => $size{'indent'},
+       'size-indent-min' => $size{'indent-min'},
+       'size-cols-max' => $size{'max'},
+       );
+       write_data(\%data,'display');
+       my @cmds = (
+       # kde 5/plasma desktop 5, this is maybe an extra package and won't be used
+       ['about-distro',''],
+       ['aticonfig','--adapter=all --od-gettemperature'],
+       ['glxinfo',''],
+       ['glxinfo','-B'],
+       ['kded','--version'],
+       ['kded1','--version'],
+       ['kded2','--version'],
+       ['kded3','--version'],
+       ['kded4','--version'],
+       ['kded5','--version'],
+       ['kded6','--version'],
+       ['kf4-config','--version'],
+       ['kf5-config','--version'],
+       ['kf6-config','--version'],
+       ['kwin_x11','--version'],
+       ['loginctl','--no-pager list-sessions'],
+       ['nvidia-settings','-q screens'],
+       ['nvidia-settings','-c :0.0 -q all'],
+       ['nvidia-smi','-q'],
+       ['nvidia-smi','-q -x'],
+       ['plasmashell','--version'],
+       ['vainfo',''],
+       ['vdpauinfo',''],
+       ['weston-info',''], 
+       ['wmctrl','-m'],
+       ['weston','--version'],
+       ['xdpyinfo',''],
+       ['Xorg','-version'],
+       ['xprop','-root'],
+       ['xrandr',''],
+       );
+       run_commands(\@cmds,'display');
+}
+sub network_data {
+       print "Collecting networking data...\n";
+#      no warnings 'uninitialized';
+       my @cmds = (
+       ['ifconfig',''],
+       ['ip','addr'],
+       ['ip','-s link'],
+       );
+       run_commands(\@cmds,'network');
+}
+sub perl_modules {
+       print "Collecting Perl module data (this can take a while)...\n";
+       my @modules = ();
+       my ($dirname,$holder,$mods,$value) = ('','','','');
+       my $filename = 'perl-modules.txt';
+       my @inc;
+       foreach (sort @INC){
+               # some BSD installs have '.' n @INC path
+               if (-d $_ && $_ ne '.'){
+                       $_ =~ s/\/$//; # just in case, trim off trailing slash
+                       $value .= "EXISTS: $_\n";
+                       push @inc, $_;
+               } 
+               else {
+                       $value .= "ABSENT: $_\n";
+               }
+       }
+       main::writer("$data_dir/perl-inc-data.txt",$value);
+       File::Find::find { wanted => sub { 
+               push @modules, File::Spec->canonpath($_) if /\.pm\z/  
+       }, no_chdir => 1 }, @inc;
+       @modules = sort(@modules);
+       foreach (@modules){
+               my $dir = $_;
+               $dir =~ s/[^\/]+$//;
+               if (!$holder || $holder ne $dir ){
+                       $holder = $dir;
+                       $value = "DIR: $dir\n";
+                       $_ =~ s/^$dir//;
+                       $value .= " $_\n";
                }
-               /^version/ {
-                       gsub(/,/, " ", $2)
-                       gsub(/^ +| +$/, "", $2)
-                       gsub(/ [ \t]+/, " ", $2)
-                       print $2
+               else {
+                       $value = $_;
+                       $value =~ s/^$dir//;
+                       $value = " $value\n";
                }
-               ' )
-       fi
-
-       echo "$module_version"
-       log_function_data "module_version: $module_version"
-       eval $LOGFE
+               $mods .= $value;
+       }
+       open (my $fh, '>', "$data_dir/$filename");
+       print $fh $mods;
+       close $fh;
 }
-
-## create array of network cards
-get_networking_data()
-{
-       eval $LOGFS
-       
-       local B_USB_NETWORKING='false' temp_array=''
-       
-       IFS=$'\n'
-       A_NETWORK_DATA=( $( 
-       echo "$Lspci_v_Data" | gawk '
-       BEGIN {
-               IGNORECASE=1
-               counter=0 # required to handle cases of > 1 instance of the same chipset
-       }
-       /^[0-9a-f:\.]+ (ethernet|network) (controller|bridge)/ || /^[0-9a-f:\.]+ [^:]+: .*(ethernet|network).*$/ {
-               nic=gensub(/^[0-9a-f:\.]+ [^:]+: (.+)$/,"\\1","g",$0)
-               #gsub(/realtek semiconductor/, "Realtek", nic)
-               #gsub(/davicom semiconductor/, "Davicom", nic)
-               # The doublequotes are necessary because of the pipes in the variable.
-               gsub(/'"$BAN_LIST_NORMAL"'/, "", nic)
-               gsub(/,/, " ", nic)
-               gsub(/^ +| +$/, "", nic)
-               gsub(/ [ \t]+/, " ", nic)
-               # construct a unique string ending for each chipset detected, this allows for
-               # multiple instances of the same exact chipsets, ie, dual gigabit 
-               nic = nic "~~" counter++
-               aPciBusId[nic] = gensub(/(^[0-9a-f:\.]+) [^:]+: .+$/,"\\1","g",$0)
-               # I do not understand why incrementing a string index makes sense? 
-               eth[nic]++ 
-               while ( getline && !/^$/ ) {
-                       gsub(/,/, "", $0)
-                       if ( /I\/O/ ) {
-                               ports[nic] = ports[nic] $4 " "
-                       }
-                       if ( /driver in use/ ) {
-                               drivers[nic] = drivers[nic] gensub( /(.*): (.*)/ ,"\\2" ,"g" ,$0 ) ""
-                       }
-                       else if ( /kernel modules/ ) {
-                               modules[nic] = modules[nic] gensub( /(.*): (.*)/ ,"\\2" ,"g" ,$0 ) ""
-                       }
-               }
-       }
-
-       END {
-               j=0
-               for (i in eth) {
-                       useDrivers=""
-                       usePorts=""
-                       useModules=""
-                       usePciBusId=""
-
-                       if ( eth[i] > 1 ) {
-                               a[j] = eth[i] "x " i
-                       }
-                       else {
-                               a[j] = i
-                       }       
-                       ## note: this loses the plural ports case, is it needed anyway?
-                       if ( ports[i] != "" ) {
-                               usePorts = ports[i]
-                       }
-                       if ( drivers[i] != "" ) {
-                               useDrivers = drivers[i]
-                       }
-                       if ( modules[i] != "" ) {
-                               useModules = modules[i]
-                       }
-                       if ( aPciBusId[i] != "" ) {
-                               usePciBusId = aPciBusId[i]
-                       }
-                       # create array primary item for master array
-                       # and strip out the counter again, this handled dual cards with same chipset
-                       sub( /~~[0-9]+$/, "", a[j] )
-                       sub( / $/, "", usePorts ) # clean off trailing whitespace
-                       print a[j] "," useDrivers "," usePorts "," useModules, "," usePciBusId
-                       j++
-               }
-       }' ) )
-       IFS="$ORIGINAL_IFS"
-       get_networking_usb_data
-       if [[ $B_SHOW_ADVANCED_NETWORK == 'true' || $B_USB_NETWORKING == 'true' ]];then
-               get_network_advanced_data
-       fi
-       temp_array=${A_NETWORK_DATA[@]}
-       log_function_data "A_NETWORK_DATA: $temp_array"
-       
-       eval $LOGFE
+sub system_data {
+       print "Collecting system data...\n";
+       my %data = (
+       'cc' => $ENV{'CC'},
+       # @(#)MIRBSD KSH R56 2018/03/09: ksh and mksh
+       'ksh-version' => system('echo -n $KSH_VERSION'), # shell, not env, variable
+       'manpath' => $ENV{'MANPATH'},
+       'path' => $ENV{'PATH'},
+       'xdg-config-home' => $ENV{'XDG_CONFIG_HOME'},
+       'xdg-config-dirs' => $ENV{'XDG_CONFIG_DIRS'},
+       'xdg-data-home' => $ENV{'XDG_DATA_HOME'},
+       'xdg-data-dirs' => $ENV{'XDG_DATA_DIRS'},
+       );
+       my @files = main::globber('/usr/bin/gcc*');
+       if (@files){
+               $data{'gcc-versions'} = join "\n",@files;
+       }
+       else {
+               $data{'gcc-versions'} = undef;
+       }
+       @files = main::globber('/sys/*');
+       if (@files){
+               $data{'sys-tree-ls-1-basic'} = join "\n", @files;
+       }
+       else {
+               $data{'sys-tree-ls-1-basic'} = undef;
+       }
+       write_data(\%data,'system');
+       # bsd tools http://cb.vu/unixtoolbox.xhtml
+       my @cmds = (
+       # general
+       ['sysctl', '-b kern.geom.conftxt'],
+       ['sysctl', '-b kern.geom.confxml'],
+       ['usbdevs','-v'],
+       # freebsd
+       ['pciconf','-l -cv'],
+       ['pciconf','-vl'],
+       ['pciconf','-l'],
+       # openbsd
+       ['pcidump',''],
+       ['pcidump','-v'],
+       # netbsd
+       ['kldstat',''],
+       ['pcictl','list'],
+       ['pcictl','list -ns'],
+       );
+       run_commands(\@cmds,'system-bsd');
+       # diskinfo -v <disk>
+       # fdisk <disk>
+       @cmds = (
+       ['clang','--version'],
+       ['dmidecode',''],
+       ['dmesg',''],
+       ['gcc','--version'],
+       ['hciconfig','-a'],
+       ['initctl','list'],
+       ['ipmi-sensors',''],
+       ['ipmi-sensors','--output-sensor-thresholds'],
+       ['ipmitool','sensor'],
+       ['lscpu',''],
+       ['lspci',''],
+       ['lspci','-k'],
+       ['lspci','-n'],
+       ['lspci','-nn'],
+       ['lspci','-nnk'],
+       ['lspci','-nnkv'],# returns ports
+       ['lspci','-nnv'],
+       ['lspci','-mm'],
+       ['lspci','-mmk'],
+       ['lspci','-mmkv'],
+       ['lspci','-mmv'],
+       ['lspci','-mmnn'],
+       ['lspci','-v'],
+       ['lsusb',''],
+       ['lsusb','-v'],
+       ['ps','aux'],
+       ['ps','-e'],
+       ['ps','-p 1'],
+       ['runlevel',''],
+       ['rc-status','-a'],
+       ['rc-status','-l'],
+       ['rc-status','-r'],
+       ['sensors',''],
+       # leaving this commented out to remind that some systems do not
+       # support strings --version, but will just simply hang at that command
+       # which you can duplicate by simply typing: strings then hitting enter.
+       # ['strings','--version'],
+       ['strings','present'],
+       ['sysctl','-a'],
+       ['systemctl','list-units'],
+       ['systemctl','list-units --type=target'],
+       ['systemd-detect-virt',''],
+       ['upower','-e'],
+       ['uptime',''],
+       ['vcgencmd','get_mem arm'],
+       ['vcgencmd','get_mem gpu'],
+       );
+       run_commands(\@cmds,'system');
+       @files = main::globber('/dev/bus/usb/*/*');
+       copy_files(\@files, 'system');
 }
-
-get_network_advanced_data()
-{
-       eval $LOGFS
-       local a_network_adv_working='' if_path='' working_path='' working_uevent_path='' dir_path=''
-       local if_id='' speed='' duplex='' mac_id='' oper_state=''  chip_id=''
-       local usb_data='' usb_vendor='' usb_product='' product_path='' driver_test=''
-       
-       for (( i=0; i < ${#A_NETWORK_DATA[@]}; i++ ))
-       do
-               IFS=","
-               a_network_adv_working=( ${A_NETWORK_DATA[i]} )
-               # reset these every go round
-               driver_test=''
-               if_id='' 
-               speed='' 
-               duplex='' 
-               mac_id='' 
-               oper_state=''
-               usb_data=''
-               chip_id=''
-               if [[ -z $( grep '^usb-' <<< ${a_network_adv_working[4]} ) ]];then
-                       # note although this may exist technically don't use it, it's a virtual path
-                       # and causes weird cat errors when there's a missing file as well as a virtual path
-                       # /sys/bus/pci/devices/0000:02:02.0/net/eth1
-                       # real paths are: /sys/devices/pci0000:00/0000:00:1e/0/0000:02:02.0/net/eth1/uevent
-                       # and on older debian kernels: /sys/devices/pci0000:00/0000:02:02.0/net:eth1/uevent
-                       # but broadcom shows this sometimes:
-                       # /sys/devices/pci0000:00/0000:00:03.0/0000:03:00.0/ssb0:0/uevent:['DRIVER=b43', 'MODALIAS=ssb:v4243id0812rev0D']:
-                       working_path="/sys/bus/pci/devices/0000:${a_network_adv_working[4]}"
-                       # now we want the real one, that xiin also displays, without symbolic links.
-                       if [[ -e $working_path ]];then
-                               working_path=$( readlink -f $working_path 2>/dev/null )
-                               # sometimes there is another directory between the path and /net
-                               if [[ ! -e $working_path/net ]];then
-                                       # using find here, probably will need to also use it in usb part since the grep
-                                       # method seems to not be working now. Slice off the rest, which leaves the basic path
-                                       working_path=$( find $working_path/*/net/*/uevent 2>/dev/null | \
-                                       sed 's|/net.*||' )
-                               fi
-                       fi
-                       # working_path=$( ls /sys/devices/pci*/*/0000:${a_network_adv_working[4]}/net/*/uevent  )
-               else
-                       # now we'll use the actual vendor:product string instead
-                       usb_data=${a_network_adv_working[10]}
-                       usb_vendor=$( cut -d ':' -f 1 <<< $usb_data )
-                       usb_product=$( cut -d ':' -f 2 <<< $usb_data )
-                       # this grep returns the path plus the contents of the file, with a colon separator, so slice that off
-                       # /sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/idVendor
-                       working_path=$( grep -s "$usb_vendor" /sys/devices/pci*/*/usb*/*/*/idVendor | \
-                       sed -e "s/idVendor:$usb_vendor//"  -e '/driver/d' )
-                       # try an alternate path if first one doesn't work
-                       # /sys/devices/pci0000:00/0000:00:0b.1/usb1/1-1/idVendor
-                       if [[ -z $working_path ]];then
-                               working_path=$( grep -s "$usb_vendor" /sys/devices/pci*/*/usb*/*/idVendor | \
-                               sed -e "s/idVendor:$usb_vendor//"  -e '/driver/d' )
-                               product_path=$( grep -s "$usb_product" /sys/devices/pci*/*/usb*/*/idProduct | \
-                               sed -e "s/idProduct:$usb_product//" -e '/driver/d' )
-                       else
-                               product_path=$( grep -s "$usb_product" /sys/devices/pci*/*/usb*/*/*/idProduct | \
-                               sed -e "s/idProduct:$usb_product//" -e '/driver/d' )
-                       fi
-                       
-                       # make sure it's the right product/vendor match here, it will almost always be but let's be sure
-                       if [[ -n $working_path && -n $product_path ]] && [[ $working_path == $product_path ]];then
-                       #if [[ -n $working_path ]];then
-                               # now ls that directory and get the numeric starting sub directory and that should be the full path
-                               # to the /net directory part
-                               dir_path=$( ls ${working_path} 2>/dev/null | grep -sE '^[0-9]' )
-                               working_uevent_path="${working_path}${dir_path}"
-                       fi
-               fi
-               # /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/uevent grep for DRIVER=
-               # /sys/devices/pci0000:00/0000:00:0b.1/usb1/1-1/1-1:1.0/uevent
-               if [[ -n $usb_data ]];then
-                       driver_test=$( grep -si 'DRIVER=' $working_uevent_path/uevent | cut -d '=' -f 2 )
-                       if [[ -n $driver_test ]];then
-                               a_network_adv_working[1]=$driver_test
-                       fi
-               fi
-               log_function_data "PRE: working_path: $working_path\nworking_uevent_path: $working_uevent_path"
-               
-               # this applies in two different cases, one, default, standard, two, for usb, this is actually
-               # the short path, minus the last longer numeric directory name, ie: 
-               # from debian squeeze 2.6.32-5-686: 
-               # /sys/devices/pci0000:00/0000:00:0b.1/usb1/1-1/net/wlan0/address
-               if [[ -e $working_path/net ]];then
-                       if_path=$( ls $working_path/net 2>/dev/null )
-                       if_id=$if_path
-                       working_path=$working_path/net/$if_path
-               # this is the normal usb detection if the first one didn't work
-               elif [[ -n $usb_data && -e $working_uevent_path/net ]];then
-                       if_path=$( ls $working_uevent_path/net 2>/dev/null )
-                       if_id=$if_path
-                       working_path=$working_uevent_path/net/$if_path
-               # 2.6.32 debian lenny kernel shows not: /net/eth0 but /net:eth0
-               else
-                       if_path=$( ls $working_path 2>/dev/null | grep 'net:' )
-                       if_id=$( cut -d ':' -f 2 <<< "$if_path" )
-                       working_path=$working_path/$if_path
-               fi
-               log_function_data "POST: working_path: $working_path\nif_path: $if_path - if_id: $if_id"
-               
-               if [[ -n $if_path ]];then
-                       if [[ -r $working_path/speed ]];then
-                               speed=$( cat $working_path/speed 2>/dev/null )
-                       fi
-                       if [[ -r $working_path/duplex ]];then
-                               duplex=$( cat $working_path/duplex 2>/dev/null )
-                       fi
-                       if [[ -r $working_path/address ]];then
-                               mac_id=$( cat $working_path/address 2>/dev/null )
-                       fi
-                       if [[ -r $working_path/operstate ]];then
-                               oper_state=$( cat $working_path/operstate 2>/dev/null )
-                       fi
-               fi
-               
-               if [[ -n ${a_network_adv_working[10]} ]];then
-                       chip_id=${a_network_adv_working[10]}
-               fi
-               A_NETWORK_DATA[i]=${a_network_adv_working[0]}","${a_network_adv_working[1]}","${a_network_adv_working[2]}","${a_network_adv_working[3]}","${a_network_adv_working[4]}","$if_id","$oper_state","$speed","$duplex","$mac_id","$chip_id
-               IFS="$ORIGINAL_IFS"
-       done
-
-       eval $LOGFE
+sub system_files {
+       print "Collecting system files data...\n";
+       my (%data,@files,@files2);
+       @files = RepoData::get($data_dir);
+       copy_files(\@files, 'repo');
+       # chdir "/etc";
+       @files = main::globber('/etc/*[-_]{[rR]elease,[vV]ersion,issue}*');
+       push (@files, '/etc/issue');
+       push (@files, '/etc/lsb-release');
+       push (@files, '/etc/os-release');
+       copy_files(\@files,'system-distro');
+       @files = main::globber('/etc/upstream[-_]{[rR]elease,[vV]ersion}/*');
+       copy_files(\@files,'system-distro');
+       @files = (
+       '/proc/1/comm',
+       '/proc/cpuinfo',
+       '/proc/meminfo',
+       '/proc/modules',
+       '/proc/net/arp',
+       '/proc/version',
+       );
+       @files2=main::globber('/sys/class/power_supply/*/uevent');
+       if (@files2){
+               @files = (@files,@files2);
+       }
+       else {
+               push (@files, '/sys-class-power-supply-empty');
+       }
+       copy_files(\@files, 'system');
+       @files = (
+       '/etc/make.conf',
+       '/etc/src.conf',
+       '/var/run/dmesg.boot',
+       );
+       copy_files(\@files,'system-bsd');
 }
-
-get_networking_usb_data()
-{
-       eval $LOGFS
-       local lsusb_path='' lsusb_data='' a_usb='' array_count=''
-       
-       # now we'll check for usb wifi, a work in progress
-       # USB_NETWORK_SEARCH
-       # alsa usb detection by damentz
-       # for every sound card symlink in /proc/asound - display information about it
-       lsusb_path=$( type -p lsusb )
-       # if lsusb exists, the file is a symlink, and contains an important usb exclusive file: continue
-       if [[ -n $lsusb_path ]]; then
-               # send error messages of lsusb to /dev/null as it will display a bunch if not a super user
-               lsusb_data="$( $lsusb_path 2>/dev/null )"
-               # also, find the contents of usbid in lsusb and print everything after the 7th word on the
-               # corresponding line. Finally, strip out commas as they will change the driver :)
-               if [[ -n $lsusb_data ]];then
-                       IFS=$'\n'
-                       a_usb=( $( 
-                       gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                               string=""
-                               separator=""
-                       }
-                       /'"$USB_NETWORK_SEARCH"'/ && !/bluetooth| hub|keyboard|mouse|printer| ps2|reader|scan|storage/ {
-                               string=""
-                               gsub( /,/, " ", $0 )
-                               gsub(/'"$BAN_LIST_NORMAL"'/, "", $0)
-                               gsub(/ [ \t]+/, " ", $0)
-                               #sub(/realtek semiconductor/, "Realtek", $0)
-                               #sub(/davicom semiconductor/, "Davicom", $0)
-                               #sub(/Belkin Components/, "Belkin", $0)
-                               
-                               for ( i=7; i<= NF; i++ ) {
-                                       string = string separator $i
-                                       separator = " "
-                               }
-                               if ( $2 != "" ){
-                                       sub(/:/, "", $4 )
-                                       print string ",,,,usb-" $2 "-" $4 ",,,,,," $6
-                               }
-                       }' <<< "$lsusb_data" ) )
-                       IFS="$ORIGINAL_IFS"
-                       if [[ ${#a_usb[@]} -gt 0 ]];then
-                               array_count=${#A_NETWORK_DATA[@]}
-                               for (( i=0; i < ${#a_usb[@]}; i++ ))
-                               do
-                                       A_NETWORK_DATA[$array_count]=${a_usb[i]}
-                                       ((array_count++))
-                               done
-                               # need this to get the driver data for -N regular output, but no need
-                               # to run the advanced stuff unless required
-                               B_USB_NETWORKING='true'
-                       fi
-               fi
-       fi
-#      echo $B_USB_NETWORKING
-       eval $LOGFE
-}
-
-get_networking_wan_ip_data()
-{
-       eval $LOGFS
-       local ip=''
-
-       # get ip using wget redirect to stdout. This is a clean, text only IP output url,
-       # single line only, ending in the ip address. May have to modify this in the future
-       # to handle ipv4 and ipv6 addresses but should not be necessary.
-       # awk has bad regex handling so checking it with grep -E instead
-       # ip=$( echo  2001:0db8:85a3:0000:0000:8a2e:0370:7334 | gawk  --re-interval '
-       # ip=$( wget -q -O - $WAN_IP_URL | gawk  --re-interval '
-       ip=$( wget -t 1 -T $WGET_TIMEOUT -q -O - $WAN_IP_URL | gawk  --re-interval '
-       {
-               #gsub("\n","",$2")
-               print $NF
-       }' )
-       # validate the data
-       if [[ -z $ip ]];then
-               ip='None Detected!'
-       elif [[ -z $( grep -Es \
-       '^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4}:[[:alnum:]]{0,4})$' <<< $ip ) ]];then
-               ip='IP Source Corrupt!'
-       fi
-       echo "$ip"
-       log_function_data "ip: $ip"
-       eval $LOGFE
-}
-
-get_networking_local_ip_data()
-{
-       eval $LOGFS
-       
-       local ip_tool_command=$( type -p ip )
-       local temp_array='' ip_tool='ip' ip_tool_data=''
-       # the chances for all new systems to have ip by default are far higher than
-       # the deprecated ifconfig. Only try for ifconfig if ip is not present in system
-       if [[ -z $ip_tool_command ]];then
-               ip_tool_command=$( type -p ifconfig )
-               ip_tool='ifconfig'
-       else
-               ip_tool_command="$ip_tool_command addr"
-       fi
-       if [[ -n "$ip_tool_command" ]];then
-               if [[ $ip_tool == 'ifconfig' ]];then
-                       ip_tool_data="$( $ip_tool_command | gawk '
-                       {
-                               line=gensub(/^([a-z]+[0-9][:]?[[:space:]].*)/, "\n\\1", $0)
-                               print line
-                       }' )"
-               # note, ip addr does not have proper record separation, so creating new lines explicitly here at start
-               # of each IF record item. Also getting rid of the unneeded numeric line starters, now it can be parsed 
-               # like ifconfig more or less
-               elif [[ $ip_tool == 'ip' ]];then
-                       ip_tool_data="$( eval ${ip_tool_command} | sed 's/^[0-9]\+:[[:space:]]\+/\n/' )"
-               fi
-       fi
-       if [[ -z $ip_tool_command ]];then
-               A_INTERFACES_DATA=( "Interfaces program 'ip' missing. Please check: $SCRIPT_NAME --recommends" )
-       elif [[ -n "$ip_tool_data" ]];then
-               IFS=$'\n' # $ip_tool_command
-               A_INTERFACES_DATA=( $( 
-               gawk -v ipTool=$ip_tool -v bsdType=$BSD_TYPE '
-               BEGIN {
-                       IGNORECASE=1
-                       interface=""
-                       ifIp=""
-                       ifIpV6=""
-                       ifMask=""
-               }
-               # skip past the lo item
-               /^lo/ {
-                       while (getline && !/^$/ ) {
-                               # do nothing, just get past this entry item
-                       }
-               }
-               /^[a-zA-Z]+[0-9]/ {
-                       # not clear on why inet is coming through, but this gets rid of it
-                       # as first line item.
-                       gsub(/,/, " ", $0)
-                       gsub(/^ +| +$/, "", $0)
-                       gsub(/ [ \t]+/, " ", $0)
-                       interface = $1
-                       # prep this this for ip addr: eth0: 
-                       sub(/:/, "", interface)
-                       ifIp=""
-                       ifIpV6=""
-                       ifMask=""
-                       aInterfaces[interface]++
-
-                       while (getline && !/^$/ ) {
-                               if ( ipTool == "ifconfig" ) {
-                                       if (/inet addr:/) {
-                                               ifIp = gensub( /addr:([0-9\.]+)/, "\\1", "g", $2 )
-                                               if (/mask:/) {
-                                                       ifMask = gensub( /mask:([0-9\.]+)/, "\\1", "g", $NF )
-                                               }
-                                       }
-                                       if (/inet6 addr:/) {
-                                               ifIpV6 = $3
-                                       }
-                                       if ( bsdType == "bsd" ) {
-                                               if ( $1 == "inet" ) {
-                                                       ifIp = $2
-                                                       if ( $3 == "netmask" ) {
-                                                               ifMask = $4
-                                                       }
-                                               }
-                                               if ( $0 ~ /inet6.*%/ ) {
-                                                       sub(/%.*/,"",$2)
-                                                       ifIpV6 = $2
-                                               }
-                                       }
-                               }
-                               else if ( ipTool == "ip" ) {
-                                       if ( $1 == "inet" ) {
-                                               ifIp = $2
-                                       }
-                                       if ( $1 == "inet6" ) {
-                                               ifIpV6 = $2
-                                       }
-                               }
-                       }
-                       # slice off the digits that are sometimes tacked to the end of the address, 
-                       # like: /64 or /24
-                       sub(/\/[0-9]+/, "", ifIp)
-                       sub(/\/[0-9]+/, "", ifIpV6)
-                       ipAddresses[interface] = ifIp "," ifMask "," ifIpV6
-               }
-               END {
-                       j=0
-                       for (i in aInterfaces) {
-                               ifData = ""
-                               a[j] = i
-                               if (ipAddresses[i] != "") {
-                                       ifData = ipAddresses[i]
-                               }
-                               # create array primary item for master array
-                               # tested needed to avoid bad data from above, if null it is garbage
-                               # this is the easiest way to handle junk I found, improve if you want
-                               if ( ifData != "" ) {
-                                       print a[j] "," ifData
-                               }
-                               j++
-                       }
-               }' <<< "$ip_tool_data" ) )
-               IFS="$ORIGINAL_IFS"
-       else
-               A_INTERFACES_DATA=( "Interfaces program $ip_tool present but created no data. " )
-       fi
-       temp_array=${A_INTERFACES_DATA[@]}
-       log_function_data "A_INTERFACES_DATA: $temp_array"
-       eval $LOGFE
-}
-# get_networking_local_ip_data;exit
-
-# get_networking_local_ip_data;exit
-get_optical_drive_data()
-{
-       eval $LOGFS
-       
-       local temp_array='' sys_uevent_path='' proc_cdrom='' link_list=''
-       local separator='' linked='' disk='' item_string='' proc_info_string='' 
-       local dev_disks_links="$( ls /dev/dvd* /dev/cd* /dev/scd* 2>/dev/null  )"
-       # get the actual disk dev location, first try default which is easier to run, need to preserve line breaks
-       local dev_disks_real="$( echo "$dev_disks_links" | xargs -L 1 readlink 2>/dev/null | sort -u )"
-       # Some systems don't support xargs -L so we need to do it manually
-       if [[ -z $dev_disks_real ]];then
-               for linked in $dev_disks_links
-               do
-                       disk=$( readlink $linked 2>/dev/null )
-                       if [[ -n $disk ]];then
-                               disk=$( basename $disk ) # puppy shows this as /dev/sr0, not sr0
-                               if [[ -z $dev_disks_real || -z $( grep $disk <<< $dev_disks_real ) ]];then
-                                       # need line break IFS for below, no white space
-                                       dev_disks_real="$dev_disks_real$separator$disk"
-                                       separator=$'\n'
-                               fi
-                       fi
-               done
-               dev_disks_real="$( sort -u <<< "$dev_disks_real" )"
-               linked=''
-               disk=''
-               separator=''
-       fi
-
-       # A_OPTICAL_DRIVE_DATA indexes: not going to use all these, but it's just as easy to build the full
-       # data array and use what we need from it as to update it later to add features or items
-       # 0 - true dev path, ie, sr0, hdc
-       # 1 - dev links to true path
-       # 2 - device vendor - for hdx drives, vendor model are one string from proc
-       # 3 - device model
-       # 4 - device rev version
-       # 5 - speed
-       # 6 - multisession support
-       # 7 - MCN support
-       # 8 - audio read
-       # 9 - cdr
-       # 10 - cdrw
-       # 11 - dvd read
-       # 12 - dvdr
-       # 13 - dvdram
-       # 14 - state
-
-       if [[ -n $dev_disks_real ]];then
-               if [[ $B_SHOW_FULL_OPTICAL == 'true' ]];then
-                       proc_cdrom="$( cat /proc/sys/dev/cdrom/info 2>/dev/null )"
-               fi
-               IFS=$'\n'
-               A_OPTICAL_DRIVE_DATA=( $(
-               for disk in $dev_disks_real
-               do
-                       for linked in $dev_disks_links 
-                       do
-                               if [[ -n $( readlink $linked | grep $disk ) ]];then
-                                       linked=$( basename $linked )
-                                       link_list="$link_list$separator$linked"
-                                       separator='~'
-                               fi
-                       done
-                       item_string="$disk,$link_list"
-                       link_list=''
-                       linked=''
-                       separator=''
-                       vendor=''
-                       model=''
-                       proc_info_string=''
-                       rev_number=''
-                       state=""
-                       sys_path=''
-                       # this is only for new sd type paths in /sys, otherwise we'll use /proc/ide
-                       if [[ -z $( grep '^hd' <<< $disk ) ]];then
-                               sys_path=$( ls /sys/devices/pci*/*/host*/target*/*/block/$disk/uevent 2>/dev/null | sed "s|/block/$disk/uevent||" )
-                               # no need to test for errors yet, probably other user systems will require some alternate paths though
-                               if [[ -n $sys_path ]];then
-                                       vendor=$( cat $sys_path/vendor 2>/dev/null )
-                                       model=$( cat $sys_path/model 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/,//g' )
-                                       state=$( cat $sys_path/state 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/,//g' )
-                                       rev_number=$( cat $sys_path/rev 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/,//g' )
-                               fi
-                       elif [[ -e /proc/ide/$disk/model ]];then
-                               vendor=$( cat /proc/ide/$disk/model 2>/dev/null )
-                       fi
-                       if [[ -n $vendor ]];then
-                               vendor=$( gawk '
-                               BEGIN {
-                                       IGNORECASE=1
-                               }
-                               {
-                                       gsub(/'"$BAN_LIST_NORMAL"'/, "", $0)
-                                       sub(/TSSTcorp/, "TSST ", $0) # seen more than one of these weird ones
-                                       gsub(/,/, " ", $0)
-                                       gsub(/^[[:space:]]*|[[:space:]]*$/, "", $0)
-                                       gsub(/ [[:space:]]+/, " ", $0)
-                                       print $0
-                               }'      <<< $vendor )
-                       fi
-                       # this needs to run no matter if there's proc data or not to create the array comma list
-                       if [[ $B_SHOW_FULL_OPTICAL == 'true' ]];then
-                               proc_info_string=$( gawk -v diskId=$disk '
-                               BEGIN {
-                                       IGNORECASE=1
-                                       position=""
-                                       speed=""
-                                       multisession=""
-                                       mcn=""
-                                       audio=""
-                                       cdr=""
-                                       cdrw=""
-                                       dvd=""
-                                       dvdr=""
-                                       dvdram=""
-                               }
-                               # first get the position of the device name from top field
-                               # we will use this to get all the other data for that column
-                               /drive name:/ {
-                                       for ( position=3; position <= NF; position++ ) {
-                                               if ( $position == diskId ) {
-                                                       break
-                                               }
-                                       }
-                               }
-                               /drive speed:/ {
-                                       speed = $position
-                               }
-                               /Can read multisession:/ {
-                                       multisession=$( position + 1 )
-                               }
-                               /Can read MCN:/ {
-                                       mcn=$( position + 1 )
-                               }
-                               /Can play audio:/ {
-                                       audio=$( position + 1 )
-                               }
-                               /Can write CD-R:/ {
-                                       cdr=$( position + 1 )
-                               }
-                               /Can write CD-RW:/ {
-                                       cdrw=$( position + 1 )
-                               }
-                               /Can read DVD:/ {
-                                       dvd=$( position + 1 )
-                               }
-                               /Can write DVD-R:/ {
-                                       dvdr=$( position + 1 )
-                               }
-                               /Can write DVD-RAM:/ {
-                                       dvdram=$( position + 1 )
-                               }
-                               END {
-                                       print speed "," multisession "," mcn "," audio "," cdr "," cdrw "," dvd "," dvdr "," dvdram
-                               }
-                               ' <<< "$proc_cdrom" )
-                       fi
-                       item_string="$item_string,$vendor,$model,$rev_number,$proc_info_string,$state"
-                       echo $item_string
-               done \
-               ) )
-               IFS="$ORIGINAL_IFS"
-       fi
-       temp_array=${A_OPTICAL_DRIVE_DATA[@]}
-       log_function_data "A_OPTICAL_DRIVE_DATA: $temp_array"
-       eval $LOGFE
+## SELF EXECUTE FOR LOG/OUTPUT
+sub run_self {
+       print "Creating $self_name output file now. This can take a few seconds...\n";
+       print "Starting $self_name from: $self_path\n";
+       my $i = ($option eq 'main-full')? ' -i' : '';
+       my $cmd = "$self_path/$self_name -FRfrploudmxxx$i -c 0 --usb --slots --debug 10 -y 120 > $data_dir/$self_name-FRfrploudmxxxyusbslots120.txt 2>&1";
+       system($cmd);
+       copy($log_file, "$data_dir") or main::error_handler('copy-failed', "$log_file", "$!");
+       system("$self_path/$self_name --recommends -y 120 > $data_dir/$self_name-recommends-120.txt 2>&1");
 }
 
-get_partition_data()
-{
-       eval $LOGFS
-       
-       local a_partition_working='' dev_item='' temp_array='' dev_working_item='' 
-       local swap_data='' df_string='' main_partition_data='' df_test='' fs_type=''
-       local mount_data='' dev_bsd_item=''
-       #local excluded_file_types='--exclude-type=aufs --exclude-type=tmpfs --exclude-type=iso9660'
-       # df doesn't seem to work in script with variables like at the command line
-       # added devfs linprocfs sysfs fdescfs which show on debian kfreebsd kernel output
-       if [[ -z $BSD_TYPE ]];then
-               swap_data="$( swapon -s 2>/dev/null )"
-               df_string='df -h -T -P --exclude-type=aufs --exclude-type=devfs --exclude-type=devtmpfs 
-               --exclude-type=fdescfs --exclude-type=iso9660 --exclude-type=linprocfs --exclude-type=procfs
-               --exclude-type=squashfs --exclude-type=sysfs --exclude-type=tmpfs --exclude-type=unionfs'
-       else
-               swap_data="$( swapctl -l 2>/dev/null )"
-               # default size is 512, -H only for size in human readable format
-               # older bsds don't support -T, pain, so we'll use partial output there
-               df_test=$( df -H -T 2>/dev/null )
-               if [[ -n $df_test ]];then
-                       df_string='df -H -T'
-               else
-                       df_string='df -H'
-               fi
-       fi
-       main_partition_data="$( eval $df_string )"
-       # set dev disk label/mapper/uuid data globals
-       get_partition_dev_data 'label'
-       get_partition_dev_data 'mapper'
-       get_partition_dev_data 'uuid'
-       
-       log_function_data 'raw' "main_partition_data:\n$main_partition_data\n\nswap_data:\n$swap_data"
-       
-       # new kernels/df have rootfs and / repeated, creating two entries for the same partition
-       # so check for two string endings of / then slice out the rootfs one, I could check for it
-       # before slicing it out, but doing that would require the same action twice re code execution
-       if [[ $( grep -cs '[[:space:]]/$' <<< "$main_partition_data" ) -gt 1 ]];then
-               main_partition_data="$( grep -vs '^rootfs' <<< "$main_partition_data" )"
-       fi
-       log_function_data 'raw' "main_partition_data_post_rootfs:\n$main_partition_data\n\nswap_data:\n$swap_data"
-       IFS=$'\n'
-       # sample line: /dev/sda2     ext3     15G  8.9G  4.9G  65% /home
-       # $NF = partition name; $(NF - 4) = partition size; $(NF - 3) = used, in gB; $(NF - 1) = percent used
-       ## note: by subtracting from the last field number NF, we avoid a subtle issue with LVM df output, where if
-       ## the first field is too long, it will occupy its own line, this way we are getting only the needed data
-       A_PARTITION_DATA=( $( echo "$main_partition_data" | gawk -v bsdType=$BSD_TYPE '
-       BEGIN {
-               IGNORECASE=1
-               fileSystem=""
-       }
-       # this has to be nulled for every iteration so it does not retain value from last iteration
-       devBase=""
-       # skipping these file systems because bsds do not support df --exclude-type=<fstype>
-       # note that using $1 to handle older bsd df, which do not support -T. This will not be reliable but we will see
-       ( bsdType != "" ) {
-               # skip if non disk/partition, or if raid primary id, which will not have a / in it
-               if ( $1 ~ /^(aufs|devfs|devtmpfs|fdescfs|iso9660|linprocfs|procfs|squashfs|sysfs|tmpfs|type|unionfs)$/ || 
-               $1 ~ /^([^\/]+)$/ ) {
-                       # note use next, not getline or it does not work right
-                       next 
-               }
-       }
-       # this is required because below we are subtracting from NF, so it has to be > 5
-       # the real issue is long file system names that force the wrap of df output: //fileserver/main
-       # but we still need to handle more dynamically long space containing file names, but later.
-       # Using df -P should fix this, ie, no wrapping of line lines, but leaving this for now
-       ( NF < 6 ) && ( $0 !~ /[0-9]+%/ ) {
-               # set the dev location here for cases of wrapped output
-               if ( NF == 1 ) {
-                       devBase=gensub( /^(\/dev\/)(.+)$/, "\\2", 1, $1 )
-               }
-               getline
-       }
-       
-       # next set devBase if it didn not get set above here
-       ( devBase == "" ) && ( $1 ~ /^\/dev\/|:\/|\/\// ) {
-               devBase=gensub( /^(\/dev\/)(.+)$/, "\\2", 1, $1 )
-       }
-       # this handles zfs type devices/partitions, which do not start with / but contain /
-       ( bsdType != "" && devBase == "" && $1 ~ /^[^\/]+\/.+/ ) {
-               devBase=gensub( /^([^\/]+\/)([^\/]+)$/, "non-dev-\\1\\2", 1, $1 )
-       }
-       # this handles yet another fredforfaen special case where a mounted drive
-       # has the search string in its name
-       $NF ~ /^\/$|^\/boot$|^\/var$|^\/home$|^\/tmp$|^\/usr$/ {
-               # note, older df in bsd do not have file system column
-               if ( NF == "7" && $(NF - 1) ~ /[0-9]+%/ ) {
-                       fileSystem=$(NF - 5)
+## UTILITIES COPY/CMD/WRITE
+sub copy_files {
+       my ($files_ref,$type,$alt_dir) = @_;
+       my ($absent,$error,$good,$name,$unreadable);
+       my $directory = ($alt_dir) ? $alt_dir : $data_dir;
+       my $working = ($type ne 'proc') ? "$type-file-": '';
+       foreach (@$files_ref) {
+               $name = $_;
+               $name =~ s/^\///;
+               $name =~ s/\//~/g;
+               # print "$name\n" if $type eq 'proc';
+               $name = "$directory/$working$name";
+               $good = $name . '.txt';
+               $absent = $name . '-absent';
+               $error = $name . '-error';
+               $unreadable = $name . '-unreadable';
+               # proc have already been tested for readable/exists
+               if ($type eq 'proc' || -e $_ ) {
+                       if ($type eq 'proc' || -r $_){
+                               copy($_,"$good") or main::toucher($error);
+                       }
+                       else {
+                               main::toucher($unreadable);
+                       }
                }
                else {
-                       fileSystem=""
+                       main::toucher($absent);
                }
-               print $NF "," $(NF - 4) "," $(NF - 3) "," $(NF - 1) ",main," fileSystem "," devBase 
        }
-       # skip all these, including the first, header line. Use the --exclude-type
-       # to handle new filesystems types we do not want listed here
-       $NF !~ /^\/$|^\/boot$|^\/var$|^\/home$|^\/tmp$|^\/usr$|^filesystem/ {
-               # this is to avoid file systems with spaces in their names, that will make
-               # the test show the wrong data in each of the fields, if no x%, then do not use
-               # using 3 cases, first default, standard, 2nd, 3rd, handles one and two spaces in name
-               if ( $(NF - 1) ~ /[0-9]+%/ ) {
-                       # note, older df in bsd do not have file system column
-                       if ( NF == "7" ) {
-                               fileSystem=$(NF - 5)
+}
+sub run_commands {
+       my ($cmds,$type) = @_;
+       my $holder = '';
+       my ($name,$cmd,$args);
+       foreach (@$cmds){
+               my @rows = @$_;
+               if (my $program = main::check_program($rows[0])){
+                       if ($rows[1] eq 'present'){
+                               $name = "$data_dir/$type-cmd-$rows[0]-present";
+                               main::toucher($name);
                        }
                        else {
-                               fileSystem=""
+                               $args = $rows[1];
+                               $args =~ s/\s|--|\/|=/-/g; # for:
+                               $args =~ s/--/-/g;# strip out -- that result from the above
+                               $args =~ s/^-//g;
+                               $args = "-$args" if $args;
+                               $name = "$data_dir/$type-cmd-$rows[0]$args.txt";
+                               $cmd = "$program $rows[1] >$name 2>&1";
+                               system($cmd);
                        }
-                       print $NF "," $(NF - 4) "," $(NF - 3) "," $(NF - 1) ",secondary," fileSystem "," devBase 
                }
-               # these two cases construct the space containing name
-               else if ( $(NF - 2) ~ /[0-9]+%/ ) {
-                       # note, older df in bsd do not have file system column
-                       if ( NF == "8" && $(NF - 6) !~ /^[0-9]+/ ) {
-                               fileSystem=$(NF - 6)
+               else {
+                       if ($holder ne $rows[0]){
+                               $name = "$data_dir/$type-cmd-$rows[0]-absent";
+                               main::toucher($name);
+                               $holder = $rows[0];
+                       }
+               }
+       }
+}
+sub write_data {
+       my ($data_ref, $type) = @_;
+       my ($empty,$error,$fh,$good,$name,$undefined,$value);
+       foreach (keys %$data_ref) {
+               $value = $$data_ref{$_};
+               $name = "$data_dir/$type-data-$_";
+               $good = $name . '.txt';
+               $empty = $name . '-empty';
+               $error = $name . '-error';
+               $undefined = $name . '-undefined';
+               if (defined $value) {
+                       if ($value || $value eq '0'){
+                               open($fh, '>', $good) or main::toucher($error);
+                               print $fh "$value";
                        }
                        else {
-                               fileSystem=""
+                               main::toucher($empty);
                        }
-                       print $(NF - 1) " " $NF "," $(NF - 5) "," $(NF - 4) "," $(NF - 2) ",secondary," fileSystem "," devBase
                }
-               else if ( $(NF - 3) ~ /[0-9]+%/ ) {
-                       # note, older df in bsd do not have file system column
-                       if ( NF == "9" && $(NF - 7) !~ /^[0-9]+/ ) {
-                               fileSystem=$(NF - 7)
+               else {
+                       main::toucher($undefined);
+               }
+       }
+}
+## TOOLS FOR DIRECTORY TREE/LS/TRAVERSE; UPLOADER
+sub build_tree {
+       my ($which) = @_;
+       if ( $which eq 'sys' && main::check_program('tree') ){
+               print "Constructing /$which tree data...\n";
+               my $dirname = '/sys';
+               my $cmd;
+               system("tree -a -L 10 /sys > $data_dir/sys-data-tree-full-10.txt");
+               opendir my($dh), $dirname or main::error_handler('open-dir',"$dirname", "$!");
+               my @files = readdir $dh;
+               closedir $dh;
+               foreach (@files){
+                       next if /^\./;
+                       $cmd = "tree -a -L 10 $dirname/$_ > $data_dir/sys-data-tree-$_-10.txt";
+                       #print "$cmd\n";
+                       system($cmd);
+               }
+       }
+       print "Constructing /$which ls data...\n";
+       if ($which eq 'sys'){
+               directory_ls($which,1);
+               directory_ls($which,2);
+               directory_ls($which,3);
+               directory_ls($which,4);
+       }
+       elsif ($which eq 'proc') {
+               directory_ls('proc',1);
+               directory_ls('proc',2,'[a-z]');
+               # don't want the /proc/self or /proc/thread-self directories, those are 
+               # too invasive
+               #directory_ls('proc',3,'[a-z]');
+               #directory_ls('proc',4,'[a-z]');
+       }
+}
+
+# include is basic regex for ls path syntax, like [a-z]
+sub directory_ls {
+       my ( $dir,$depth,$include) = @_;
+       $include ||= '';
+       my ($exclude) = ('');
+       # wd do NOT want to see anything in self or thread-self!!
+       # $exclude = 'I self -I thread-self' if $dir eq 'proc';
+       my $cmd = do {
+               if ( $depth == 1 ){ "ls -l $exclude /$dir/$include 2>/dev/null" }
+               elsif ( $depth == 2 ){ "ls -l $exclude /$dir/$include*/ 2>/dev/null" }
+               elsif ( $depth == 3 ){ "ls -l $exclude /$dir/$include*/*/ 2>/dev/null" }
+               elsif ( $depth == 4 ){ "ls -l $exclude /$dir/$include*/*/*/ 2>/dev/null" }
+               elsif ( $depth == 5 ){ "ls -l $exclude /$dir/$include*/*/*/*/ 2>/dev/null" }
+               elsif ( $depth == 5 ){ "ls -l $exclude /$dir/$include*/*/*/*/ 2>/dev/null" }
+       };
+       my @working = ();
+       my $output = '';
+       my ($type);
+       my $result = qx($cmd);
+       open my $ch, '<', \$result or main::error_handler('open-data',"$cmd", "$!");
+       while ( my $line = <$ch> ){
+               chomp($line);
+               $line =~ s/^\s+|\s+$//g;
+               @working = split /\s+/, $line;
+               $working[0] ||= '';
+               if ( scalar @working > 7 ){
+                       if ($working[0] =~ /^d/ ){
+                               $type = "d - ";
+                       }
+                       elsif ($working[0] =~ /^l/){
+                               $type = "l - ";
                        }
                        else {
-                               fileSystem=""
+                               $type = "f - ";
                        }
-                       print $(NF - 2) " " $(NF - 1) " " $NF "," $(NF - 6) "," $(NF - 5) "," $(NF - 3) ",secondary," fileSystem "," devBase 
+                       $working[9] ||= '';
+                       $working[10] ||= '';
+                       $output = $output . "  $type$working[8] $working[9] $working[10]\n";
+               }
+               elsif ( $working[0] !~ /^total/ ){
+                       $output = $output . $line . "\n";
                }
-       }' )
-       
-       # now add the swap partition data, don't want to show swap files, just partitions,
-       # though this can include /dev/ramzswap0. Note: you can also use /proc/swaps for this
-       # data, it's the same exact output as swapon -s
-       $( echo "$swap_data" | gawk -v bsdType=$BSD_TYPE '
-       BEGIN {
-               swapCounter = 1
-               usedHolder=""
-               sizeHolder=""
        }
-       /^\/dev/ {
-               if ( bsdType == "" ) {
-                       usedHolder=$4
-                       sizeHolder=$3
+       close $ch;
+       my $file = "$data_dir/$dir-data-ls-$depth.txt";
+       open my $fh, '>', $file or main::error_handler('create',"$file", "$!");
+       print $fh $output;
+       close $fh;
+       # print "$output\n";
+}
+sub proc_traverse_data {
+       print "Building /proc file list...\n";
+       # get rid pointless error:Can't cd to (/sys/kernel/) debug: Permission denied
+       no warnings 'File::Find';
+       $parse_src = 'proc';
+       File::Find::find( \&wanted, "/proc");
+       proc_traverse_processor();
+       @content = ();
+}
+sub proc_traverse_processor {
+       my ($data,$fh,$result,$row,$sep);
+       my $proc_dir = "$data_dir/proc";
+       print "Adding /proc files...\n";
+       mkdir $proc_dir or main::error_handler('mkdir', "$proc_dir", "$!");
+       # @content = sort @content; 
+       copy_files(\@content,'proc',$proc_dir);
+#      foreach (@content){
+#              print "$_\n";
+#      }
+}
+
+sub sys_traverse_data {
+       print "Building /sys file list...\n";
+       # get rid pointless error:Can't cd to (/sys/kernel/) debug: Permission denied
+       no warnings 'File::Find';
+       $parse_src = 'sys';
+       File::Find::find( \&wanted, "/sys");
+       sys_traverse_processsor();
+       @content = ();
+}
+sub sys_traverse_processsor {
+       my ($data,$fh,$result,$row,$sep);
+       my $filename = "sys-data-parse.txt";
+       print "Parsing /sys files...\n";
+       # no sorts, we want the order it comes in
+       # @content = sort @content; 
+       foreach (@content){
+               $data='';
+               $sep='';
+               my $b_fh = 1;
+               open($fh, '<', $_) or $b_fh = 0;
+               # needed for removing -T test and root
+               if ($b_fh){
+                       while ($row = <$fh>) {
+                               chomp $row;
+                               $data .= $sep . '"' . $row . '"';
+                               $sep=', ';
+                       }
                }
                else {
-                       usedHolder=$3
-                       sizeHolder=$2
-               }
-               size = sprintf( "%.2f", sizeHolder*1024/1000**3 )
-               devBase = gensub( /^(\/dev\/)(.+)$/, "\\2", 1, $1 )
-               used = sprintf( "%.2f", usedHolder*1024/1000**3 )
-               percentUsed = sprintf( "%.0f", ( usedHolder/sizeHolder )*100 )
-               print "swap-" swapCounter "," size "GB," used "GB," percentUsed "%,main," "swap," devBase
-               swapCounter = ++swapCounter
-       }' ) )
-       IFS="$ORIGINAL_IFS"
-       
-       temp_array=${A_PARTITION_DATA[@]}
-       # echo $temp_array
-       log_function_data "1: A_PARTITION_DATA:\n$temp_array"
-       
-       # we'll use this for older systems where no filesystem type is shown in df
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               mount_data="$( mount )"
-       fi
-       # now we'll handle some fringe cases where irregular df -hT output shows /dev/disk/.. instead of 
-       # /dev/h|sdxy type data for column 1, . A_PARTITION_DATA[6]
-       # Here we just search for the uuid/label and then grab the end of the line to get the right dev item.
-       for (( i=0; i < ${#A_PARTITION_DATA[@]}; i++ ))
-       do
-               IFS=","
-               a_partition_working=( ${A_PARTITION_DATA[i]} )
-               IFS="$ORIGINAL_IFS"
-               
-               dev_item=${a_partition_working[6]} # reset each loop
-               fs_type=${a_partition_working[5]}
-               # older bsds have df minus -T so can't get fs type easily, try using mount instead
-               if [[ $BSD_TYPE == 'bsd' && -z $fs_type && -n $dev_item ]];then
-                       dev_bsd_item=$( sed -e 's/non-dev-//' -e 's|/|\\/|g' <<< "$dev_item" )
-                       fs_type=$( gawk -F '(' '
-                       /'$dev_bsd_item'/ {
-                               # slice out everything after / plus the first comma
-                               sub( /,.*/, "", $2 )
-                               print $2
-                               exit
-                       }' <<< "$mount_data" )
-               fi
-               # note: for swap this will already be set
-               if [[ -n $( grep -E '(by-uuid|by-label)' <<< $dev_item ) ]];then
-                       dev_working_item=$( basename $dev_item )
-                       if [[ -n $DEV_DISK_UUID ]];then
-                               dev_item=$( echo "$DEV_DISK_UUID" | gawk '
-                                       $0 ~ /[ /t]'$dev_working_item'[ /t]/ {
-                                               item=gensub( /..\/..\/(.+)/, "\\1", 1, $NF )
-                                               print item
-                                               exit
-                                       }' )
-                       fi
-                       # if we didn't find anything for uuid try label
-                       if [[ -z $dev_item && -n $DEV_DISK_LABEL ]];then
-                               dev_item=$( echo "$DEV_DISK_LABEL" | gawk '
-                                       $0 ~ /[ /t]'$dev_working_item'[ /t]/ {
-                                               item=gensub( /..\/..\/(.+)/, "\\1", 1, $NF )
-                                               print item
-                                               exit
-                                       }' )
-                       fi
-               elif [[ -n $( grep 'mapper/' <<< $dev_item ) ]];then
-                       # get the mapper actual dev item
-                       dev_item=$( get_dev_processed_item "$dev_item" )
-               fi
-               
-               if [[ -n $dev_item ]];then
-                       # assemble everything we could get for dev/h/dx, label, and uuid
-                       IFS=","
-                       A_PARTITION_DATA[i]=${a_partition_working[0]}","${a_partition_working[1]}","${a_partition_working[2]}","${a_partition_working[3]}","${a_partition_working[4]}","$fs_type","$dev_item
-                       IFS="$ORIGINAL_IFS"
-               fi
-       done
-       temp_array=${A_PARTITION_DATA[@]}
-       # echo $temp_array
-       log_function_data "2: A_PARTITION_DATA:\n$temp_array"
-       if [[ $B_SHOW_LABELS == 'true' || $B_SHOW_UUIDS == 'true' ]];then
-               get_partition_data_advanced
-       fi
-       eval $LOGFE
-}
-
-# first get the locations of the mount points for label/uuid detection
-get_partition_data_advanced()
-{
-       eval $LOGFS
-       local a_partition_working='' dev_partition_data=''
-       local dev_item='' dev_label='' dev_uuid='' temp_array=''
-       local mount_point=''
-       # set dev disk label/mapper/uuid data globals
-       get_partition_dev_data 'label'
-       get_partition_dev_data 'mapper'
-       get_partition_dev_data 'uuid'
-
-       if [[ $B_MOUNTS_FILE == 'true' ]];then
-               for (( i=0; i < ${#A_PARTITION_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_partition_working=( ${A_PARTITION_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       
-                       # note: for swap this will already be set
-                       if [[ -z ${a_partition_working[6]} ]];then
-                               
-                               mount_point=$( sed 's|/|\\/|g'  <<< ${a_partition_working[0]} )
-                               #echo mount_point $mount_point
-                               dev_partition_data=$( gawk '
-                               BEGIN {
-                                       IGNORECASE = 1
-                                       partition = ""
-                                       partTemp = ""
-                               }
-                               # trying to handle space in name
-#                              gsub( /\\040/, " ", $0 )
-                               /[ \t]'$mount_point'[ \t]/ && $1 != "rootfs" {
-                                       # initialize the variables
-                                       label = ""
-                                       uuid = ""
-
-                                       # slice out the /dev
-                                       partition=gensub( /^(\/dev\/)(.+)$/, "\\2", 1, $1 )
-                                       # label and uuid can occur for root, set partition to null now
-                                       if ( partition ~ /by-label/ ) {
-                                               label=gensub( /^(\/dev\/disk\/by-label\/)(.+)$/, "\\2", 1, $1 )
-                                               partition = ""
-                                       }
-                                       if ( partition ~ /by-uuid/ ) {
-                                               uuid=gensub( /^(\/dev\/disk\/by-uuid\/)(.+)$/, "\\2", 1, $1 )
-                                               partition = ""
-                                       }
-
-                                       # handle /dev/root for / id
-                                       if ( partition == "root" ) {
-                                               # if this works, great, otherwise, just set this to null values
-                                               partTemp="'$( readlink /dev/root 2>/dev/null )'"
-                                               if ( partTemp != "" ) {
-                                                       if ( partTemp ~ /[hsv]d[a-z][0-9]{1,2}/ ) {
-                                                               partition=gensub( /^(\/dev\/)(.+)$/, "\\2", 1, partTemp )
-                                                       }
-                                                       else if ( partTemp ~ /by-uuid/ ) {
-                                                               uuid=gensub( /^(\/dev\/disk\/by-uuid\/)(.+)$/, "\\2", 1, partTemp )
-                                                               partition="" # set null to let real location get discovered
-                                                       }
-                                                       else if ( partTemp ~ /by-label/ ) {
-                                                               label=gensub( /^(\/dev\/disk\/by-label\/)(.+)$/, "\\2", 1, partTemp )
-                                                               partition="" # set null to let real location get discovered
-                                                       }
-                                               }
-                                               else {
-                                                       partition = ""
-                                                       label = ""
-                                                       uuid = ""
-                                               }
-                                       }
-                                       print partition "," label "," uuid
-                                       exit
-                               }'      $FILE_MOUNTS )
-
-                               # assemble everything we could get for dev/h/dx, label, and uuid
-                               IFS=","
-                               A_PARTITION_DATA[i]=${a_partition_working[0]}","${a_partition_working[1]}","${a_partition_working[2]}","${a_partition_working[3]}","${a_partition_working[4]}","${a_partition_working[5]}","$dev_partition_data
-                               IFS="$ORIGINAL_IFS"
-                       fi
-                       ## now we're ready to proceed filling in the data
-                       IFS=","
-                       a_partition_working=( ${A_PARTITION_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       # get the mapper actual dev item first, in case it's mapped
-                       dev_item=$( get_dev_processed_item "${a_partition_working[6]}" )
-                       # make sure not to slice off rest if it's a network mounted file system
-                       if [[ -n $dev_item && -z $( grep -E '(^//|:/)' <<< $dev_item ) ]];then
-                               dev_item=$( basename $dev_item ) ## needed to avoid error in case name still has / in it
-                       fi
-                       dev_label=${a_partition_working[7]}
-                       dev_uuid=${a_partition_working[8]}
-                       # then if dev data/uuid is incomplete, try to get missing piece
-                       # it's more likely we'll get a uuid than a label. But this should get the
-                       # dev item set no matter what, so then we can get the rest of any missing data
-                       # first we'll get the dev_item if it's missing
-                       if [[ -z $dev_item ]];then
-                               if [[ -n $DEV_DISK_UUID && -n $dev_uuid ]];then
-                                       dev_item=$( echo "$DEV_DISK_UUID" | gawk '
-                                               $0 ~ /[ \t]'$dev_uuid'[ \t]/ {
-                                                       item=gensub( /..\/..\/(.+)/, "\\1", 1, $NF )
-                                                       print item
-                                                       exit
-                                               }' )
-                               elif [[ -n $DEV_DISK_LABEL && -n $dev_label ]];then
-                                       dev_item=$( echo "$DEV_DISK_LABEL" | gawk '
-                                               # first we need to change space x20 in by-label back to a real space
-                                               #gsub( /x20/, " ", $0 )
-                                               # then we can see if the string is there
-                                               $0 ~ /[ \t]'$dev_label'[ \t]/ {
-                                                       item=gensub( /..\/..\/(.+)/, "\\1", 1, $NF )
-                                                       print item
-                                                       exit
-                                               }' )
-                               fi
-                       fi
-                       
-                       # this can trigger all kinds of weird errors if it is a non /dev path, like: remote:/machine/name
-                       if [[ -n $dev_item && -z $( grep -E '(^//|:/)' <<< $dev_item ) ]];then
-                               if [[ -n $DEV_DISK_UUID && -z $dev_uuid ]];then
-                                       dev_uuid=$( echo "$DEV_DISK_UUID" | gawk  '
-                                       /'$dev_item'$/ {
-                                               print $(NF - 2)
-                                               exit
-                                       }' )
-                               fi
-                               if [[ -n $DEV_DISK_LABEL && -z $dev_label ]];then
-                                       dev_label=$( echo "$DEV_DISK_LABEL" | gawk '
-                                       /'$dev_item'$/ {
-                                               print $(NF - 2)
-                                               exit
-                                       }' )
-                               fi
-                       fi
-
-                       # assemble everything we could get for dev/h/dx, label, and uuid
-                       IFS=","
-                       A_PARTITION_DATA[i]=${a_partition_working[0]}","${a_partition_working[1]}","${a_partition_working[2]}","${a_partition_working[3]}","${a_partition_working[4]}","${a_partition_working[5]}","$dev_item","$dev_label","$dev_uuid
-                       IFS="$ORIGINAL_IFS"
-               done
-               log_function_data 'cat' "$FILE_MOUNTS"
-       else
-               if [[ $BSD_TYPE == 'bsd' ]];then
-                       get_partition_data_advanced_bsd
-               fi
-       fi
-       temp_array=${A_PARTITION_DATA[@]}
-       # echo $temp_array
-       log_function_data "3-advanced: A_PARTITION_DATA:\n$temp_array"
-       eval $LOGFE
-}
-
-get_partition_data_advanced_bsd()
-{
-       eval $LOGFS
-       local gpart_data="$( gpart list 2>/dev/null )"
-       local a_partition_working='' label_uuid='' dev_item=''
-       
-       if [[ -n $gpart_data ]];then
-               for (( i=0; i < ${#A_PARTITION_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_partition_working=( ${A_PARTITION_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       # no need to use the rest of the name if it's not a straight /dev/item
-                       dev_item=$( basename ${a_partition_working[6]} )
-                       
-                       label_uuid=$( gawk -F ':' '
-                       BEGIN {
-                               IGNORECASE=1
-                               label=""
-                               uuid=""
-                       }
-                       /^[0-9]+\.[[:space:]]*Name.*'$dev_item'/ {
-                               while ( getline && $1 !~ /^[0-9]+\.[[:space:]]*Name/ ) {
-                                       if ( $1 ~ /rawuuid/ ) {
-                                               gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2)
-                                               uuid=$2
-                                       }
-                                       if ( $1 ~ /label/ ) {
-                                               gsub(/^[[:space:]]+|[[:space:]]+$|none|\(null\)/,"",$2)
-                                               label=$2
-                                       }
-                               }
-                               print label","uuid
-                               exit
-                       }' <<< "$gpart_data" )
-
-                       # assemble everything we could get for dev/h/dx, label, and uuid
-                       IFS=","
-                       A_PARTITION_DATA[i]=${a_partition_working[0]}","${a_partition_working[1]}","${a_partition_working[2]}","${a_partition_working[3]}","${a_partition_working[4]}","${a_partition_working[5]}","${a_partition_working[6]}","$label_uuid
-                       IFS="$ORIGINAL_IFS"
-               done
-       fi
-       eval $LOGFE
+                       $data = '<unreadable>';
+               }
+               $result .= "$_:[$data]\n";
+               # print "$_:[$data]\n"
+       }
+       # print scalar @content . "\n";
+       open ($fh, '>', "$data_dir/$filename");
+       print $fh $result;
+       close $fh;
+       # print $fh "$result";
 }
-
-# args: $1 - uuid/label/id/mapper
-get_partition_dev_data()
-{
-       eval $LOGFS
-       
-       # only run these tests once per directory to avoid excessive queries to fs
-       case $1 in
-               id)
-                       if [[ $B_ID_SET != 'true' ]];then
-                               if [[ -d /dev/disk/by-id ]];then
-                                       DEV_DISK_ID="$( ls -l /dev/disk/by-id )"
-                               fi
-                               B_ID_SET='true'
-                       fi
-                       ;;
-               label)
-                       if [[ $B_LABEL_SET != 'true' ]];then
-                               if [[ -d /dev/disk/by-label ]];then
-                                       DEV_DISK_LABEL="$( ls -l /dev/disk/by-label )"
-                               fi
-                               B_LABEL_SET='true'
-                       fi
-                       ;;
-               mapper)
-                       if [[ $B_MAPPER_SET != 'true' ]];then
-                               if [[ -d /dev/mapper ]];then
-                                       DEV_DISK_MAPPER="$( ls -l /dev/mapper )"
-                               fi
-                               B_MAPPER_SET='true'
-                       fi
-                       ;;
-               uuid)
-                       if [[ $B_UUID_SET != 'true' ]];then
-                               if [[ -d /dev/disk/by-uuid ]];then
-                                       DEV_DISK_UUID="$( ls -l /dev/disk/by-uuid )"
-                               fi
-                               B_UUID_SET='true'
-                       fi
-                       ;;
-               
-       esac
-       log_function_data 'raw' "DEV_DISK_LABEL:\n$DEV_DISK_LABEL\n\nDEV_DISK_UUID:\n$DEV_DISK_UUID\n\nDEV_DISK_ID:\n$DEV_DISK_ID\n\nDEV_DISK_MAPPER:\n$DEV_DISK_MAPPER"
-       # debugging section, uncomment to insert user data
-#      DEV_DISK_LABEL='
-#
-# '
-# DEV_DISK_UUID='
-#
-# '
-# DEV_DISK_MAPPER='
-#
-# '
-       eval $LOGFE
-}
-
-# args: $1 - dev item, check for mapper, then get actual dev item if mapped
-# eg: lrwxrwxrwx 1 root root       7 Sep 26 15:10 truecrypt1 -> ../dm-2 
-get_dev_processed_item()
-{
-       eval $LOGFS
-       
-       local dev_item=$1 dev_return=''
+sub wanted {
+       return if -d; # not directory
+       return unless -e; # Must exist
+       return unless -f; # Must be file
+       return unless -r; # Must be readable
+       if ($parse_src eq 'sys'){
+               # note: a new file in 4.11 /sys can hang this, it is /parameter/ then
+               # a few variables. Since inxi does not need to see that file, we will
+               # not use it. Also do not need . files or __ starting files
+               # print $File::Find::name . "\n";
+               # block maybe: cfgroup\/
+               return if $File::Find::name =~ /\/(\.[a-z]|kernel\/|parameters\/|debug\/)/;
+               # comment this one out if you experience hangs or if 
+               # we discover syntax of foreign language characters
+               # Must be ascii like. This is questionable and might require further
+               # investigation, it is removing some characters that we might want
+               # NOTE: this made a bunch of files on arm systems unreadable so we handle 
+               # the readable tests in copy_files()
+               # return unless -T; 
+       }
+       elsif ($parse_src eq 'proc') {
+               return if $File::Find::name =~ /^\/proc\/[0-9]+\//;
+               return if $File::Find::name =~ /^\/proc\/bus\/pci\//;
+               return if $File::Find::name =~ /^\/proc\/(irq|spl|sys)\//;
+               # these choke on sudo/root: kmsg kcore kpage and we don't want keys or kallsyms
+               return if $File::Find::name =~ /^\/proc\/k/; 
+               return if $File::Find::name =~ /(\/mb_groups|debug)$/;
+       }
+       # print $File::Find::name . "\n";
+       push (@content, $File::Find::name);
+       return;
+}
+# args: 1 - path to file to be uploaded
+# args: 2 - optional: alternate ftp upload url
+# NOTE: must be in format: ftp.site.com/incoming
+sub upload_file {
+       require Net::FTP;
+       import Net::FTP;
+       my ($self, $ftp_url) = @_;
+       my ($ftp, $domain, $host, $user, $pass, $dir, $error);
+       $ftp_url ||= main::get_defaults('ftp-upload');
+       $ftp_url =~ s/\/$//g; # trim off trailing slash if present
+       my @url = split(/\//, $ftp_url);
+       my $file_path = "$user_data_dir/$debug_gz";
+       $host = $url[0];
+       $dir = $url[1];
+       $domain = $host;
+       $domain =~ s/^ftp\.//;
+       $user = "anonymous";
+       $pass = "anonymous\@$domain";
        
-       if [[ -n $DEV_DISK_MAPPER && -n $( grep -is 'mapper/' <<< $dev_item ) ]];then
-               dev_return=$( echo "$DEV_DISK_MAPPER" | gawk '
-               $( NF - 2 ) ~ /^'$( basename $dev_item )'$/ {
-                       item=gensub( /..\/(.+)/, "\\1", 1, $NF )
-                       print item
-               }' )
-       fi
-       if [[ -z $dev_return ]];then
-               dev_return=$dev_item
-       fi
+       print $line3;
+       print "Uploading to: $ftp_url\n";
+       # print "$host $domain $dir $user $pass\n";
+       print "File to be uploaded:\n$file_path\n";
        
-       echo $dev_return
-
-       eval $LOGFE
+       if ($host && ( $file_path && -e $file_path ) ){
+               # NOTE: important: must explicitly set to passive true/1
+               $ftp = Net::FTP->new($host, Debug => 0, Passive => 1);
+               $ftp->login($user, $pass) || main::error_handler('ftp-login', $ftp->message);
+               $ftp->binary();
+               $ftp->cwd($dir);
+               print "Connected to FTP server.\n";
+               $ftp->put($file_path) || main::error_handler('ftp-upload', $ftp->message);
+               $ftp->quit;
+               print "Uploaded file successfully!\n";
+               print $ftp->message;
+               if ($b_debug_gz){
+                       print "Removing debugger gz file:\n$file_path\n";
+                       unlink $file_path or main::error_handler('remove',"$file_path", "$!");
+                       print "File removed.\n";
+               }
+               print "Debugger data generation and upload completed. Thank you for your help.\n";
+       }
+       else {
+               main::error_handler('ftp-bad-path', "$file_path");
+       }
 }
-
-get_patch_version_string()
-{
-       local patch_version_number=$( sed 's/^[0]*//' <<< $SCRIPT_PATCH_NUMBER )
-       
-       if [[ -n $patch_version_number ]];then
-               patch_version_number="-$patch_version_number"
-               # for cases where it was for example: 00-bsd cleaned to --bsd trim out one -
-               if [[ -n $( grep '\--' <<< $patch_version_number ) ]];then
-                       patch_version_number=$( sed 's/--/-/' <<< $patch_version_number )
-               fi
-       fi
-       echo $patch_version_number
 }
 
-get_pciconf_data()
-{
-       eval $LOGFS
-       
-       local pciconf_data='' temp_array=''
-       
-       if [[ $B_PCICONF == 'true' ]];then
-               pciconf_data="$( pciconf -lv 2>/dev/null )"
-               if [[ -n $pciconf_data ]];then
-                       pciconf_data=$( gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                       }
-                       {
-                                       gsub(/'"$BAN_LIST_NORMAL"'/, "", $0)
-                                       gsub(/[[:space:]]+=[[:space:]]+/, "=",$0)
-                                       gsub(/^[[:space:]]+|'"'"'|\"|,/, "", $0)
-                                       gsub(/=0x/,"=",$0)
-                                       # line=gensub(/.*[[:space:]]+(class=[^[:space:]]*|card=[^[:space:]]*)|chip=[^[:space:]]*|rev=[^[:space:]]*|hdr=[^[:space:]]*).*/,"\n\\1","g",$0)
-                                       line=gensub(/(.*@.*)/,"\n\\1",$0)
-                                       print line
-                       }' <<< "$pciconf_data" )
-                       # create empty last line with this spacing trick
-                       pciconf_data="$pciconf_data
-
-EOF"
-                       # echo "$pciconf_data"
-                       # now insert into arrays
-                       IFS=$'\n'
-                       A_PCICONF_DATA=( $( gawk '
-                       BEGIN {
-                               fullLine=""
-                               driver=""
-                               vendor=""
-                               device=""
-                               class=""
-                               chipId=""
-                               pciId=""
-                               itemData=""
-                               IGNORECASE=1
-                       }
-                       /^.*@/ {
-                               pciId=""
-                               vendor=""
-                               class=""
-                               driver=""
-                               device=""
-                               chipId=""
-                               itemData=$1
-                               
-                               driver=gensub(/^([^@]+)@.*/, "\\1", itemData )
-                               pciId=gensub(/^.*@pci([0-9\.:]+).*/, "\\1", itemData )
-                               sub(/:$/, "", pciId)
-                               itemData=$4
-                               chipId=gensub(/.*chip=([0-9a-f][0-9a-f][0-9a-f][0-9a-f])([0-9a-f][0-9a-f][0-9a-f][0-9a-f]).*/, "\\2:\\1", itemData )
-                               if ( $2 == "class=020000" ) {
-                                       class="network"
-                               }
-                               else if ( $2 == "class=030000" ) {
-                                       class="display"
-                               }
-                               else if ( $2 == "class=040300" ) {
-                                       class="audio"
-                               }
-                               
-                               while ( getline && $1 !~ /^$/ ) {
-                                       if ( $1 ~ /^vendor/ ) {
-                                               sub(/^vendor=/, "", $1 )
-                                               vendor=$0
-                                       }
-                                       else if ( $1 ~ /^device/ ) {
-                                               sub(/^device=/, "", $1 )
-                                               device=$0
-                                       }
-                                       else if ( $1 ~ /^class=/ && class == "" ) {
-                                               sub(/^class=/, "", $1)
-                                               class=$0
-                                       }
-                               }
-                               if ( device == "" ) {
-                                       device=vendor
-                               }
-                               
-                               fullLine=class "," device "," vendor "," driver "," pciId "," chipId
-                               print fullLine
-                       
-                       }' <<< "$pciconf_data" ))
-                       IFS="$ORIGINAL_IFS"
-               fi
-       else
-               A_PCICONF_DATA='pciconf-not-installed'
-       fi
-       B_PCICONF_SET='true'
-       temp_array=${A_PCICONF_DATA[@]}
-       log_function_data "$temp_array"
-       log_function_data "$pciconf_data"
-       eval $LOGFE
-}
-
-# packs standard card arrays using the pciconf stuff
-# args: $1 - audio/network/display - matches first item in A_PCICONF_DATA arrays
-get_pciconf_card_data()
-{
-       eval $LOGFS
-       local a_temp='' array_string='' j=0 device_string=''
-       local ip_tool_command=$( type -p ifconfig )
-       local mac='' state='' speed='' duplex='' network_string=''
-       
-       for (( i=0;i<${#A_PCICONF_DATA[@]};i++ ))
-       do
-               IFS=','
-               a_temp=( ${A_PCICONF_DATA[i]} )
-               IFS="$ORIGINAL_IFS"
-               
-               if [[ ${a_temp[0]} == $1 ]];then
-                       # don't print the vendor if it's already in the device name
-                       if [[ -z $( grep -i "${a_temp[2]}" <<< "${a_temp[1]}" ) ]];then
-                               device_string="${a_temp[2]} ${a_temp[1]}"
-                       else
-                               device_string=${a_temp[1]}
-                       fi
-                       case $1 in
-                               audio)
-                                       array_string="$device_string,${a_temp[3]},,,${a_temp[4]},,${a_temp[5]}"
-                                       A_AUDIO_DATA[j]=$array_string
-                                       ;;
-                               display)
-                                       array_string="$device_string,${a_temp[4]},${a_temp[5]}"
-                                       A_GRAPHICS_CARD_DATA[j]=$array_string
-                                       ;;
-                               network)
-                                       if [[ -n $ip_tool_command && -n ${a_temp[3]} ]];then
-                                               network_string=$(       $ip_tool_command ${a_temp[3]} | gawk '
-                                               BEGIN {
-                                                       IGNORECASE=1
-                                                       mac=""
-                                                       state=""
-                                                       speed=""
-                                                       duplex=""
-                                               }
-                                               /^[[:space:]]*ether/ {
-                                                       mac = $2
-                                               }
-                                               /^[[:space:]]*media/ {
-                                                       if ( $0 ~ /<.*>/ ) {
-                                                               duplex=gensub(/.*<([^>]+)>.*/,"\\1",$0)
-                                                       }
-                                                       if ( $0 ~ /\(.*\)/ ) {
-                                                               speed=gensub(/.*\(([^<[:space:]]+).*\).*/,"\\1",$0)
-                                                       }
-                                               }
-                                               /^[[:space:]]*status/ {
-                                                       sub(/.*status[:]?[[:space:]]*/,"", $0)
-                                                       state=$0
-                                               }
-                                               END {
-                                                       print state "~" speed "~" mac "~" duplex
-                                               }')
-                                       fi
-                                       if [[ -n $network_string ]];then
-                                               mac=$( cut -d '~' -f 3 <<< $network_string )
-                                               state=$( cut -d '~' -f 1 <<< $network_string )
-                                               speed=$( cut -d '~' -f 2 <<< $network_string )
-                                               duplex=$( cut -d '~' -f 4 <<< $network_string )
-                                       fi
-                                       array_string="$device_string,${a_temp[3]},,,${a_temp[4]},${a_temp[3]},$state,$speed,$duplex,$mac,${a_temp[5]}"
-                                       A_NETWORK_DATA[j]=$array_string
-                                       ;;
-                       esac
-                       ((j++))
-               fi
-       done
-       
-       eval $LOGFE
-}
+#### -------------------------------------------------------------------
+#### DOWNLOADER
+#### -------------------------------------------------------------------
 
-# args: $1 - type cpu/mem 
-get_ps_tcm_data()
-{
-       eval $LOGFS
-       local array_length='' reorder_temp='' i=0 head_tail='' sort_type='' ps_data=''
-       
-       # bummer, have to make it more complex here because of reverse sort
-       # orders in output, pesky lack of support of +rss in old systems
-       case $1 in
-               mem)
-                       if [[ $BSD_TYPE != 'bsd' ]];then
-                               sort_type='ps aux --sort -rss'
-                               head_tail='head'
-                       else
-                               sort_type='ps aux -m'
-                               head_tail='head'
-                       fi
-                       ;;
-               cpu)
-                       if [[ $BSD_TYPE != 'bsd' ]];then
-                               sort_type='ps aux --sort %cpu'
-                               head_tail='tail'
-                       else
-                               sort_type='ps aux -r'
-                               head_tail='head'
-                       fi
-                       ;;
-       esac
-       
-       # throttle potential irc abuse
-       if [[ $B_RUNNING_IN_SHELL != 'true' && $PS_COUNT -gt 5 ]];then
-               PS_THROTTLED=$PS_COUNT
-               PS_COUNT=5
-       fi
-       # use eval here to avoid glitches with -
-       ps_data="$( eval $sort_type )"
-
-       IFS=$'\n'
-       # note that inxi can use a lot of cpu, and can actually show up here as the script runs
-       A_PS_DATA=( $( echo "$ps_data" | grep -Ev "($SCRIPT_NAME|%CPU|[[:space:]]ps[[:space:]])" | $head_tail -n $PS_COUNT | gawk '
-       BEGIN {
-               IGNORECASE=1
-               appName=""
-               appPath=""
-               appStarterName=""
-               appStarterPath=""
-               cpu=""
-               mem=""
-               pid=""
-               user=""
-               rss=""
-       }
-       {
-               cpu=$3
-               mem=$4
-               pid=$2
-               user=$1
-               rss=sprintf( "%.2f", $6/1024 )
-               # have to get rid of [,],(,) eg: [lockd] which break the printout function compare in bash
-               gsub(/\[|\]|\(|\)/,"~", $0 )
-               if ( $12 ~ /^\// ){
-                       appStarterPath=$11
-                       appPath=$12
+sub download_file {
+       my ($type, $url, $file) = @_;
+       my ($cmd,$args,$timeout) = ('','','');
+       my $debug_data = '';
+       my $result = 1;
+       $dl{'no-ssl-opt'} ||= '';
+       $dl{'spider'} ||= '';
+       $file ||= 'N/A'; # to avoid debug error
+       if ( ! $dl{'dl'} ){
+               return 0;
+       }
+       if ($dl{'timeout'}){
+               $timeout = "$dl{'timeout'}$dl_timeout";
+       }
+       # print "$dl{'no-ssl-opt'}\n";
+       # print "$dl{'dl'}\n";
+       # tiny supports spider sort of
+       ## NOTE: 1 is success, 0 false for Perl
+       if ($dl{'dl'} eq 'tiny' ){
+               $cmd = "Using tiny: type: $type \nurl: $url \nfile: $file";
+               $result = get_file($type, $url, $file);
+               $debug_data = ($type ne 'stdout') ? $result : 'Success: stdout data not null.';
+       }
+       # But: 0 is success, and 1 is false for these
+       # when strings are returned, they will be taken as true
+       else {
+               if ($type eq 'stdout'){
+                       $args = $dl{'stdout'};
+                       $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $url $dl{'null'}";
+                       $result = qx($cmd);
+                       $debug_data = ($result) ? 'Success: stdout data not null.' : 'Download resulted in null data!';
                }
-               else {
-                       appStarterPath=$11
-                       appPath=$11
-               }
-               appStarterName=gensub( /(\/.*\/)(.*)/, "\\2", "1", appStarterPath )
-               appName=gensub( /(\/.*\/)(.*)/, "\\2", "1", appPath )
-               print appName "," appPath "," appStarterName "," appStarterPath "," cpu "," mem "," pid "," rss "," user
-       }
-       ' ) )
-       # make the array ordered highest to lowest so output looks the way we expect it to
-       # this isn't necessary for -rss, and we can't make %cpu ordered the other way, so
-       # need to reverse it here. -rss is used because on older systems +rss is not supported
-       if [[ $1 == 'cpu' && $BSD_TYPE != 'bsd' ]];then
-               array_length=${#A_PS_DATA[@]}; 
-               while (( $i < $array_length/2 ))
-               do 
-                       reorder_temp=${A_PS_DATA[i]}f
-                       A_PS_DATA[i]=${A_PS_DATA[$array_length-$i-1]}
-                       A_PS_DATA[$array_length-$i-1]=$reorder_temp
-                       (( i++ ))
-               done 
-       fi
-
-       IFS="$ORIGINAL_IFS"
-       
-#      echo ${A_PS_DATA[@]}
-       eval $LOGFE
+               elsif ($type eq 'file') {
+                       $args = $dl{'file'};
+                       $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $file $url $dl{'null'}";
+                       system($cmd);
+                       $result = ($?) ? 0 : 1; # reverse these into Perl t/f
+                       $debug_data = $result;
+               }
+               elsif ( $dl{'dl'} eq 'wget' && $type eq 'spider'){
+                       $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $dl{'spider'} $url";
+                       system($cmd);
+                       $result = ($?) ? 0 : 1; # reverse these into Perl t/f
+                       $debug_data = $result;
+               }
+       }
+       print "-------\nDownloader Data:\n$cmd\nResult: $debug_data\n" if $test[1];
+       log_data('data',"$cmd\nResult: $result") if $b_log;
+       return $result;
 }
 
-# mdstat syntax information: http://www-01.ibm.com/support/docview.wss?uid=isg3T1011259
-# note that this does NOT use either Disk or Partition information for now, ie, there
-# is no connection between the data types, but the output should still be consistent
-get_raid_data()
-{
-       eval $LOGFS
-       
-       local mdstat=''
-               
-       if [[ $B_MDSTAT_FILE == 'true' ]];then
-               mdstat="$( cat $FILE_MDSTAT 2>/dev/null )"
-       fi
-       
-       if [[ -n $mdstat ]];then
-               # need to make sure there's always a newline in front of each record type, and
-               # also correct possible weird formats for the output from older kernels etc.
-               mdstat="$( sed -e 's/^md/\nmd/' -e 's/^unused[[:space:]]/\nunused /' \
-               -e 's/read_ahead/\nread_ahead/' -e 's/^resync=/\nresync=/' -e 's/^Event/\nEvent/' \
-               -e 's/^[[:space:]]*$//' -e 's/[[:space:]]read_ahead/\nread_ahead/' <<< "$mdstat" )"
-               # some fringe cases do not end as expected, so need to add newlines plus EOF to make sure while loop doesn't spin
-               mdstat=$( echo -e "$mdstat\n\nEOF" )
-
-               IFS=$'\n'
-               A_RAID_DATA=( $(
-               gawk '
-               BEGIN {
-                       IGNORECASE=1
-                       RS="\n"
+sub get_file {
+       my ($type, $url, $file) = @_;
+       my $response = HTTP::Tiny->new->get($url);
+       my $return = 1;
+       my $debug = 0;
+       my $fh;
+       $file ||= 'N/A';
+       log_data('dump','%{$response}',\%{$response}) if $b_log;
+       # print Dumper \%{$response};
+       if ( ! $response->{success} ){
+               my $content = $response->{content};
+               $content ||= "N/A\n";
+               my $msg = "Failed to connect to server/file!\n";
+               $msg .= "Response: ${content}Downloader: HTTP::Tiny URL: $url\nFile: $file";
+               log_data('data',$msg) if $b_log;
+               print error_defaults('download-error',$msg) if $test[1];
+               $return = 0;
+       }
+       else {
+               if ( $debug ){
+                       print "$response->{success}\n";
+                       print "$response->{status} $response->{reason}\n";
+                       while (my ($key, $value) = each %{$response->{headers}}) {
+                               for (ref $value eq "ARRAY" ? @$value : $value) {
+                                       print "$key: $_\n";
+                               }
+                       }
                }
-               
-               /^personalities/ {
-                       KernelRaidSupport = gensub(/personalities[[:space:]]*:[[:space:]]*(.*)/, "\\1", 1, $0)
-                       # clean off the brackets
-                       gsub(/[\[\]]/,"",KernelRaidSupport)
-                       print "KernelRaidSupport," KernelRaidSupport
+               if ( $type eq "stdout" || $type eq "ua-stdout" ){
+                       $return = $response->{content};
                }
-               /^read_ahead/ {
-                       ReadAhead=gensub(/read_ahead (.*)/, "\\1", 1 )
-                       print "ReadAhead," ReadAhead
+               elsif ($type eq "spider"){
+                       # do nothing, just use the return value
                }
-               /^Event:/ {
-                       print "raidEvent," $NF
+               elsif ($type eq "file"){
+                       open($fh, ">", $file);
+                       print $fh $response->{content}; # or die "can't write to file!\n";
+                       close $fh;
                }
-               # print logic will search for this value and use it to print out the unused devices data
-               /^unused devices/ {
-                       unusedDevices = gensub(/^unused devices:[[:space:]][<]?([^>]*)[>]?.*/, "\\1", 1, $0)
-                       print "UnusedDevices," unusedDevices
+       }
+       return $return;
+}
+
+sub set_downloader {
+       eval $start if $b_log;
+       $dl{'no-ssl'} = '';
+       $dl{'null'} = '';
+       $dl{'spider'} = '';
+       # we only want to use HTTP::Tiny if it's present in user system.
+       # It is NOT part of core modules. IO::Socket::SSL is also required 
+       # For some https connections so only use tiny as option if both present
+       if ($dl{'tiny'}){
+               if (check_module('HTTP::Tiny') && check_module('IO::Socket::SSL')){
+                       import HTTP::Tiny;
+                       import IO::Socket::SSL;
+                       $dl{'tiny'} = 1;
                }
-               
-               /^md/ {
-                       # reset for each record loop through
-                       deviceState = ""
-                       bitmapValues = ""
-                       blocks = ""
-                       chunkSize = ""
-                       components = ""
-                       device = ""
-                       deviceReport = ""
-                       finishTime = ""
-                       recoverSpeed = ""
-                       recoveryProgressBar = ""
-                       recoveryPercent = ""
-                       raidLevel = ""
-                       sectorsRecovered = ""
-                       separator = ""
-                       superBlock = ""
-                       uData = ""
-                       
-                       while ( !/^[[:space:]]*$/  ) {
-                               gsub(/,/, " ", $0 )
-                               gsub(/[[:space:]]+/, " ", $0 )
-                               if ( $0 ~ /^md/ ) {
-                                       device = gensub(/(md.*)[[:space:]]?:/, "\\1", "1", $1 )
-                               }
-                               if ( $0 ~ /mirror|raid[0-9]+/ ) {
-                                       raidLevel = gensub(/(.*)raid([0-9]+)(.*)/, "\\2", "g", $0 )
-                               }
-                               if ( $0 ~ /(active \(auto-read-only\)|active|inactive)/ ) {
-                                       deviceState = gensub(/(.*) (active \(auto-read-only\)|active|inactive) (.*)/, "\\2", "1", $0 )
-                               }
-                               # gawk will not return all the components using gensub, only last one
-                               separator = ""
-                               for ( i=3; i<=NF; i++ ) {
-                                       if ( $i ~ /[hs]d[a-z][0-9]*(\[[0-9]+\])?(\([SF]\))?/ ) {
-                                               components = components separator $i
-                                               separator=" "
-                                       }
-                               }
-                               if ( $0 ~ /blocks/ ) {
-                                       blocks = gensub(/(.*[[:space:]]+)?([0-9]+)[[:space:]]blocks.*/, "\\2", "1", $0)
-                               }
-                               if ( $0 ~ /super[[:space:]][0-9\.]+/ ) {
-                                       superBlock = gensub(/.*[[:space:]]super[[:space:]]([0-9\.]+)[[:space:]].*/, "\\1", "1", $0)
-                               }
-                               if ( $0 ~ /algorithm[[:space:]][0-9\.]+/ ) {
-                                       algorithm = gensub(/.*[[:space:]]algorithm[[:space:]]([0-9\.]+)[[:space:]].*/, "\\1", "1", $0)
-                               }
-                               if ( $0 ~ /\[[0-9]+\/[0-9]+\]/ ) {
-                                       deviceReport = gensub(/.*[[:space:]]\[([0-9]+\/[0-9]+)\][[:space:]].*/, "\\1", "1", $0)
-                                       uData = gensub(/.*[[:space:]]\[([U_]+)\]/, "\\1", "1", $0)
-                               }
-                               # need to avoid this:  bitmap: 0/10 pages [0KB], 16384KB chunk
-                               # while currently all the normal chunks are marked with k, not kb, this can change in the future
-                               if ( $0 ~ /[0-9]+[k] chunk/ && $0 !~ /bitmap/ ) {
-                                       chunkSize = gensub(/(.*) ([0-9]+[k]) chunk.*/, "\\2", "1", $0)
-                               }
-                               if ( $0 ~ /^resync=/ ) {
-                                       sub(/resync=/,"")
-                                       print "resyncStatus," $0
-                               }
-                               if ( $0 ~ /\[[=]*>[\.]*\].*(resync|recovery)/ ) {
-                                       recoveryProgressBar = gensub(/.*(\[[=]*>[\.]*\]).*/, "\\1",1,$0)
-                               }
-                               if ( $0 ~ / (resync|recovery)[[:space:]]*=/ ) {
-                                       recoveryPercent = gensub(/.* (resync|recovery)[[:space:]]*=[[:space:]]*([0-9\.]+%).*/, "\\1~\\2", 1 )
-                                       if ( $0 ~ /[[:space:]]\([0-9]+\/[0-9]+\)/ ) {
-                                               sectorsRecovered = gensub(/.* \(([0-9]+\/[0-9]+)\).*/, "\\1", 1, $0 )
-                                       }
-                                       if ( $0 ~ /finish[[:space:]]*=/ ) {
-                                               finishTime = gensub(/.* finish[[:space:]]*=[[:space:]]*([[0-9\.]+)([a-z]+) .*/, "\\1 \\2", 1, $0 )
-                                       }
-                                       if ( $0 ~ /speed[[:space:]]*=/ ) {
-                                               recoverSpeed = gensub(/.* speed[[:space:]]*=[[:space:]]*([[0-9\.]+)([a-z]+\/[a-z]+)/, "\\1 \\2", 1, $0 )
-                                       }
-                               }
-                               if ( $0 ~ /bitmap/ ) {
-                                       bitmapValues = gensub(/(.*[[:space:]])?bitmap:(.*)/, "\\2", 1, $0 )
-                               }
-                               
-                               getline
-                       }
-                       raidString = device "," deviceState "," raidLevel "," components "," deviceReport "," uData 
-                       raidString = raidString "," blocks "," superBlock "," algorithm "," chunkSize "," bitmapValues
-                       raidString = raidString "," recoveryProgressBar "," recoveryPercent "," sectorsRecovered "," finishTime "," recoverSpeed
-                       
-                       print raidString
-               }
-               ' <<< "$mdstat" ) )
-               IFS="$ORIGINAL_IFS"
-       else
-               if [[ $BSD_TYPE == 'bsd' ]];then
-                       get_raid_data_bsd
-               fi
-       fi
-       B_RAID_SET='true'
-       temp_array=${A_RAID_DATA[@]}
-       log_function_data "A_RAID_DATA: $temp_array"
-#      echo -e "A_RAID_DATA:\n${temp_array}"
-       
-       eval $LOGFE
+               else {
+                       $dl{'tiny'} = 0;
+               }
+       }
+       #print $dl{'tiny'} . "\n";
+       if ($dl{'tiny'}){
+               $dl{'dl'} = 'tiny';
+               $dl{'file'} = '';
+               $dl{'stdout'} = '';
+               $dl{'timeout'} = '';
+       }
+       elsif ( $dl{'curl'} && check_program('curl')  ){
+               $dl{'dl'} = 'curl';
+               $dl{'file'} = '  -L -s -o ';
+               $dl{'no-ssl'} = ' --insecure';
+               $dl{'stdout'} = ' -L -s ';
+               $dl{'timeout'} = ' -y ';
+       }
+       elsif ($dl{'wget'} && check_program('wget') ){
+               $dl{'dl'} = 'wget';
+               $dl{'file'} = ' -q -O ';
+               $dl{'no-ssl'} = ' --no-check-certificate';
+               $dl{'spider'} = ' -q --spider';
+               $dl{'stdout'} = '  -q -O -';
+               $dl{'timeout'} = ' -T ';
+       }
+       elsif ($dl{'fetch'} && check_program('fetch')){
+               $dl{'dl'} = 'fetch';
+               $dl{'file'} = ' -q -o ';
+               $dl{'no-ssl'} = ' --no-verify-peer';
+               $dl{'stdout'} = ' -q -o -';
+               $dl{'timeout'} = ' -T ';
+       }
+       elsif ( $bsd_type eq 'openbsd' && check_program('ftp') ){
+               $dl{'dl'} = 'ftp';
+               $dl{'file'} = ' -o ';
+               $dl{'null'} = ' 2>/dev/null';
+               $dl{'stdout'} = ' -o - ';
+               $dl{'timeout'} = '';
+       }
+       else {
+               $dl{'dl'} = '';
+       }
+       # no-ssl-opt is set to 1 with --no-ssl, so it is true, then assign
+       $dl{'no-ssl-opt'} = $dl{'no-ssl'} if $dl{'no-ssl-opt'};
+       eval $end if $b_log;
 }
 
-get_raid_data_bsd()
-{
-       eval $LOGFS
-       local zpool_path=$( type -p zpool 2>/dev/null )
-       local zpool_data=''
-       
-       if [[ -n $zpool_path ]];then
-               B_BSD_RAID='true'
-               # bsd sed does not support inserting a true \n so use this trick
-               zpool_data="$( $zpool_path list -v | sed $SED_RX 's/^([^[:space:]])/\
-\1/' )"
-#              echo "$zpool_data"
-               IFS=$'\n'
-               A_RAID_DATA=( $(
-               gawk '
-               BEGIN {
-                       raidString=""
-                       separator=""
-                       components=""
-                       reportSize=""
-                       blocksAvailable=""
-                       chunkRaidAllocated=""
-               }
-               /SIZE.*ALLOC/ {
-                       sub(/.*ALLOC.*/,"", $0)
-               }
-               /^[^[:space:]]/ {
-                       components=""
-                       separator=""
-                       raidLevel=""
-                       device=$1
-                       deviceState=$7
-                       reportSize=$2
-                       blocksAvailable=$4
-                       chunkRaidAllocated=$3
-                       
-                       # go to the next line now, this will probably need fixing later with weird data sets
-                       getline
-                       if ( $1 != "" ) {
-                               raidLevel="zfs " $1
-                       }
-                       
-                       while ( getline && $1 !~ /^$/ ) {
-                               # print $1
-                               components = components separator $1
-                               separator=" "
-                       }
-
-                       # print $1
-                       raidString = device "," deviceState "," raidLevel "," components "," reportSize "," uData 
-                       raidString = raidString "," blocksAvailable "," superBlock "," algorithm "," chunkRaidAllocated 
-                       # none of these are used currently
-                       raidString = raidString "," bitmapValues  "," recoveryProgressBar "," recoveryPercent 
-                       raidString = raidString "," sectorsRecovered "," finishTime "," recoverSpeed
-                       gsub(/~/,"",raidString)
-                       print raidString
-               }' <<< "$zpool_data" ) )
-               IFS="$ORIGINAL_IFS"
-               get_raid_component_data_bsd
-       fi
-       eval $LOGFE
-}
-
-# note, we've already tested for zpool so no further tests required
-get_raid_component_data_bsd()
-{
-       eval $LOGFS
-       local a_raid_data='' array_string='' component='' component_string='' 
-       local zpool_status='' device='' separator='' component_status=''
-       
-       for (( i=0; i<${#A_RAID_DATA[@]}; i++))
-       do
-               IFS=","
-               a_raid_data=( ${A_RAID_DATA[i]} )
-               IFS="$ORIGINAL_IFS"
-               separator=''
-               component_string=''
-               component_status=''
-               zpool_status=''
-               device=${a_raid_data[0]}
-               zpool_status="$( zpool status $device )"
-               # we will remove ONLINE for status and only use OFFLINE/DEGRADED as tests
-               # for print output display of issues with components
-               for component in ${a_raid_data[3]}
-               do
-                       component_status=$( gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                       }
-                       $1 ~ /^'$component'$/ {
-                               sub( /ONLINE/, "", $2 )
-                               print "'$component'" $2
-                               exit
-                       }' <<< "$zpool_status" )
-                       component_string="$component_string$separator$component_status"
-                       separator=' '
-               done
-               array_string="$device,${a_raid_data[1]},${a_raid_data[2]},$component_string,${a_raid_data[4]}"
-               array_string="$array_string,${a_raid_data[5]},${a_raid_data[6]},${a_raid_data[7]},${a_raid_data[8]}"
-               array_string="$array_string,${a_raid_data[9]},${a_raid_data[10]},${a_raid_data[11]},${a_raid_data[12]},"
-               array_string="$array_string${a_raid_data[13]},${a_raid_data[14]},${a_raid_data[15]}"
-               IFS=","
-               A_RAID_DATA[i]=$array_string
-               IFS="$ORIGINAL_IFS"
-       done
-       
-       eval $LOGFE
+sub set_perl_downloader {
+       my ($downloader) = @_;
+       $downloader =~ s/perl/tiny/;
+       return $downloader;
 }
-# get_raid_data_bsd;exit
 
-# Repos will be added as we get distro package manager data to create the repo data. 
-# This method will output the file name also, which is useful to create output that's 
-# neat and readable. Each line of the total number contains the following sections,
-# separated by a : for splitting in the print function
-# part one, repo type/string : part two, file name, if present, of info : part 3, repo data
-get_repo_data()
-{
-       eval $LOGFS
-       local repo_file='' repo_data_working='' repo_data_working2='' repo_line='' repo_files=''
-       local repo_name=''
-       local apt_file='/etc/apt/sources.list' yum_repo_dir='/etc/yum.repos.d/' yum_conf='/etc/yum.conf'
-       local pacman_conf='/etc/pacman.conf' pacman_repo_dir='/etc/pacman.d/' pisi_dir='/etc/pisi/'
-       local zypp_repo_dir='/etc/zypp/repos.d/' freebsd_conf='/etc/portsnap.conf'
-       
-       # apt - debian, buntus, also sometimes some yum/rpm repos may create apt repos here as well
-       if [[ -f $apt_file || -d $apt_file.d ]];then
-               REPO_DATA="$( grep -Esv '(^[[:space:]]*$|^[[:space:]]*#)' $apt_file $apt_file.d/*.list | sed $SED_RX 's/^(.*)/apt sources:\1/' )"
-       fi
-       # yum - fedora, redhat, centos, etc. Note that rpmforge also may create apt sources
-       # in /etc/apt/sources.list.d/. Therefore rather than trying to assume what package manager is
-       # actually running, inxi will merely note the existence of each repo type for apt/yum. 
-       # Also, in rpm, you can install apt-rpm for the apt-get command, so it's not good to check for
-       # only the commands in terms of selecting which repos to show.
-       if [[ -d $yum_repo_dir || -f $yum_conf || -d $zypp_repo_dir ]];then
-               if [[ -d $yum_repo_dir || -f $yum_conf ]];then
-                       # older redhats put their yum data in /etc/yum.conf
-                       repo_files=$( ls $yum_repo_dir*.repo $yum_conf 2>/dev/null )
-                       repo_name='yum'
-               elif [[ -d $zypp_repo_dir ]];then
-                       repo_files=$( ls $zypp_repo_dir*.repo 2>/dev/null )
-                       repo_name='zypp'
-               fi
-               if [[ -n $repo_files ]];then
-                       for repo_file in $repo_files
-                       do
-                               repo_data_working="$( gawk -v repoFile=$repo_file '
-                               # construct the string for the print function to work with, file name: data
-                               function print_line( fileName, repoId, repoUrl ){
-                                       print "'$repo_name' sources:" fileName ":" repoId repoUrl
-                               }
-                               BEGIN {
-                                       FS="\n"
-                                       IGNORECASE=1
-                                       enabledStatus=""
-                                       repoTitle=""
-                                       urlData=""
-                               }
-                               # this is a hack, assuming that each item has these fields listed, we collect the 3
-                               # items one by one, then when the url/enabled fields are set, we print it out and
-                               # reset the data. Not elegant but it works. Note that if enabled was not present
-                               # we assume it is enabled then, and print the line, reset the variables. This will
-                               # miss the last item, so it is printed if found in END
-                               /^\[.+\]/ {
-                                       if ( urlData != "" && repoTitle != "" ){
-                                               print_line( repoFile, repoTitle, urlData )
-                                               enabledStatus=""
-                                               urlData=""
-                                               repoTitle=""
-                                       }
-                                       gsub( /\[|\]/, "", $1 ) # strip out the brackets
-                                       repoTitle = $1 " ~ "
-                               }
-                               /^(mirrorlist|baseurl)/ {
-                                       sub( /(mirrorlist|baseurl)[[:space:]]*=[[:space:]]*/, "", $1 ) # strip out the field starter
-                                       urlData = $1
-                               }
-                               # note: enabled = 1. enabled = 0 means disabled
-                               /^enabled[[:space:]]*=/ {
-                                       enabledStatus = $1
-                               }
-                               # print out the line if all 3 values are found, otherwise if a new
-                               # repoTitle is hit above, it will print out the line there instead
-                               { 
-                                       if ( urlData != "" && enabledStatus != "" && repoTitle != "" ){
-                                               if ( enabledStatus !~ /enabled[[:space:]]*=[[:space:]]*0/ ){
-                                                       print_line( repoFile, repoTitle, urlData )
-                                               }
-                                               enabledStatus=""
-                                               urlData=""
-                                               repoTitle=""
-                                       }
-                               }
-                               END {
-                                       # print the last one if there is data for it
-                                       if ( urlData != ""  && repoTitle != "" ){
-                                               print_line( repoFile, repoTitle, urlData )
-                                       }
-                               }
-                               ' $repo_file )"
-                               
-                               # then load the global for each file as it gets filled
-                               if [[ -n $repo_data_working ]];then
-                                       if [[ -z $REPO_DATA ]];then
-                                               REPO_DATA="$repo_data_working"
-                                       else
-                                               REPO_DATA="$REPO_DATA
-$repo_data_working"
-                                       fi
-                                       repo_data_working=''
-                               fi
-                       done
-               fi
-       # pacman - archlinux, going to assume that pisi and arch/pacman, etc don't have the above issue with apt/yum
-       elif [[ -f $pacman_conf ]];then
-               # get list of mirror include files, trim white space off ends
-               repo_data_working="$( gawk '
-               BEGIN {
-                       FS="="
-                       IGNORECASE=1
-               }
-               /^[[:space:]]*Include/ {
-                       sub(/^[[:space:]]+|[[:space:]]+$/,"",$2)
-                       print $2
-               }
-               ' $pacman_conf )"
-               # sort into unique paths only, to be used to search for server = data
-               repo_data_working=$( sort -bu <<< "$repo_data_working" | uniq ) 
-               repo_data_working="$repo_data_working $pacman_conf"
-               for repo_file in $repo_data_working 
-               do
-                       if [[ -f $repo_file ]];then
-                               # inserting a new line after each found / processed match
-                               repo_data_working2="$repo_data_working2$( gawk -v repoFile=$repo_file '
-                               BEGIN {
-                                       FS="="
-                                       IGNORECASE=1
-                               }
-                               /^[[:space:]]*Server/ {
-                                       sub(/^[[:space:]]+|[[:space:]]+$/,"",$2)
-                                       print "pacman repo servers:" repoFile ":" $2 "\\n"
-                               }
-                               ' $repo_file )"
-                       else
-                               echo "Error: file listed in $pacman_conf does not exist - $repo_file"
-                       fi
-               done
-               # execute line breaks
-               REPO_DATA="$( echo -e $repo_data_working2 )"
-       # pisi - pardus
-       elif [[ -d $pisi_dir && -n $( type -p pisi ) ]];then
-               REPO_DATA="$( pisi list-repo )"
-               # now we need to create the structure: repo info: repo path
-               # we do that by looping through the lines of the output and then
-               # putting it back into the <data>:<url> format print repos expects to see
-               # note this structure in the data, so store first line and make start of line
-               # then when it's an http line, add it, and create the full line collection.
-# Pardus-2009.1 [Aktiv]
-#      http://packages.pardus.org.tr/pardus-2009.1/pisi-index.xml.bz2
-# Contrib [Aktiv]
-#      http://packages.pardus.org.tr/contrib-2009/pisi-index.xml.bz2
-               while read repo_line
-               do
-                       repo_line=$( gawk '
-                       {
-                               # need to dump leading/trailing spaces and clear out color codes for irc output
-                               sub(/^[[:space:]]+|[[:space:]]+$/,"",$0)
-#                              gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/,"",$0) # leaving this pattern in case need it
-                               gsub(/\e\[([0-9];)?[0-9]+m/,"",$0)
-                               print $0
-                       }' <<< $repo_line )
-                       if [[ -n $( grep '://' <<< $repo_line ) ]];then
-                               repo_data_working="$repo_data_working:$repo_line\n"
-                       else
-                               repo_data_working="${repo_data_working}pisi repo:$repo_line"
-                       fi
-               done <<< "$REPO_DATA"
-               # echo and execute the line breaks inserted
-               REPO_DATA="$( echo -e $repo_data_working )"
-       # Mandriva/Mageia using: urpmq
-       elif [[ -n $( type -p urpmq ) ]];then
-               REPO_DATA="$( urpmq --list-media active --list-url )"
-               # now we need to create the structure: repo info: repo path
-               # we do that by looping through the lines of the output and then
-               # putting it back into the <data>:<url> format print repos expects to see
-               # note this structure in the data, so store first line and make start of line
-               # then when it's an http line, add it, and create the full line collection.
-# Contrib ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/contrib/release
-# Contrib Updates ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/contrib/updates
-# Non-free ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/non-free/release
-# Non-free Updates ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/non-free/updates
-# Nonfree Updates (Local19) /mnt/data/mirrors/mageia/distrib/cauldron/x86_64/media/nonfree/updates
-               while read repo_line
-               do
-                       repo_line=$( gawk '
-                       {
-                               # need to dump leading/trailing spaces and clear out color codes for irc output
-                               sub(/^[[:space:]]+|[[:space:]]+$/,"",$0)
-#                              gsub(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/,"",$0) # leaving this pattern in case need it
-                               gsub(/\e\[([0-9];)?[0-9]+m/,"",$0)
-                               print $0
-                       }' <<< $repo_line )
-                       # urpmq output is the same each line, repo name space repo url, can be:
-                       # rsync://, ftp://, file://, http:// OR repo is locally mounted on FS in some cases
-                       if [[ -n $( grep -E '(://|[[:space:]]/)' <<< $repo_line ) ]];then
-                               # cut out the repo first
-                               repo_data_working2=$( grep -Eo '([^[:space:]]+://|[[:space:]]/).*' <<< $repo_line )
-                               # then get the repo name string by slicing out the url string
-                               repo_name=$( sed "s|[[:space:]]*$repo_data_working2||" <<< $repo_line )
-                               repo_data_working="${repo_data_working}urpmq repo:$repo_name:$repo_data_working2\n"
-                       fi
-               done <<< "$REPO_DATA"
-               # echo and execute the line breaks inserted
-               REPO_DATA="$( echo -e $repo_data_working )"
-       elif [[ -f $freebsd_conf ]];then
-               REPO_DATA="$( gawk -F '=' -v repoFile=$freebsd_conf '
-               BEGIN {
-                       IGNORECASE=1
-               }
-               /^SERVERNAME/ {
-                       print "BSD ports servers:" repoFile ":" $2
-               }
-               ' $freebsd_conf )"
-       fi
-       eval $LOGFE
-}
-
-get_runlevel_data()
-{
-       eval $LOGFS
-       local runlvl=''
-       local runlevel_path=$( type -p runlevel )
-       if [[ -n $runlevel_path ]];then
-               runlvl="$( $runlevel_path | gawk '{ print $2 }' )"
-       fi
-       echo $runlvl
-       eval $LOGFE
-}
-
-# note: it appears that at least as of 2014-01-13, /etc/inittab is going to be used for
-# default runlevel in upstart/sysvinit. systemd default is not always set so check to see 
-# if it's linked.
-get_runlevel_default()
-{
-       eval $LOGFS
-       local default_runlvl=''
-       local inittab='/etc/inittab'
-       local systemd_default='/etc/systemd/system/default.target'
-       local upstart_default='/etc/init/rc-sysinit.conf'
-       
-       # note: systemd systems do not necessarily have this link created
-       if [[ -L $systemd_default  ]];then
-               default_runlvl=$( readlink $systemd_default )
-               if [[ -n $default_runlvl ]];then
-                       default_runlvl=$( basename $default_runlvl )
-               fi
-       # http://askubuntu.com/questions/86483/how-can-i-see-or-change-default-run-level
-       # note that technically default can be changed at boot but for inxi purposes that does
-       # not matter, we just want to know the system default
-       elif [[ -e $upstart_default ]];then
-               # env DEFAULT_RUNLEVEL=2
-               default_runlvl=$( gawk -F '=' '/^env[[:space:]]+DEFAULT_RUNLEVEL/ {
-               print $2
-               }' $upstart_default )
-       fi
-       
-       # handle weird cases where null but inittab exists
-       if [[ -z $default_runlvl && -f $inittab ]];then
-               default_runlvl=$( gawk -F ':' '
-               /^id.*initdefault/ {
-                       print $2
-               }' $inittab )
-       fi
-       echo $default_runlvl
-       eval $LOGFE
+#### -------------------------------------------------------------------
+#### ERROR HANDLER
+#### -------------------------------------------------------------------
+
+sub error_handler {
+       eval $start if $b_log;
+       my ( $err, $one, $two) = @_;
+       my ($b_help,$b_recommends);
+       my ($b_exit,$errno) = (1,0);
+       my $message = do {
+               if ( $err eq 'empty' ) { 'empty value' }
+               ## Basic rules
+               elsif ( $err eq 'not-in-irc' ) { 
+                       $errno=1; "You can't run option $one in an IRC client!" }
+               ## Internal/external options
+               elsif ( $err eq 'bad-arg' ) { 
+                       $errno=10; $b_help=1; "Unsupported value: $two for option: $one" }
+               elsif ( $err eq 'bad-arg-int' ) { 
+                       $errno=11; "Bad internal argument: $one" }
+               elsif ( $err eq 'distro-block' ) { 
+                       $errno=20; "Option: $one has been disabled by the $self_name distribution maintainer." }
+               elsif ( $err eq 'option-feature-incomplete' ) { 
+                       $errno=21; "Option: '$one' feature: '$two' has not been implemented yet." }
+               elsif ( $err eq 'unknown-option' ) { 
+                       $errno=22; $b_help=1; "Unsupported option: $one" }
+               ## Data
+               elsif ( $err eq 'open-data' ) { 
+                       $errno=32; "Error opening data for reading: $one \nError: $two" }
+               elsif ( $err eq 'download-error' ) { 
+                       $errno=33; "Error downloading file with $dl{'dl'}: $one \nError: $two" }
+               ## Files:
+               elsif ( $err eq 'copy-failed' ) { 
+                       $errno=40; "Error copying file: $one \nError: $two" }
+               elsif ( $err eq 'create' ) { 
+                       $errno=41; "Error creating file: $one \nError: $two" }
+               elsif ( $err eq 'downloader-error' ) { 
+                       $errno=42; "Error downloading file: $one \nfor download source: $two" }
+               elsif ( $err eq 'file-corrupt' ) { 
+                       $errno=43; "Downloaded file is corrupted: $one" }
+               elsif ( $err eq 'mkdir' ) { 
+                       $errno=44; "Error creating directory: $one \nError: $two" }
+               elsif ( $err eq 'open' ) { 
+                       $errno=45; $b_exit=0; "Error opening file: $one \nError: $two" }
+               elsif ( $err eq 'open-dir' ) { 
+                       $errno=46; "Error opening directory: $one \nError: $two" }
+               elsif ( $err eq 'output-file-bad' ) { 
+                       $errno=47; "Value for --output-file must be full path, a writable directory, \nand include file name. Path: $two" }
+               elsif ( $err eq 'not-writable' ) { 
+                       $errno=48; "The file: $one is not writable!" }
+               elsif ( $err eq 'open-dir-failed' ) { 
+                       $errno=49; "The directory: $one failed to open with error: $two" }
+               elsif ( $err eq 'remove' ) { 
+                       $errno=50; "Failed to remove file: $one Error: $two" }
+               elsif ( $err eq 'rename' ) { 
+                       $errno=51; "There was an error moving files: $one\nError: $two" }
+               elsif ( $err eq 'write' ) { 
+                       $errno=52; "Failed writing file: $one - Error: $two!" }
+               ## Downloaders
+               elsif ( $err eq 'missing-downloader' ) { 
+                       $errno=60; "Downloader program $two could not be located on your system." }
+               elsif ( $err eq 'missing-perl-downloader' ) { 
+                       $errno=61; $b_recommends=1; "Perl downloader missing required module." }
+               ## FTP
+               elsif ( $err eq 'ftp-bad-path' ) { 
+                       $errno=70; "Unable to locate for FTP upload file:\n$one" }
+               elsif ( $err eq 'ftp-login' ) { 
+                       $errno=71; "There was an error with login to ftp server: $one" }
+               elsif ( $err eq 'ftp-upload' ) { 
+                       $errno=72; "There was an error with upload to ftp server: $one" }
+               ## Modules
+               elsif ( $err eq 'required-module' ) { 
+                       $errno=80; $b_recommends=1; "The required $one Perl module is not installed:\n$two" }
+               ## DEFAULT
+               else {
+                       $errno=255; "Error handler ERROR!! Unsupported options: $err!"}
+       };
+       print_line("Error $errno: $message\n");
+       if ($b_help){
+               print_line("Check -h for correct parameters.\n");
+       }
+       if ($b_recommends){
+               print_line("See --recommends for more information.\n");
+       }
+       eval $end if $b_log;
+       exit 0 if $b_exit;
 }
 
-get_sensors_data()
+sub error_defaults {
+       my ($type,$one) = @_;
+       $one ||= '';
+       my %errors = (
+       'download-error' => "Download Failure:\n$one\n",
+       );
+       return $errors{$type};
+}
+
+#### -------------------------------------------------------------------
+#### RECOMMENDS
+#### -------------------------------------------------------------------
+
+## CheckRecommends
 {
-       eval $LOGFS
-       
-       
-       local temp_array=''
-               
-       IFS=$'\n'
-       if [[ -n $Sensors_Data ]];then
-               # note: non-configured sensors gives error message, which we need to redirect to stdout
-               # also, -F ':' no space, since some cases have the data starting right after,like - :1287
-               A_SENSORS_DATA=( $( 
-               gawk -F ':' -v userCpuNo="$SENSORS_CPU_NO" '
-               BEGIN {
-                       IGNORECASE=1
-                       core0Temp="" # only if all else fails...
-                       cpuTemp=""
-                       cpuTempReal=""
-                       fanWorking=""
-                       indexCountaFanMain=0
-                       indexCountaFanDefault=0
-                       i=""
-                       j=""
-                       moboTemp=""
-                       moboTempReal=""
-                       psuTemp=""
-                       separator=""
-                       sysFanString=""
-                       temp1=""
-                       temp2=""
-                       tempFanType="" # set to 1 or 2
-                       tempUnit=""
-                       tempWorking=""
-                       tempWorkingUnit=""
-               }
-               # new data arriving: gpu temp in sensors, have to skip that
-               /^('"$SENSORS_GPU_SEARCH"')-pci/ {
-                       while ( getline && !/^$/ ) {
-                               # do nothing, just skip it
-                       }
-               }
-               # dumping the extra + signs after testing for them,  nobody has negative temps.
-               # also, note gawk treats ° as a space, so we have to get the C/F data
-               # there are some guesses here, but with more sensors samples it will get closer.
-               # note: using arrays starting at 1 for all fan arrays to make it easier overall
-               # more validation because gensub if fails to get match returns full string, so
-               # we have to be sure we are working with the actual real string before assiging
-               # data to real variables and arrays. Extracting C/F degree unit as well to use
-               # when constructing temp items for array. 
-               # note that because of charset issues, no tempUnit="°" tempWorkingUnit degree sign 
-               # used, but it is required in testing regex to avoid error.
-               /^(M\/B|MB|SIO|SYS)(.*)\+([0-9]+)(.*)[ \t°](C|F)/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       moboTemp=gensub( /[ \t]+\+([0-9\.]*)(.*)/, "\\1", 1, $2 )
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+package CheckRecommends;
+sub run {
+       main::error_handler('not-in-irc', 'recommends') if $b_irc;
+       my (@data,@rows);
+       my $line = make_line();
+       my $pm = get_pm();
+       @data = basic_data($line);
+       push @rows,@data;
+       if (!$bsd_type){
+               @data = check_items('required system directories',$line,$pm);
+               push @rows,@data;
+       }
+       @data = check_items('recommended system programs',$line,$pm);
+       push @rows,@data;
+       @data = check_items('recommended display information programs',$line,$pm);
+       push @rows,@data;
+       @data = check_items('recommended downloader programs',$line,$pm);
+       push @rows,@data;
+       @data = check_items('recommended Perl modules',$line,$pm);
+       push @rows,@data;
+       @data = check_items('recommended directories',$line,'');
+       push @rows,@data;
+       @data = check_items('recommended files',$line,'');
+       push @rows,@data;
+       @data = (
+       ['0', '', '', "$line"],
+       ['0', '', '', "Ok, all done with the checks. Have a nice day."],
+       ['0', '', '', " "],
+       );
+       push @rows,@data;
+       #print Data::Dumper::Dumper \@rows;
+       main::print_basic(@rows); 
+       exit 1;
+}
+
+sub basic_data {
+       my ($line) = @_;
+       my (@data,@rows);
+       my $client = $client{'name-print'};
+       $client .= ' ' . $client{'version'} if $client{'version'};
+       my $default_shell = 'N/A';
+       if ($ENV{'SHELL'}){
+               $default_shell = $ENV{'SHELL'};
+               $default_shell =~ s/.*\///;
+       }
+       my $sh = main::check_program('sh');
+       my $sh_real = Cwd::abs_path($sh);
+       @rows = (
+       ['0', '', '', "$self_name will now begin checking for the programs it needs 
+       to operate."],
+       ['0', '', '', "" ],
+       ['0', '', '', "Check $self_name --help or the man page (man $self_name) 
+       to see what options are available." ],
+       ['0', '', '', "$line" ],
+       ['0', '', '', "Test: core tools:" ],
+       ['0', '', '', "" ],
+       ['0', '', '', "Perl version: ^$]" ],
+       ['0', '', '', "Current shell: " . $client ],
+       ['0', '', '', "Default shell: " . $default_shell ],
+       ['0', '', '', "sh links to: $sh_real" ],
+       );
+       return @rows;
+}
+sub check_items {
+       my ($type,$line,$pm) = @_;
+       my (@data,%info,@missing,$row,@rows,$result,@unreadable);
+       my ($b_dir,$b_file,$b_module,$b_program,$item);
+       my ($about,$extra,$extra2,$extra3,$extra4,$info_os,$install) = ('','','','','','info','');
+       if ($type eq 'required system directories'){
+               @data = qw(/proc /sys);
+               $b_dir = 1;
+               $item = 'Directory';
+       }
+       elsif ($type eq 'recommended system programs'){
+               if ($bsd_type){
+                       @data = qw(camcontrol dig dmidecode fdisk file glabel gpart ifconfig ipmi-sensors 
+                       ipmitool lsusb sudo smartctl sysctl tree upower uptime usbdevs);
+                       $info_os = 'info-bsd';
                }
-               /^CPU(.*)\+([0-9]+)(.*)[ \t°](C|F)/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       cpuTemp=gensub( /[ \t]+\+([0-9\.]+)(.*)/, "\\1", 1, $2 )
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+               else {
+                       @data = qw(dig dmidecode fdisk file hddtemp ifconfig ip ipmitool ipmi-sensors
+                       lsblk lsusb modinfo runlevel sensors strings sudo tree upower uptime);
                }
-               /^(P\/S|Power)(.*)\+([0-9]+)(.*)[ \t°](C|F)/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       psuTemp=gensub( /[ \t]+\+([0-9\.]+)(.*)/, "\\1", 1, $2 )
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+               $b_program = 1;
+               $item = 'Program';
+               $extra2 = "Note: IPMI sensors are generally only found on servers. To access 
+               that data, you only need one of the ipmi items.";
+       }
+       elsif ($type eq 'recommended display information programs'){
+               if ($bsd_type){
+                       @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr);
+                       $info_os = 'info-bsd';
                }
-               $1 ~ /^temp1$/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       tempWorking=gensub( /[ \t]+\+([0-9\.]+)(.*)/, "\\1", 1, $2 )
-                       if ( temp1 == "" || tempWorking > 0 ) {
-                               temp1=tempWorking
-                       }
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+               else {
+                       @data = qw(glxinfo wmctrl xdpyinfo xprop xrandr);
                }
-               $1 ~ /^temp2$/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       tempWorking=gensub( /[ \t]+\+([0-9\.]+)(.*)/, "\\1", 1, $2 )
-                       if ( temp2 == "" || tempWorking > 0 ) {
-                               temp2=tempWorking
-                       }
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+               $b_program = 1;
+               $item = 'Program';
+       }
+       elsif ($type eq 'recommended downloader programs'){
+               if ($bsd_type){
+                       @data = qw(curl dig fetch ftp wget);
+                       $info_os = 'info-bsd';
                }
-               
-               # final fallback if all else fails, funtoo user showed sensors putting
-               # temp on wrapped second line, not handled
-               /^(core0|core 0)(.*)\+([0-9]+)(.*)[ \t°](C|F)/ && $2 ~ /^[ \t]*\+([0-9]+)/ {
-                       tempWorking=gensub( /[ \t]+\+([0-9\.]+)(.*)/, "\\1", 1, $2 )
-                       if ( core0Temp == "" || tempWorking > 0 ) {
-                               core0Temp=tempWorking
-                       }
-                       tempWorkingUnit=gensub( /[ \t]+\+([0-9\.]+)[ \t°]+([CF])(.*)/, "\\2", 1, $2 )
-                       if ( tempWorkingUnit ~ /^C|F$/ && tempUnit == "" ){
-                               tempUnit=tempWorkingUnit
-                       }
+               else {
+                       @data = qw(curl dig wget);
                }
-               
-               # note: can be cpu fan:, cpu fan speed:, etc. Some cases have no space before
-               # $2 starts (like so :1234 RPM), so skip that space test in regex
-               /^CPU(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       aFanMain[1]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
+               $b_program = 1;
+               $extra = ' (You only need one of these)';
+               $extra2 = "Perl HTTP::Tiny is the default downloader tool if IO::Socket::SSL is present.
+               See --help --alt 40-44 options for how to override default downloader(s) in case of issues. ";
+               $extra3 = "If dig is installed, it is the default for WAN IP data. 
+               Strongly recommended. Dig is fast and accurate.";
+               $extra4 = ". However, you really only need dig in most cases. All systems should have ";
+               $extra4 .= "at least one of the downloader options present.";
+               $item = 'Program';
+       }
+       elsif ($type eq 'recommended Perl modules'){
+               @data = qw(HTTP::Tiny IO::Socket::SSL Time::HiRes Cpanel::JSON::XS JSON::XS XML::Dumper);
+               $b_module = 1;
+               $item = 'Perl Module';
+               $extra = ' (Optional)';
+               $extra2 = "None of these are strictly required, but if you have them all, you can eliminate
+               some recommended non Perl programs from the install. ";
+               $extra3 = "HTTP::Tiny and IO::Socket::SSL must both be present to use as a downloader option. 
+               For json export Cpanel::JSON::XS is preferred over JSON::XS.";
+       }
+       elsif ($type eq 'recommended directories'){
+               if ($bsd_type){
+                       @data = qw(/dev);
                }
-               /^(M\/B|MB|SYS)(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       aFanMain[2]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
+               else {
+                       @data = qw(/dev /dev/disk/by-id /dev/disk/by-label /dev/disk/by-path 
+                       /dev/disk/by-uuid /sys/class/dmi/id);
                }
-               /(Power|P\/S|POWER)(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       aFanMain[3]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
+               $b_dir = 1;
+               $item = 'Directory';
+       }
+       elsif ($type eq 'recommended files'){
+               if ($bsd_type){
+                       @data = qw(/var/run/dmesg.boot /var/log/Xorg.0.log);
                }
-               # note that the counters are dynamically set for fan numbers here
-               # otherwise you could overwrite eg aux fan2 with case fan2 in theory
-               # note: cpu/mobo/ps are 1/2/3
-               # NOTE: test: ! i in array does NOT work, this appears to be an awk/gawk bug
-               /^(AUX(1)? |CASE(1)? |CHASSIS(1)? )(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       for ( i = 4; i < 7; i++ ){
-                               if ( i in aFanMain ){
-                                       ##
-                               }
-                               else {
-                                       aFanMain[i]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
-                                       break
-                               }
-                       }
+               else {
+                       @data = qw(/etc/lsb-release /etc/os-release /proc/asound/cards 
+                       /proc/asound/version /proc/cpuinfo /proc/mdstat /proc/meminfo /proc/modules 
+                       /proc/mounts /proc/scsi/scsi /var/log/Xorg.0.log );
                }
-               /^(AUX([2-9]) |CASE([2-9]) |CHASSIS([2-9]) )(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       for ( i = 5; i < 30; i++ ){
-                               if ( i in aFanMain ) {
-                                       ##
-                               }
-                               else {
-                                       sysFanNu = i
-                                       aFanMain[i]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
-                                       break
-                               }
-                       }
+               $b_file = 1;
+               $item = 'File';
+               $extra2 = "Note that not all of these are used by every system, 
+               so if one is missing it's usually not a big deal.";
+       }
+       @rows = (
+       ['0', '', '', "$line" ],
+       ['0', '', '', "Test: $type$extra:" ],
+       ['0', '', '', " " ],
+       );
+       if ($extra2){
+               $rows[scalar @rows] = (['0', '', '', $extra2]);
+               $rows[scalar @rows] = (['0', '', '', ' ']);
+       }
+       if ($extra3){
+               $rows[scalar @rows] = (['0', '', '', $extra3]);
+               $rows[scalar @rows] = (['0', '', '', ' ']);
+       }
+       foreach (@data){
+               $install = '';
+               $about = '';
+               %info = item_data($_);
+               $about = $info{$info_os};
+               if ( ( $b_dir && -d $_ ) || ( $b_file && -r $_ ) ||
+                    ($b_program && main::check_program($_) ) || ($b_module && main::check_module($_)) ){
+                       $result = 'Present';
                }
-               # in rare cases syntax is like: fan1: xxx RPM
-               /^(FAN(1)?[ \t:])(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       aFanDefault[1]=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
+               elsif ($b_file && -f $_){
+                       $result = 'Unreadable';
+                       push @unreadable, "$_";
                }
-               /^FAN([2-9]|1[0-9])(.*)[ \t]*([0-9]+)[ \t]RPM/ {
-                       fanWorking=gensub( /[ \t]*([0-9]+)[ \t]+(.*)/, "\\1", 1, $2 )
-                       sysFanNu=gensub( /fan([0-9]+)/, "\\1", 1, $1 )
-                       if ( sysFanNu ~ /^([0-9]+)$/ ) {
-                               # add to array if array index does not exist OR if number is > existing number
-                               if ( sysFanNu in aFanDefault ) {
-                                       if ( fanWorking >= aFanDefault[sysFanNu] ) {
-                                               aFanDefault[sysFanNu]=fanWorking
-                                       }
-                               }
-                               else {
-                                       aFanDefault[sysFanNu]=fanWorking
-                               }
-                       }
+               else {
+                       $result = 'Missing';
+                       $install = " ~ Install package: $info{$pm}" if (($b_program || $b_module) && $pm);
+                       push @missing, "$_$install";
                }
-               
-               END {
-                       # first we need to handle the case where we have to determine which temp/fan to use for cpu and mobo:
-                       # note, for rare cases of weird cool cpus, user can override in their prefs and force the assignment
-                       if ( temp1 != "" && temp2 != "" ){
-                               if ( userCpuNo != "" && userCpuNo ~ /(1|2)/ ) {
-                                       tempFanType=userCpuNo
-                               }
-                               else {
-                                       # first some fringe cases with cooler cpu than mobo: assume which is cpu temp based on fan speed
-                                       # but only if other fan speed is 0
-                                       if ( temp1 >= temp2 && 1 in aFanDefault && 2 in aFanDefault && aFanDefault[1] == 0 && aFanDefault[2] > 0 ) {
-                                               tempFanType=2
-                                       }
-                                       else if ( temp2 >= temp1 && 1 in aFanDefault && 2 in aFanDefault && aFanDefault[2] == 0 && aFanDefault[1] > 0 ) {
-                                               tempFanType=1
-                                       }
-                                       # then handle the standard case if these fringe cases are false
-                                       else if ( temp1 >= temp2 ) {
-                                               tempFanType=1
-                                       }
-                                       else {
-                                               tempFanType=2
-                                       }
-                               }
-                       }
-                       # need a case for no temps at all reported, like with old intels
-                       else if ( temp2 == "" && cpuTemp == "" ){
-                               if ( temp1 == "" && moboTemp == "" ){
-                                       tempFanType=1
-                               }
-                               else if ( temp1 != "" && moboTemp == "" ){
-                                       tempFanType=1
-                               }
-                               else if ( temp1 != "" && moboTemp != "" ){
-                                       tempFanType=1
-                               }
-                       }
-                       
-                       # then get the real cpu temp, best guess is hottest is real
-                       if ( cpuTemp != "" ){
-                               cpuTempReal=cpuTemp
-                       }
-                       else if ( tempFanType != "" ){
-                               if ( tempFanType == 1 ){
-                                       cpuTempReal=temp1
-                               }
-                               else {
-                                       cpuTempReal=temp2
-                               }
-                       }
-                       else {
-                               cpuTempReal=temp1
-                       }
-                       # if all else fails, use core0 temp if it is present and cpu is null
-                       if ( cpuTempReal == "" && core0Temp != "" ) {
-                               cpuTempReal=core0Temp
-                       }
+               $row = make_row($_,$about,$result);
+               $rows[scalar @rows] = (['0', '', '', $row]);
+       }
+       $rows[scalar @rows] = (['0', '', '', " "]);
+       if (@missing){
+               $rows[scalar @rows] = (['0', '', '', "The following $type are missing$extra4:"]);
+               foreach (@missing) {
+                       $rows[scalar @rows] = (['0', '', '', "$item: $_"]);
+               }
+       }
+       if (@unreadable){
+               $rows[scalar @rows] = (['0', '', '', "The following $type are not readable: "]);
+               foreach (@unreadable) {
+                       $rows[scalar @rows] = (['0', '', '', "$item: $_"]);
+               }
+       }
+       if (!@missing && !@unreadable){
+               $rows[scalar @rows] = (['0', '', '', "All $type are present"]);
+       }
+       return @rows;
+}
 
-                       # then the real mobo temp
-                       if ( moboTemp != "" ){
-                               moboTempReal=moboTemp
-                       }
-                       else if ( tempFanType != "" ){
-                               if ( tempFanType == 1 ) {
-                                       moboTempReal=temp2
-                               }
-                               else {
-                                       moboTempReal=temp1
-                               }
-                       }
-                       else {
-                               moboTempReal=temp2
-                       }
-                       # then set the cpu fan speed
-                       if ( aFanMain[1] == "" ) {
-                               # note, you cannot test for aFanDefault[1] or [2] != "" 
-                               # because that creates an array item in gawk just by the test itself
-                               if ( tempFanType == 1 && 1 in aFanDefault ) {
-                                       aFanMain[1]=aFanDefault[1]
-                                       aFanDefault[1]=""
-                               }
-                               else if ( tempFanType == 2 && 2 in aFanDefault ) {
-                                       aFanMain[1]=aFanDefault[2]
-                                       aFanDefault[2]=""
-                               }
-                       }
+sub item_data {
+       my ($type) = @_;
+       my %data = (
+       # directory data
+       '/sys/class/dmi/id' => ({
+       'info' => '-M system, motherboard, bios',
+       }),
+       '/dev' => ({
+       'info' => '-l,-u,-o,-p,-P,-D disk partition data',
+       }),
+       '/dev/disk/by-id' => ({
+       'info' => '-D serial numbers',
+       }),
+       '/dev/disk/by-path' => ({
+       'info' => '-D extra data',
+       }),
+       '/dev/disk/by-label' => ({
+       'info' => '-l,-o,-p,-P partition labels',
+       }),
+       '/dev/disk/by-uuid' => ({
+       'info' => '-u,-o,-p,-P partition uuid',
+       }),
+       '/proc' => ({
+       'info' => '',
+       }),
+       '/sys' => ({
+       'info' => '',
+       }),
+       # file data
+       '/etc/lsb-release' => ({
+       'info' => '-S distro version data (older version)',
+       }),
+       '/etc/os-release' => ({
+       'info' => '-S distro version data (newer version)',
+       }),
+       '/proc/asound/cards' => ({
+       'info' => '-A sound card data',
+       }),
+       '/proc/asound/version' => ({
+       'info' => '-A ALSA data',
+       }),
+       '/proc/cpuinfo' => ({
+       'info' => '-C cpu data',
+       }),
+       '/proc/mdstat' => ({
+       'info' => '-R mdraid data (if you use dm-raid)',
+       }),
+       '/proc/meminfo' => ({
+       'info' => '-I,-tm, -m memory data',
+       }),
+       '/proc/modules' => ({
+       'info' => '-G module data (sometimes)',
+       }),
+       '/proc/mounts' => ({
+       'info' => '-P,-p partition advanced data',
+       }),
+       '/proc/scsi/scsi' => ({
+       'info' => '-D Advanced hard disk data (used rarely)',
+       }),
+       '/var/log/Xorg.0.log' => ({
+       'info' => '-G graphics driver load status',
+       }),
+       '/var/run/dmesg.boot' => ({
+       'info' => '-D,-d disk data',
+       }),
+       # system tools
+       # apt-dpkg,apt-get; pm-arch,pacman; rpm-redhat,suse
+       'curl' => ({
+       'info' => '-i (if no dig); -w,-W; -U',
+       'info-bsd' => '-i (if no dig); -w,-W; -U',
+       'apt' => 'curl',
+       'pacman' => 'curl',
+       'rpm' => 'curl',
+       }),
+       'camcontrol' => ({
+       'info' => '',
+       'info-bsd' => '-R; -D; -P. Get actual gptid /dev path',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'dig' => ({
+       'info' => '-i wlan IP',
+       'info-bsd' => '-i wlan IP',
+       'apt' => 'dnsutils',
+       'pacman' => 'dnsutils',
+       'rpm' => 'bind-utils',
+       }),
+       'dmidecode' => ({
+       'info' => '-M if no sys machine data; -m',
+       'info-bsd' => '-M if null sysctl; -m; -B if null sysctl',
+       'apt' => 'dmidecode',
+       'pacman' => 'dmidecode',
+       'rpm' => 'dmidecode',
+       }),
+       'fdisk' => ({
+       'info' => '-D partition scheme (fallback)',
+       'info-bsd' => '-D partition scheme',
+       'apt' => 'fdisk',
+       'pacman' => 'util-linux',
+       'rpm' => 'util-linux',
+       }),
+       'fetch' => ({
+       'info' => '',
+       'info-bsd' => '-i (if no dig); -w,-W; -U',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'file' => ({
+       'info' => '-o unmounted file system (if no lsblk)',
+       'info-bsd' => '-o unmounted file system',
+       'apt' => 'file',
+       'pacman' => 'file',
+       'rpm' => 'file',
+       }),
+       'ftp' => ({
+       'info' => '',
+       'info-bsd' => '-i (if no dig); -w,-W; -U',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'glabel' => ({
+       'info' => '',
+       'info-bsd' => '-R; -D; -P. Get actual gptid /dev path',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'gpart' => ({
+       'info' => '',
+       'info-bsd' => '-p,-P file system, size',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'hciconfig' => ({
+       'info' => 'Experimental',
+       'info-bsd' => '',
+       'apt' => 'bluez',
+       'pacman' => 'bluez-utils',
+       'rpm' => 'bluez-utils',
+       }),
+       'hddtemp' => ({
+       'info' => '-Dx show hdd temp',
+       'info-bsd' => '-Dx show hdd temp',
+       'apt' => 'hddtemp',
+       'pacman' => 'hddtemp',
+       'rpm' => 'hddtemp',
+       }),
+       'ifconfig' => ({
+       'info' => '-i ip LAN (deprecated)',
+       'info-bsd' => '-i ip LAN',
+       'apt' => 'net-tools',
+       'pacman' => 'net-tools',
+       'rpm' => 'net-tools',
+       }),
+       'ip' => ({
+       'info' => '-i ip LAN',
+       'info-bsd' => '',
+       'apt' => 'iproute',
+       'pacman' => 'iproute2',
+       'rpm' => 'iproute',
+       }),
+       'ipmi-sensors' => ({
+       'info' => '-s IPMI sensors (servers)',
+       'info-bsd' => '',
+       'apt' => 'freeipmi-tools',
+       'pacman' => 'freeipmi',
+       'rpm' => 'freeipmi',
+       }),
+       'ipmitool' => ({
+       'info' => '-s IPMI sensors (servers)',
+       'info-bsd' => '-s IPMI sensors (servers)',
+       'apt' => 'ipmitool',
+       'pacman' => 'ipmitool',
+       'rpm' => 'ipmitool',
+       }),
+       'lsblk' => ({
+       'info' => '-o unmounted file system (best option)',
+       'info-bsd' => '-o unmounted file system',
+       'apt' => 'util-linux',
+       'pacman' => 'util-linux',
+       'rpm' => 'util-linux-ng',
+       }),
+       'lsusb' => ({
+       'info' => '-A usb audio; -N usb networking; --usb',
+       'info-bsd' => '-A; -N; --usb. Alternate to usbdevs',
+       'apt' => 'usbutils',
+       'pacman' => 'usbutils',
+       'rpm' => 'usbutils',
+       }),
+       'modinfo' => ({
+       'info' => 'Ax; -Nx module version',
+       'info-bsd' => '',
+       'apt' => 'module-init-tools',
+       'pacman' => 'module-init-tools',
+       'rpm' => 'module-init-tools',
+       }),
+       'runlevel' => ({
+       'info' => '-I fallback to Perl',
+       'info-bsd' => '',
+       'apt' => 'systemd or sysvinit',
+       'pacman' => 'systemd',
+       'rpm' => 'systemd or sysvinit',
+       }),
+       'sensors' => ({
+       'info' => '-s sensors output',
+       'info-bsd' => '',
+       'apt' => 'lm-sensors',
+       'pacman' => 'lm-sensors',
+       'rpm' => 'lm-sensors',
+       }),
+       'smartctl' => ({
+       'info' => '-Dx show hdd temp',
+       'info-bsd' => '-Dx show hdd temp',
+       'apt' => '',
+       'pacman' => '',
+       'rpm' => '',
+       }),
+       'strings' => ({
+       'info' => '-I sysvinit version',
+       'info-bsd' => '',
+       'apt' => 'binutils',
+       'pacman' => '?',
+       'rpm' => '?',
+       }),
+       'sysctl' => ({
+       'info' => '',
+       'info-bsd' => '-C; -I; -m; -tm',
+       'apt' => '?',
+       'pacman' => '?',
+       'rpm' => '?',
+       }),
+       'sudo' => ({
+       'info' => '-Dx hddtemp-user; -o file-user',
+       'info-bsd' => '-Dx hddtemp-user; -o file-user',
+       'apt' => 'sudo',
+       'pacman' => 'sudo',
+       'rpm' => 'sudo',
+       }),
+       'tree' => ({
+       'info' => '--debugger 20,21 /sys tree',
+       'info-bsd' => '--debugger 20,21 /sys tree',
+       'apt' => 'tree',
+       'pacman' => 'tree',
+       'rpm' => 'tree',
+       }),
+       'upower' => ({
+       'info' => '-sx attached device battery info',
+       'info-bsd' => '-sx attached device battery info',
+       'apt' => 'upower',
+       'pacman' => 'upower',
+       'rpm' => 'upower',
+       }),
+       'uptime' => ({
+       'info' => '-I uptime',
+       'info-bsd' => '-I uptime',
+       'apt' => 'procps',
+       'pacman' => 'procps',
+       'rpm' => 'procps',
+       }),
+       'usbdevs' => ({
+       'info' => '',
+       'info-bsd' => '-A; -N; --usb;',
+       'apt' => 'usbutils',
+       'pacman' => 'usbutils',
+       'rpm' => 'usbutils',
+       }),
+       'wget' => ({
+       'info' => '-i (if no dig); -w,-W; -U',
+       'info-bsd' => '-i (if no dig); -w,-W; -U',
+       'apt' => 'wget',
+       'pacman' => 'wget',
+       'rpm' => 'wget',
+       }),
+       # display tools
+       'glxinfo' => ({
+       'info' => '-G glx info',
+       'info-bsd' => '-G glx info',
+       'apt' => 'mesa-utils',
+       'pacman' => 'mesa-demos',
+       'rpm' => 'glx-utils (openSUSE 12.3 and later Mesa-demo-x)',
+       }),
+       'wmctrl' => ({
+       'info' => '-S active window manager (fallback)',
+       'info-bsd' => '-S active window managerr (fallback)',
+       'apt' => 'wmctrl',
+       'pacman' => 'wmctrl',
+       'rpm' => 'wmctrl',
+       }),
+       'xdpyinfo' => ({
+       'info' => '-G multi screen resolution',
+       'info-bsd' => '-G multi screen resolution',
+       'apt' => 'X11-utils',
+       'pacman' => 'xorg-xdpyinfo',
+       'rpm' => 'xorg-x11-utils',
+       }),
+       'xprop' => ({
+       'info' => '-S desktop data',
+       'info-bsd' => '-S desktop data',
+       'apt' => 'X11-utils',
+       'pacman' => 'xorg-xprop',
+       'rpm' => 'x11-utils',
+       }),
+       'xrandr' => ({
+       'info' => '-G single screen resolution',
+       'info-bsd' => '-G single screen resolution',
+       'apt' => 'x11-xserver-utils',
+       'pacman' => 'xrandr',
+       'rpm' => 'x11-server-utils',
+       }),
+       # Perl modules
+       'Cpanel::JSON::XS' => ({
+       'info' => '--output json - required for export.',
+       'info-bsd' => '--output json - required for export.',
+       'apt' => 'libcpanel-json-xs-perl',
+       'pacman' => 'perl-cpanel-json-xs',
+       'rpm' => 'perl-Cpanel-JSON-XS',
+       }),
+       'HTTP::Tiny' => ({
+       'info' => '-U; -w,-W; -i (if dig not installed).',
+       'info-bsd' => '-U; -w,-W; -i (if dig not installed)',
+       'apt' => 'libhttp-tiny-perl',
+       'pacman' => 'Core Modules',
+       'rpm' => 'Perl-http-tiny',
+       }),
+       'IO::Socket::SSL' => ({
+       'info' => '-U; -w,-W; -i (if dig not installed).',
+       'info-bsd' => '-U; -w,-W; -i (if dig not installed)',
+       'apt' => 'libio-socket-ssl-perl',
+       'pacman' => 'perl-io-socket-ssl',
+       'rpm' => 'perl-IO-Socket-SSL',
+       }),
+       'JSON::XS' => ({
+       'info' => '--output json - required for export (legacy).',
+       'info-bsd' => '--output json - required for export (legacy).',
+       'apt' => 'libjson-xs-perl',
+       'pacman' => 'perl-json-xs',
+       'rpm' => 'perl-JSON-XS',
+       }),
+       'Time::HiRes' => ({
+       'info' => '-C cpu sleep (not required); --debug timers',
+       'info-bsd' => '-C cpu sleep (not required); --debug timers',
+       'apt' => 'Core Modules',
+       'pacman' => 'Core Modules',
+       'rpm' => 'perl-Time-HiRes',
+       }),
+       'XML::Dumper' => ({
+       'info' => '--output xml - Crude and raw.',
+       'info-bsd' => '--output xml - Crude and raw.',
+       'apt' => 'libxml-dumper-perl',
+       'pacman' => 'perl-xml-dumper',
+       'rpm' => 'perl-XML-Dumper',
+       }),
+       );
+       my $ref = $data{$type};
+       my %values = %$ref;
+       return %values;
+}
+sub get_pm {
+       my ($pm) = ('');
+       if (main::check_program('dpkg')){
+               $pm = 'apt';
+       }
+       elsif (main::check_program('pacman')){
+               $pm = 'pacman';
+       }
+       elsif (main::check_program('rpm')){
+               $pm = 'rpm';
+       }
+       return $pm;
+}
+# note: end will vary, but should always be treated as longest value possible.
+# expected values: Present/Missing
+sub make_row {
+       my ($start,$middle,$end) = @_;
+       my ($dots,$line,$sep) = ('','',': ');
+       foreach (0 .. ($size{'max'} - 16 - length("$start$middle"))){
+               $dots .= '.';
+       }
+       $line = "$start$sep$middle$dots $end";
+       return $line;
+}
+sub make_line {
+       my $line = '';
+       foreach (0 .. $size{'max'} - 2 ){
+               $line .= '-';
+       }
+       return $line;
+}
+}
 
-                       # then we need to get the actual numeric max array count for both fan arrays
-                       for (i = 0; i <= 29; i++) {
-                               if ( i in aFanMain && i > indexCountaFanMain ) {
-                                       indexCountaFanMain=i
-                               }
-                       }
-                       for (i = 0; i <= 14; i++) {
-                               if ( i in aFanDefault && i > indexCountaFanDefault ) {
-                                       indexCountaFanDefault=i
-                               }
-                       }
-                       
-                       # clear out any duplicates. Primary fan real trumps fan working always if same speed
-                       for (i = 1; i <= indexCountaFanMain; i++) {
-                               if ( i in aFanMain && aFanMain[i] != "" && aFanMain[i] != 0 ) {
-                                       for (j = 1; j <= indexCountaFanDefault; j++) {
-                                               if ( j in aFanDefault && aFanMain[i] == aFanDefault[j] ) {
-                                                       aFanDefault[j] = ""
-                                               }
-                                       }
-                               }
-                       }
+#### -------------------------------------------------------------------
+#### TOOLS
+#### -------------------------------------------------------------------
 
-                       # now see if you can find the fast little mobo fan, > 5000 rpm and put it as mobo
-                       # note that gawk is returning true for some test cases when aFanDefault[j] < 5000
-                       # which has to be a gawk bug, unless there is something really weird with arrays
-                       # note: 500 > aFanDefault[j] < 1000 is the exact trigger, and if you manually 
-                       # assign that value below, the > 5000 test works again, and a print of the value
-                       # shows the proper value, so the corruption might be internal in awk. 
-                       # Note: gensub is the culprit I think, assigning type string for range 501-1000 but 
-                       # type integer for all others, this triggers true for >
-                       for (j = 1; j <= indexCountaFanDefault; j++) {
-                               if ( j in aFanDefault && int( aFanDefault[j] ) > 5000 && aFanMain[2] == "" ) {
-                                       aFanMain[2] = aFanDefault[j]
-                                       aFanDefault[j] = ""
-                                       # then add one if required for output
-                                       if ( indexCountaFanMain < 2 ) {
-                                               indexCountaFanMain = 2
-                                       }
-                               }
-                       }
+# Duplicates the functionality of awk to allow for one liner
+# type data parsing. note: -1 corresponds to awk NF
+# args 1: array of data; 2: search term; 3: field result; 4: separator
+# correpsonds to: awk -F='separator' '/search/ {print $2}' <<< @data
+# array is sent by reference so it must be dereferenced
+# NOTE: if you just want the first row, pass it \S as search string
+# NOTE: if $num is undefined, it will skip the second step
+sub awk {
+       eval $start if $b_log;
+       my ($ref,$search,$num,$sep) = @_;
+       my ($result);
+       # print "search: $search\n";
+       return if ! @$ref || ! $search;
+       foreach (@$ref){
+               if (/$search/i){
+                       $result = $_;
+                       $result =~ s/^\s+|\s+$//g;
+                       last;
+               }
+       }
+       if ($result && defined $num){
+               $sep ||= '\s+';
+               $num-- if $num > 0; # retain the negative values as is
+               $result = (split /$sep/, $result)[$num];
+               $result =~ s/^\s+|,|\s+$//g if $result;
+       }
+       eval $end if $b_log;
+       return $result;
+}
 
-                       # then construct the sys_fan string for echo, note that iteration 1
-                       # makes: fanDefaultString separator null, ie, no space or ,
-                       for (j = 1; j <= indexCountaFanDefault; j++) {
-                               fanDefaultString = fanDefaultString separator aFanDefault[j]
-                               separator=","
-                       }
-                       separator="" # reset to null for next loop
-                       # then construct the sys_fan string for echo
-                       for (j = 1; j <= indexCountaFanMain; j++) {
-                               fanMainString = fanMainString separator aFanMain[j]
-                               separator=","
-                       }
-                       
-                       # and then build the temps:
-                       if ( moboTempReal != "" ) {
-                               moboTempReal = moboTempReal tempUnit
-                       }
-                       if ( cpuTempReal != "" ) {
-                               cpuTempReal = cpuTempReal tempUnit
-                       }
-                       
-                       # if they are ALL null, print error message. psFan is not used in output currently
-                       if ( cpuTempReal == "" && moboTempReal == "" && aFanMain[1] == "" && aFanMain[2] == "" && aFanMain[3] == "" && fanDefaultString == "" ) {
-                               print "No active sensors found. Have you configured your sensors yet?"
-                       }
-                       else {
-                               # then build array arrays: 
-                               print cpuTempReal "," moboTempReal "," psuTemp
-                               # this is for output, a null print line does NOT create a new array index in bash
-                               if ( fanMainString == "" ) {
-                                       fanMainString=","
-                               }
-                               print fanMainString
-                               print fanDefaultString
-                       }
-               }' <<< "$Sensors_Data" ) )
-       fi
-       
-       IFS="$ORIGINAL_IFS"
-       temp_array=${A_SENSORS_DATA[@]}
-       log_function_data "A_SENSORS_DATA: $temp_array"
-#      echo "A_SENSORS_DATA: ${A_SENSORS_DATA[@]}"
-       eval $LOGFE
+# $1 - Perl module to check
+sub check_module {
+       my ($module) = @_;
+       my $b_present = 0;
+       eval "require $module";
+       $b_present = 1 if !$@;
+       return $b_present;
 }
 
-get_sensors_output()
-{
-       local sensors_path=$( type -p sensors ) sensors_data=''
-       
-       if [[ -n $sensors_path ]];then
-               sensors_data="$( $sensors_path 2>/dev/null )"
-               if [[ -n "$sensors_data" ]];then
-                       # make sure the file ends in newlines then characters, the newlines are lost in the echo unless
-                       # the data ends in some characters
-                       sensors_data="$sensors_data\n\n###" 
-               fi
-       fi
-       echo -e "$sensors_data"
-}
-
-get_shell_data()
-{
-       eval $LOGFS
+# arg: 1 - string or path to search gneerated @paths data for.
+# note: a few nano seconds are saved by using raw $_[0] for program
+sub check_program {
+       (grep { return "$_/$_[0]" if -e "$_/$_[0]"} @paths)[0];
+}
 
-       local shell_type="$( ps -p $PPID -o comm= 2>/dev/null )"
-       local shell_version='' 
-       
-       if [[ $B_EXTRA_DATA == 'true' && -n $shell_type ]];then
-               case $shell_type in
-                       bash)
-                               shell_version=$( get_de_app_version "$shell_type" "^GNU[[:space:]]bash,[[:space:]]version" "4" | \
-                               sed $SED_RX 's/(\(.*|-release|-version)//' )
-                               ;;
-                       # csh/dash use dpkg package version data, debian/buntu only
-                       csh)
-                               shell_version=$( get_de_app_version "$shell_type" "^tcsh" "2" )
-                               ;;
-                       dash)
-                               shell_version=$( get_de_app_version "$shell_type" "$shell_type" "3" )
-                               ;;
-                       ksh)
-                               shell_version=$( get_de_app_version "$shell_type" "version" "5" )
-                               ;;
-                       tcsh)
-                               shell_version=$( get_de_app_version "$shell_type" "^tcsh" "2" )
-                               ;;
-                       zsh)
-                               shell_version=$( get_de_app_version "$shell_type" "^zsh" "2" )
-                               ;;
-               esac
-       fi
-       if [[ -n $shell_version ]];then
-               shell_type="$shell_type $shell_version"
-       fi
-       echo $shell_type
-       
-       eval $LOGFS
+sub cleanup {
+       # maybe add in future: , $fh_c, $fh_j, $fh_x
+       foreach my $fh ($fh_l){
+               if ($fh){
+                       close $fh;
+               }
+       }
 }
 
-get_shell_parent()
-{
-       eval $LOGFS
-       local shell_parent='' script_parent='' 
-       
-       # removed --no-headers to make bsd safe, adding in -j to make output the same
-       script_parent=$( ps -j -fp $PPID 2>/dev/null | gawk '/'"$PPID"'/ { print $3 }' )
-       log_function_data "script parent: $script_parent"
-       shell_parent=$( ps -j -p $script_parent 2>/dev/null | gawk '/'"$script_parent"'/ { print $NF}' )
-       # no idea why have to do script_parent action twice in su case, but you do, oh well.
-       if [[ $shell_parent == 'su' ]];then
-               script_parent=$( ps -j -fp $script_parent 2>/dev/null | gawk '/'"$script_parent"'/ { print $3 }' )
-               script_parent=$( ps -j -fp $script_parent 2>/dev/null | gawk '/'"$script_parent"'/ { print $3 }' )
-               shell_parent=$( ps -j -p $script_parent 2>/dev/null | gawk '/'"$script_parent"'/ { print $NF}' )
-       fi
-       echo $shell_parent
-       log_function_data "shell parent final: $shell_parent"
-       eval $LOGFE
-}
-
-# this will be used for some bsd data types
-# args: $1 - option type
-get_sysctl_data()
-{
-       eval $LOGFS
-       
-       local sysctl_data=''
-       
-       if [[ $B_SYSCTL ]];then
-               sysctl_data="$( sysctl -$1 )"
-       fi
-       # log_function_data "sysctl_data: $sysctl_data"
-       echo "$sysctl_data"
-       eval $LOGFE
+# returns count of files in directory, if 0, dir is empty
+sub count_dir_files {
+       return unless -d $_[0];
+       opendir my $dh, $_[0] or error_handler('open-dir-failed', "$_[0]", $!); 
+       my $count = grep { ! /^\.{1,2}/ } readdir $dh; # strips out . and ..
+       return $count;
 }
 
-get_tty_console_irc()
-{
-       eval $LOGFS
-       local tty_number=''
-       if [[ -n ${IRC_CLIENT} ]];then
-               tty_number=$( gawk '
-                       BEGIN {
-                               IGNORECASE=1
-                       }
-                       # if multiple irc clients open, can give wrong results
-                       # so make sure to also use the PPID number to get the right tty
-                       /.*'$PPID'.*'${IRC_CLIENT}'/ {
-                               gsub(/[^0-9]/, "", $7)
-                               print $7
-                               exit
-                       }' <<< "$Ps_aux_Data" )
-       fi
-       log_function_data "tty_number: $tty_number"
-       echo $tty_number
-       eval $LOGFE
-}
-
-get_tty_number()
-{
-       eval $LOGFS
-       
-       local tty_number=$( basename "$( tty 2>/dev/null )" | sed 's/[^0-9]*//g' )
-       
-       echo $tty_number
-       
-       eval $LOGFE
+# args: 1 - the string to get piece of
+# 2 - the position in string, starting at 1 for 0 index.
+# 3 - the separator, default is ' '
+sub get_piece {
+       eval $start if $b_log;
+       my ($string, $num, $sep) = @_;
+       $num--;
+       $sep ||= '\s+';
+       $string =~ s/^\s+|\s+$//g;
+       my @temp = split(/$sep/, $string);
+       eval $end if $b_log;
+       if ( exists $temp[$num] ){
+               $temp[$num] =~ s/,//g;
+               return $temp[$num];
+       }
 }
 
-get_unmounted_partition_data()
-{
-       eval $LOGFS
-       local a_unmounted_working='' mounted_partitions='' separator='|' unmounted_fs=''
-       local dev_working='' uuid_working='' label_working='' a_raid_working='' raid_partitions=''
-       
-       if [[ $B_PARTITIONS_FILE == 'true' ]];then
-               # set dev disk label/uuid data globals
-               get_partition_dev_data 'label'
-               get_partition_dev_data 'uuid'
-               # load the raid data array here so we can exclude its partitions
-               if [[ $B_RAID_SET != 'true' ]];then
-                       get_raid_data
-               fi
-               # sr0 type cd drives are showing up now as unmounted partitions
-               mounted_partitions="scd[0-9]+|sr[0-9]+|cdrom[0-9]*|cdrw[0-9]*|dvd[0-9]*|dvdrw[0-9]*"
-               # create list for slicing out the mounted partitions
-               for (( i=0; i < ${#A_PARTITION_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_unmounted_working=( ${A_PARTITION_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       if [[ -n ${a_unmounted_working[6]} ]];then
-                               mounted_partitions="$mounted_partitions$separator${a_unmounted_working[6]}"
-                       fi
-               done
-               # now we need to exclude the mdraid partitions from the unmounted partition output as well
-               for (( i=0; i < ${#A_RAID_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_raid_working=( ${A_RAID_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       if [[ -n ${a_raid_working[3]} ]];then
-                               raid_partitions=$( sed $SED_RX 's/(\([^\)]*\)|\[[^\]]*\])//g' <<< ${a_raid_working[3]}\
-                               | sed 's/[[:space:]]\+/|/g' )
-                               mounted_partitions="$mounted_partitions$separator$raid_partitions"
-                       fi
-               done
-       
-               A_UNMOUNTED_PARTITION_DATA=( $( grep -Ev '[[:space:]]('$mounted_partitions')$' $FILE_PARTITIONS | gawk '
-               BEGIN {
-                       IGNORECASE=1
-               }
-               # note that size 1 means it is a logical extended partition container
-               # lvm might have dm-1 type syntax
-               # need to exclude loop type file systems, squashfs for example
-               /[a-z][0-9]+$|dm-[0-9]+$/ && $3 != 1 && $NF !~ /loop/ {
-                       size = sprintf( "%.2f", $3*1024/1000**3 )
-                       print $4 "," size "G"
-               }' ) )
-
-               for (( i=0; i < ${#A_UNMOUNTED_PARTITION_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_unmounted_working=( ${A_UNMOUNTED_PARTITION_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       
-                       label_working=$( grep -E "${a_unmounted_working[0]}$" <<< "$DEV_DISK_LABEL"  | gawk '{
-                               print $(NF - 2)
-                       }' )
-                       uuid_working=$( grep -E "${a_unmounted_working[0]}$" <<< "$DEV_DISK_UUID"  | gawk '{
-                               print $(NF - 2)
-                       }' )
-                       unmounted_fs=$( get_unmounted_partition_filesystem "/dev/${a_unmounted_working[0]}" )
-                       
-                       IFS=","
-                       A_UNMOUNTED_PARTITION_DATA[i]=${a_unmounted_working[0]}","${a_unmounted_working[1]}","$label_working","$uuid_working","$unmounted_fs
-                       IFS="$ORIGINAL_IFS"
-               done
-       fi
-#      echo "${A_PARTITION_DATA[@]}"
-#      echo "${A_UNMOUNTED_PARTITION_DATA[@]}"
-       eval $LOGFE
-}
-
-# a few notes, normally file -s requires root, but you can set user rights in /etc/sudoers.
-# list of file systems: http://en.wikipedia.org/wiki/List_of_file_systems
-# args: $1 - /dev/<disk><part> to be tested for
-get_unmounted_partition_filesystem()
-{
-       eval $LOGFS
-       local partition_filesystem='' sudo_command=''
-       
-       if [[ $B_FILE_TESTED != 'true' ]];then
-               B_FILE_TESTED='true'
-               FILE_PATH=$( type -p file )
-       fi
-       
-       if [[ $B_SUDO_TESTED != 'true' ]];then
-               B_SUDO_TESTED='true'
-               SUDO_PATH=$( type -p sudo )
-       fi
-       
-       if [[ -n $FILE_PATH && -n $1 ]];then
-               # only use sudo if not root, -n option requires sudo -V 1.7 or greater. sudo will just error out
-               # which is the safest course here for now, otherwise that interactive sudo password thing is too annoying
-               # important: -n makes it non interactive, no prompt for password
-               if [[ $B_ROOT != 'true' && -n $SUDO_PATH ]];then
-                       sudo_command='sudo -n '
-               fi
-               # this will fail if regular user and no sudo present, but that's fine, it will just return null
-               # note the hack that simply slices out the first line if > 1 items found in string
-               # also, if grub/lilo is on partition boot sector, no file system data is available
-               # BSD fix: -Eio -Em 1
-               partition_filesystem=$( eval $sudo_command $FILE_PATH -s $1 | grep -Eio '(ext2|ext3|ext4|ext5|ext[[:space:]]|ntfs|fat32|fat16|fat[[:space:]]\(.*\)|vfat|fatx|tfat|swap|btrfs|ffs[[:space:]]|hfs\+|hfs[[:space:]]plus|hfs[[:space:]]extended[[:space:]]version[[:space:]][1-9]|hfsj|hfs[[:space:]]|jfs[[:space:]]|nss[[:space:]]|reiserfs|reiser4|ufs2|ufs[[:space:]]|xfs[[:space:]]|zfs[[:space:]])' | grep -Em 1 '.*' )
-               if [[ -n $partition_filesystem ]];then
-                       echo $partition_filesystem
-               fi
-       fi
-       eval $LOGFE
+# arg: 1 - command to turn into an array; 2 - optional: splitter
+# 3 - optionsl, strip and clean data
+# similar to reader() except this creates an array of data 
+# by lines from the command arg
+sub grabber {
+       eval $start if $b_log;
+       my ($cmd,$split,$strip) = @_;
+       $split ||= "\n";
+       my @rows = split /$split/, qx($cmd);
+       if ($strip && @rows){
+               @rows = grep {/^\s*[^#]/} @rows;
+               @rows = map {s/^\s+|\s+$//g; $_} @rows if @rows;
+       }
+       eval $end if $b_log;
+       return @rows;
 }
 
-## return uptime string
-get_uptime()
-{
-       eval $LOGFS
-       ## note: removing gsub(/ /,"",a); to get get space back in there, goes right before print a
-       local uptime_value="$( uptime | gawk '{
-               a = gensub(/^.*up *([^,]*).*$/,"\\1","g",$0)
-               print a
-       }' )"
-       echo "$uptime_value"
-       log_function_data "uptime_value: $uptime_value"
-       eval $LOGFE
-}
-
-get_weather_data()
-{
-       eval $LOGFS
-       
-       local location_site='http://geoip.ubuntu.com/lookup'
-       local weather_feed='http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query='
-       local weather_spider='http://wunderground.com/'
-       local data_grab_error='' wget_error=0 
-       local b_test_loc=false b_test_weather=false b_debug=false
-       local test_dir="$HOME/bin/scripts/inxi/data/weather/"
-       local test_location='location2.xml' test_weather='weather-feed.xml'
-       local location_data='' location='' weather_data='' location_array_value='' a_location=''
-       local weather_array_value='' site_elevation='' temp_array=''
-       
-       # first we get the location data, once that is parsed and handled, we move to getting the 
-       # actual weather data, assuming no errors
-       if [[ -n $ALTERNATE_WEATHER_LOCATION ]];then
-               # note, this api does not support spaces in names, replace spaces with + sign.
-               location=$ALTERNATE_WEATHER_LOCATION
-               # echo $ALTERNATE_WEATHER_LOCATION;exit
-       else
-               if [[ $b_test_loc != 'true' ]];then
-                       location_data=$( wget -q -t 1 -T $WGET_TIMEOUT -O- $location_site || wget_error=$? )
-                       log_function_data "$location_data"
-                       if [[ $wget_error -ne 0 ]];then
-                               data_grab_error="Error: location server up but download error - wget: $wget_error"
-                       fi
-                       wget_error=0
-               else
-                       if [[ -f $test_dir$test_location ]];then
-                               location_data="$( cat $test_dir$test_location )"
-                       else
-                               data_grab_error="Error: location xml local file not found."
-                       fi
-               fi
-               if [[ -n $data_grab_error ]];then
-                       :
-               elif [[ -z $( grep -i '<Response' <<< $location_data ) ]];then
-                       data_grab_error="Error: location downloaded but data contains no xml."
-               else
-                       # clean up xml and make easy to process with newlines, note, bsd sed has no support for inserting
-                       # \n dircctly so we have to use this hack
-                       # location_data="$( sed $SED_RX 's|><|>\n<|g' <<< $location_data )"
-                       location_data="$( sed $SED_RX 's|><|>\
-<|g' <<< $location_data )"
-                       # echo -e "ld:\n$location_data"
-                       location_array_value=$( gawk '
-                       function clean(data) {
-                               returnData=""
-                               # some lines might be empty, so ignore those
-                               if (data !~ /^<[^>]+>$/ ) {
-                                       returnData=gensub(/(.*>)([^<]*)(<.*)/, "\\2", 1, data)
-                               }
-                               return returnData
-                       }
-                       BEGIN {
-                               IGNORECASE=1
-                               locationString=""
-                               countryCode=""
-                               countryCode3=""
-                               countryName=""
-                               regionCode=""
-                               regionName=""
-                               city=""
-                               postalCode=""
-                               latitude=""
-                               longitude=""
-                               timeZone=""
-                               areaCode=""
-                       }
-                       /CountryCode/ {
-                               if ( $0 ~ /CountryCode3/ ){
-                                       countryCode3=clean($0)
-                               }
-                               else {
-                                       countryCode=clean($0)
-                               }
-                       }
-                       /CountryName/ {
-                               countryName = clean($0)
-                       }
-                       /RegionCode/ {
-                               regionCode = clean($0)
-                       }
-                       /RegionName/ {
-                               regionName = clean($0)
-                       }
-                       /City/ {
-                               city = clean($0)
-                       }
-                       /ZipPostalCode/ {
-                               postalCode = clean($0)
-                       }
-                       /Latitude/ {
-                               latitude = clean($0)
-                       }
-                       /Longitude/ {
-                               longitude = clean($0)
-                       }
-                       /TimeZone/ {
-                               timeZone = clean($0)
-                       }
-                       END {
-                               locationString = city ";" regionCode ";" regionName ";" countryName ";" countryCode ";" countryCode3 
-                               locationString = locationString  ";" latitude "," longitude ";" postalCode ";" timeZone
-                               print locationString
-                       }' <<< "$location_data" )
-               fi
-               A_WEATHER_DATA[0]=$location_array_value
-               IFS=";"
-               a_location=( ${A_WEATHER_DATA[0]} )
-               IFS="$ORIGINAL_IFS"
-               
-               # assign location, cascade from most accurate
-               # latitude,longitude first
-               if [[ -n ${a_location[6]} ]];then
-                       location="${a_location[6]}"
-               # city,state next
-               elif [[ -n ${a_location[0]} && -n ${a_location[1]} ]];then
-                       location="${a_location[0]},${a_location[1]}"
-               # postal code last, that can be a very large region
-               elif [[ -n ${a_location[7]} ]];then
-                       location=${a_location[7]}
-               fi
-       fi
-       if [[ $b_debug == 'true' ]];then
-               echo -e "location array:\n${A_WEATHER_DATA[0]}"
-               echo "location: $location"
-       fi
-       log_function_data "location: $location"
-       
-       if [[ -z $location && -z $data_grab_error ]];then
-               data_grab_error="Error: location data downloaded but no location detected."
-       fi
-
-       # now either dump process or go on to get weather data
-       if [[ -z $data_grab_error ]];then
-               if [[ $b_test_weather != 'true' ]];then
-                       weather_data="$( wget -q -t 1 -T $WGET_TIMEOUT -O- $weather_feed"$location" || wget_error=$? )"
-                       if [[ $wget_error -ne 0 ]];then
-                               data_grab_error="Error: weather server up but download error - wget: $wget_error"
-                       fi
-                       log_function_data "$weather_data"
-               else
-                       if [[ -f $test_dir$test_weather ]];then
-                               weather_data="$( cat $test_dir$test_weather)"
-                       else
-                               data_grab_error="Error: weather feed xml local file not found."
-                       fi
-               fi
-               if [[ -z $data_grab_error && -z $( grep -i '<current_observation' <<< $weather_data ) ]];then
-                       data_grab_error="Error: weather data downloaded but shows no xml start."
-               fi
-               if [[ -z $data_grab_error ]];then
-                       # trim off zeros
-                       weather_data=$( sed 's/^[[:space:]]*//' <<< "$weather_data" )
-                       site_elevation=$( grep -im 1 '<elevation>' <<< "$weather_data" | sed $SED_RX -e 's/<[^>]*>//g' \
-                       -e 's/\.[0-9]*//' )
-                       # we need to grab the location data from the feed for remote checks 
-                       if [[ -n $ALTERNATE_WEATHER_LOCATION && -n $weather_data ]];then
-                               location_data=$( sed -e '/<current_observation>/,/<display_location>/d' -e '/<\/display_location>/,/<\/current_observation>/d' <<< "$weather_data" )
-                               # echo -e "ld1:\n$location_data"
-                               A_WEATHER_DATA[0]=$( gawk '
-                               function clean(data) {
-                                       returnData=""
-                                       # some lines might be empty, so ignore those
-                                       if (data !~ /^<[^>]+>$/ ) {
-                                               returnData=gensub(/(.*>)([^<]*)(<.*)/, "\\2", 1, data)
-                                               gsub(/^[[:space:]]+|[[:space:]]+$|^NA$|^N\/A$/, "", returnData)
-                                       }
-                                       return returnData
-                               }
-                               BEGIN {
-                                       IGNORECASE=1
-                                       city=""
-                                       state=""
-                                       country=""
-                               }
-                               /<city>/ {
-                                       city=clean($0)
-                               }
-                               /<state>/ {
-                                       state=clean($0)
-                               }
-                               /<country>/ {
-                                       country=clean($0)
-                               }
-                               END {
-                                       print city ";" state ";;;;" country
-                               }' <<< "$location_data" )
-                               # echo -e "location:\n${A_WEATHER_DATA[0]}"
-                       fi
-                       
-                       # clean off everything before/after observation_location
-                       weather_data=$( sed -e '/<current_observation>/,/<observation_location>/d' \
-                       -e '/<icons>/,/<\/current_observation>/d' <<< "$weather_data" -e 's/^[[:space:]]*$//g' -e '/^$/d' )
-                       
-                       # echo "$weather_data";exit 
-                       weather_array_value=$( gawk -v siteElevation="$site_elevation" '
-                       function clean(data) {
-                               returnData=""
-                               # some lines might be empty, so ignore those
-                               if (data !~ /^<[^>]+>$/ ) {
-                                       returnData=gensub(/(.*>)([^<]*)(<.*)/, "\\2", 1, data)
-                                       gsub(/^[[:space:]]+|[[:space:]]+$|^NA$|^N\/A$/, "", returnData)
-                               }
-                               return returnData
-                       }
-                       BEGIN {
-                               IGNORECASE=1
-                               observationTime=""
-                               localTime=""
-                               weather=""
-                               tempString=""
-                               humidity=""
-                               windString=""
-                               pressureString=""
-                               dewpointString=""
-                               heatIndexString=""
-                               windChillString=""
-                               weatherString=""
-                       }
-                       /observation_time>/ {
-                               observationTime=clean($0)
-                               sub(/Last Updated on /, "", observationTime )
-                       }
-                       /local_time>/ {
-                               localTime=clean($0)
-                       }
-                       /<weather/ {
-                               weather=clean($0)
-                       }
-                       /temperature_string/ {
-                               tempString=clean($0)
-                       }
-                       /relative_humidity/ {
-                               humidity=clean($0)
-                       }
-                       /wind_string/ {
-                               windString=clean($0)
-                       }
-                       /pressure_string/ {
-                               pressureString=clean($0)
-                       }
-                       /heat_index_string/ {
-                               heatIndexString=clean($0)
-                       }
-                       /windchill_string/ {
-                               windChillString=clean($0)
-                       }
-                       END {
-                               weatherString = observationTime ";" localTime ";" weather ";" tempString ";" humidity 
-                               weatherString = weatherString ";" windString ";" pressureString ";" dewpointString ";" heatIndexString
-                               weatherString = weatherString ";" windChillString ";" siteElevation
-                               print weatherString
-                       }' <<< "$weather_data" )
-               fi
-               if [[ -z $weather_array_value ]];then
-                       data_grab_error="Error: weather info downloaded but no data detected."
-               else
-                       A_WEATHER_DATA[1]=$weather_array_value
-               fi
-       fi
-       # now either dump process or go on to get weather data
-       if [[ -n $data_grab_error ]];then
-               A_WEATHER_DATA=$data_grab_error
-               log_function_data "data grab error: $data_grab_error"
-       fi
-       
-       if [[ $b_debug == 'true' ]];then
-               echo "site_elevation: $site_elevation"
-               echo "${A_WEATHER_DATA[1]}"
-       fi
-       temp_array=${A_WEATHER_DATA[@]}
-       log_function_data "A_WEATHER_DATA: $temp_array"
-       
-       eval $LOGFE
+# args: 1 - string value to glob
+sub globber {
+       eval $start if $b_log;
+       my @files = <$_[0]>;
+       eval $end if $b_log;
+       return @files;
 }
-# ALTERNATE_WEATHER_LOCATION='portland,or'
-# get_weather_data;exit
 
-#### -------------------------------------------------------------------
-#### special data handling for specific options and conditions
-#### -------------------------------------------------------------------
+# gets array ref, which may be undefined, plus join string
+# this helps avoid debugger print errors when we are printing arrays
+# which we don't know are defined or not null.
+# args: 1 - array ref; 2 - join string; 3 - default value, optional
+sub joiner {
+       my ($ref,$join,$default) = @_;
+       my @arr = @$ref;
+       $default ||= '';
+       my $string = '';
+       foreach (@arr){
+               if (defined $_){
+                       $string .= $_ . $join;
+               }
+               else {
+                       $string .= $default . $join;
+               }
+       }
+       return $string;
+}
 
-# args: $1 - string to strip color code characters out of
-# returns count of string length minus colors
-calculate_line_length()
-{
-       local string="$1"
-       # ansi: \e[1;34m irc: \x0312
-       string=$( sed -e "s/\x1b\[[0-9]\{1,2\}\(;[0-9]\{1,2\}\)\{0,2\}m//g" -e "s/\\\x0[0-9]\{1,3\}//g" <<< $string )
-       count=${#string}
-       echo $count
+# returns array of:
+# 0 - match string; 1 - search number; 2 - version string; 3 - Print name
+# 4 - console 0/1; 5 - 0/1 exit version loop at first iteration; 
+# 6 - 0/1 write to stderr
+# arg: 1 - program lower case name
+sub program_values {
+       my ($app) = @_;
+       my (@client_data);
+       # note: setting index 1 and 2 to 0 will trip flags to not do version
+       my %data = (
+       ## Clients
+       'bitchx' => ['bitchx',2,'','BitchX',1,0,0],# special
+       'finch' => ['finch',2,'-v','Finch',1,1,0],
+       'gaim' => ['[0-9.]+',2,'-v','Gaim',0,1,0],
+       'ircii' => ['[0-9.]+',3,'-v','ircII',1,1,0],
+       'irssi' => ['irssi',2,'-v','Irssi',1,1,0],
+       'irssi-text' => ['irssi',2,'-v','Irssi',1,1,0],
+       'konversation' => ['konversation',2,'-v','Konversation',0,0,0],
+       'kopete' => ['Kopete',2,'-v','Kopete',0,0,0],
+       'kvirc' => ['[0-9.]+',2,'-v','KVIrc',0,0,1], # special
+       'pidgin' => ['[0-9.]+',2,'-v','Pidgin',0,1,0],
+       'quassel' => ['',1,'-v','Quassel [M]',0,0,0], # special
+       'quasselclient' => ['',1,'-v','Quassel',0,0,0],# special
+       'quasselcore' => ['',1,'-v','Quassel (core)',0,0,0],# special
+       'gribble' => ['^Supybot',2,'--version','Gribble',1,0,0],# special
+       'limnoria' => ['^Supybot',2,'--version','Limnoria',1,0,0],# special
+       'supybot' => ['^Supybot',2,'--version','Supybot',1,0,0],# special
+       'weechat' => ['[0-9.]+',1,'-v','WeeChat',1,0,0],
+       'weechat-curses' => ['[0-9.]+',1,'-v','WeeChat',1,0,0],
+       'xchat-gnome' => ['[0-9.]+',2,'-v','X-Chat-Gnome',1,1,0],
+       'xchat' => ['[0-9.]+',2,'-v','X-Chat',1,1,0],
+       ## Desktops / wm
+       '3dwm' => ['^3dwm',0,'0','3dwm',0,1,0], # unknown syntax
+       '9wm' => ['^9wm',3,'-version','9wm',0,1,0],
+       'afterstep' => ['^afterstep',3,'--version','AfterStep',0,1,0],
+       'amiwm' => ['^amiwm',0,'0','AmiWM',0,1,0],
+       'awesome' => ['^awesome',2,'--version','Awesome',0,1,0],
+       'blackbox' => ['^Blackbox',2,'--version','Blackbox',0,1,0],
+       'budgie' => ['^budgie-desktop',2,'--version','Budgie',0,1,0],
+       'cinnamon' => ['^cinnamon',2,'--version','Cinnamon',0,1,0],
+       'compiz' => ['^compiz',2,'--version','Compiz',0,1,0],
+       'dwm' => ['^dwm',1,'-v','Dwm',0,1,1],
+       'fluxbox' => ['^fluxbox',2,'--version','Fluxbox',0,1,0],
+       'flwm' => ['^flwm',0,'0','FLWM',0,0,1],
+       'fvwm' => ['^fvwm',2,'--version','FVWM',0,0,1],
+       'fvwm2' => ['^fvwm',2,'--version','FVWM2',0,0,1],
+       # command: fvwm
+       'fvwm-crystal' => ['^fvwm',2,'--version','FVWM-Crystal',0,0,0], 
+       'gala' => ['^gala',2,'--version','gala',0,1,0], # super slow result
+       'gnome-about' => ['gnome',3,'--version','Gnome',0,1,0],
+       'gnome-shell' => ['gnome',3,'--version','Gnome',0,1,0],
+       # fails to return version when in wm, but outside does. weird.
+       'herbstluftwm' => ['^herbstluftwm',2,'--version','herbstluftwm',0,1,0],
+       'jwm' => ['^jwm',2,'--version','JWM',0,1,0],
+       # i3 version 4.13 (2016-11-08) © 2009 Michael Stapelberg and contributors
+       'i3' => ['^i3',3,'--version','i3',0,1,0],
+       'icewm' => ['^icewm',2,'--version','IceWM',0,1,0],
+       'kded' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0],
+       'kded1' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0],
+       'kded2' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0],
+       'kded3' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0],
+       'kded4' => ['^KDE Development Platform:',4,'--version','KDE',0,1,0],
+       'lxde' => ['^lxpanel',2,'--version','LXDE',0,1,0],
+       # command: lxqt-panel
+       'lxqt' => ['^lxqt-panel',2,'--version','LXQt',0,1,0],
+       'marco' => ['^marco',2,'--version','marco',0,1,0],
+       'matchbox' => ['^matchbox',0,'0','Matchbox',0,1,0],
+       'matchbox-window-manager' => ['^matchbox',2,'--help','Matchbox',0,0,0],
+       'mate-about' => ['^MATE[[:space:]]DESKTOP',-1,'--version','MATE',0,1,0],
+       # note, mate-session when launched with full path returns full path in version string
+       'mate-session' => ['mate-session',-1,'--version','MATE',0,1,0], 
+       'metacity' => ['^metacity',2,'--version','Metacity',0,1,0],
+       'muffin' => ['^muffin',2,'--version','muffin',0,1,0],
+       'mwm' => ['^mwm',0,'0','mwm',0,1,0],
+       'notion' => ['^.',1,'--version','notion',0,1,0],
+       'openbox' => ['^openbox',2,'--version','Openbox',0,1,0],
+       'pantheon' => ['^pantheon',0,'0','Pantheon',0,1,0],
+       'pekwm' => ['^pekwm',3,'--version','PekWM',0,1,0],
+       'plasmashell' => ['^plasmashell',2,'--version','KDE Plasma',0,1,0],
+       'qtdiag' => ['^qt',2,'--version','Qt',0,1,0],
+       'ratpoison' => ['^ratpoison',2,'--version','Ratpoison',0,1,0],
+       'sawfish' => ['^sawfish',3,'--version','Sawfish',0,1,0],
+       'scrotwm' => ['^scrotwm.*welcome.*',5,'-v','Scrotwm',0,1,1],
+       'spectrwm' => ['^spectrwm.*welcome.*wm',5,'-v','Spectrwm',0,1,0],
+       'twm' => ['^twm',0,'0','twm',0,1,0],
+       'unity' => ['^unity',2,'--version','Unity',0,1,0],
+       'windowlab' => ['^windowlab',2,'-about','WindowLab',0,1,0],
+       'wm2' => ['^wm2',0,'0','wm2',0,1,0],
+       'wmaker' => ['^Window[[:space:]]*Maker',-1,'--version','WindowMaker',0,1,0],
+       'wmii' => ['^wmii',0,'0','wmii',0,1,0], # note: in debian, wmii is wmii3
+       'wmii2' => ['^wmii2',1,'--version','wmii2',0,1,0],
+       'xfce4-panel' => ['^xfce4-panel',2,'--version','Xfce',0,1,0],
+       'xfce5-panel' => ['^xfce5-panel',2,'--version','Xfce',0,1,0],
+       'xfdesktop' => ['xfdesktop[[:space:]]version',5,'--version','Xfce',0,1,0],
+       # command: xfdesktop
+       'xfdesktop-toolkit' => ['Built[[:space:]]with[[:space:]]GTK',4,'--version','Gtk',0,1,0],
+       'xmonad' => ['^xmonad',2,'--version','XMonad',0,1,0],
+       ## Shells
+       'bash' => ['^GNU[[:space:]]bash,[[:space:]]version',4,'--version','Bash',1,0,0],
+       'csh' => ['^tcsh',2,'--version','csh',1,0,0],
+       'dash' => ['dash',3,'--version','Dash',1,0,0], # no version, uses dpkg query, sigh
+       # ksh/lksh/mksh/pdksh can't be handled with version but we'll use the search string to 
+       # trigger version return and tests
+       'ksh' => ['ksh',5,'-v','ksh',1,0,0], 
+       'lksh' => ['ksh',5,'-v','lksh',1,0,0], 
+       'loksh' => ['ksh',5,'-v','lksh',1,0,0], 
+       'mksh' => ['ksh',5,'-v','mksh',1,0,0], 
+       'pdksh' => ['ksh',5,'-v','pdksh',1,0,0], 
+       'tcsh' => ['^tcsh',2,'--version','tcsh',1,0,0],
+       'zsh' => ['^zsh',2,'--version','zsh',1,0,0],
+       ## Tools
+       'clang' => ['clang',3,'--version','Clang',1,0,0],
+       'gcc' => ['^gcc',3,'--version','GCC',1,0,0],
+       'gcc-apple' => ['Apple[[:space:]]LLVM',2,'--version','LLVM',1,0,0],
+       'sudo' => ['^Sudo',3,'-V','Sudo',1,1,0], # sudo pre 1.7 does not have --version
+       );
+       if ( defined $data{$app} ){
+               my $ref = $data{$app};
+               @client_data = @$ref;
+       }
+       #my $debug = main::Dumper \@client_data;
+       main::log_data('dump',"Client Data",\@client_data) if $b_log;
+       return @client_data;
 }
 
-## multiply the core count by the data to be calculated, bmips, cache
-# args: $1 - string to handle; $2 - cpu count
-calculate_multicore_data()
-{
-       eval $LOGFS
-       local string_number=$1 string_data=''
-
-       if [[ -n $( grep -Ei '( mb| kb)' <<< $1 ) ]];then
-               string_data=" $( gawk '{print $2}' <<< $1 )" # add a space for output
-               string_number=$( gawk '{print $1}' <<< $1 )
-       fi
-       # handle weird error cases where it's not a number
-       if [[ -n $( grep -E '^[0-9\.,]+$' <<< $string_number ) ]];then
-               string_number=$( echo $string_number $2 | gawk '{
-                       total = $1*$2
-                       print total
-               }' )
-       elif [[ $string_number == '' ]];then
-               string_number='N/A'
-       else
-               # I believe that the above returns 'unknown' by default so no need for extra text
-               string_number="$string_number "
-       fi
-       echo "$string_number$string_data"
-       log_function_data "string_numberstring_data: $string_number$string_data"
-       eval $LOGFE
-}
-
-# prints out shortened list of flags, the main ones of interest
-# args: $1 - string of cpu flags to process
-process_cpu_flags()
-{
-       eval $LOGFS
-       
-       local cpu_flags_working=$1
-       local bits=$( uname -m | grep 64 )
-       
-       # no need to show pae for 64 bit cpus, it's pointless
-       if [[ -n $bits ]];then
-               cpu_flags_working=$( sed 's/[[:space:]]*pae//' <<< "$cpu_flags_working" )
-       fi
-       # must have a space after last item in list for RS=" "
-       cpu_flags_working="$cpu_flags_working "
-       
-       # nx = AMD stack protection extensions
-       # lm = Intel 64bit extensions
-       # sse, sse2, pni = sse1,2,3,4,5 gfx extensions
-       # svm = AMD pacifica virtualization extensions
-       # vmx = Intel IVT (vanderpool) virtualization extensions
-       cpu_flags=$( 
-       echo "$cpu_flags_working" | gawk '
-       BEGIN {
-               RS=" "
-               count = 0
-               i = 1 # start at one because of for increment issue
-               flag_string = ""
+# args: 1 - desktop/app command for --version; 2 - search string; 
+# 3 - space print number; 4 - [optional] version arg: -v, version, etc
+# 5 - [optional] exit first find 0/1; 6 - [optional] 0/1 stderr output
+sub program_version {
+       eval $start if $b_log;
+       my ($app, $search, $num,$version,$exit,$b_stderr) = @_;
+       my ($cmd,$line,$output);
+       my $version_nu = '';
+       my $count = 0;
+       #print "app:$app\n";
+       $exit ||= 100; # basically don't exit ever
+       $version ||= '--version';
+       # adjust to array index, not human readable
+       $num-- if (defined $num && $num > 0);
+       # ksh: Version JM 93t+ 2010--03-05
+       # mksh: @(#)MIRBSD KSH R56 2018/03/09
+       # loksh: @(#)PD KSH v5.2.14 99/07/13.2
+       # --version opens a new ksh, sigh... This so far does not work
+       # because the ENV/Shell variable is not visible in subshells
+       if ($search eq 'ksh'){
+               my $ksh = system('echo -n $KSH_VERSION');
+               if ( $ksh ){
+                       my @temp = split /\s+/, $ksh;
+                       if ($temp[2]){
+                               $temp[2] =~ s/^v//i; # trim off leading v
+                               log_data('data',"Program *ksh array: @temp version: $temp[2]") if $b_log;
+                               return $temp[2];
+                       }
+               }
+               return 0;
        }
-       
-       /^(lm|nx|pae|pni|svm|vmx|(sss|ss)e([2-9])?([a-z])?(_[0-9])?)$/ {
-               if ( $0 == "pni" ){
-                       a_flags[i] = "sse3"
+       # konvi in particular doesn't like using $ENV{'PATH'} as set, so we need
+       # to always assign the full path if it hasn't already been done
+       if ( $app !~ /^\// ){
+               if (my $program = check_program($app) ){
+                       $app = $program;
                }
                else {
-                       a_flags[i] = $0
+                       log_data('data',"$app not found in path.");
+                       return 0;
                }
-               i++
        }
-       END {
-               count = asort( a_flags )
-               # note: why does gawk increment before the loop and not after? weird.
-               for ( i=0; i <= count; i++ ){
-                       if ( flag_string == "" ) {
-                               flag_string = a_flags[i] 
-                       }
-                       else {
-                               flag_string = flag_string " " a_flags[i]
+       # note, some wm/apps send version info to stderr instead of stdout
+       if ( $b_stderr ) {
+               $cmd = "$app $version 2>&1";
+       }
+#      elsif ( $app eq 'csh' ){
+#              $app = 'tcsh';
+#      }
+       # quick debian/buntu hack until I find a universal way to get version for these
+       elsif ( $app eq 'dash' ){
+               $cmd = "dpkg -l $app 2>/dev/null";
+       }
+       else {
+               $cmd = "$app $version 2>/dev/null";
+       }
+       log_data('data',"version: $version num: $num search: $search command: $cmd") if $b_log;
+       $output = qx($cmd);
+       # print "$cmd : $output\n";
+       # sample: dwm-5.8.2, ©.. etc, why no space? who knows. Also get rid of v in number string
+       # xfce, and other, output has , in it, so dump all commas and parentheses
+       if ($output){
+               open my $ch, '<', \$output or error_handler('open-data',"$cmd", "$!");
+               while (<$ch>){
+                       #chomp;
+                       last if $count > $exit;
+                       if ( $_ =~ /$search/i ) {
+                               $_ = trimmer($_);
+                               # print "$_ ::$num\n";
+                               my @data = split /\s+/, $_;
+                               $version_nu = $data[$num];
+                               last if ! defined $version_nu;
+                               # some distros add their distro name before the version data, which 
+                               # breaks version detection. A quick fix attempt is to just add 1 to $num 
+                               # to get the next value.
+                               $version_nu = $data[$num+1] if $data[$num+1] && $version_nu =~ /version/i;
+                               $version_nu =~ s/(\([^)]+\)|,|dwm-|wmii2-|wmii-|\||\(|\))//g if $version_nu;
+                               # trim off leading v but only when followed by a number
+                               $version_nu =~ s/^v([0-9])/$1/i if $version_nu; 
+                               # print "$version_nu\n";
+                               last;
                        }
+                       $count++;
                }
-               print flag_string
-       }' )
-
-       #grep -oE '\<(nx|lm|sse[0-9]?|pni|svm|vmx)\>' | tr '\n' ' '))
-       if [[ -z $cpu_flags ]];then
-               cpu_flags="-"
-       fi
-       echo "$cpu_flags"
-       log_function_data "cpu_flags: $cpu_flags"
-       eval $LOGFE
+               close $ch if $ch;
+       }
+       log_data('data',"Program version: $version_nu") if $b_log;
+       eval $end if $b_log;
+       return $version_nu;
 }
-
-#### -------------------------------------------------------------------
-#### print and processing of output data
-#### -------------------------------------------------------------------
-
-#### MASTER PRINT FUNCTION - triggers all line item print functions
-## main function to print out, master for all sub print functions.
-print_it_out()
-{
-       eval $LOGFS
-       # note that print_it_out passes local variable values on to its children,
-       # and in some cases, their children, with Lspci_v_Data
-       local Lspci_v_Data='' Lspci_n_Data='' # only for verbose
-       local Sysctl_a_Data='' Dmesg_Boot_Data=''
-       
-       if [[ -n $BSD_TYPE ]];then
-               Sysctl_a_Data="$( get_sysctl_data 'a' )"
-               Dmesg_Boot_Data="$( get_dmesg_boot_data )"
-       fi
-
-       if [[ $B_SHOW_SHORT_OUTPUT == 'true' ]];then
-               print_short_data
-       else
-               Lspci_v_Data="$( get_lspci_data 'v' )"
-               if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                       Lspci_n_Data="$( get_lspci_data 'n' )"
-               fi
-               if [[ $B_SHOW_SYSTEM == 'true' ]];then
-                       print_system_data
-               fi
-               if [[ $B_SHOW_MACHINE == 'true' ]];then
-                       print_machine_data
-               fi
-               if [[ $B_SHOW_WEATHER == 'true' ]];then
-                       print_weather_data
-               fi
-               if [[ $B_SHOW_BASIC_CPU == 'true' || $B_SHOW_CPU == 'true' ]];then
-                       print_cpu_data
-               fi
-               if [[ $B_SHOW_GRAPHICS == 'true' ]];then
-                       print_graphics_data
-               fi
-               if [[ $B_SHOW_AUDIO == 'true' ]];then
-                       print_audio_data
-               fi
-               if [[ $B_SHOW_NETWORK == 'true' ]];then
-                       print_networking_data
-               fi
-               if [[ $B_SHOW_DISK_TOTAL == 'true' || $B_SHOW_BASIC_DISK == 'true' || $B_SHOW_DISK == 'true' ]];then
-                       print_hard_disk_data
-               fi
-               if [[ $B_SHOW_PARTITIONS == 'true' ]];then
-                       print_partition_data
-               fi
-               if [[ $B_SHOW_RAID == 'true' || $B_SHOW_BASIC_RAID == 'true' ]];then
-                       print_raid_data
-               fi
-               if [[ $B_SHOW_UNMOUNTED_PARTITIONS == 'true' ]];then
-                       print_unmounted_partition_data
-               fi
-               if [[ $B_SHOW_SENSORS == 'true' ]];then
-                       print_sensors_data
-               fi
-               if [[ $B_SHOW_REPOS == 'true' ]];then
-                       print_repo_data
-               fi
-               if [[ $B_SHOW_PS_CPU_DATA == 'true' || $B_SHOW_PS_MEM_DATA == 'true' ]];then
-                       print_ps_data
-               fi
-               if [[ $B_SHOW_INFO == 'true' ]];then
-                       print_info_data
-               fi
-       fi
-       eval $LOGFE
-}
-
-#### SHORT OUTPUT PRINT FUNCTION, ie, verbosity 0
-# all the get data stuff is loaded here to keep execution time down for single line print commands
-# these will also be loaded in each relevant print function for long output
-print_short_data()
-{
-       eval $LOGFS
-       local current_kernel=$( get_kernel_version ) 
-       local processes=$(( $( wc -l <<< "$Ps_aux_Data" ) - 1 ))
-       local short_data='' i='' b_background_black='false'
-       local memory=$( get_memory_data )
-       local up_time="$( get_uptime )"
-
-       # set A_CPU_CORE_DATA
-       get_cpu_core_count
-       local cpc_plural='' cpu_count_print='' model_plural=''
-       local cpu_physical_count=${A_CPU_CORE_DATA[0]}
-       local cpu_core_count=${A_CPU_CORE_DATA[3]}
-       local cpu_core_alpha=${A_CPU_CORE_DATA[1]}
-       local cpu_type=${A_CPU_CORE_DATA[2]}
-
-       if [[ $cpu_physical_count -gt 1 ]];then
-               cpc_plural='(s)'
-               model_plural='s'
-               cpu_count_print="$cpu_physical_count "
-       fi
-
-       local cpu_data_string="${cpu_count_print}${cpu_core_alpha} core"
-#      local cpu_core_count=${A_CPU_CORE_DATA[0]}
-
-       # load A_HDD_DATA
-       get_hdd_data_basic
-       ## note: if hdd_model is declared prior to use, whatever string you want inserted will
-       ## be inserted first. In this case, it's desirable to print out (x) before each disk found.
-       local a_hdd_data_count=$(( ${#A_HDD_DATA[@]} - 1 ))
-       IFS=","
-       local a_hdd_basic_working=( ${A_HDD_DATA[$a_hdd_data_count]} )
-       IFS="$ORIGINAL_IFS"
-       local hdd_capacity=${a_hdd_basic_working[0]}
-       local hdd_used=${a_hdd_basic_working[1]}
-
-       # load A_CPU_DATA
-       get_cpu_data
-
-       IFS=","
-       local a_cpu_working=(${A_CPU_DATA[0]})
-       IFS="$ORIGINAL_IFS"
-       local cpu_model="${a_cpu_working[0]}"
-       ## assemble data for output
-       local cpu_clock="${a_cpu_working[1]}" # old CPU3
-       # this gets that weird min/max final array item, which almost never contains any data of use
-       local min_max_clock_nu=$(( ${#A_CPU_DATA[@]} - 1 ))
-       local min_max_clock=${A_CPU_DATA[$min_max_clock_nu]}
-       # this handles the case of for example ARM cpus, which will not have data for
-       # min/max, since they don't have speed. Since that sets a flag, not found, just
-       # look for that and use the speed from the first array array, same where we got 
-       # model from
-       if [[ "$min_max_clock" == 'N/A' && ${a_cpu_working[1]} != '' ]];then
-               min_max_clock="${a_cpu_working[1]} MHz"
-       fi
-       local patch_version_number=$( get_patch_version_string )
-
-       #set_color_scheme 12
-       if [[ $B_RUNNING_IN_SHELL == 'false' ]];then
-               for i in $C1 $C2 $CN
-               do
-                       case "$i" in
-                               "$GREEN"|"$WHITE"|"$YELLOW"|"$CYAN")
-                                       b_background_black='true'
-                                       ;;
-                       esac
-               done
-               if [[ $b_background_black == 'true' ]];then
-                       for i in C1 C2 CN
-                       do
-                               ## these need to be in quotes, don't know why
-                               if [[ ${!i} == $NORMAL ]];then
-                                       declare $i="${!i}15,1"
-                               else
-                                       declare $i="${!i},1"
-                               fi
-                       done
-                       #C1="${C1},1"; C2="${C2},1"; CN="${CN},1"
-               fi
-       fi
-       short_data="${C1}CPU$cpc_plural${C2}${SEP1}${cpu_data_string} ${cpu_model}$model_plural (${cpu_type}) clocked at ${min_max_clock}${SEP2}${C1}Kernel${C2}${SEP1}${current_kernel}${SEP2}${C1}Up${C2}${SEP1}${up_time}${SEP2}${C1}Mem${C2}${SEP1}${memory}${SEP2}${C1}HDD${C2}${SEP1}${hdd_capacity}($hdd_used)${SEP2}${C1}Procs${C2}${SEP1}${processes}${SEP2}"
-
-       if [[ $SHOW_IRC -gt 0 ]];then
-               short_data="${short_data}${C1}Client${C2}${SEP1}${IRC_CLIENT}${IRC_CLIENT_VERSION}${SEP2}"
-       fi
-       short_data="${short_data}${C1}$SCRIPT_NAME${C2}${SEP1}$SCRIPT_VERSION_NUMBER$patch_version_number${SEP2}${CN}"
-       if [[ $SCHEME -gt 0 ]];then
-               short_data="${short_data} $NORMAL"
-       fi
-       print_screen_output "$short_data"
-       eval $LOGFE
-}
-
-#### LINE ITEM PRINT FUNCTIONS
-
-# print sound card data
-print_audio_data()
-{
-       eval $LOGFS
-       local i='' card_id='' audio_data='' a_audio_data='' port_data='' pci_bus_id='' card_string=''
-       local a_audio_working='' audio_driver='' alsa_data='' port_plural='' module_version='' chip_id=''
-       local bus_usb_text='' bus_usb_id='' line_starter='Audio:' alsa='' alsa_version='' print_data=''
-       local driver=''
-       # set A_AUDIO_DATA and get alsa data
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               if [[ $B_PCICONF_SET == 'false' ]];then
-                       get_pciconf_data
-               fi
-               get_pciconf_card_data 'audio'
-       else
-               get_audio_data
-       fi
-       
-       get_audio_alsa_data
-       # alsa driver data now prints out no matter what
-       if [[ -n $A_ALSA_DATA ]];then
-               IFS=","
-               if [[ -n ${A_ALSA_DATA[0]} ]];then
-                       alsa=${A_ALSA_DATA[0]}
-               else
-                       alsa='N/A'
-               fi
-               if [[ -n ${A_ALSA_DATA[1]} ]];then
-                       alsa_version=${A_ALSA_DATA[1]}
-               else
-                       alsa_version='N/A'
-               fi
-               alsa_data="${C1}Sound:${C2} $alsa ${C1}ver$SEP3${C2} $alsa_version"
-               IFS="$ORIGINAL_IFS"
-       fi
-       # note, error handling is done in the get function, so this will never be null, but
-       # leaving the test just in case it's changed.
-       if [[ -n ${A_AUDIO_DATA[@]} ]];then
-               for (( i=0; i< ${#A_AUDIO_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_audio_working=( ${A_AUDIO_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       port_data=''
-                       audio_driver=''
-                       audio_data=''
-                       card_string=''
-                       port_plural=''
-                       module_version=''
-                       pci_bus_id=''
-                       bus_usb_text=''
-                       bus_usb_id=''
-                       print_data=''
-                       card_id=''
-                       chip_id=''
-                       
-                       if [[ ${#A_AUDIO_DATA[@]} -gt 1 ]];then
-                               card_id="-$(( $i + 1 ))"
-                       fi
-                       if [[ $BSD_TYPE != 'bsd' ]];then
-                               if [[ -n ${a_audio_working[3]} && $B_EXTRA_DATA == 'true' ]];then
-                                       module_version=$( print_module_version "${a_audio_working[3]}" 'audio' )
-                               elif [[ -n ${a_audio_working[1]} && $B_EXTRA_DATA == 'true' ]];then
-                                       module_version=$( print_module_version "${a_audio_working[1]}" 'audio' )
-                               fi
-                       fi
-                       # we're testing for the presence of the 2nd array item here, which is the driver name
-                       if [[ -n ${a_audio_working[1]} ]];then
-                               # note: linux drivers can have numbers, like tg3
-                               if [[ $BSD_TYPE == 'bsd' ]];then
-                                       driver=$( sed 's/[0-9]$//' <<< ${a_audio_working[1]} )
-                               else
-                                       driver=${a_audio_working[1]}
-                               fi
-                               audio_driver="${C1}driver$SEP3${C2} ${driver} "
-                       fi
-                       if [[ -n ${a_audio_working[2]} && $B_EXTRA_DATA == 'true' ]];then
-                               if [[ $( wc -w <<< ${a_audio_working[2]} ) -gt 1 ]];then
-                                       port_plural='s'
-                               fi
-                               port_data="${C1}port$port_plural$SEP3${C2} ${a_audio_working[2]} "
-                       fi
-                       if [[ -n ${a_audio_working[4]} && $B_EXTRA_DATA == 'true' ]];then
-                               if [[ ${a_audio_working[1]} != 'USB Audio' ]];then
-                                       bus_usb_text='bus-ID'
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               if [[ $BSD_TYPE != 'bsd' ]];then
-                                                       chip_id=$( get_lspci_chip_id "${a_audio_working[4]}" )
-                                               else
-                                                       chip_id=${a_audio_working[6]}
-                                               fi
-                                       fi
-                               else
-                                       bus_usb_text='usb-ID'
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               chip_id=${a_audio_working[5]}
-                                       fi
-                               fi
-                               bus_usb_id=${a_audio_working[4]}
-                               pci_bus_id="${C1}$bus_usb_text$SEP3${C2} $bus_usb_id "
-                               if [[ -n $chip_id ]];then
-                                       chip_id="${C1}chip-ID$SEP3${C2} $chip_id "
-                               fi
-                       fi
-                       if [[ -n ${a_audio_working[0]} ]];then
-                               card_string="${C1}Card$card_id:${C2} ${a_audio_working[0]} "
-                               audio_data="$audio_driver$port_data$pci_bus_id$chip_id"
-                       fi
-                       # only print alsa on last line if short enough, otherwise print on its own line
-                       if [[ $i -eq 0 ]];then
-                               if [[ -n $alsa_data && $( calculate_line_length "$card_string${audio_data}$alsa_data" ) -lt $COLS_INNER ]];then
-                                       audio_data="$audio_data$alsa_data"
-                                       alsa_data=''
-                               fi
-                       fi
-                       if [[ -n $audio_data ]];then
-                               if [[ $( calculate_line_length "$card_string$audio_data" ) -lt $COLS_INNER ]];then
-                                       print_data=$( create_print_line "$line_starter" "$card_string$audio_data" )
-                                       print_screen_output "$print_data"
-                               # print the line
-                               else
-                                       # keep the driver on the same line no matter what, looks weird alone on its own line
-                                       if [[ $B_EXTRA_DATA != 'true' ]];then
-                                               print_data=$( create_print_line "$line_starter" "$card_string$audio_data" )
-                                               print_screen_output "$print_data"
-                                       else
-                                               print_data=$( create_print_line "$line_starter" "$card_string" )
-                                               print_screen_output "$print_data"
-                                               line_starter=' '
-                                               print_data=$( create_print_line "$line_starter" "$audio_data" )
-                                               print_screen_output "$print_data"
-                                       fi
-                               fi
-                               line_starter=' '
-                       fi
-               done
-       fi
-       if [[ -n $alsa_data ]];then
-               alsa_data=$( sed 's/ALSA/Advanced Linux Sound Architecture/' <<< $alsa_data )
-               alsa_data=$( create_print_line "$line_starter" "$alsa_data" )
-               print_screen_output "$alsa_data"
-       fi
-       eval $LOGFE
-}
-
-print_cpu_data()
-{
-       eval $LOGFS
-       local cpu_data='' i='' cpu_clock_speed='' cpu_multi_clock_data=''
-       local bmip_data='' cpu_cache='' cpu_vendor='' cpu_flags='' flag_feature='flags'
-       local a_cpu_working='' cpu_model='' cpu_clock='' cpu_null_error=''
-       local cpc_plural='' cpu_count_print='' model_plural='' cpu_data_string=''
-       local cpu_physical_count='' cpu_core_count='' cpu_core_alpha='' cpu_type=''
-       local cpu_2_data=''
-
-       ##print_screen_output "A_CPU_DATA[0]=\"${A_CPU_DATA[0]}\""
-       # Array A_CPU_DATA always has one extra element: max clockfreq found.
-       # that's why its count is one more than you'd think from cores/cpus alone
-       # load A_CPU_DATA
-       get_cpu_data
-
-       IFS=","
-       a_cpu_working=(${A_CPU_DATA[0]})
-       IFS="$ORIGINAL_IFS"
-
-       # Strange (and also some expected) behavior encountered. If print_screen_output() uses $1
-       # as the parameter to output to the screen, then passing "<text1> ${ARR[@]} <text2>"
-       # will output only <text1> and first element of ARR. That "@" splits in elements and "*" _doesn't_,
-       # is to be expected. However, that text2 is consecutively truncated is somewhat strange, so take note.
-       # This has been confirmed by #bash on freenode.
-       # The above mentioned only emerges when using the debugging markers below
-       ## print_screen_output "a_cpu_working=\"***${a_cpu_working[@]} $hostName+++++++\"----------"
-       # unless all these are null, process whatever you have
-       if [[ -n ${a_cpu_working[0]} || -n ${a_cpu_working[1]} || -n ${a_cpu_working[2]} || -n ${a_cpu_working[3]} ]];then
-               cpu_model="${a_cpu_working[0]}"
-               ## assemble data for output
-               cpu_clock="${a_cpu_working[1]}"
-
-               cpu_vendor=${a_cpu_working[5]}
-
-               # set A_CPU_CORE_DATA
-               get_cpu_core_count
-               cpu_physical_count=${A_CPU_CORE_DATA[0]}
-               cpu_core_count=${A_CPU_CORE_DATA[3]}
-               cpu_core_alpha=${A_CPU_CORE_DATA[1]}
-               cpu_type=${A_CPU_CORE_DATA[2]}
-               
-               if [[ $cpu_physical_count -gt 1 ]];then
-                       cpc_plural='(s)'
-                       cpu_count_print="$cpu_physical_count "
-                       model_plural='s'
-               fi
-
-               cpu_data_string="${cpu_count_print}${cpu_core_alpha} core"
-               cpu_data=$( create_print_line "CPU$cpc_plural:" "${C1}${cpu_data_string}${C2} ${a_cpu_working[0]}$model_plural (${cpu_type})" )
-               if [[ $B_SHOW_CPU == 'true' ]];then
-                       # update for multicore, bogomips x core count.
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-       #                       if [[ $cpu_vendor != 'intel' ]];then
-                               # ARM may use the faked 1 cpucorecount to make this work
-                               # echo $cpu_core_count $cpu_physical_count
-                               if [[ -n ${a_cpu_working[4]} ]];then
-                                       bmip_data=$( calculate_multicore_data "${a_cpu_working[4]}" "$(( $cpu_core_count * $cpu_physical_count ))" )
-                               fi
-       #                       else
-       #                               bmip_data="${a_cpu_working[4]}"
-       #                       fi
-                               # bogomips are a linux thing, but my guess is over time bsds will use them somewhere anyway
-                               if [[ $BSD_TYPE == 'bsd' && -z $bmip_data ]];then
-                                       bmip_data=''
-                               else
-                                       bmip_data="${C1}bmips$SEP3${C2} $bmip_data "
-                               fi
-                       fi
-                       ## note: this handles how intel reports L2, total instead of per core like AMD does
-                       # note that we need to multiply by number of actual cpus here to get true cache size
-                       if [[ -n ${a_cpu_working[2]} ]];then
-                               if [[ $cpu_vendor != 'intel' ]];then
-                                       cpu_cache=$( calculate_multicore_data "${a_cpu_working[2]}" "$(( $cpu_core_count * $cpu_physical_count ))"  )
-                               else
-                                       cpu_cache=$( calculate_multicore_data "${a_cpu_working[2]}" "$cpu_physical_count"  )
-                               fi
-                       else
-                               cpu_cache='N/A'
-                       fi
-                       # only print shortened list
-                       if [[ $B_CPU_FLAGS_FULL != 'true' ]];then
-                               # gawk has already sorted this output, no flags returns -
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       cpu_flags=$( process_cpu_flags "${a_cpu_working[3]}" "${a_cpu_working[6]}" )
-                                       cpu_flags="($cpu_flags)"
-                                       if [[ ${a_cpu_working[6]} == 'true' ]];then
-                                               flag_feature='features'
-                                       fi
-                                       cpu_flags="${C1}$flag_feature$SEP3${C2} $cpu_flags "
-                               fi
-                       fi
-                       # arm cpus do not have flags or cache
-                       if [[ ${a_cpu_working[6]} != 'true' ]];then
-                               cpu_data="$cpu_data${C2} ${C1}cache$SEP3${C2} $cpu_cache${CN}"
-                               cpu_2_data="$cpu_flags$bmip_data${CN}"
-                       else
-                               cpu_data="$cpu_data${C2} (ARM)$bmip_data${CN}"
-                       fi
-               fi
-               # we don't this printing out extra line unless > 1 cpu core
-               if [[ ${#A_CPU_DATA[@]} -gt 2 && $B_SHOW_CPU == 'true' ]];then
-                       cpu_clock_speed='' # null < verbosity level 5
-               else
-                       cpu_data="$cpu_data ${C1}clocked at${C2} ${a_cpu_working[1]} MHz${CN}"
-               fi
-               cpu_2_data="$cpu_2_data$cpu_clock_speed"
-       else
-               if [[ $BSD_TYPE == 'bsd' && $B_ROOT != 'true' ]];then
-                       cpu_null_error=' No permissions for sysctl use?'
-               fi
-               cpu_data=$( create_print_line "CPU:" "${C2}No CPU data available.$cpu_null_error" )
-       fi
-       # echo ln: $( calculate_line_length "$cpu_data $cpu_2_data" )
-       # echo icols: $COLS_INNER
-       # echo tc: $TERM_COLUMNS
-       # echo :$cpu_2_data:
-       if [[ -n $cpu_2_data && $( calculate_line_length "$cpu_data $cpu_2_data" ) -gt $COLS_INNER ]];then
-               print_screen_output "$cpu_data"
-               cpu_data=$( create_print_line " " "$cpu_2_data" )
-               print_screen_output "$cpu_data"
-       else
-               print_screen_output "$cpu_data $cpu_2_data"
-       fi
-       # we don't this printing out extra line unless > 1 cpu core
-       # note the numbering, the last array item is the min/max/not found for cpu speeds
-       if [[ ${#A_CPU_DATA[@]} -gt 2 && $B_SHOW_CPU == 'true' ]];then
-               for (( i=0; i < ${#A_CPU_DATA[@]}-1; i++ ))
-               do
-                       IFS=","
-                       a_cpu_working=(${A_CPU_DATA[i]})
-                       IFS="$ORIGINAL_IFS"
-                       # note: the first iteration will create a first space, for color code separation below
-                       cpu_multi_clock_data="$cpu_multi_clock_data ${C1}$(( i + 1 )):${C2} ${a_cpu_working[1]} MHz${CN}"
-                       # someone actually appeared with a 16 core system, so going to stop the cpu core throttle
-                       # if this had some other purpose which we can't remember we'll add it back in
-                       #if [[ $i -gt 10 ]];then
-                       #       break
-                       #fi
-               done
-               if [[ -n $cpu_multi_clock_data ]];then
-                       cpu_multi_clock_data=$( create_print_line " " "${C1}Clock Speeds:${C2}$cpu_multi_clock_data" )
-                       print_screen_output "$cpu_multi_clock_data"
-               fi
-       fi
-       if [[ $B_CPU_FLAGS_FULL == 'true' ]];then
-               print_cpu_flags_full "${a_cpu_working[3]}" "${a_cpu_working[6]}"
-       fi
-       eval $LOGFE
-}
-
-# takes list of all flags, split them and prints x per line
-# args: $1 - cpu flag string; $2 - arm true/false
-print_cpu_flags_full()
-{
-       eval $LOGFS
-       # note: sort only sorts lines, not words in a string, so convert to lines
-       local cpu_flags_full="$( echo $1 | tr " " "\n" | sort )" 
-       local a_cpu_flags='' line_starter='' temp_string=''
-       local i=0 counter=0 starter_length=15 flag='' flag_data=''
-       local line_length='' flag_feature='Flags' spacer='' flag_string=''
-       
-       if [[ $2 == 'true' ]];then
-               flag_feature='Features'
-       fi
-       # build the flag line array
-       for flag in $cpu_flags_full
-       do
-               temp_string="$flag_string$spacer$flag"
-               spacer=' '
-               # handle inner line starter
-               if [[ $counter -eq 0 ]];then
-                       line_length=$(( $COLS_INNER - $starter_length ))
-               else
-                       line_length=$COLS_INNER
-               fi
-               if [[ ${#temp_string} -gt $line_length ]];then
-                       a_cpu_flags[$counter]=$temp_string
-                       flag_string=''
-                       spacer=''
-                       (( counter++ ))
-               else
-                       flag_string=$temp_string
-               fi
-               temp_string=''
-       done
-       if [[ -n $flag_string ]];then
-               a_cpu_flags[$counter]=$flag_string
-       fi
-       # then print it out
-       for (( i=0; i < ${#a_cpu_flags[@]};i++ ))
-       do
-               if [[ $i -eq 0 ]];then
-                       line_starter="${C1}CPU $flag_feature$SEP3${C2} "
-               else
-                       line_starter=''
-               fi
-               flag_data=$( create_print_line " " "$line_starter${a_cpu_flags[$i]}" )
-               print_screen_output "$flag_data"
-       done
-       eval $LOGFE
-}
-
-print_graphics_data()
-{
-       eval $LOGFS
-       local graphics_data='' card_id='' i='' root_alert='' root_x_string='' a_graphics_working=''
-       local b_is_mesa='false' display_full_string='' card_bus_id='' card_data=''
-       local res_tty='Resolution' xorg_data='' display_vendor_string='' chip_id=''
-       local spacer='' driver='' driver_string='' driver_plural='' direct_render_string=''
-       local separator_loaded='' separator_unloaded='' separator_failed='' 
-       local loaded='' unloaded='' failed='' display_server_string=''
-       local line_starter='Graphics:'
-       local screen_resolution="$( get_graphics_res_data )"
-       
-       # set A_DISPLAY_SERVER_DATA
-       get_graphics_display_server_data
-       local display_vendor=${A_DISPLAY_SERVER_DATA[0]}
-       local display_version=${A_DISPLAY_SERVER_DATA[1]}
-       # set A_GLX_DATA
-       get_graphics_glx_data
-       local glx_renderer="${A_GLX_DATA[0]}"
-       local glx_version="${A_GLX_DATA[1]}"
-       # this can contain a long No case debugging message, so it's being sliced off
-       # note: using grep -ioE '(No|Yes)' <<< ${A_GLX_DATA[2]} did not work in Arch, no idea why
-       local glx_direct_render=$( gawk '{print $1}' <<< "${A_GLX_DATA[2]}" )
-
-       # set A_GRAPHICS_CARD_DATA
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               if [[ $B_PCICONF_SET == 'false' ]];then
-                       get_pciconf_data
-               fi
-               get_pciconf_card_data 'display'
-       else
-               get_graphics_card_data
-       fi
-       # set A_GRAPHIC_DRIVERS
-       get_graphics_driver
-       
-       if [[ ${#A_GRAPHIC_DRIVERS[@]} -eq 0 ]];then
-               driver=' N/A'
-       else
-               for (( i=0; i < ${#A_GRAPHIC_DRIVERS[@]}; i++ ))
-               do
-                       IFS=","
-                       a_graphics_working=( ${A_GRAPHIC_DRIVERS[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       case ${a_graphics_working[1]} in
-                               loaded)
-                                       loaded="$loaded$separator_loaded${a_graphics_working[0]}"
-                                       separator_loaded=','
-                                       ;;
-                               unloaded)
-                                       unloaded="$unloaded$separator_unloaded${a_graphics_working[0]}"
-                                       separator_unloaded=','
-                                       ;;
-                               failed)
-                                       failed="$failed$separator_failed${a_graphics_working[0]}"
-                                       separator_failed=','
-                                       ;;              
-                       esac
-               done
-       fi
-       if [[ -n $loaded ]];then
-               driver="${driver} $loaded"
-       fi
-       if [[ -n $unloaded ]];then
-               driver="${driver} (unloaded: $unloaded)"
-       fi
-       if [[ -n $failed ]];then
-               driver="${driver} ${RED}FAILED:${C2} $failed"
-       fi
-       # sometimes for some reason there is no driver found but the array is started
-       if [[ -z $driver ]];then
-               driver=' N/A'
-       fi
-
-       if [[ ${#A_GRAPHIC_DRIVERS[@]} -gt 1 ]];then
-               driver_plural='s'
-       fi
-       driver_string="${C1}driver$driver_plural$SEP3${C2}$driver "
-       
-       # some basic error handling:
-       if [[ -z $screen_resolution ]];then
-               screen_resolution='N/A'
-       fi
-       if [[ -z $display_vendor || -z $display_version ]];then
-               display_vendor_string="N/A"
-       else
-               display_vendor_string="$display_vendor $display_version"
-       fi
-       display_server_string="${C1}Display Server${SEP3}${C2} $display_vendor_string "
-
-       if [[ $B_ROOT == 'true' ]];then
-               root_x_string='for root '
-               if [[ $B_RUNNING_IN_SHELL == 'true' || $B_CONSOLE_IRC == 'true' ]];then
-                       res_tty='tty size'
-               fi
-       fi
-       if [[ $B_RUNNING_IN_DISPLAY != 'true' ]];then
-               root_x_string="${root_x_string}out of X"
-               res_tty='tty size'
-       fi
-       
-       if [[ -n $root_x_string ]];then
-               root_x_string="${C1}Advanced Data:${C2} N/A $root_x_string"
-       fi
-
-       display_full_string="$display_server_string$driver_string${C1}${res_tty}$SEP3${C2} ${screen_resolution} $root_x_string"
-
-       if [[ ${#A_GRAPHICS_CARD_DATA[@]} -gt 0 ]];then
-               for (( i=0; i < ${#A_GRAPHICS_CARD_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_graphics_working=( ${A_GRAPHICS_CARD_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       card_bus_id=''
-                       card_data=${a_graphics_working[0]}
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                               if [[ -n ${a_graphics_working[1]} ]];then
-                                       card_bus_id=" ${C1}bus-ID$SEP3${C2} ${a_graphics_working[1]}"
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               if [[ $BSD_TYPE != 'bsd' ]];then
-                                                       chip_id=$( get_lspci_chip_id "${a_graphics_working[1]}" )
-                                               else
-                                                       chip_id=${a_graphics_working[2]}
-                                               fi
-                                       fi
-                               else
-                                       card_bus_id=" ${C1}bus-ID$SEP3${C2} N/A"
-                               fi
-                       fi
-                       if [[ -n $chip_id ]];then
-                               chip_id=" ${C1}chip-ID$SEP3${C2} $chip_id"
-                       fi
-                       if [[ ${#A_GRAPHICS_CARD_DATA[@]} -gt 1 ]];then
-                               card_id="Card-$(($i+1)):"
-                       else
-                               card_id='Card:'
-                       fi
-                       graphics_data="${C1}$card_id${C2} $card_data$card_bus_id$chip_id "
-                       if [[ ${#A_GRAPHICS_CARD_DATA[@]} -gt 1 ]];then
-                               graphics_data=$( create_print_line "$line_starter" "${graphics_data}" )
-                               print_screen_output "$graphics_data"
-                               line_starter=' '
-                               graphics_data=''
-                       fi
-               done
-       # handle cases where card detection fails, like in PS3, where lspci gives no output, or headless boxes..
-       else
-               graphics_data="${C1}Card:${C2} Failed to Detect Video Card! "
-       fi
-       if [[ -n $graphics_data && $( calculate_line_length "${graphics_data}$display_full_string" ) -lt $COLS_INNER ]];then
-               graphics_data=$( create_print_line "$line_starter" "${graphics_data}$display_full_string" )
-       else
-               if [[ -n $graphics_data ]];then
-                       graphics_data=$( create_print_line "$line_starter" "$graphics_data" )
-                       print_screen_output "$graphics_data"
-                       line_starter=' '
-               fi
-               graphics_data=$( create_print_line "$line_starter" "$display_full_string" )
-       fi
-       print_screen_output "$graphics_data"
-       # if [[ -z $glx_renderer || -z $glx_version ]];then
-       #       b_is_mesa='true'
-       # fi
-
-       ## note: if glx render or display_version have no content, then mesa is true
-       # if [[ $B_SHOW_DISPLAY_DATA == 'true' ]] && [[ $b_is_mesa != 'true' ]];then
-       if [[ $B_SHOW_DISPLAY_DATA == 'true' && $B_ROOT != 'true' ]];then
-               if [[ -z $glx_renderer ]];then
-                       glx_renderer='N/A'
-               fi
-               if [[ -z $glx_version ]];then
-                       glx_version='N/A'
-               fi
-               if [[ -z $glx_direct_render ]];then
-                       glx_direct_render='N/A'
-               fi
-               if [[ $B_HANDLE_CORRUPT_DATA == 'true' || $B_EXTRA_DATA == 'true' ]];then
-                       direct_render_string=" ${C1}Direct Rendering$SEP3${C2} ${glx_direct_render}${CN}"
-               fi
-               graphics_data="${C1}GLX Renderer$SEP3${C2} ${glx_renderer} ${C1}GLX Version$SEP3${C2} ${glx_version}${CN}$direct_render_string"
-               graphics_data=$( create_print_line " " "$graphics_data" )
-               
-               print_screen_output "$graphics_data"
-       fi
-       eval $LOGFE
+# print program_version('bash', 'bash', 4) . "\n";
+
+# arg: 1 - full file path, returns array of file lines.
+# 2 - optionsl, strip and clean data
+# note: chomp has to chomp the entire action, not just <$fh>
+sub reader {
+       eval $start if $b_log;
+       my ($file,$strip) = @_;
+       return if ! $file;
+       open( my $fh, '<', $file ) or error_handler('open', $file, $!);
+       chomp(my @rows = <$fh>);
+       if ($strip && @rows){
+               @rows = grep {/^\s*[^#]/} @rows;
+               @rows = map {s/^\s+|\s+$//g; $_} @rows if @rows;
+       }
+       eval $end if $b_log;
+       return @rows;
 }
 
-print_hard_disk_data()
-{
-       eval $LOGFS
-       local hdd_data='' hdd_data_2='' a_hdd_working='' hdd_temp_data='' hdd_string=''
-       local hdd_serial=''
-       local dev_data='' size_data='' hdd_model='' usb_data='' hdd_name='' divisor=5
-       local Line_Starter='Drives:' # inherited by print_optical_drives
-
-       # load A_HDD_DATA
-       get_hdd_data_basic
-       ## note: if hdd_model is declared prior to use, whatever string you want inserted will
-       ## be inserted first. In this case, it's desirable to print out (x) before each disk found.
-       local a_hdd_data_count=$(( ${#A_HDD_DATA[@]} - 1 ))
-       IFS=","
-       local a_hdd_basic_working=( ${A_HDD_DATA[$a_hdd_data_count]} )
-       IFS="$ORIGINAL_IFS"
-       local hdd_capacity="${a_hdd_basic_working[0]}"
-       local hdd_used=${a_hdd_basic_working[1]}
-       local bsd_unsupported='Hard drive data not yet supported for BSD systems.'
-       local hdd_name_temp=''
-
-       if [[ $B_SHOW_BASIC_DISK == 'true' || $B_SHOW_DISK == 'true' ]];then
-       ## note: the output part of this should be in the print hdd data function, not here
-               get_hard_drive_data_advanced
-               for (( i=0; i < ${#A_HDD_DATA[@]} - 1; i++ ))
-               do
-                       # this adds the (x) numbering in front of each disk found, and creates the full disk string
-                       IFS=","
-                       a_hdd_working=( ${A_HDD_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       if [[ $B_SHOW_DISK == 'true' ]];then
-                               if [[ -n ${a_hdd_working[3]} ]];then
-                                       usb_data="${a_hdd_working[3]} "
-                               else
-                                       usb_data=''
-                               fi
-                               dev_data="/dev/${a_hdd_working[0]} "
-                               size_data=" ${C1}size$SEP3${C2} ${a_hdd_working[1]}"
-                               if [[ $B_EXTRA_DATA == 'true' && -n $dev_data ]];then
-                                       hdd_temp_data=$( get_hdd_temp_data "$dev_data" )
-                                       # error handling is done in get data function
-                                       if [[ -n $hdd_temp_data ]];then
-                                               hdd_temp_data=" ${C1}temp$SEP3${C2} ${hdd_temp_data}C"
-                                       else
-                                               hdd_temp_data=''
-                                       fi
-                               fi
-                               if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                       hdd_serial=$( get_hdd_serial_number "${a_hdd_working[0]}" )
-                                       if [[ -z $hdd_serial ]];then
-                                               hdd_serial='N/A'
-                                       fi
-                                       hdd_serial=" ${C1}serial$SEP3${C2} $hdd_serial"
-                                       divisor=1 # print every line
-                               else
-                                       divisor=2 # for modulus line print out, either 2 items for full, or default for short
-                               fi
-                               dev_data="${C1}id$SEP3${C2} /dev/${a_hdd_working[0]} "
-                       fi
-                       if [[ -n ${a_hdd_working[2]} ]];then
-                               hdd_name_temp=${a_hdd_working[2]}
-                       else
-                               hdd_name_temp='N/A'
-                       fi
-                       hdd_name="${C1}model$SEP3${C2} $hdd_name_temp"
-                       hdd_string="$usb_data$dev_data$hdd_name$size_data$hdd_serial$hdd_temp_data"
-                       hdd_model="${hdd_model}${C1}$(($i+1)):${C2} $hdd_string "
-                       # printing line one, then new lines according to $divisor setting, and after, if leftovers, print that line.
-                       case $i in 
-                               0)
-                                       if [[ $divisor -eq 1 ]];then
-                                               hdd_data=$( create_print_line "$Line_Starter" "${C1}HDD Total Size:${C2} ${hdd_capacity} (${hdd_used})" )
-                                               print_screen_output "$hdd_data"
-                                               Line_Starter=' '
-                                               hdd_data=$( create_print_line "$Line_Starter" "${hdd_model}" )
-                                               print_screen_output "$hdd_data"
-                                               hdd_model=''
-                                       else
-                                               hdd_data=$( create_print_line "$Line_Starter" "${C1}HDD Total Size:${C2} ${hdd_capacity} (${hdd_used}) ${hdd_model}" )
-                                               print_screen_output "$hdd_data"
-                                               hdd_model=''
-                                               Line_Starter=' '
-                                       fi
-                                       ;;
-                               *)
-                                       # using modulus here, if divisible by $divisor, print line, otherwise skip
-                                       if [[ $(( $i % $divisor )) -eq 0 ]];then
-                                               hdd_data=$( create_print_line "$Line_Starter" "${hdd_model}${CN}" )
-                                               print_screen_output "$hdd_data"
-                                               hdd_model=''
-                                               Line_Starter=' '
-                                       fi
-                                       ;;
-                       esac
-               done
-               # then print any leftover items
-               if [[ -n $hdd_model ]];then
-                       hdd_data=$( create_print_line "$Line_Starter" "${hdd_model}${CN}" )
-                       print_screen_output "$hdd_data"
-               fi
-               # temporary message to indicate not yet supported
-               if [[ $BSD_TYPE == 'bsd' ]];then
-                       hdd_data=$bsd_unsupported
-                       hdd_data=$( create_print_line "$Line_Starter" "$hdd_data${CN}" )
-                       print_screen_output "$hdd_data"
-                       Line_Starter=' '
-               fi
-       else
-               # temporary message to indicate not yet supported
-               hdd_data="${C1}HDD Total Size:${C2} ${hdd_capacity} (${hdd_used})"
-               if [[ $BSD_TYPE == 'bsd' ]];then
-                       hdd_data=$bsd_unsupported
-               fi
-               hdd_data=$( create_print_line "$Line_Starter" "$hdd_data${CN}" )
-               print_screen_output "$hdd_data"
-               Line_Starter=' '
-       fi
-       if [[ $B_SHOW_FULL_OPTICAL == 'true' || $B_SHOW_BASIC_OPTICAL == 'true' ]];then
-               print_optical_drive_data
-       fi
-
-       eval $LOGFE
-}
-
-print_info_data()
-{
-       eval $LOGFS
-
-       local info_data='' line_starter='Info:' runlvl_default='' runlvl='' runlvl_title='runlevel' 
-       local init_data='' init_type='' init_version='' rc_type='' rc_version=''
-       local client_data='' shell_data='' shell_parent='' tty_session=''
-       local memory="$( get_memory_data )"
-       local processes=$(( $( wc -l <<< "$Ps_aux_Data" ) - 1 ))
-       local up_time="$( get_uptime )"
-       local patch_version_number=$( get_patch_version_string )
-       local gcc_string='' gcc_installed='' gcc_others='' closing_data='' 
-       
-       if [[ -z $memory ]];then
-               memory='N/A'
-       fi
-       
-       if [[ $B_EXTRA_DATA == 'true' ]];then
-               get_gcc_system_version
-               if [[ ${#A_GCC_VERSIONS[@]} -gt 0 ]];then
-                       if [[ -n ${A_GCC_VERSIONS[0]} ]];then
-                               gcc_installed=${A_GCC_VERSIONS[0]}
-                       else
-                               gcc_installed='N/A'
-                       fi
-                       if [[ $B_EXTRA_EXTRA_DATA == 'true' && -n ${A_GCC_VERSIONS[1]} ]];then
-                               gcc_others=" ${C1}alt$SEP3${C2} $( tr ',' '/' <<< ${A_GCC_VERSIONS[1]} )"
-                       fi
-                       gcc_installed="${C1}Gcc sys$SEP3${C2} $gcc_installed$gcc_others "
-               fi
-       fi
-       if [[  $B_RUNNING_IN_SHELL == 'true' ]];then
-               shell_data=$( get_shell_data )
-               if [[ -n $shell_data ]];then
-                       # note, if you start this in tty, it will give 'login' as the parent, which we don't want.
-                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                               if [[ $B_RUNNING_IN_DISPLAY != 'true' ]];then
-                                       shell_parent=$( get_tty_number )
-                                       shell_parent="tty $shell_parent"
-                               else
-                                       shell_parent=$( get_shell_parent )
-                               fi
-                               if [[ $shell_parent == 'login' ]];then
-                                       shell_parent=''
-                               elif [[ -n $shell_parent ]];then
-                                       shell_parent=" running in $shell_parent"
-                               fi
-                       fi
-                       IRC_CLIENT="$IRC_CLIENT ($shell_data$shell_parent)"
-               fi
-       fi
-
-       # Some code could look superfluous but BitchX doesn't like lines not ending in a newline. F*&k that bitch!
-       # long_last=$( echo -ne "${C1}Processes$SEP3${C2} ${processes}${CN} | ${C1}Uptime$SEP3${C2} ${up_time}${CN} | ${C1}Memory$SEP3${C2} ${MEM}${CN}" )
-       info_data="${C1}Processes$SEP3${C2} ${processes} ${C1}Uptime$SEP3${C2} ${up_time} ${C1}Memory$SEP3${C2} ${memory}${CN} "
-
-       # this only triggers if no X data is present or if extra data switch is on
-       if [[ $B_SHOW_DISPLAY_DATA != 'true' || $B_EXTRA_DATA == 'true' ]];then
-               get_init_data
-               if [[ ${A_INIT_DATA[0]} == 'systemd' && -z $( grep -E '^[0-9]$' <<< ${A_INIT_DATA[4]} ) ]];then
-                       runlvl_title='target'
-               fi
-               init_type=${A_INIT_DATA[0]}
-               if [[ -z $init_type ]];then
-                       init_type='N/A'
-               fi
-               
-               if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                       init_version=${A_INIT_DATA[1]}
-                       if [[ -z $init_version ]];then
-                               init_version='N/A'
-                       fi
-                       init_version=" ${C1}v$SEP3${C2} $init_version"
-                       rc_version=${A_INIT_DATA[3]}
-                       if [[ -n $rc_version ]];then
-                               rc_version=" ${C1}v$SEP3${C2} $rc_version"
-                       fi
-                       
-                       runlvl_default=${A_INIT_DATA[5]}
-               fi
-               # currently only using openrc here, otherwise show nothing
-               rc_type=${A_INIT_DATA[2]}
-               if [[ -n $rc_type ]];then
-                       rc_type=" ${C1}rc$SEP3${C2} $rc_type$rc_version"
-               fi
-               init_type="${C1}Init$SEP3${C2} $init_type$init_version "
-               
-               runlvl=${A_INIT_DATA[4]}
-               if [[ -n $runlvl ]];then
-                       runlvl="${C1}$runlvl_title$SEP3${C2} $runlvl "
-               fi
-               if [[ -n $runlvl_default ]];then
-                       runlvl_default="${C1}default$SEP3${C2} $runlvl_default "
-               fi
-               init_data="$init_type$rc_type$runlvl$runlvl_default"
-       fi
-       if [[ $SHOW_IRC -gt 0 ]];then
-               client_data="${C1}Client$SEP3${C2} ${IRC_CLIENT}${IRC_CLIENT_VERSION} "
-       fi
-       info_data="${info_data}"
-       closing_data="$client_data${C1}$SCRIPT_NAME$SEP3${C2} $SCRIPT_VERSION_NUMBER$patch_version_number${CN}"
-       
-       # sometimes gcc is very long, and default runlevel can be long with systemd, so create a gcc-less line first
-       if [[ $( calculate_line_length "${info_data}${init_data}${gcc_installed}${closing_data}" ) -gt $COLS_INNER ]];then
-               info_data=${info_data}${init_data}
-               info_data=$( create_print_line "$line_starter" "$info_data" )
-               print_screen_output "$info_data"
-               init_data=''
-               info_data=''
-               # closing_data=''
-               line_starter=' '
-               #echo 1
-       fi
-       if [[ $( calculate_line_length "${info_data}${init_data}${gcc_installed}${closing_data}" ) -gt $COLS_INNER ]];then
-               info_data=${info_data}${init_data}${gcc_installed}
-               info_data=$( create_print_line "$line_starter" "$info_data" )
-               print_screen_output "$info_data"
-               info_data=''
-               gcc_installed=''
-               init_data=''
-               line_starter=' '
-               #echo 2
-       fi
-       info_data="${info_data}${init_data}${gcc_installed}${closing_data}"
-       
-       info_data=$( create_print_line "$line_starter" "$info_data" )
-       if [[ $SCHEME -gt 0 ]];then
-               info_data="${info_data} ${NORMAL}"
-       fi
-       print_screen_output "$info_data"
-       
-       eval $LOGFE
+# args: 1 - the file to create if not exists
+sub toucher {
+       my $file = shift;
+       if ( ! -e $file ){
+               open( my $fh, '>', $file ) or error_handler('create', $file, $!);
+       }
 }
 
-print_machine_data()
-{
-       eval $LOGFS
-       
-       local system_line='' mobo_line='' bios_line='' chassis_line=''
-       local mobo_vendor='' mobo_model='' mobo_version='' mobo_serial=''
-       local bios_vendor='' bios_version='' bios_date='' bios_rom=''
-       local system_vendor='' product_name='' product_version='' product_serial='' product_uuid=''
-       local chassis_vendor='' chassis_type='' chassis_version='' chassis_serial='' 
-       local b_skip_system='false' b_skip_chassis='false'
-       local sysDmiError='No /sys/class/dmi, using '
-       local sysDmiNull='No /sys/class/dmi machine data: try newer kernel, or install dmidecode'
-       # set A_MACHINE_DATA
-       get_machine_data
-       
-       if [[ -n $BSD_TYPE ]];then
-               sysDmiError=''
-               sysDmiNull='No machine data available. Is dmidecode installed?'
-       fi
-
-       IFS=','
-       ## keys for machine data are:
-       # 0-sys_vendor 1-product_name 2-product_version 3-product_serial 4-product_uuid 
-       # 5-board_vendor 6-board_name 7-board_version 8-board_serial 
-       # 9-bios_vendor 10-bios_version 11-bios_date 
-       ## with extra data: 
-       # 12-chassis_vendor 13-chassis_type 14-chassis_version 15-chassis_serial
-       
-       if [[ ${#A_MACHINE_DATA[@]} -gt 0 ]];then
-               # note: in some case a mobo/version will match a product name/version, do not print those
-               # but for laptops, or even falsely id'ed desktops with batteries, let's print it all if it matches
-               # there can be false id laptops if battery appears so need to make sure system is filled
-               if [[ -z ${A_MACHINE_DATA[0]} ]];then
-                       b_skip_system='true'
-               else
-                       if [[ $B_PORTABLE != 'true'  ]];then
-                               # ibm / ibm can be true; dell / quantum is false, so in other words, only do this
-                               # in case where the vendor is the same and the version is the same and not null, 
-                               # otherwise the version information is going to be different in all cases I think
-                               if [[ -n ${A_MACHINE_DATA[0]} && ${A_MACHINE_DATA[0]} == ${A_MACHINE_DATA[5]} ]];then
-                                       if [[ -n ${A_MACHINE_DATA[2]} && ${A_MACHINE_DATA[2]} == ${A_MACHINE_DATA[7]} ]] || \
-                                       [[ -z ${A_MACHINE_DATA[2]} && ${A_MACHINE_DATA[1]} == ${A_MACHINE_DATA[6]} ]];then
-                                               b_skip_system='true'
-                                       fi
-                               fi
-                       fi
-               fi
-               # no point in showing chassis if system isn't there, it's very unlikely that would be correct
-               if [[ $B_EXTRA_EXTRA_DATA == 'true' && $b_skip_system != 'true' ]];then
-                       if [[ -n ${A_MACHINE_DATA[7]} && ${A_MACHINE_DATA[14]} == ${A_MACHINE_DATA[7]} ]];then
-                               b_skip_chassis='true'
-                       fi
-                       if [[ -n ${A_MACHINE_DATA[12]} && $b_skip_chassis != 'true' ]];then
-                               # no need to print the vendor string again if it's the same
-                               if [[ ${A_MACHINE_DATA[12]} != ${A_MACHINE_DATA[0]} ]];then
-                                       chassis_vendor=" ${A_MACHINE_DATA[12]}"
-                               fi
-                               if [[ -n ${A_MACHINE_DATA[13]} ]];then
-                                       chassis_type=" ${C1}type$SEP3${C2} ${A_MACHINE_DATA[13]}"
-                               fi
-                               if [[ -n ${A_MACHINE_DATA[14]} ]];then
-                                       chassis_version=" ${C1}version$SEP3${C2} ${A_MACHINE_DATA[14]}"
-                               fi
-                               if [[ -n ${A_MACHINE_DATA[15]} && $B_OUTPUT_FILTER != 'true' ]];then
-                                       chassis_serial=" ${C1}serial$SEP3${C2} ${A_MACHINE_DATA[15]}"
-                               fi
-                               if [[ -n "$chassis_vendor$chassis_type$chassis_version$chassis_serial" ]];then
-                                       chassis_line="${C1}Chassis$SEP3${C2}$chassis_vendor$chassis_type$chassis_version$chassis_serial"
-                               fi
-                       fi
-               fi
-               if [[ -n ${A_MACHINE_DATA[5]} ]];then
-                       mobo_vendor=${A_MACHINE_DATA[5]}
-               else
-                       mobo_vendor='N/A'
-               fi
-               if [[ -n ${A_MACHINE_DATA[6]} ]];then
-                       mobo_model=${A_MACHINE_DATA[6]}
-               else
-                       mobo_model='N/A'
-               fi
-               if [[ -n ${A_MACHINE_DATA[7]} ]];then
-                       mobo_version=" ${C1}version$SEP3${C2} ${A_MACHINE_DATA[7]}"
-               fi
-               if [[ -n ${A_MACHINE_DATA[8]} && $B_OUTPUT_FILTER != 'true' ]];then
-                       mobo_serial=" ${C1}serial$SEP3${C2} ${A_MACHINE_DATA[8]}"
-               fi
-               if [[ -n ${A_MACHINE_DATA[9]} ]];then
-                       bios_vendor=${A_MACHINE_DATA[9]}
-               else
-                       bios_vendor='N/A'
-               fi
-               if [[ -n ${A_MACHINE_DATA[10]} ]];then
-                       bios_version=${A_MACHINE_DATA[10]}
-                       if [[ -n ${A_MACHINE_DATA[16]} ]];then
-                               bios_version="$bios_version rv ${A_MACHINE_DATA[16]}"
-                       fi
-               else
-                       bios_version='N/A'
-               fi
-               if [[ -n ${A_MACHINE_DATA[11]} ]];then
-                       bios_date=${A_MACHINE_DATA[11]}
-               else
-                       bios_date='N/A'
-               fi
-               if [[ $B_EXTRA_EXTRA_DATA == 'true' && -n ${A_MACHINE_DATA[17]} ]];then
-                       bios_rom=" ${C1}rom size$SEP3${C2} ${A_MACHINE_DATA[17]}"
-               fi
-               mobo_line="${C1}Mobo$SEP3${C2} $mobo_vendor ${C1}model$SEP3${C2} $mobo_model$mobo_version$mobo_serial"
-               bios_line="${C1}Bios$SEP3${C2} $bios_vendor ${C1}version$SEP3${C2} $bios_version ${C1}date$SEP3${C2} $bios_date$bios_rom"
-               if [[ $( calculate_line_length "$mobo_line$bios_line" ) -lt $COLS_INNER ]];then
-                       mobo_line="$mobo_line $bios_line"
-                       bios_line=''
-               fi
-               if [[ $b_skip_system == 'true' ]];then
-                       system_line=$mobo_line
-                       mobo_line=''
-               else
-                       # this has already been tested for above so we know it's not null
-                       system_vendor=${A_MACHINE_DATA[0]}
-                       if [[ $B_PORTABLE == 'true' ]];then
-                               system_vendor="$system_vendor (portable)"
-                       fi
-                       if [[ -n ${A_MACHINE_DATA[1]} ]];then
-                               product_name=${A_MACHINE_DATA[1]}
-                       else
-                               product_name='N/A'
-                       fi
-                       if [[ -n ${A_MACHINE_DATA[2]} ]];then
-                               product_version=" ${C1}version$SEP3${C2} ${A_MACHINE_DATA[2]}"
-                       fi
-                       if [[ -n ${A_MACHINE_DATA[3]} && $B_OUTPUT_FILTER != 'true' ]];then
-                               product_serial=" ${C1}serial$SEP3${C2} ${A_MACHINE_DATA[3]} "
-                       fi
-                       system_line="${C1}System$SEP3${C2} $system_vendor ${C1}product$SEP3${C2} $product_name$product_version$product_serial"
-                       if [[ -n $chassis_line && $( calculate_line_length "$system_line$chassis_line" ) -lt $COLS_INNER ]];then
-                               system_line="$system_line $chassis_line"
-                               chassis_line=''
-                       fi
-               fi
-               IFS="$ORIGINAL_IFS"
-       else
-               system_line="${C2}$sysDmiNull${CN}"
-       fi
-       # patch to dump all of above if dmidecode was data source and non root user
-       if [[ ${A_MACHINE_DATA[0]} == 'dmidecode-non-root-user' || ${A_MACHINE_DATA[0]} == 'dmidecode-no-smbios-dmi-data' ]];then
-               if [[ ${A_MACHINE_DATA[0]} == 'dmidecode-non-root-user' ]];then
-                       system_line="${C2}${sysDmiError}dmidecode: you must be root to run dmidecode${CN}"
-               elif [[ ${A_MACHINE_DATA[0]} == 'dmidecode-no-smbios-dmi-data' ]];then
-                       system_line="${C2}${sysDmiError}dmidecode: no machine data available${CN}"
-               fi
-               mobo_line=''
-               bios_line=''
-               chassis_line=''
-       fi
-       system_line=$( create_print_line "Machine:" "$system_line" )
-       print_screen_output "$system_line"
-       if [[ -n $mobo_line ]];then
-               mobo_line=$( create_print_line " " "$mobo_line" )
-               print_screen_output "$mobo_line"
-       fi
-       if [[ -n $bios_line ]];then
-               bios_line=$( create_print_line " " "$bios_line" )
-               print_screen_output "$bios_line"
-       fi
-       if [[ -n $chassis_line ]];then
-               chassis_line=$( create_print_line " " "$chassis_line" )
-               print_screen_output "$chassis_line"
-       fi
-       
-       eval $LOGFE
+# calling it trimmer to avoid conflicts with existing trim stuff
+# arg: 1 - string to be right left trimmed. Also slices off \n so no chomp needed
+# this thing is super fast, no need to log its times etc, 0.0001 seconds or less
+sub trimmer {
+       #eval $start if $b_log;
+       my ($str) = @_;
+       $str =~ s/^\s+|\s+$|\n$//g; 
+       #eval $end if $b_log;
+       return $str;
 }
 
-# args: $1 - module name (could be > 1, so loop it ); $2 - audio (optional)
-print_module_version()
-{
-       eval $LOGFS
-       local module_versions='' module='' version='' prefix='' modules=$1
-       
-       # note that sound driver data tends to have upper case, but modules are lower
-       if [[ $2 == 'audio' ]];then
-               if [[ -z $( grep -E '^snd' <<< $modules ) ]];then
-                       prefix='snd_' # sound modules start with snd_
-               fi
-               modules=$( tr '[A-Z]' '[a-z]' <<< $modules )
-               modules=$( tr '-' '_' <<< $modules )
-               # special intel processing, generally no version info though
-               if [[ $modules == 'hda intel' ]];then
-                       modules='hda_intel'
-               elif [[ $modules == 'intel ich' ]];then
-                       modules='intel8x0'
-               fi
-       fi
-
-       for module in $modules
-       do
-               version=$( get_module_version_number "$prefix$module" )
-               if [[ -n $version ]];then
-                       module_versions="$module_versions $version"
-               fi
-       done
-
-       if [[ -n $module_versions ]];then
-               echo " ${C1}ver$SEP3${C2}$module_versions"
-       fi
-       eval $LOGFE
-}
-
-print_networking_data()
-{
-       eval $LOGFS
-       local i='' card_id='' network_data='' a_network_working='' port_data='' driver_data=''
-       local card_string='' port_plural='' module_version='' pci_bus_id='' bus_usb_text=''
-       local bus_usb_id='' line_starter='Network:' card_string='' card_data='' chip_id=''
-       local driver=''
-       # set A_NETWORK_DATA
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               if [[ $B_PCICONF_SET == 'false' ]];then
-                       get_pciconf_data
-               fi
-               get_pciconf_card_data 'network'
-       else
-               get_networking_data
-       fi
-
-       # will never be null because null is handled in get_network_data, but in case we change
-       # that leaving this test in place.
-       if [[ -n ${A_NETWORK_DATA[@]} ]];then
-               for (( i=0; i < ${#A_NETWORK_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_network_working=( ${A_NETWORK_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       bus_usb_id=''
-                       bus_usb_text=''
-                       card_data=''
-                       card_string=''
-                       driver_data=''
-                       module_version=''
-                       network_data=''
-                       pci_bus_id=''
-                       port_data=''
-                       port_plural=''
-                       card_id=''
-                       chip_id=''
-
-                       if [[ ${#A_NETWORK_DATA[@]} -gt 1 ]];then
-                               chip_id="-$(( $i + 1 ))"
-                       fi
-                       if [[ -n ${a_network_working[1]} && $B_EXTRA_DATA == 'true' && $BSD_TYPE != 'bsd' ]];then
-                               module_version=$( print_module_version "${a_network_working[1]}" )
-                       fi
-                       if [[ -n ${a_network_working[1]} ]];then
-                               # note: linux drivers can have numbers, like tg3
-                               if [[ $BSD_TYPE == 'bsd' ]];then
-                                       driver=$( sed 's/[0-9]*$//' <<< ${a_network_working[1]} )
-                               else
-                                       driver=${a_network_working[1]}
-                               fi
-                               driver_data="${C1}driver$SEP3${C2} ${driver}$module_version "
-                       fi
-                       if [[ -n ${a_network_working[2]} && $B_EXTRA_DATA == 'true' ]];then
-                               if [[ $( wc -w <<< ${a_network_working[2]} ) -gt 1 ]];then
-                                       port_plural='s'
-                               fi
-                               port_data="${C1}port$port_plural$SEP3${C2} ${a_network_working[2]} "
-                       fi
-                       if [[ -n ${a_network_working[4]} && $B_EXTRA_DATA == 'true' ]];then
-                               if [[ -z $( grep '^usb-' <<< ${a_network_working[4]} ) ]];then
-                                       bus_usb_text='bus-ID'
-                                       bus_usb_id=${a_network_working[4]}
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               if [[ $BSD_TYPE != 'bsd' ]];then
-                                                       chip_id=$( get_lspci_chip_id "${a_network_working[4]}" )
-                                               else
-                                                       chip_id=${a_network_working[10]}
-                                               fi
-                                       fi
-                               else
-                                       bus_usb_text='usb-ID'
-                                       bus_usb_id=$( cut -d '-' -f '2-4' <<< ${a_network_working[4]} )
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               chip_id=${a_network_working[10]}
-                                       fi
-                               fi
-                               pci_bus_id="${C1}$bus_usb_text$SEP3${C2} $bus_usb_id"
-                               if [[ -n $chip_id ]];then
-                                       chip_id=" ${C1}chip-ID$SEP3${C2} $chip_id"
-                               fi
-                       fi
-                       card_string="${C1}Card$card_id:${C2} ${a_network_working[0]} "
-                       card_data="$driver_data$port_data$pci_bus_id$chip_id"
-                       if [[ $( calculate_line_length "$card_string$card_data" ) -gt $COLS_INNER ]];then
-                               network_data=$( create_print_line "$line_starter" "$card_string" )
-                               line_starter=' '
-                               card_string=''
-                               print_screen_output "$network_data"
-                       fi
-                       network_data=$( create_print_line "$line_starter" "$card_string$card_data" )
-                       line_starter=' '
-                       print_screen_output "$network_data"
-                       if [[ $B_SHOW_ADVANCED_NETWORK == 'true' ]];then
-                               print_network_advanced_data
-                       fi
-               done
-       else
-               network_data="${C1}Card:${C2} Failed to Detect Network Card! "
-               network_data=$( create_print_line "$line_starter" "$network_data" )
-               print_screen_output "$network_data"
-       fi
-       if [[ $B_SHOW_IP == 'true' ]];then
-               print_networking_ip_data
-       fi
-       eval $LOGFE
-}
-
-print_network_advanced_data()
-{
-       eval $LOGFS
-       local network_data='' if_id='N/A' duplex='N/A' mac_id='N/A' speed='N/A' oper_state='N/A'
-       local b_is_wifi='false' speed_string='' duplex_string=''
-       
-               # first check if it's a known wifi id'ed card, if so, no print of duplex/speed
-       if [[ -n $( grep -Esi '(wireless|wifi|wi-fi|wlan|802\.11|centrino)' <<< ${a_network_working[0]} ) ]];then
-               b_is_wifi='true'
-       fi
-       if [[ -n ${a_network_working[5]} ]];then
-               if_id=${a_network_working[5]}
-       fi
-       if [[ -n ${a_network_working[6]} ]];then
-               oper_state=${a_network_working[6]}
-       fi
-       # no print out for wifi since it doesn't have duplex/speed data availabe
-       # note that some cards show 'unknown' for state, so only testing explicitly
-       # for 'down' string in that to skip showing speed/duplex
-       if [[ $b_is_wifi != 'true' && $oper_state != 'down' ]];then
-               if [[ -n ${a_network_working[7]} ]];then
-                       # make sure the value is strictly numeric before appending Mbps
-                       if [[ -n $( grep -E '^[0-9\.,]+$' <<< "${a_network_working[7]}" ) ]];then
-                               speed="${a_network_working[7]} Mbps"
-                       else
-                               speed=${a_network_working[7]}
-                       fi
-               fi
-               speed_string="${C1}speed$SEP3${C2} $speed "
-               if [[ -n ${a_network_working[8]} ]];then
-                       duplex=${a_network_working[8]}
-               fi
-               duplex_string="${C1}duplex$SEP3${C2} $duplex "
-       fi
-       if [[ -n ${a_network_working[9]} ]];then
-               if [[ $B_OUTPUT_FILTER == 'true' ]];then
-                       mac_id=$FILTER_STRING
-               else
-                       mac_id=${a_network_working[9]}
-               fi
-       fi
-       network_data="${C1}IF:${C2} $if_id ${C1}state$SEP3${C2} $oper_state $speed_string$duplex_string${C1}mac$SEP3${C2} $mac_id"
-       network_data=$( create_print_line " " "$network_data" )
-       print_screen_output "$network_data"
-       
-       eval $LOGFE
+# args: 1 - hash
+# send array, assign to hash, return array, uniq values only.
+sub uniq {
+       my %seen;
+       grep !$seen{$_}++, @_;
 }
 
-print_networking_ip_data()
-{
-       eval $LOGFS
-       local ip=$( get_networking_wan_ip_data )
-       local wan_ip_data='' a_interfaces_working='' interfaces='' i=''
-       local if_id='' if_ip='' if_ipv6='' if_ipv6_string='' full_string='' if_string=''
-       local if_id_string='' if_ip_string=''
-       local line_max=$(( $COLS_INNER - 40 ))
-
-       # set A_INTERFACES_DATA
-       get_networking_local_ip_data
-       # first print output for wan ip line. Null is handled in the get function
-       if [[ -z $ip ]];then
-               ip='N/A'
-       else
-               if [[ $B_OUTPUT_FILTER == 'true' ]];then
-                       ip=$FILTER_STRING
-               fi
-       fi
-       wan_ip_data="${C1}WAN IP:${C2} $ip "
-       # then create the list of local interface/ip
-       i=0 ## loop starts with 1 by auto-increment so it only shows cards > 1
-       while [[ -n ${A_INTERFACES_DATA[i]} ]]
-       do
-               IFS=","
-               a_interfaces_working=(${A_INTERFACES_DATA[i]})
-               IFS="$ORIGINAL_IFS"
-               if_id='N/A'
-               if_ip='N/A'
-               if_ipv6='N/A'
-               if_ipv6_string=''
-               if [[ -z $( grep '^Interface' <<< ${a_interfaces_working[0]} ) ]];then
-                       if [[ -n ${a_interfaces_working[1]} ]];then
-                               if [[ $B_OUTPUT_FILTER == 'true' ]];then
-                                       if_ip=$FILTER_STRING
-                               else
-                                       if_ip=${a_interfaces_working[1]}
-                               fi
-                       fi
-                       if_ip_string=" ${C1}ip$SEP3${C2} $if_ip"
-                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                               if [[ -n ${a_interfaces_working[3]} ]];then
-                                       if [[ $B_OUTPUT_FILTER == 'true' ]];then
-                                               if_ipv6=$FILTER_STRING
-                                       else
-                                               if_ipv6=${a_interfaces_working[3]}
-                                       fi
-                               fi
-                               if_ipv6_string=" ${C1}ip-v6$SEP3${C2} $if_ipv6"
-                       fi
-               fi
-               if [[ -n ${a_interfaces_working[0]} ]];then
-                       if_id=${a_interfaces_working[0]}
-               fi
-               if_string="$wan_ip_data$if_string${C1}IF:${C2} $if_id$if_ip_string$if_ipv6_string "
-               wan_ip_data=''
-               if [[ $( calculate_line_length "$if_string" ) -gt $line_max ]];then
-                       full_string=$( create_print_line " " "$if_string" )
-                       print_screen_output "$full_string"
-                       if_string=''
-               fi
-               ((i++))
-       done
-       
-       # then print out anything not printed already
-       if [[ -n $if_string ]];then
-               full_string=$( create_print_line " " "$if_string" )
-               print_screen_output "$full_string"
-       fi
-       eval $LOGFE
-}
 
-print_optical_drive_data()
-{
-       eval $LOGFS
-       local a_drives='' drive_data='' counter='' 
-       local drive_id='' drive_links='' vendor='' speed='' multisession='' mcn='' audio=''
-       local dvd='' state='' rw_support='' rev='' separator='' drive_string=''
-       get_optical_drive_data
-       # 0 - true dev path, ie, sr0, hdc
-       # 1 - dev links to true path
-       # 2 - device vendor - for hdx drives, vendor model are one string from proc
-       # 3 - device model
-       # 4 - device rev version
-       if [[ ${#A_OPTICAL_DRIVE_DATA[@]} -gt 0 ]];then
-               for (( i=0; i < ${#A_OPTICAL_DRIVE_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_drives=(${A_OPTICAL_DRIVE_DATA[i]})
-                       IFS="$ORIGINAL_IFS"
-                       audio=''
-                       drive_data=''
-                       drive_id=''
-                       drive_links=''
-                       dvd='' 
-                       mcn='' 
-                       multisession='' 
-                       rev='' 
-                       rw_support='' 
-                       separator=''
-                       speed='' 
-                       state='' 
-                       vendor=''
-                       if [[ ${#A_OPTICAL_DRIVE_DATA[@]} -eq 1 && -z ${a_drives[0]} && -z ${a_drives[1]} ]];then
-                               drive_string="No optical drives detected."
-                               B_SHOW_FULL_OPTICAL='false'
-                       else
-                               if [[ ${#A_OPTICAL_DRIVE_DATA[@]} -gt 1 ]];then
-                                       counter="-$(( i + 1 ))"
-                               fi
-                               if [[ -z ${a_drives[0]} ]];then
-                                       drive_id='N/A'
-                               else
-                                       drive_id="/dev/${a_drives[0]}"
-                               fi
-                               drive_links=$( sed 's/~/,/g' <<< ${a_drives[1]} )
-                               if [[ -z $drive_links ]];then
-                                       drive_links='N/A'
-                               fi
-                               if [[ -n ${a_drives[2]} ]];then
-                                       vendor=${a_drives[2]}
-                                       if [[ -n ${a_drives[3]} ]];then
-                                               vendor="$vendor ${a_drives[3]}"
-                                       fi
-                               fi
-                               if [[ -z $vendor ]];then
-                                       if [[ -n ${a_drives[3]} ]];then
-                                               vendor=${a_drives[3]}
-                                       else
-                                               vendor='N/A'
-                                       fi
-                               fi
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       if [[ -n ${a_drives[4]} ]];then
-                                               rev=${a_drives[4]}
-                                       else
-                                               rev='N/A'
-                                       fi
-                                       rev=" ${C1}rev$SEP3${C2} $rev"
-                               fi
-                               drive_string="$drive_id ${C1}model$SEP3${C2} $vendor$rev ${C1}dev-links$SEP3${C2} $drive_links"
-                       fi
-                       drive_data="${C1}Optical${counter}:${C2} $drive_string"
-                       drive_data=$( create_print_line "$Line_Starter" "$drive_data" )
-                       print_screen_output "$drive_data"
-                       Line_Starter=' '
-                       # 5 - speed
-                       # 6 - multisession support
-                       # 7 - MCN support
-                       # 8 - audio read
-                       # 9 - cdr
-                       # 10 - cdrw
-                       # 11 - dvd read
-                       # 12 - dvdr
-                       # 13 - dvdram
-                       # 14 - state
-                       if [[ $B_SHOW_FULL_OPTICAL == 'true' ]];then
-                               if [[ -z ${a_drives[5]} ]];then
-                                       speed='N/A'
-                               else
-                                       speed="${a_drives[5]}x"
-                               fi
-                               if [[ -z ${a_drives[8]} ]];then
-                                       audio='N/A'
-                               elif [[ ${a_drives[8]} == 1 ]];then
-                                       audio='yes'
-                               else
-                                       audio='no'
-                               fi
-                               audio=" ${C1}audio$SEP3${C2} $audio"
-                               if [[ -z ${a_drives[6]} ]];then
-                                       multisession='N/A'
-                               elif [[ ${a_drives[6]} == 1 ]];then
-                                       multisession='yes'
-                               else
-                                       multisession='no'
-                               fi
-                               multisession=" ${C1}multisession$SEP3${C2} $multisession"
-                               if [[ -z ${a_drives[11]} ]];then
-                                       dvd='N/A'
-                               elif [[ ${a_drives[11]} == 1 ]];then
-                                       dvd='yes'
-                               else
-                                       dvd='no'
-                               fi
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       if [[ -z ${a_drives[14]} ]];then
-                                               state='N/A'
-                                       else
-                                               state="${a_drives[14]}"
-                                       fi
-                                       state=" ${C1}state$SEP3${C2} $state"
-                               fi
-                               if [[ -n ${a_drives[9]} && ${a_drives[9]} == 1 ]];then
-                                       rw_support='cd-r'
-                                       separator=','
-                               fi
-                               if [[ -n ${a_drives[10]} && ${a_drives[10]} == 1 ]];then
-                                       rw_support="${rw_support}${separator}cd-rw"
-                                       separator=','
-                               fi
-                               if [[ -n ${a_drives[12]} && ${a_drives[12]} == 1 ]];then
-                                       rw_support="${rw_support}${separator}dvd-r"
-                                       separator=','
-                               fi
-                               if [[ -n ${a_drives[13]} && ${a_drives[13]} == 1 ]];then
-                                       rw_support="${rw_support}${separator}dvd-ram"
-                                       separator=','
-                               fi
-                               if [[ -z $rw_support ]];then
-                                       rw_support='none'
-                               fi
-                               
-                               drive_data="${C1}Features: speed$SEP3${C2} $speed$multisession$audio ${C1}dvd$SEP3${C2} $dvd ${C1}rw$SEP3${C2} $rw_support$state"
-                               drive_data=$( create_print_line "$Line_Starter" "$drive_data" )
-                               print_screen_output "$drive_data"
-                       fi
-               done
-       else
-               :
-       fi
-       eval $LOGFE
-}
-
-print_partition_data()
-{
-       eval $LOGFS
-       local a_partition_working='' partition_used='' partition_data=''
-       local counter=0 i=0 a_partition_data='' line_starter='' line_max=$(( $COLS_INNER - 25 ))
-       local partitionIdClean='' part_dev='' full_dev='' part_label='' full_label=''
-       local part_uuid='' full_uuid='' dev_remote='' full_fs='' line_max_label_uuid=$COLS_INNER
-       local b_non_dev='false' holder=''
-
-       # set A_PARTITION_DATA
-       get_partition_data
-
-       for (( i=0; i < ${#A_PARTITION_DATA[@]}; i++ ))
-       do
-               IFS=","
-               a_partition_working=(${A_PARTITION_DATA[i]})
-               IFS="$ORIGINAL_IFS"
-               full_label=''
-               full_uuid=''
-
-               if [[ $B_SHOW_PARTITIONS_FULL == 'true' ]] || [[ ${a_partition_working[4]} == 'main' ]];then
-                       if [[ -n ${a_partition_working[2]} ]];then
-                               partition_used="${C1}used$SEP3${C2} ${a_partition_working[2]} (${a_partition_working[3]}) "
-                       else
-                               partition_used='' # reset partition used to null
-                       fi
-                       if [[ -n ${a_partition_working[5]} ]];then
-                               full_fs="${a_partition_working[5]}"
-                       else
-                               full_fs='N/A' # reset partition fs type
-                       fi
-                       full_fs="${C1}fs$SEP3${C2} $full_fs "
-
-                       if [[ $B_SHOW_LABELS == 'true' || $B_SHOW_UUIDS == 'true' ]];then
-                               if [[ -n ${a_partition_working[6]} ]];then
-                                       if [[ -z $( grep -E '(^//|:/|non-dev)' <<< ${a_partition_working[6]} ) ]];then
-                                               part_dev="/dev/${a_partition_working[6]}"
-                                               dev_remote='dev'
-                                       elif [[ -n $( grep '^non-dev' <<< ${a_partition_working[6]} ) ]];then
-                                               holder=$( sed 's/non-dev-//' <<< ${a_partition_working[6]} )
-                                               part_dev="$holder"
-                                               dev_remote='raid'
-                                       else
-                                               part_dev="${a_partition_working[6]}"
-                                               dev_remote='remote'
-                                       fi
-                               else
-                                       dev_remote='dev'
-                                       part_dev='N/A'
-                               fi
-                               full_dev="${C1}$dev_remote$SEP3${C2} $part_dev "
-                               if [[ $B_SHOW_LABELS == 'true' && $dev_remote != 'remote' ]];then
-                                       if [[ -n ${a_partition_working[7]} ]];then
-                                               part_label="${a_partition_working[7]}"
-                                       else
-                                               part_label='N/A'
-                                       fi
-                                       full_label="${C1}label$SEP3${C2} $part_label "
-                               fi
-                               if [[ $B_SHOW_UUIDS == 'true' && $dev_remote != 'remote' ]];then
-                                       if [[ -n ${a_partition_working[8]} ]];then
-                                               part_uuid="${a_partition_working[8]}"
-                                       else
-                                               part_uuid='N/A'
-                                       fi
-                                       full_uuid="${C1}uuid$SEP3${C2} $part_uuid"
-                               fi
-                       fi
-                       # don't show user names in output
-                       if [[ $B_OUTPUT_FILTER == 'true' ]];then
-                               partitionIdClean=$( sed $SED_RX "s|/home/([^/]+)/(.*)|/home/$FILTER_STRING/\2|" <<< ${a_partition_working[0]} )
-                       else
-                               partitionIdClean=${a_partition_working[0]}
-                       fi
-                       id_size_fs="${C1}ID:${C2} $partitionIdClean ${C1}size$SEP3${C2} ${a_partition_working[1]} $partition_used$full_fs$full_dev"
-                       label_uuid="$full_label$full_uuid"
-                       # label/uuid always print one per line, so only wrap if it's very long
-                       if [[ $B_SHOW_UUIDS == 'true' && $B_SHOW_LABELS == 'true' && $( calculate_line_length "$id_size_fs$label_uuid" ) -gt $line_max_label_uuid ]];then
-                               a_partition_data[$counter]="$id_size_fs"
-                               ((counter++))
-                               a_partition_data[$counter]="$label_uuid"
-                       else
-                               a_partition_data[$counter]="${a_partition_data[$counter]}$id_size_fs$label_uuid"
-                       fi
-                       # because these lines can vary widely, using dynamic length handling here
-                       if [[ $B_SHOW_LABELS == 'true' || $B_SHOW_UUIDS == 'true' ]] || [[ $( calculate_line_length "${a_partition_data[$counter]}" ) -gt $line_max ]];then
-                               ((counter++))
-                       fi
-               fi
-       done
-       # print out all lines, line starter on first line
-       for (( i=0; i < ${#a_partition_data[@]};i++ ))
-       do
-               if [[ $i -eq 0 ]];then
-                       line_starter='Partition:'
-               else
-                       line_starter=' '
-               fi
-               partition_data=$( create_print_line "$line_starter" "${a_partition_data[$i]}" )
-               print_screen_output "$partition_data"
-       done
-       
-       eval $LOGFE
+# arg: 1 file full  path to write to; 2 - arrayof data to write. 
+# note: turning off strict refs so we can pass it a scalar or an array reference.
+sub writer {
+       my ($path, $ref_content) = @_;
+       my ($content);
+       no strict 'refs';
+       # print Dumper $ref_content, "\n";
+       if (ref $ref_content eq 'ARRAY'){
+               $content = join "\n", @$ref_content or die "failed with error $!";
+       }
+       else {
+               $content = scalar $ref_content;
+       }
+       open(my $fh, ">", $path) or error_handler('open',"$path", "$!");
+       print $fh $content;
+       close $fh;
 }
 
-print_program_version()
-{
-       local patch_version_number=$( get_patch_version_string )
-       local program_version="${C1}$SCRIPT_NAME$SEP3${C2} $SCRIPT_VERSION_NUMBER$patch_version_number${CN}"
-       # great trick from: http://ideatrash.net/2011/01/bash-string-padding-with-sed.html
-       # left pad: sed -e :a -e 's/^.\{1,80\}$/& /;ta'
-       # right pad: sed -e :a -e 's/^.\{1,80\}$/ &/;ta'
-       # center pad: sed -e :a -e 's/^.\{1,80\}$/ & /;ta'
-       #local line_max=$COLS_INNER
-       #program_version="$( sed -e :a -e "s/^.\{1,$line_max\}$/ &/;ta" <<< $program_version )" # use to create padding if needed
-       # program_version=$( create_print_line "Version:" "$program_version" )
-       print_screen_output "$program_version"
-}
-
-print_ps_data()
-{
-       eval $LOGFS
-       
-       local b_print_first='true' 
-
-       if [[ $B_SHOW_PS_CPU_DATA == 'true' ]];then
-               get_ps_tcm_data 'cpu'
-               print_ps_item 'cpu' "$b_print_first"
-               b_print_first='false' 
-       fi
-       if [[ $B_SHOW_PS_MEM_DATA == 'true' ]];then
-               get_ps_tcm_data 'mem'
-               print_ps_item 'mem' "$b_print_first"
-       fi
-       
-       eval $LOGFE
+#### -------------------------------------------------------------------
+#### UPDATER
+##### -------------------------------------------------------------------
+
+# arg 1: type to return
+sub get_defaults {
+       my ($type) = @_;
+       my %defaults = (
+       'ftp-upload' => 'ftp.techpatterns.com/incoming',
+       'inxi-branch-1' => 'https://github.com/smxi/inxi/raw/one/',
+       'inxi-branch-2' => 'https://github.com/smxi/inxi/raw/two/',
+       'inxi-dev' => 'https://smxi.org/in/',
+       'inxi-main' => 'https://github.com/smxi/inxi/raw/master/',
+       'inxi-pinxi' => 'https://github.com/smxi/inxi/raw/inxi-perl/',
+       'inxi-man' => "https://smxi.org/in/$self_name.1.gz",
+       'inxi-man-gh' => "https://github.com/smxi/inxi/raw/master/$self_name.1",
+       'pinxi-man' => "https://smxi.org/in/$self_name.1.gz",
+       'pinxi-man-gh' => "https://github.com/smxi/inxi/raw/inxi-perl/$self_name.1",
+       );
+       if ( exists $defaults{$type}){
+               return $defaults{$type};
+       }
+       else {
+               error_handler('bad-arg-int', $type);
+       }
 }
 
-# args: $1 - cpu/mem; $2 true/false
-print_ps_item()
-{
-       eval $LOGFS
-       local a_ps_data='' ps_data='' line_starter='' line_start_data='' full_line=''
-       local app_name='' app_pid='' app_cpu='' app_mem='' throttled='' app_daemon=''
-       local b_print_first=$2 line_counter=0 i=0 count_nu='' extra_data=''
-       
-       if [[ -n $PS_THROTTLED ]];then
-               throttled=" ${C1} - throttled from${C2} $PS_THROTTLED"
-       fi
-       case $1 in
-               cpu)
-                       line_start_data="${C1}CPU - % used - top ${C2} $PS_COUNT ${C1}active$throttled "
-                       ;;
-               mem)
-                       line_start_data="${C1}Memory - MB / % used - top ${C2} $PS_COUNT ${C1}active$throttled"
-                       ;;
-       esac
+# args: 1 - download url, not including file name; 2 - string to print out
+# 3 - update type option
+# note that 1 must end in / to properly construct the url path
+sub update_me {
+       eval $start if $b_log;
+       my ( $self_download, $download_id ) = @_;
+       my $downloader_error=1;
+       my $file_contents='';
+       my $output = '';
+       $self_path =~ s/\/$//; # dirname sometimes ends with /, sometimes not
+       $self_download =~ s/\/$//; # dirname sometimes ends with /, sometimes not
+       my $full_self_path = "$self_path/$self_name";
        
-       if [[ $b_print_first == 'true' ]];then
-               line_starter='Processes:'
-       else
-               line_starter=' '
-       fi
-       
-       # appName, appPath, appStarterName, appStarterPath, cpu, mem, pid, vsz, user
-       ps_data=$( create_print_line "$line_starter" "$line_start_data" )
-       print_screen_output "$ps_data"
-
-       for (( i=0; i < ${#A_PS_DATA[@]}; i++ ))
-       do
-               IFS=","
-               a_ps_data=(${A_PS_DATA[i]})
-               IFS="$ORIGINAL_IFS"
-               
-               # handle the converted app names, with ~..~ means it didn't have a path
-               if [[ -n $( grep -E '^~.*~$' <<<  ${a_ps_data[0]} ) ]];then
-                       app_daemon='daemon'
-               else
-                       app_daemon='command'
-               fi
-
-               app_name=" ${C1}$app_daemon$SEP3${C2} ${a_ps_data[0]}"
-               if [[ ${a_ps_data[0]} != ${a_ps_data[2]} ]];then
-                       app_name="$app_name ${C1}(started by$SEP3${C2} ${a_ps_data[2]}${C1})${C2}"
-               fi
-               app_pid=" ${C1}pid$SEP3${C2} ${a_ps_data[6]}"
-               #  ${C1}user:${C2} ${a_ps_data[8]}
-               case $1 in
-                       cpu)
-                               app_cpu=" ${C1}cpu$SEP3${C2} ${a_ps_data[4]}%"
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       extra_data=" ${C1}mem$SEP3${C2} ${a_ps_data[7]}MB (${a_ps_data[5]}%)${C2}"
-                               fi
-                               ;;
-                       mem)
-                               app_mem=" ${C1}mem$SEP3${C2} ${a_ps_data[7]}MB (${a_ps_data[5]}%)${C2}"
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       extra_data=" ${C1}cpu$SEP3${C2} ${a_ps_data[4]}%"
-                               fi
-                               ;;
-               esac
-               (( line_counter++ ))
-               count_nu="${C1}$line_counter:${C2}"
-               full_line="$count_nu$app_cpu$app_mem$app_name$app_pid$extra_data"
-               ps_data=$( create_print_line " " "$full_line" )
-               print_screen_output "$ps_data"
-       done
+       if ( $b_irc ){
+               error_handler('not-in-irc', "-U/--update" )
+       }
+       if ( ! -w $full_self_path ){
+               error_handler('not-writable', "$self_name", '');
+       }
+       $output .= "Starting $self_name self updater.\n";
+       $output .= "Using $dl{'dl'} as downloader.\n";
+       $output .= "Currently running $self_name version number: $self_version\n";
+       $output .= "Current version patch number: $self_patch\n";
+       $output .= "Current version release date: $self_date\n";
+       $output .= "Updating $self_name in $self_path using $download_id as download source...\n";
+       print $output;
+       $output = '';
+       $self_download = "$self_download/$self_name";
+       $file_contents = download_file('stdout', $self_download);
        
-       eval $LOGFE
+       # then do the actual download
+       if (  $file_contents ){
+               # make sure the whole file got downloaded and is in the variable
+               if ( $file_contents =~ /###\*\*EOF\*\*###/ ){
+                       open(my $fh, '>', $full_self_path);
+                       print $fh $file_contents or error_handler('write', "$full_self_path", "$!" );
+                       close $fh;
+                       qx( chmod +x '$self_path/$self_name' );
+                       set_version_data();
+                       $output .= "Successfully updated to $download_id version: $self_version\n";
+                       $output .= "New $download_id version patch number: $self_patch\n";
+                       $output .= "New $download_id version release date: $self_date\n";
+                       $output .= "To run the new version, just start $self_name again.\n";
+                       $output .= "$line3\n";
+                       $output .= "Starting download of man page file now.\n";
+                       print $output;
+                       $output = '';
+                       if ($b_man){
+                               update_man($download_id);
+                       }
+                       else {
+                               print "Skipping man download because branch version is being used.\n";
+                       }
+                       exit 1;
+               }
+               else {
+                       error_handler('file-corrupt', "$self_name");
+               }
+       }
+       # now run the error handlers on any downloader failure
+       else {
+               error_handler('download-error', $self_download, $download_id);
+       }
+       eval $end if $b_log;
 }
 
-print_raid_data()
-{
-       eval $LOGFS
-       local device='' device_string='' device_state='' raid_level='' device_components=''
-       local device_report='' u_data='' blocks='' super_blocks='' algorithm='' chunk_size=''
-       local bitmap_values='' recovery_progress_bar='' recovery_percent='' recovered_sectors=''
-       local finish_time='' recovery_speed='' raid_counter=0 device_counter=1 basic_counter=1
-       local a_raid_working='' raid_data='' kernel_support='' read_ahead='' unused_devices=''
-       local basic_raid='' basic_raid_separator='' basic_raid_plural='' inactive=''
-       local component_separator='' device_id='' print_string='' loop_limit=0 array_count_unused=''
-       local array_count='' raid_event='' b_print_lines='true'
-       local no_raid_detected='' dev_string='/dev/'
-       local empty_raid_data='' report_size='report' blocks_avail='blocks' chunk_raid_usage='chunk size'
-       
-       if [[ -n $BSD_TYPE ]];then
-               no_raid_detected='No zfs software RAID detected - other types not yet supported.'
-               empty_raid_data='No zfs RAID data available - other types not yet supported.'
-               report_size='full size'
-               blocks_avail='available size'
-               chunk_raid_usage='raid allocated'
-       else
-               no_raid_detected="No RAID data available - $FILE_MDSTAT is missing - is md_mod kernel module loaded?"
-               empty_raid_data="No RAID devices detected - $FILE_MDSTAT and md_mod kernel raid module present"
-       fi
-       
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               dev_string=''
-       fi
-       if [[ $B_RAID_SET != 'true' ]];then
-               get_raid_data
-       fi
-
-       for (( i=0; i < ${#A_RAID_DATA[@]}; i++ ))
-       do
-               IFS=","
-               a_raid_working=(${A_RAID_DATA[i]})
-               IFS="$ORIGINAL_IFS"
-               
-               # reset on each iteration
-               algorithm=''
-               bitmap_values=''
-               blocks=''
-               component_separator=''
-               device=''
-               device_components=''
-               device_id=''
-               device_report=''
-               device_state=''
-               failed=''
-               finish_time=''
-               inactive=''
-               raid_event=''
-               raid_level=''
-               recovery_percent=''
-               recovery_progress_bar=''
-               recovered_sectors=''
-               recovery_speed=''
-               spare=''
-               super_blocks=''
-               u_data=''
-               
-               if [[ -n $( grep '^md' <<< ${a_raid_working[0]} ) && -z $BSD_TYPE ]] || \
-               [[ -n $BSD_TYPE && ${a_raid_working[0]} != '' ]];then
-                       if [[ $B_SHOW_BASIC_RAID == 'true' ]];then
-                               if [[ $basic_raid != '' ]];then
-                                       basic_raid_plural='s'
-                               fi
-                               if [[ ${a_raid_working[1]} == 'inactive' ]];then
-                                       inactive=" - ${a_raid_working[1]}"
-                               fi
-                               basic_raid="$basic_raid$basic_raid_separator${C1}$basic_counter${SEP3}${C2} $dev_string${a_raid_working[0]}$inactive"
-                               basic_raid_separator=' '
-                               (( basic_counter++ ))
-                       else
-                               device_id="-$device_counter"
-                               device="$dev_string${a_raid_working[0]}"
-                               
-                               (( device_counter++ ))
-                               if [[ ${a_raid_working[1]} != '' ]];then
-                                       device_state=" - ${a_raid_working[1]}"
-                               fi
-                               
-                               if [[ ${a_raid_working[2]} == '' ]];then
-                                       raid_level='N/A'
-                               else
-                                       raid_level=${a_raid_working[2]}
-                               fi
-                               # there's one case: md0 : inactive  that has to be protected against
-                               if [[ ${a_raid_working[2]} == '' && ${a_raid_working[1]} == 'inactive' ]];then
-                                       raid_level=''
-                               else
-                                       raid_level=" ${C1}raid${SEP3}${C2} $raid_level"
-                               fi
-                               if [[ ${a_raid_working[4]} != '' ]];then
-                                       device_report="${a_raid_working[4]}"
-                               else
-                                       device_report="N/A"
-                               fi
-                               if [[ $B_EXTRA_DATA == 'true' ]];then
-                                       if [[ ${a_raid_working[6]} != '' ]];then
-                                               blocks=${a_raid_working[6]}
-                                       else
-                                               blocks='N/A'
-                                       fi
-                                       blocks=" ${C1}$blocks_avail${SEP3}${C2} $blocks"
-                                       
-                                       if [[ ${a_raid_working[9]} != '' ]];then
-                                               chunk_size=${a_raid_working[9]}
-                                       else
-                                               chunk_size='N/A'
-                                       fi
-                                       chunk_size=" ${C1}$chunk_raid_usage${SEP3}${C2} $chunk_size"
-                                       if [[ ${a_raid_working[10]} != '' ]];then
-                                               bitmap_value='true'
-                                               bitmap_value=" ${C1}bitmap${SEP3}${C2} $bitmap_value"
-                                       fi
-                               fi
-                               if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                       if [[ ${a_raid_working[5]} != '' ]];then
-                                               u_data=" ${a_raid_working[5]}"
-                                       fi
-                                       if [[ ${a_raid_working[7]} != '' ]];then
-                                               super_blocks=" ${C1}super blocks${SEP3}${C2} ${a_raid_working[7]}"
-                                       fi
-                                       if [[ ${a_raid_working[8]} != '' ]];then
-                                               algorithm=" ${C1}algorithm${SEP3}${C2} ${a_raid_working[8]}"
-                                       fi
-                               fi
-                               if [[ ${a_raid_working[3]} == '' ]];then
-                                       if [[ ${a_raid_working[1]} != 'inactive' ]];then
-                                               device_components='N/A'
-                                       fi
-                               else
-                                       for component in ${a_raid_working[3]}
-                                       do
-                                               if [[ $B_EXTRA_DATA != 'true' ]];then
-                                                       component=$( sed 's/\[[0-9]\+\]//' <<< $component )
-                                               fi
-                                               # NOTE: for bsd zfs, states are: ONLINE,DEGRADED,OFFLINE (at least)
-                                               if [[ -n $( grep -E '(F|DEGRADED)' <<< $component ) ]];then
-                                                       component=$( sed -e 's/(F)//' -e 's/F//' -e 's/DEGRADED//' <<<  $component )
-                                                       failed="$failed $component"
-                                                       component=''
-                                               elif [[ -n $( grep -E '(S|OFFLINE)' <<< $component ) ]];then
-                                                       component=$( sed -e 's/(S)//' -e 's/S//' -e 's/OFFLINE//' <<<  $component )
-                                                       spare="$spare $component"
-                                                       component=''
-                                               else
-                                                       device_components="$device_components$component_separator$component"
-                                                       component_separator=' '
-                                               fi
-                                       done
-
-                                       if [[ $failed != '' ]];then
-                                               failed=" ${C1}FAILED${SEP3}${C2}$failed${C2}"
-                                       fi
-                                       if [[ $spare != '' ]];then
-                                               spare=" ${C1}spare${SEP3}${C2}$spare${C2}"
-                                       fi
-                                       
-                                       if [[ -n $device_components || -n $spare || -n $failed ]];then
-                                               if [[ $B_EXTRA_DATA != 'true' && -z $BSD_TYPE ]];then
-                                                       if [[ $device_report != 'N/A' && -n $device_components ]];then
-                                                               device_components="$device_report - $device_components"
-                                                       fi
-                                               fi
-                                               if [[ $device_components == '' ]];then
-                                                       device_components='none'
-                                               fi
-                                               device_components="${C1}online${SEP3}${C2} $device_components"
-                                               device_components=" ${C1}components${SEP3}${C2} $device_components$failed$spare"
-                                       fi
-                               fi
-                               a_raid_data[$raid_counter]="${C1}Device$device_id${SEP3}${C2} $device$device_state$raid_level$device_components"
-                               
-                               if [[ $B_EXTRA_DATA == 'true' && ${a_raid_working[1]} != 'inactive' ]];then
-                                       a_raid_data[$raid_counter]="${C1}Device$device_id${SEP3}${C2} $device$device_state$device_components"
-                                       (( raid_counter++ ))
-                                       print_string="${C1}Info${SEP3}${C2}$raid_level ${C1}$report_size${SEP3}${C2} $device_report$u_data"
-                                       print_string="$print_string$blocks$chunk_size$bitmap_value$super_blocks$algorithm"
-                                       a_raid_data[$raid_counter]="$print_string"
-                               else
-                                       a_raid_data[$raid_counter]="${C1}Device$device_id${SEP3}${C2} $device$device_state$raid_level$device_components"
-                               fi
-                               (( raid_counter++ ))
-                               
-                               # now let's do the recover line if required
-                               if [[ ${a_raid_working[12]} != '' ]];then
-                                       recovery_percent=$( cut -d '~' -f 2 <<< ${a_raid_working[12]} )
-                                       if [[ ${a_raid_working[14]} != '' ]];then
-                                               finish_time=${a_raid_working[14]}
-                                       else
-                                               finish_time='N/A'
-                                       fi
-                                       finish_time=" ${C1}time remaining${SEP3}${C2} $finish_time"
-                                       if [[ $B_EXTRA_DATA == 'true' ]];then
-                                               if [[ ${a_raid_working[13]} != '' ]];then
-                                                       recovered_sectors=" ${C1}sectors${SEP3}${C2} ${a_raid_working[13]}"
-                                               fi
-                                       fi
-                                       if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                                               if [[ ${a_raid_working[11]} != '' ]];then
-                                                       recovery_progress_bar=" ${a_raid_working[11]}"
-                                               fi
-                                               if [[ ${a_raid_working[15]} != '' ]];then
-                                                       recovery_speed=" ${C1}speed${SEP3}${C2} ${a_raid_working[15]}"
-                                               fi
-                                       fi
-                                       
-                                       a_raid_data[$raid_counter]="${C1}Recovering${SEP3}${C2} $recovery_percent$recovery_progress_bar$recovered_sectors$finish_time$recovery_speed"
-                                       (( raid_counter++ ))
-                               fi
-                       fi
-               elif [[ ${a_raid_working[0]} == 'KernelRaidSupport' ]];then
-                       if [[ ${a_raid_working[1]} == '' ]];then
-                               kernel_support='N/A'
-                       else
-                               kernel_support=${a_raid_working[1]}
-                       fi
-                       kernel_support=" ${C1}supported${SEP3}${C2} $kernel_support"
-               elif [[ ${a_raid_working[0]} == 'ReadAhead' ]];then
-                       if [[ ${a_raid_working[1]} != '' ]];then
-                               read_ahead=${a_raid_working[1]}
-                               read_ahead=" ${C1}read ahead${SEP3}${C2} $read_ahead"
-                       fi
-               elif [[ ${a_raid_working[0]} == 'UnusedDevices' ]];then
-                       if [[ ${a_raid_working[1]} == '' ]];then
-                               unused_devices='N/A'
-                       else
-                               unused_devices=${a_raid_working[1]}
-                       fi
-                       unused_devices="${C1}Unused Devices${SEP3}${C2} $unused_devices"
-               elif [[ ${a_raid_working[0]} == 'raidEvent' ]];then
-                       if [[ ${a_raid_working[1]} != '' ]];then
-                               raid_event=${a_raid_working[1]}
-                               raid_event=" ${C1}Raid Event${SEP3}${C2} ${a_raid_working[1]}"
-                       fi
-               fi
-       done
+sub update_man {
+       my ($download_id) = @_;
+       my $man_file_location=set_man_location();
+       my $man_file_path="$man_file_location/$self_name.1" ;
+       my ($man_file_url,$output) = ('','');
        
-       if [[ $B_SHOW_BASIC_RAID == 'true' && $basic_raid != '' ]];then
-               a_raid_data[0]="${C1}Device$basic_raid_plural${SEP3}${C2} $basic_raid"
-       fi
-       # note bsd temp test hack to make it run
-       if [[ $B_MDSTAT_FILE != 'true' && -z $BSD_TYPE ]] || \
-       [[ -n $BSD_TYPE && $B_BSD_RAID == 'false' ]];then
-               if [[ $B_SHOW_RAID_R == 'true' ]];then
-                       a_raid_data[0]="$no_raid_detected"
-               else
-                       b_print_lines='false'
-               fi
-       else
-               if [[ ${a_raid_data[0]} == '' ]];then
-                       if [[ $B_SHOW_BASIC_RAID != 'true' ]];then
-                               a_raid_data[0]="$empty_raid_data"
-                       else
-                               b_print_lines='false'
-                       fi
-               fi
-               # now let's add on the system line and the unused device line. Only print on -xx
-               if [[ $kernel_support$read_ahead$raid_event != '' ]];then
-                       array_count=${#a_raid_data[@]}
-                       a_raid_data[array_count]="${C1}System${SEP3}${C2}$kernel_support$read_ahead$raid_event"
-                       loop_limit=1
-               fi
-               if [[ $unused_devices != '' ]];then
-                       array_count_unused=${#a_raid_data[@]}
-                       a_raid_data[array_count_unused]="$unused_devices"
-                       loop_limit=2
-               fi
-       fi
-
-       # we don't want to print anything if it's -b and no data is present, just a waste of a line
-       if [[ $b_print_lines == 'true' ]];then
-               # print out all lines, line starter on first line
-               for (( i=0; i < ${#a_raid_data[@]} - $loop_limit;i++ ))
-               do
-                       if [[ $i -eq 0 ]];then
-                               line_starter='RAID:'
-                       else
-                               line_starter=' '
-                       fi
-                       if [[ $B_EXTRA_EXTRA_DATA == 'true' && $array_count != '' ]];then
-                               if [[ $i == 0 ]];then
-                                       raid_data=$( create_print_line "$line_starter" "${a_raid_data[array_count]}" )
-                                       print_screen_output "$raid_data"
-                                       line_starter=' '
-                               fi
-                       fi
-                       raid_data=$( create_print_line "$line_starter" "${a_raid_data[i]}" )
-                       print_screen_output "$raid_data"
-                       if [[ $B_EXTRA_EXTRA_DATA == 'true' && $array_count_unused != '' ]];then
-                               if [[ $i == $(( array_count_unused - 2 )) ]];then
-                                       raid_data=$( create_print_line "$line_starter" "${a_raid_data[array_count_unused]}" )
-                                       print_screen_output "$raid_data"
-                               fi
-                       fi
-               done
-       fi
-       
-       eval $LOGFE
+       my $b_downloaded = 0;
+       if ( ! -d $man_file_location ){
+               print "The required man directory was not detected on your system.\n";
+               print "Unable to continue: $man_file_location\n";
+               return 0;
+       }
+       if ( ! -w $man_file_location ){
+               print "Cannot write to $man_file_location! Are you root?\n";
+               print "Unable to continue: $man_file_location\n";
+               return 0;
+       }
+       if ( -f "/usr/share/man/man8/inxi.8.gz" ){
+               print "Updating man page location to man1.\n";
+               rename "/usr/share/man/man8/inxi.8.gz", "$man_file_location/inxi.1.gz";
+               if ( check_program('mandb') ){
+                       system( 'mandb' );
+               }
+       }
+        # first choice is inxi.1/pinxi.1 from gh, second gz from smxi.org
+       if ( $download_id ne 'dev server' && (my $program = check_program('gzip'))){
+               $man_file_url=get_defaults($self_name . '-man-gh'); 
+               print "Downloading Man page file...\n";
+               $b_downloaded = download_file('file', $man_file_url, $man_file_path);
+               if ($b_downloaded){
+                       print "Download successful. Compressing file...\n";
+                       system("$program -9 -f $man_file_path > $man_file_path.gz");
+                       my $err = $?;
+                       if ($err > 0){
+                               print "Oh no! Something went wrong compressing the manfile:\n";
+                               print "Local path: $man_file_path Error: $err\n";
+                       }
+                       else {
+                               print "Download and install of man page successful.\nCheck to make sure it works: man $self_name\n";
+                       }
+               }
+       }
+       else {
+               $man_file_url = get_defaults($self_name . '-man'); 
+               # used to use spider tests, but only wget supports that, so no need
+               print "Downloading Man page file gz...\n";
+               $man_file_path .= '.gz';
+               # returns perl, 1 for true, 0 for false, even when using shell tool returns
+               $b_downloaded = download_file('file', $man_file_url,  $man_file_path );
+               if ($b_downloaded) {
+                       print "Download and install of man page successful.\nCheck to make sure it works: man $self_name\n";
+               }
+       }
+       if ( !$b_downloaded ){
+               print "Oh no! Something went wrong downloading the Man file at:\n$man_file_url\n";
+               print "Try -U with --dbg 1 for more information on the failure.\n";
+       }
 }
 
-# currently only apt using distros support this feature, but over time we can add others
-print_repo_data()
-{
-       eval $LOGFS
-       local repo_count=0 repo_line='' file_name='' file_content='' file_name_holder=''
-       local repo_full='' b_print_next_line='false' repo_type=''
-       
-       get_repo_data
-       
-       if [[ -n $REPO_DATA ]];then
-               # loop through the variable's lines one by one, update counter each iteration
-               while read repo_line
-               do
-                       (( repo_count++ ))
-                       repo_type=$( cut -d ':' -f 1 <<< $repo_line )
-                       file_name=$( cut -d ':' -f 2 <<< $repo_line )
-                       file_content=$( cut -d ':' -f 3-7 <<< $repo_line )
-                       # this will dump unwanted white space line starters. Some irc channels
-                       # use bots that show page title for urls, so need to break the url by adding 
-                       # a white space.
-                       if [[ $B_RUNNING_IN_SHELL != 'true' ]];then
-                               file_content=$( echo $file_content | sed 's|://|: //|' )
-                       else
-                               file_content=$( echo $file_content )
-                       fi
-                       # echo $file_name : $file_name_holder : $repo_type : $file_content
-                       # check file name, if different, update the holder for print out
-                       if [[ $file_name != $file_name_holder ]];then
-                               if [[ $repo_type == 'pisi repo' || $repo_type == 'urpmq repo' ]];then
-                                       repo_full="${C1}$repo_type:${C2} $file_name"
-                               else
-                                       repo_full="${C1}Active $repo_type in file:${C2} $file_name"
-                               fi
-                               file_name_holder=$file_name
-                               b_print_next_line='true'
-                       else
-                               repo_full=$file_content
-                       fi
-                       # first line print Repos: 
-                       if [[ $repo_count -eq 1 ]];then
-                               repo_full=$( create_print_line "Repos:" "$repo_full" )
-                       else
-                               repo_full=$( create_print_line " " "$repo_full" )
-                       fi
-                       print_screen_output "$repo_full"
-                       # this prints the content of the file as well as the file name
-                       if [[ $b_print_next_line == 'true' ]];then
-                               repo_full=$( create_print_line " " "$file_content" )
-                               print_screen_output "$repo_full"
-                               b_print_next_line='false'
-                       fi
-               done <<< "$REPO_DATA"
-       else
-               repo_full=$( create_print_line "Repos:" "${C1}Error:${C2} $SCRIPT_NAME does not support this feature for your distro yet." )
-               print_screen_output "$repo_full"
-       fi
-       eval $LOGFE
-}
-
-print_sensors_data()
-{
-       eval $LOGFS
-       local mobo_temp='' cpu_temp='' psu_temp='' cpu_fan='' mobo_fan='' ps_fan='' sys_fans='' sys_fans2='' 
-       local temp_data='' fan_data='' fan_data2='' b_is_error='false' fan_count=0 gpu_temp=''
-       local a_sensors_working=''
-       local no_sensors_message='None detected - is lm-sensors installed and configured?'
-       local Sensors_Data="$( get_sensors_output )"
-       get_sensors_data
-       
-       if [[ $BSD_TYPE == 'bsd' ]];then
-               no_sensors_message='This feature is not yet supported for BSD systems.'
-       fi
-       
-       IFS=","
-       a_sensors_working=( ${A_SENSORS_DATA[0]} )
-       IFS="$ORIGINAL_IFS"
-       # initial error cases, for missing app or unconfigured sensors. Note that array 0
-       # always has at least 3 items, cpu/mobo/psu temp in it. If the count is 0, then
-       # no sensors are installed/configured
-       if [[ ${#a_sensors_working[@]} -eq 0 ]];then
-               cpu_temp=$no_sensors_message
-               b_is_error='true'
-       else
-               for (( i=0; i < ${#A_SENSORS_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_sensors_working=( ${A_SENSORS_DATA[i]} )
-                       IFS="$ORIGINAL_IFS"
-                       case $i in
-                               # first the temp data
-                               0)
-                                       if [[ -n ${a_sensors_working[0]} ]];then
-                                               cpu_temp=${a_sensors_working[0]}
-                                       else
-                                               cpu_temp='N/A'
-                                       fi
-                                       cpu_temp="${C1}System Temperatures: cpu$SEP3${C2} $cpu_temp "
-
-                                       if [[ -n ${a_sensors_working[1]} ]];then
-                                               mobo_temp=${a_sensors_working[1]}
-                                       else
-                                               mobo_temp='N/A'
-                                       fi
-                                       mobo_temp="${C1}mobo$SEP3${C2} $mobo_temp "
-
-                                       if [[ -n ${a_sensors_working[2]} ]];then
-                                               psu_temp="${C1}psu$SEP3${C2} ${a_sensors_working[2]} "
-                                       fi
-                                       gpu_temp=$( get_gpu_temp_data )
-                                       # dump the unneeded screen data for single gpu systems 
-                                       if [[ $( wc -w <<< $gpu_temp ) -eq 1 && $B_EXTRA_DATA != 'true' ]];then
-                                               gpu_temp=$( cut -d ':' -f 2 <<< $gpu_temp )
-                                       fi
-                                       if [[ -n $gpu_temp ]];then
-                                               gpu_temp="${C1}gpu$SEP3${C2} ${gpu_temp} "
-                                       fi
-                                       ;;
-                               # then the fan data from main fan array
-                               1)
-                                       for (( j=0; j < ${#a_sensors_working[@]}; j++ ))
-                                       do
-                                               case $j in
-                                                       0)
-                                                               # we need to make sure it's either cpu fan OR cpu fan and sys fan 1
-                                                               if [[ -n ${a_sensors_working[0]} ]];then
-                                                                       cpu_fan="${a_sensors_working[0]}"
-                                                               elif [[ -z ${a_sensors_working[0]} && -n ${a_sensors_working[1]} ]];then
-                                                                       cpu_fan="${a_sensors_working[1]}"
-                                                               else
-                                                                       cpu_fan='N/A'
-                                                               fi
-                                                               cpu_fan="${C1}Fan Speeds (in rpm): cpu$SEP3${C2} $cpu_fan "
-                                                               (( fan_count++ ))
-                                                               ;;
-                                                       1)
-                                                               if [[ -n ${a_sensors_working[1]} ]];then
-                                                                       mobo_fan="${C1}mobo$SEP3${C2} ${a_sensors_working[1]} "
-                                                                       (( fan_count++ ))
-                                                               fi
-                                                               ;;
-                                                       2)
-                                                               if [[ -n ${a_sensors_working[2]} ]];then
-                                                                       ps_fan="${C1}psu$SEP3${C2} ${a_sensors_working[2]} "
-                                                                       (( fan_count++ ))
-                                                               fi
-                                                               ;;
-                                                       [3-9]|[1-9][0-9])
-                                                               if [[ -n ${a_sensors_working[$j]} ]];then
-                                                                       fan_number=$(( $j - 2 )) # sys fans start on array key 5
-                                                                       # wrap after fan 6 total
-                                                                       if [[ $fan_count -lt 7 ]];then
-                                                                               sys_fans="$sys_fans${C1}sys-$fan_number$SEP3${C2} ${a_sensors_working[$j]} "
-                                                                       else
-                                                                               sys_fans2="$sys_fans2${C1}sys-$fan_number$SEP3${C2} ${a_sensors_working[$j]} "
-                                                                       fi
-                                                                       (( fan_count++ ))
-                                                               fi
-                                                               ;;
-                                               esac
-                                       done
-                                       ;;
-                               2)
-                                       for (( j=0; j < ${#a_sensors_working[@]}; j++ ))
-                                       do
-                                               case $j in
-                                                       [0-9]|[1-9][0-9])
-                                                               if [[ -n ${a_sensors_working[$j]} ]];then
-                                                                       fan_number=$(( $j + 1 )) # sys fans start on array key 5
-                                                                       # wrap after fan 6 total
-                                                                       if [[ $fan_count -lt 7 ]];then
-                                                                               sys_fans="$sys_fans${C1}fan-$fan_number$SEP3${C2} ${a_sensors_working[$j]} "
-                                                                       else
-                                                                               sys_fans2="$sys_fans2${C1}fan-$fan_number$SEP3${C2} ${a_sensors_working[$j]} "
-                                                                       fi
-                                                                       (( fan_count++ ))
-                                                               fi
-                                                               ;;
-                                               esac
-                                       done
-                                       ;;
-                       esac
-               done
-       fi
-       # turning off all output for case where no sensors detected or no sensors output 
-       # unless -s used explicitly. So for -F type output won't show unless valid or -! 1 used
-       if [[ $b_is_error != 'true' || $B_SHOW_SENSORS == 'true' || $B_TESTING_1 == 'true' ]];then
-               temp_data="$cpu_temp$mobo_temp$psu_temp$gpu_temp"
-               temp_data=$( create_print_line "Sensors:" "$temp_data" )
-               print_screen_output "$temp_data"
-               # don't print second or subsequent lines if error data
-               fan_data="$cpu_fan$mobo_fan$ps_fan$sys_fans"
-               if [[ $b_is_error != 'true' && -n $fan_data ]];then
-                       fan_data=$( create_print_line " " "$fan_data" )
-                       print_screen_output "$fan_data"
-                       # and then second wrapped fan line if needed
-                       if [[ -n $sys_fans2 ]];then
-                               fan_data2=$( create_print_line " " "$sys_fans2" )
-                               print_screen_output "$fan_data2"
-                       fi
-               fi
-       fi
-       eval $LOGFE
-}
-
-print_system_data()
-{
-       eval $LOGFS
-       local system_data='' bits='' desktop_environment='' dm_data='' de_extra_data=''
-       local host_kernel_string='' de_distro_string='' host_string='' desktop_type='Desktop'
-       local host_name=$HOSTNAME
-       local current_kernel=$( get_kernel_version ) 
-       local distro="$( get_distro_data )"
-       local tty_session=''
-       
-       # I think these will work, maybe, if logged in as root and in X
-       if [[ $B_RUNNING_IN_DISPLAY == 'true' ]];then
-               desktop_environment=$( get_desktop_environment )
-               if [[ -z $desktop_environment ]];then
-                       desktop_environment='N/A'
-               fi
-               
-               if [[  $B_EXTRA_EXTRA_EXTRA_DATA == 'true' ]];then
-                       de_extra_data=$( get_desktop_extra_data )
-                       if [[ -n $de_extra_data ]];then
-                               de_extra_data=" ${C1}info$SEP3${C2} $de_extra_data"
-                       fi
-               fi
-       else
-               tty_session=$( get_tty_number )
-               if [[ -z $tty_session && $B_CONSOLE_IRC == 'true' ]];then
-                       tty_session=$( get_tty_console_irc )
-               fi
-               if [[ -n $tty_session ]];then
-                       tty_session=" $tty_session"
-               fi
-               desktop_environment="tty$tty_session"
-               desktop_type='Console'
-       fi
-       # having dm type can be useful if you are accessing remote system
-       # or are out of X and don't remember which dm is running the system
-       if [[  $B_EXTRA_EXTRA_DATA == 'true' ]];then
-               dm_data=$( get_display_manager )
-               # here we only want the dm info to show N/A if in X
-               if [[ -z $dm_data && $B_RUNNING_IN_DISPLAY == 'true' ]];then
-                       dm_data='N/A'
-               fi
-               # only print out of X if dm_data has info, then it's actually useful, but
-               # for headless servers, no need to print dm stuff.
-               if [[ -n $dm_data ]];then
-                       dm_data=" ${C1}dm$SEP3${C2} $dm_data"
-               fi
-       fi
-       
-       de_distro_string="${C1}$desktop_type$SEP3${C2} $desktop_environment$de_extra_data$dm_data ${C1}Distro$SEP3${C2} $distro"
-       if [[ $B_EXTRA_DATA == 'true' ]];then
-               gcc_string=$( get_gcc_kernel_version )
-               if [[ -n $gcc_string ]];then
-                       gcc_string=", ${C1}gcc$SEP3${C2} $gcc_string"
-               fi
-       fi
-       # check for 64 bit first
-       if [[ -n $( uname -m | grep -E '(x86_64|amd64)' ) ]];then
-               bits="64"
-       else
-               bits="32"
-       fi
-       bits=" (${bits} bit${gcc_string})"
-       if [[ $B_SHOW_HOST == 'true' ]];then
-               if [[ -z $HOSTNAME ]];then
-                       if [[ -n $( type p hostname ) ]];then
-                               host_name=$( hostname )
-                       fi
-                       if [[ -z $host_name ]];then
-                               host_name='N/A'
-                       fi
-               fi
-               host_string="${C1}Host$SEP3${C2} $host_name "
-               system_data=$( create_print_line "System:" "$host_string$host_name ${C1}Kernel$SEP3${C2}" )
-       fi
-       host_kernel_string="$host_string${C1}Kernel$SEP3${C2} $current_kernel$bits "
-       if [[ $( calculate_line_length "$host_kernel_string$de_distro_string" ) -lt $COLS_INNER ]];then
-               system_data="$host_kernel_string$de_distro_string"
-               system_data=$( create_print_line "System:" "$system_data" )
-       else
-               system_data=$( create_print_line "System:" "$host_kernel_string" )
-               print_screen_output "$system_data"
-               system_data=$( create_print_line " " "$de_distro_string" )
-       fi
-       print_screen_output "$system_data"
-       eval $LOGFE
-}
-
-print_unmounted_partition_data()
-{
-       eval $LOGFS
-       local a_unmounted_data='' line_starter='' unmounted_data='' full_fs=''
-       local full_dev='' full_size='' full_label='' full_uuid='' full_string=''
-       local bsd_unsupported='This feature is not yet supported for BSD systems.'
-       
-       if [[ -z ${A_PARTITION_DATA} ]];then
-               get_partition_data
-       fi
-       get_unmounted_partition_data
-       
-       if [[ ${#A_UNMOUNTED_PARTITION_DATA[@]} -ge 1 ]];then
-               for (( i=0; i < ${#A_UNMOUNTED_PARTITION_DATA[@]}; i++ ))
-               do
-                       IFS=","
-                       a_unmounted_data=(${A_UNMOUNTED_PARTITION_DATA[i]})
-                       IFS="$ORIGINAL_IFS"
-                       if [[ -z ${a_unmounted_data[0]} ]];then
-                               full_dev='N/A'
-                       else
-                               full_dev="/dev/${a_unmounted_data[0]}"
-                       fi
-                       full_dev="${C1}ID:${C2} $full_dev"
-                       if [[ -z ${a_unmounted_data[1]} ]];then
-                               full_size='N/A'
-                       else
-                               full_size=${a_unmounted_data[1]}
-                       fi
-                       full_size="${C1}size$SEP3${C2} $full_size"
-                       if [[ -z ${a_unmounted_data[2]} ]];then
-                               full_label='N/A'
-                       else
-                               full_label=${a_unmounted_data[2]}
-                       fi
-                       full_label="${C1}label$SEP3${C2} $full_label"
-                       if [[ -z ${a_unmounted_data[3]} ]];then
-                               full_uuid='N/A'
-                       else
-                               full_uuid=${a_unmounted_data[3]}
-                       fi
-                       full_uuid="${C1}uuid$SEP3${C2} $full_uuid"
-                       if [[ -z ${a_unmounted_data[4]} ]];then
-                               full_fs=''
-                       else
-                               full_fs="${C1}fs$SEP3${C2} ${a_unmounted_data[4]}"
-                       fi
-                       full_string="$full_dev $full_size $full_label $full_uuid $full_fs"
-                       if [[ $i -eq 0 ]];then
-                               line_starter='Unmounted:'
-                       else
-                               line_starter=' '
-                       fi
-                       # temporary message to indicate not yet supported
-                       if [[ $BSD_TYPE == 'bsd' ]];then
-                               full_string=$bsd_unsupported
-                       fi
-                       unmounted_data=$( create_print_line "$line_starter" "$full_string" )
-                       print_screen_output "$unmounted_data"
-               done
-       else
-               unmounted_data=$( create_print_line "Unmounted:" "No unmounted partitions detected" )
-               print_screen_output "$unmounted_data"
-       fi
-       
-       eval $LOGFE
+sub set_man_location {
+       my $location='';
+       my $default_location='/usr/share/man/man1';
+       my $man_paths=qx(man --path 2>/dev/null);
+       my $man_local='/usr/local/share/man';
+       my $b_use_local=0;
+       if ( $man_paths && $man_paths =~ /$man_local/ ){
+               $b_use_local=1;
+       }
+       # for distro installs
+       if ( -f "$default_location/inxi.1.gz" ){
+               $location=$default_location;
+       }
+       else {
+               if ( $b_use_local ){
+                       if ( ! -d "$man_local/man1" ){
+                               mkdir "$man_local/man1";
+                       }
+                       $location="$man_local/man1";
+               }
+       }
+       if ( ! $location ){
+               $location=$default_location;
+       }
+       return $location;
 }
 
-print_weather_data()
-{
-       eval $LOGFS
-       
-       local weather_data='' location_string='' local_time='' time_string='' pressure=''
-       local a_location='' a_weather='' weather_string='' weather='' temp='' winds='' humidity=''
-       local time_zone='' observation_time='' city='' state='' country='' altitude=''
-       local heat_index="" wind_chill='' dewpoint='' xxx_humidity=''
-       local openP='(' closeP=')'
-       
-       if [[ $B_RUNNING_IN_SHELL == 'false' ]];then
-               openP=''
-               closeP=''
-       fi
-       
-       get_weather_data
-       
-       # city ";" regionCode ";" regionName ";" countryName ";" countryCode ";" countryCode3 
-       #  ";" latitude "," longitude ";" postalCode ";" timeZone
-       
-       # observationTime ";" localTime ";" weather ";" tempString ";" humidity 
-       # ";" windString ";" pressureString ";" dewpointString ";" heatIndexString
-       # ";" windChillString ";" siteElevation
-
-       if [[ ${#A_WEATHER_DATA[@]} -eq 2 ]];then
-               IFS=";"
-               a_location=(${A_WEATHER_DATA[0]})
-               a_weather=(${A_WEATHER_DATA[1]})
-               IFS="$ORIGINAL_IFS"
-               
-               if [[ -n ${a_weather[3]} ]];then
-                       temp=${a_weather[3]}
-               else
-                       temp='N/A'
-               fi
-               if [[ -n ${a_weather[2]} ]];then
-                       weather=" - ${a_weather[2]}"
-               else
-                       weather=''
-               fi
-               if [[ $B_EXTRA_DATA == 'true' ]];then
-                       if [[ -n ${a_weather[5]} ]];then
-                               winds=" ${C1}Wind$SEP3${C2} ${a_weather[5]}"
-                       fi
-               fi
-               if [[ $B_EXTRA_EXTRA_DATA == 'true' ]];then
-                       if [[ -n ${a_weather[4]} ]];then
-                               humidity=" ${C1}Humidity$SEP3${C2} ${a_weather[4]}"
-                       fi
-                       if [[ -n ${a_weather[6]} ]];then
-                               pressure="${C1}Pressure$SEP3${C2} ${a_weather[6]} "
-                       fi
-               fi
-               weather_string="${C1}Conditions$SEP3${C2} $temp$weather$winds$humidity"
-               
-               if [[ -n ${a_weather[1]} ]];then
-                       local_time=" ${a_weather[1]}"
-               else
-                       local_time=" $(date)"
-               fi
-               if [[ $B_EXTRA_DATA == 'true' && -n ${a_location[8]} ]];then
-                       time_zone=" (${a_location[8]})"
-               fi
-               time_string="${C1}Time$SEP3${C2}$local_time$time_zone"
-
-               if [[ $B_EXTRA_DATA != 'true' ]];then
-                       weather_data="$weather_string $time_string"
-                       weather_data=$( create_print_line "Weather:" "$weather_data" )
-                       print_screen_output "$weather_data"
-               else
-                       weather_data="$weather_string"
-                       weather_data=$( create_print_line "Weather:" "$weather_data" )
-                       print_screen_output "$weather_data"
-                       if [[ $B_EXTRA_EXTRA_EXTRA_DATA == 'true' ]];then
-                               if [[ -n ${a_weather[8]} ]];then
-                                       heat_index="${C1}Heat Index$SEP3${C2} ${a_weather[8]} "
-                               fi
-                               if [[ -n ${a_weather[9]} ]];then
-                                       wind_chill="${C1}Wind Chill$SEP3${C2} ${a_weather[9]} "
-                               fi
-                               if [[ -n ${a_weather[7]} ]];then
-                                       dew_point="${C1}Dew Point$SEP3${C2} ${a_weather[7]} "
-                               fi
-                               if [[ -n ${a_weather[0]} ]];then
-                                       observation_time=" ${C1}Observation Time$SEP3${C2} ${a_weather[0]} "
-                               fi
-                               if [[ $B_OUTPUT_FILTER != 'true' ]];then
-                                       if [[ -n ${a_location[0]} ]];then
-                                               city=" ${a_location[0]}"
-                                       fi
-                                       if [[ -n ${a_location[1]} ]];then
-                                               state=" ${a_location[1]}"
-                                       fi
-                                       if [[ -n ${a_location[5]} ]];then
-                                               country=" $openP${a_location[5]}$closeP"
-                                       fi
-                                       if [[ -n ${a_weather[10]} ]];then
-                                               altitude=" ${C1}Altitude$SEP3${C2} ${a_weather[10]}"
-                                       fi
-                                       location_string="${C1}Location$SEP3${C2}$city$state$country$altitude "
-                               else
-                                       location_string=$time_string$observation_time
-                                       time_string=''
-                                       observation_time=''
-                               fi
-                               # the last three are oftenblank
-                               if [[ -z "$heat_index$wind_chill$dew_point" ]];then
-                                       weather_data=$( create_print_line " " "$pressure$location_string" )
-                                       print_screen_output "$weather_data"
-                               else
-                                       weather_data=$( create_print_line " " "$pressure$heat_index$wind_chill$dew_point" )
-                                       print_screen_output "$weather_data"
-                                       if [[ $B_OUTPUT_FILTER != 'true' ]];then
-                                               weather_data=$( create_print_line " " "$location_string" )
-                                               print_screen_output "$weather_data"
-                                       fi
-                               fi
-                               if [[ -n $time_string$observation_time ]];then
-                                       weather_data=$( create_print_line " " "$time_string$observation_time" )
-                                       print_screen_output "$weather_data"
-                               fi
-                       else
-                               if [[ -n $pressure$time_string ]];then
-                                       weather_data="$pressure$time_string"
-                                       weather_data=$( create_print_line " " "$weather_data" )
-                                       print_screen_output "$weather_data"
-                               fi
-                       fi
-               fi
-       else
-               weather_data=$( create_print_line "Weather:" "Weather data failure: $(date)" )
-               print_screen_output "$weather_data"
-               weather_data=$( create_print_line " " "${A_WEATHER_DATA}" )
-               print_screen_output "$weather_data"
-       fi
-       eval $LOGFE
+# update for updater output version info
+# note, this is only now used for self updater function so it can get
+# the values from the UPDATED file, NOT the running program!
+sub set_version_data {
+       open (my $fh, '<', "$self_path/$self_name");
+       while( my $row = <$fh>){
+               chomp $row;
+               $row =~ s/'|;//g;
+               if ($row =~ /^my \$self_name/ ){
+                       $self_name = (split /=/, $row)[1];
+               }
+               elsif ($row =~ /^my \$self_version/ ){
+                       $self_version = (split /=/, $row)[1];
+               }
+               elsif ($row =~ /^my \$self_date/ ){
+                       $self_date = (split /=/, $row)[1];
+               }
+               elsif ($row =~ /^my \$self_patch/ ){
+                       $self_patch = (split /=/, $row)[1];
+               }
+               elsif ($row =~ /^## END INXI INFO/){
+                       last;
+               }
+       }
+       close $fh;
 }
 
 ########################################################################
-#### SCRIPT EXECUTION
+#### OPTIONS HANDLER / VERSION
+########################################################################
+
+sub get_options{
+       eval $start if $b_log;
+       my (@args) = @_;
+       $show{'short'} = 1;
+       my ($b_downloader,$b_help,$b_no_man,$b_no_man_force,$b_recommends,$b_updater,$b_version,
+       $b_use_man,$self_download, $download_id);
+       GetOptions (
+       'admin' => sub {
+               $b_admin = 1;},
+       'A|audio' => sub {
+               $show{'short'} = 0;
+               $show{'audio'} = 1;},
+       'b|basic' => sub {
+               $show{'short'} = 0;
+               $show{'battery'} = 1;
+               $show{'cpu-basic'} = 1;
+               $show{'raid-basic'} = 1;
+               $show{'disk-total'} = 1;
+               $show{'graphic'} = 1;
+               $show{'info'} = 1;
+               $show{'machine'} = 1;
+               $show{'network'} = 1;
+               $show{'system'} = 1;},
+       'B|battery' => sub {
+               $show{'short'} = 0;
+               $show{'battery'} = 1;
+               $show{'battery-forced'} = 1; },
+       'c|color:i' => sub {
+               my ($opt,$arg) = @_;
+               if ( $arg >= 0 && $arg < get_color_scheme('count') ){
+                       set_color_scheme($arg);
+               }
+               elsif ( $arg >= 94 && $arg <= 99 ){
+                       $colors{'selector'} = $arg;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               } },
+       'C|cpu' => sub {
+               $show{'short'} = 0;
+               $show{'cpu'} = 1; },
+       'd|disk-full|optical' => sub {
+               $show{'short'} = 0;
+               $show{'disk'} = 1;
+               $show{'optical'} = 1; },
+       'D' => sub {
+               $show{'short'} = 0;
+               $show{'disk'} = 1; },
+       'f|flags|flag' => sub {
+               $show{'short'} = 0;
+               $show{'cpu'} = 1;
+               $show{'cpu-flag'} = 1; },
+       'F|full' => sub {
+               $show{'short'} = 0;
+               $show{'audio'} = 1;
+               $show{'battery'} = 1;
+               $show{'cpu'} = 1;
+               $show{'disk'} = 1;
+               $show{'graphic'} = 1;
+               $show{'info'} = 1;
+               $show{'machine'} = 1;
+               $show{'network'} = 1;
+               $show{'network-advanced'} = 1;
+               $show{'partition'} = 1;
+               $show{'raid'} = 1;
+               $show{'sensor'} = 1;
+               $show{'system'} = 1; },
+       'G|graphics|graphic' => sub {
+               $show{'short'} = 0;
+               $show{'graphic'} = 1; },
+       'i|ip' => sub {
+               $show{'short'} = 0;
+               $show{'ip'} = 1;
+               $show{'network'} = 1;
+               $show{'network-advanced'} = 1;
+               $b_downloader = 1 if ! check_program('dig');},
+       'I|info' => sub {
+               $show{'short'} = 0;
+               $show{'info'} = 1; },
+       'l|labels|label' => sub {
+               $show{'short'} = 0;
+               $show{'label'} = 1;
+               $show{'partition'} = 1; },
+       'limit:i' => sub {
+               my ($opt,$arg) = @_;
+               if ($arg != 0){
+                       $limit = $arg;
+               }
+               else {
+                       error_handler('bad-arg',$opt,$arg);
+               } },
+       'm|memory' => sub {
+               $show{'short'} = 0;
+               $show{'ram'} = 1; },
+       'M|machine' => sub {
+               $show{'short'} = 0;
+               $show{'machine'} = 1; },
+       'n|network-advanced' => sub {
+               $show{'short'} = 0;
+               $show{'network'} = 1;
+               $show{'network-advanced'} = 1; },
+       'N|network' => sub {
+               $show{'short'} = 0;
+               $show{'network'} = 1; },
+       'o|unmounted' => sub {
+               $show{'short'} = 0;
+               $show{'unmounted'} = 1; },
+       'p|partition-full' => sub {
+               $show{'short'} = 0;
+               $show{'partition'} = 0;
+               $show{'partition-full'} = 1; },
+       'P|partitions|partition' => sub {
+               $show{'short'} = 0;
+               $show{'partition'} = 1; },
+       'r|repos|repo' => sub {
+               $show{'short'} = 0;
+               $show{'repo'} = 1; },
+       'R|raid' => sub {
+               $show{'short'} = 0;
+               $show{'raid'} = 1;
+               $show{'raid-forced'} = 1; },
+       's|sensors|sensor' => sub {
+               $show{'short'} = 0;
+               $show{'sensor'} = 1; },
+       'sleep:s' => sub {
+               my ($opt,$arg) = @_;
+               $arg ||= 0;
+               if ($arg >= 0){
+                       $cpu_sleep = $arg;
+               }
+               else {
+                       error_handler('bad-arg',$opt,$arg);
+               } },
+       'slots|slot' => sub {
+               $show{'short'} = 0;
+               $show{'slot'} = 1; },
+       'S|system' => sub {
+               $show{'short'} = 0;
+               $show{'system'} = 1; },
+       't|processes|process:s' => sub {
+               my ($opt,$arg) = @_;
+               $show{'short'} = 0;
+               $arg ||= 'cm';
+               my $num = $arg;
+               $num =~ s/^[cm]+// if $num;
+               if ( $arg =~ /^([cm]+)([0-9]+)?$/ && (!$num || $num =~ /^\d+/) ){
+                       $show{'process'} = 1;
+                       if ($arg =~ /c/){
+                               $show{'ps-cpu'} = 1;
+                       }
+                       if ($arg =~ /m/){
+                               $show{'ps-mem'} = 1;
+                       }
+                       $ps_count = $num if $num;
+               }
+               else {
+                       error_handler('bad-arg',$opt,$arg);
+               } },
+       'usb' => sub {
+               $show{'short'} = 0;
+               $show{'usb'} = 1; },
+       'u|uuid' => sub {
+               $show{'short'} = 0;
+               $show{'partition'} = 1;
+               $show{'uuid'} = 1; },
+       'v|verbosity:i' => sub {
+               my ($opt,$arg) = @_;
+               $show{'short'} = 0;
+               if ( $arg =~ /^[0-8]$/ ){
+                       if ($arg == 0 ){
+                               $show{'short'} = 1;
+                       }
+                       if ($arg >= 1 ){
+                               $show{'cpu-basic'} = 1;
+                               $show{'disk-total'} = 1;
+                               $show{'graphic'} = 1;
+                               $show{'info'} = 1;
+                               $show{'system'} = 1;
+                       }
+                       if ($arg >= 2 ){
+                               $show{'battery'} = 1;
+                               $show{'disk-basic'} = 1;
+                               $show{'raid-basic'} = 1;
+                               $show{'machine'} = 1;
+                               $show{'network'} = 1;
+                       }
+                       if ($arg >= 3 ){
+                               $show{'network-advanced'} = 1;
+                               $show{'cpu'} = 1;
+                               $extra = 1;
+                       }
+                       if ($arg >= 4 ){
+                               $show{'disk'} = 1;
+                               $show{'partition'} = 1;
+                       }
+                       if ($arg >= 5 ){
+                               $show{'audio'} = 1;
+                               $show{'ram'} = 1;
+                               $show{'label'} = 1;
+                               $show{'optical-basic'} = 1;
+                               $show{'ram'} = 1;
+                               $show{'raid'} = 1;
+                               $show{'sensor'} = 1;
+                               $show{'uuid'} = 1;
+                       }
+                       if ($arg >= 6 ){
+                               $show{'optical'} = 1;
+                               $show{'partition-full'} = 1;
+                               $show{'unmounted'} = 1;
+                               $show{'usb'} = 1;
+                               $extra = 2;
+                       }
+                       if ($arg >= 7 ){
+                               $b_downloader = 1 if ! check_program('dig');
+                               $show{'cpu-flag'} = 1;
+                               $show{'ip'} = 1;
+                               $show{'raid-forced'} = 1;
+                               $extra = 3;
+                       }
+                       if ($arg >= 8 ){
+                               $b_downloader = 1;
+                               $show{'slot'} = 1;
+                               $show{'process'} = 1;
+                               $show{'ps-cpu'} = 1;
+                               $show{'ps-mem'} = 1;
+                               $show{'repo'} = 1;
+                               #$show{'weather'} = 1;
+                       }
+               }
+               else {
+                       error_handler('bad-arg',$opt,$arg);
+               } },
+       'w|weather' => sub {
+               my ($opt) = @_;
+               $show{'short'} = 0;
+               $b_downloader = 1;
+               if ( $b_weather ){
+                       $show{'weather'} = 1;
+               }
+               else {
+                       error_handler('distro-block', $opt);
+               } },
+       'W|weather-location:s' => sub {
+               my ($opt,$arg) = @_;
+               $arg ||= '';
+               $arg =~ s/\s//g;
+               $show{'short'} = 0;
+               $b_downloader = 1;
+               if ( $b_weather ){
+                       if ($arg){
+                               $show{'weather'} = 1;
+                               $show{'weather-location'} = $arg;
+                       }
+                       else {
+                               error_handler('bad-arg',$opt,$arg);
+                       }
+               }
+               else {
+                       error_handler('distro-block', $opt);
+               } },
+       'weather-unit:s' => sub {
+               my ($opt,$arg) = @_;
+               $arg ||= '';
+               $arg =~ s/\s//g;
+               $arg = lc($arg) if $arg;
+               if ($arg && $arg =~ /^(c|f|cf|fc|i|m|im|mi)$/){
+                       my %units = ('c'=>'m','f'=>'i','cf'=>'mi','fc'=>'im');
+                       $arg = $units{$arg} if defined $units{$arg};
+                       $weather_unit = $arg;
+               }
+               else {
+                       error_handler('bad-arg',$opt,$arg);
+               } },
+       'x|extra:i' => sub {
+               my ($opt,$arg) = @_;
+               if ($arg > 0){
+                       $extra = $arg;
+               }
+               else {
+                       $extra++;
+               } },
+       'y|width:i' => sub {
+               my ($opt, $arg) = @_;
+               $arg = 2000 if defined $arg && $arg == -1;
+               if ( $arg =~ /\d/ && $arg >= 80 ){
+                       set_display_width($arg);
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               } },
+       'z|filter' => sub {
+               $show{'filter'} = 1; },
+       'Z|filter-override' => sub {
+               $show{'filter-override'} = 1; },
+       ## Start non data options
+       'alt:i' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg == 40) {
+                       $dl{'tiny'} = 0;
+                       $b_downloader = 1;}
+               elsif ($arg == 41) {
+                       $dl{'curl'} = 0;
+                       $b_downloader = 1;}
+               elsif ($arg == 42) {
+                       $dl{'fetch'} = 0;
+                       $b_downloader = 1;}
+               elsif ($arg == 43) {
+                       $dl{'wget'} = 0;
+                       $b_downloader = 1;}
+               elsif ($arg == 44) {
+                       $dl{'curl'} = 0;
+                       $dl{'fetch'} = 0;
+                       $dl{'wget'} = 0;
+                       $b_downloader = 1;}
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'arm' => sub {
+               $b_arm = 1 },
+       'bsd:s' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^(darwin|dragonfly|freebsd|openbsd|netbsd)$/i){
+                       $bsd_type = lc($arg);
+                       $b_fake_bsd = 1;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }
+       },
+       'bsd-data:s' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^(dboot|pciconf|sysctl|usbdevs)$/i){
+                       $b_fake_dboot = 1 if $arg eq 'dboot';
+                       $b_fake_pciconf = 1 if $arg eq 'pciconf';
+                       $b_fake_sysctl = 1 if $arg eq 'sysctl';
+                       $b_fake_usbdevs = 1 if $arg eq 'usbdevs';
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }
+       },
+       'dbg:i' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg > 0) {
+                       $test[$arg] = 1;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'debug:i' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^[1-3]|1[0-3]|2[0-4]$/){
+                       $debug=$arg;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               } },
+       'display:s' => sub { 
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^:?([0-9]+)?$/){
+                       $display=$arg;
+                       $display ||= ':0';
+                       $display = ":$display" if $display !~ /^:/;
+                       $b_display = ($b_root) ? 0 : 1;
+                       $b_force_display = 1;
+                       $display_opt = "-display $display";
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               } },
+       'dmidecode' => sub {
+               $b_dmidecode_force = 1 },
+       'downloader:s' => sub { 
+               my ($opt,$arg) = @_;
+               $arg = lc($arg);
+               if ($arg =~ /^(curl|fetch|ftp|perl|wget)$/){
+                       if ($arg eq 'perl' && (!check_module('HTTP::Tiny') || !check_module('IO::Socket::SSL') )){
+                               error_handler('missing-perl-downloader', $opt, $arg);
+                       }
+                       elsif ( !check_program($arg)) {
+                               error_handler('missing-downloader', $opt, $arg);
+                       }
+                       else {
+                               # this dumps all the other data and resets %dl for only the
+                               # desired downloader.
+                               $arg = set_perl_downloader($arg);
+                               %dl = ('dl' => $arg, $arg => 1);
+                               $b_downloader = 1;
+                       }
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               } },
+       'ftp:s'  => sub { 
+               my ($opt,$arg) = @_;
+               # pattern: ftp.x.x/x
+               if ($arg =~ /^ftp\..+\..+\/[^\/]+$/ ){
+                       $ftp_alt = $arg;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'h|help|?' => sub {
+               $b_help = 1; },
+       'host|hostname' => sub {
+               $show{'host'} = 1 },
+       'indent-min:i' => sub {
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^\d+$/){
+                       $size{'indent-min'} = 1;
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'irc' => sub {
+               $b_irc = 1; },
+       'man' => sub {
+               $b_use_man = 1; },
+       'output:s' => sub {
+               my ($opt,$arg) = @_;
+               if ($arg =~ /^(json|screen|xml)$/){
+                       if ($arg =~ /json|screen|xml/){
+                               $output_type = $arg;
+                       }
+                       else {
+                               error_handler('option-feature-incomplete', $opt, $arg);
+                       }
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'no-host|no-hostname' => sub {
+               $show{'host'} = 0 },
+       'no-man' => sub {
+               $b_no_man_force = 0; },
+       'no-ssl' => sub {
+               $dl{'no-ssl-opt'}=1 },
+       'output-file:s' => sub {
+               my ($opt,$arg) = @_;
+               if ($arg){
+                       if ($arg eq 'print' || check_output_path($arg)){
+                               $output_file = $arg;
+                       }
+                       else {
+                               error_handler('output-file-bad', $opt, $arg);
+                       }
+               }
+               else {
+                       error_handler('bad-arg', $opt, $arg);
+               }},
+       'proc' => sub {
+               $b_proc_debug = 1; },
+       'recommends' => sub {
+               $b_recommends = 1; },
+       'U|update:s' => sub { # 1,2,3 OR http://myserver/path/inxi
+               my ($opt,$arg) = @_;
+               $b_downloader = 1;
+               if ( $b_update ){
+                       $b_updater = 1;
+                       if (!$arg && $self_name eq 'pinxi'){
+                               $b_man = 1;
+                               $download_id = 'inxi-perl branch';
+                               $self_download = get_defaults('inxi-pinxi');
+                       }
+                       elsif ($arg && $arg eq '3'){
+                               $b_man = 1;
+                               $download_id = 'dev server';
+                               $self_download = get_defaults('inxi-dev');
+                       }
+                       else {
+                               if (!$arg){
+                                       $download_id = 'main branch';
+                                       $self_download = get_defaults('inxi-main');
+                                       $b_man = 1;
+                                       $b_use_man = 1;
+                               }
+                               elsif ( $arg =~ /^[12]$/){
+                                       $download_id = "branch $arg";
+                                       $self_download = get_defaults("inxi-branch-$arg");
+                               }
+                               elsif ( $arg =~ /^http/){
+                                       $download_id = 'alt server';
+                                       $self_download = $arg;
+                               }
+                       }
+                       if (!$self_download){
+                               error_handler('bad-arg', $opt, $arg);
+                       }
+               }
+               else {
+                       error_handler('distro-block', $opt);
+               } },
+       'V|version' => sub { 
+               $b_version = 1 },
+       'wm' => sub { 
+               $b_wmctrl = 1 },
+       '<>' => sub {
+               my ($opt) = @_;
+               error_handler('unknown-option', "$opt", "" ); }
+       ) ; #or error_handler('unknown-option', "@ARGV", '');
+       ## run all these after so that we can change widths, downloaders, etc
+       eval $end if $b_log;
+       CheckRecommends::run() if $b_recommends;
+       set_downloader() if $b_downloader;
+       show_version() if $b_version;
+       show_options() if $b_help;
+       $b_man = 0 if (!$b_use_man || $b_no_man_force);
+       update_me( $self_download, $download_id ) if $b_updater;
+       if ($output_type){
+               if ($output_type ne 'screen' && ! $output_file){
+                       error_handler('bad-arg', '--output', '--output-file not provided');
+               }
+       }
+       if ( $show{'ram'} || $show{'slot'} || 
+            ( ( $bsd_type || $b_dmidecode_force ) && ($show{'machine'} || $show{'battery'}) ) ){
+               $b_dmi = 1;
+       }
+       if ($show{'audio'} || $show{'graphic'} || $show{'network'} || $show{'raid'} || $show{'raid-forced'} ){
+               $b_pci = 1;
+       }
+       if ($show{'usb'} || $show{'audio'}  || $show{'network'} ){
+               # to detect wan/lan, we have to use long form to get as much data as possible
+               $usb_level = ($show{'usb'} || $show{'network'}) ? 2 : 1;
+       }
+       if ($bsd_type && ($show{'short'} || $show{'battery'} || $show{'cpu'} || $show{'cpu-basic'} || 
+          $show{'info'} || $show{'machine'} || $show{'process'} || $show{'ram'}  || $show{'sensor'} ) ){
+               $b_sysctl = 1;
+       }
+       if ($show{'filter-override'}){
+               $show{'filter'} = 0;
+       }
+       $b_sudo = 1 if ( $show{'unmounted'} || ($extra > 0 && $show{'disk'}) );
+       # override for things like -b or -v2 to -v3
+       $show{'cpu-basic'} = 0 if $show{'cpu'};
+       $show{'optical-basic'} = 0 if $show{'optical'};
+       $show{'partition'} = 0 if $show{'partition-full'};
+       if ($show{'disk'} || $show{'optical'} ){
+               $show{'disk-basic'} = 0;
+               $show{'disk-total'} = 0;
+       }
+       if ($bsd_type && ($show{'short'} || $show{'disk-basic'} || $show{'disk-total'} || $show{'disk'})){
+               $b_dm_boot_disk = 1;
+       }
+       if ($bsd_type && ($show{'optical-basic'} || $show{'optical'})){
+               $b_dm_boot_optical = 1
+       }
+} 
+
+sub show_options {
+       error_handler('not-in-irc', 'help') if $b_irc;
+       my (@row,@rows,@data);
+       my $line = '';
+       my $color_scheme_count = get_color_scheme('count') - 1; 
+       my $partition_string='partition';
+       my $partition_string_u='Partition';
+       my $flags = ($b_arm) ? 'features' : 'flags' ;
+       if ( $bsd_type ){
+               $partition_string='slice';
+               $partition_string_u='Slice';
+       }
+       # fit the line to the screen!
+       for my $i ( 0 .. ( ( $size{'max'} / 2 ) - 2 ) ){
+               $line = $line . '- ';
+       }
+       @rows = (
+       ['0', '', '', "$self_name supports the following options. You can combine
+       these or list them one by one. For more detailed information, see man^$self_name.
+       Examples:^$self_name^-v4^-c6 OR 
+       $self_name^-bDc^6. If you start $self_name with no arguments, it will display 
+       a short system summary." ],
+       ['0', '', '', '' ],
+       ['0', '', '', "The following options, if used without -F, -b, or -v, will 
+       show option line(s): A, B, C, D, G, I, M, N, P, R, S, W, d, f, i, l, m, n, 
+       o, p, r, s, t, u, w, --slots, --usb - you can use these alone or together 
+       to show just the line(s) you want to see. If you use them with -v [level], 
+       -b or -F, $self_name will combine the outputs." ],
+       ['0', '', '', $line ],
+       ['0', '', '', "Output Control Options:" ],
+       ['1', '-A', '--audio', "Audio/sound card(s), driver, sound server." ],
+       ['1', '-b', '--basic', "Basic output, short form. Same as $self_name^-v^2." ],
+       ['1', '-B', '--battery', "System battery info, including charge and condition, plus 
+       extra info (if battery present)." ],
+       ['1', '-c', '--color', "Set color scheme (0-42). Example:^$self_name^-c^11" ],
+       ['1', '', '', "Color selectors let you set the config file value for the 
+       selection (NOTE: IRC and global only show safe color set)" ],
+       ['2', '94', '', "Console, out of X" ],
+       ['2', '95', '', "Terminal, running in X - like xTerm" ],
+       ['2', '96', '', "Gui IRC, running in X - like Xchat, Quassel, Konversation etc." ],
+       ['2', '97', '', "Console IRC running in X - like irssi in xTerm" ],
+       ['2', '98', '', "Console IRC not in  X" ],
+       ['2', '99', '', "Global - Overrides/removes all settings. Setting specific 
+       removes global." ],
+       ['1', '-C', '--cpu', "CPU output, including per CPU clock speed and max 
+       CPU speed (if available)." ],
+       ['1', '-d', '--disk-full, --optical', "Optical drive data (and floppy disks, 
+       if present). Triggers -D." ],
+       ['1', '-D', '--disk', "Hard Disk info, including total storage and details 
+       for each disk. Disk total used percentage includes swap partition size(s)." ],
+       ['1', '-f', '--flags', "All CPU $flags. Triggers -C. Not shown with -F to 
+       avoid spamming." ],
+       ['1', '-F', '--full', "Full output. Includes all Upper Case line letters 
+       except -W, plus -s and -n. Does not show extra verbose options such 
+       as -d -f -i -l -m -o -p -r -t -u -x, unless specified." ],
+       ['1', '-G', '--graphics', "Graphics info (card(s), driver, display protocol 
+       (if available), display server, resolution, renderer, OpenGL version)." ],
+       ['1', '-i', '--ip', "WAN IP address and local interfaces (requires ifconfig 
+       or ip network tool). Triggers -n. Not shown with -F for user security reasons. 
+       You shouldn't paste your local/WAN IP." ],
+       ['1', '-I', '--info', "General info, including processes, uptime, memory, 
+       IRC client or shell type, $self_name version." ],
+       ['1', '-l', '--label', "$partition_string_u labels. Triggers -P. 
+       For full -p output, use -pl." ],
+       ['1', '-m', '--memory', "Memory (RAM) data. Requires root. Numbers of 
+       devices (slots) supported and individual memory devices (sticks of memory etc). 
+       For devices, shows device locator, size, speed, type (e.g. DDR3). 
+       If neither -I nor -tm are selected, also shows RAM used/total." ],
+       ['1', '-M', '--machine', "Machine data. Device type (desktop, server, laptop, 
+       VM etc.), motherboard, BIOS and, if    present, system builder (e.g. Lenovo). 
+       Shows UEFI/BIOS/UEFI [Legacy]. Older systems/kernels without the required /sys 
+       data can use dmidecode instead, run as root. Dmidecode can be forced with --dmidecode" ],
+       ['1', '-n', '--network-advanced', "Advanced Network card info. Triggers -N. Shows 
+       interface, speed, MAC id, state, etc. " ],
+       ['1', '-N', '--network', "Network card(s), driver." ],
+       ['1', '-o', '--unmounted', "Unmounted $partition_string info (includes UUID 
+       and Label if available). Shows file system type if you have lsblk installed 
+       (Linux) or, for BSD/GNU Linux, if 'file' installed and you are root or if 
+       you have added to /etc/sudoers (sudo v. 1.7 or newer)." ],
+       ['1', '', '', "Example: ^<username>^ALL^=^NOPASSWD:^/usr/bin/file^" ],
+       ['1', '-p', '--partitions-full', "Full $partition_string information (-P plus all other 
+       detected ${partition_string}s)." ],
+       ['1', '-P', '--partitions', "Basic $partition_string info. Shows, if detected: 
+       / /boot /home /opt /tmp /usr /var /var/log /var/tmp. Use -p to see all 
+       mounted ${partition_string}s." ],
+       ['1', '-r', '--repos', "Distro repository data. Supported repo types: APK, 
+       APT, EOPKG, PACMAN, PACMAN-G2, PISI, PORTAGE, PORTS (BSDs), SLACKPKG, 
+       URPMQ, YUM/ZYPP." ],
+       ['1', '-R', '--raid', "RAID data. Shows RAID devices, states, levels, 
+       and components. md-raid: If device is resyncing, also shows resync progress line." ],
+       ['1', '-s', '--sensors', "Sensors output (if sensors installed/configured): 
+       mobo/CPU/GPU temp; detected fan speeds. GPU temp only for Fglrx/Nvidia drivers. 
+       Nvidia shows screen number for > 1 screen. IPMI sensors if present." ],
+       ['1', '', '--slots', "PCI slots: type, speed, status. Requires root." ],
+       ['1', '-S', '--system', "System info: host name, kernel, desktop environment 
+       (if in X/Wayland), distro." ],
+       ['1', '-t', '--processes', "Processes. Requires extra options: c (CPU), m 
+       (memory), cm (CPU+memory). If followed by numbers 1-x, shows that number 
+       of processes for each type (default: 5; if in IRC, max: 5). " ],
+       ['1', '', '', "Make sure that there is no space between letters and 
+       numbers (e.g.^-t^cm10)." ],
+       ['1', '', '--usb', "Show USB data: Hubs and Devices." ],
+       ['1', '-u', '--uuid', "$partition_string_u UUIDs. Triggers -P. For full -p 
+       output, use -pu." ],
+       ['1', '-v', '--verbosity', "Set $self_name verbosity level (0-8). 
+       Should not be used with -b or -F. Example: $self_name^-v^4" ],
+       ['2', '0', '', "Same as: $self_name" ],
+       ['2', '1', '', "Basic verbose, -S + basic CPU + -G + basic Disk + -I." ],
+       ['2', '2', '', "Networking card (-N), Machine (-M), Battery (-B; if present), 
+       and, if present, basic RAID (devices only; notes if inactive). 
+       Same as $self_name^-b" ],
+       ['2', '3', '', "Advanced CPU (-C), battery (-B), network (-n); 
+       triggers -x. " ],
+       ['2', '4', '', "$partition_string_u size/used data (-P) for 
+       (if present) /, /home, /var/, /boot. Shows full disk data (-D). " ],
+       ['2', '5', '', "Audio card (-A), sensors (-s), memory/RAM (-m), 
+       $partition_string label^(-l), UUID^(-u), short form of optical drives, 
+       standard RAID data (-R). " ],
+       ['2', '6', '', "Full $partition_string (-p), unmounted $partition_string (-o), 
+       optical drive (-d), USB (--usb), full RAID; triggers -xx." ],
+       ['2', '7', '', "Network IP data (-i); triggers -xxx."],
+       ['2', '8', '', "Everything available, including repos (-r), processes 
+       (-tcm), PCI slots (--slots)."],
+       );
+       push @data, @rows;
+       # if distro maintainers don't want the weather feature disable it
+       if ( $b_weather ){
+               @rows = (
+               ['1', '-w', '--weather', "Local weather data/time. To check an alternate
+               location, see -W."],
+               ['1', '-W', '--weather-location', "[location] Supported options for 
+               [location]: postal code; city, state/country; latitude, longitude. 
+               Only use if you want the weather somewhere other than the machine running
+               $self_name. Use only ASCII characters, replace spaces in city/state/country 
+               names with '+'. Example:^$self_name^-W^new+york,ny"],
+               ['1', '', '--weather-unit', "Set weather units to metric (m), imperial (i), 
+               metric/imperial (mi), or imperial/metric (im)."],
+               );
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '-x', '--extra', "Adds the following extra data (only works with 
+       verbose or line output, not short form):" ],
+       ['2', '-B', '', "Vendor/model, status (if available); attached devices 
+       (e.g. wireless mouse, keyboard, if present)." ],
+       ['2', '-C', '', "CPU $flags, Bogomips on CPU; CPU microarchitecture + 
+       revision (if found, or unless --admin, then shows as 'stepping')." ],
+       ['2', '-d', '', "Extra optical drive features data; adds rev version to 
+       optical drive." ],
+       ['2', '-D', '', "HDD temp with disk data if you have hddtemp installed, 
+       if you are root, or if you have added to /etc/sudoers (sudo v. 1.7 or newer). 
+       Example:^<username>^ALL^=^NOPASSWD:^/usr/sbin/hddtemp" ],
+       ['2', '-G', '', "Direct rendering status (in X); Screen number GPU is 
+       running on (Nvidia only)." ],
+       ['2', '-i', '', "For IPv6, show additional scope addresses: Global, Site, 
+       Temporary, Unknown. See --limit for large counts of IP addresses." ],
+       ['2', '-I', '', "Default system GCC. With -xx, also shows other installed 
+       GCC versions. If running in shell, not in IRC client, shows shell version 
+       number, if detected. Init/RC type and runlevel (if available)." ],
+       ['2', '-m', '', "Max memory module size (if available), device type." ],
+       ['2', '-N -A', '', "Version/port(s)/driver version (if available)." ],
+       ['2', '-N -A -G', '', "PCI Bus ID/USB ID number of card." ],
+       ['2', '-R', '', "md-raid: second RAID Info line with extra data: 
+       blocks, chunk size, bitmap (if present). Resync line, shows blocks 
+       synced/total blocks. Hardware RAID driver version, bus ID." ],
+       ['2', '-s', '', "Basic voltages (ipmi, lm-sensors if present): 12v, 5v, 3.3v, vbat." ],
+       ['2', '-S', '', "Kernel gcc version; system base of distro (if relevant 
+       and detected)" ],
+       ['2', '-t', '', "Adds memory use output to CPU (-xt c), and CPU use to 
+       memory (-xt m)." ],
+       ['2', '--usb', '', "For Devices, shows USB version/speed." ],
+       );
+       push @data, @rows;
+       if ( $b_weather ){
+               @rows = (['2', '-w -W', '', "Wind speed and direction, humidity, pressure, 
+               and (-w only) time zone." ]);
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '-xx', '--extra 2', "Show extra, extra data (only works with verbose 
+       or line output, not short form):" ],
+       ['2', '-A', '', "Chip vendor:product ID for each audio device." ],
+       ['2', '-B', '', "Serial number, voltage now/minimum (if available)." ],
+       ['2', '-C', '', "Minimum CPU speed, if available." ],
+       ['2', '-D', '', "Disk transfer speed; NVMe lanes; Disk serial number." ],
+       ['2', '-G', '', "Chip vendor:product ID for each video card; OpenGL 
+       compatibility version, if free drivers and available; compositor (experimental);
+       alternate Xorg drivers (if available). Alternate means driver is on automatic 
+       driver check list of Xorg for the card vendor, but is not installed on system." ],
+       ['2', '-I', '', "Other detected installed gcc versions (if present). System 
+       default runlevel. Adds parent program (or tty) for shell info if not in 
+       IRC. Adds Init version number, RC (if found)." ],
+       ['2', '-m', '', "Manufacturer, part number; single/double bank (if found)." ],
+       ['2', '-M', '', "Chassis info, BIOS ROM size (dmidecode only), if available." ],
+       ['2', '-N', '', "Chip vendor:product ID." ],
+       ['2', '-R', '', "md-raid: Superblock (if present), algorithm. If resync, 
+       shows progress bar. Hardware RAID Chip vendor:product ID." ],
+       ['2', '-s', '', "DIMM/SOC voltages (ipmi only)." ],
+       ['2', '-S', '', "Display manager (dm) in desktop output (e.g. kdm, 
+       gdm3, lightdm); active window manager if detected; desktop toolkit, 
+       if available (Xfce/KDE/Trinity only)." ],
+       ['2', '--slots', '', "Slot length." ],
+       ['2', '--usb', '', "Vendor:chip ID." ],
+       );
+       push @data, @rows;
+       if ( $b_weather ){
+               @rows = (['2', '-w -W', '', "Wind chill, dew point, heat index, if available." ]);
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '-xxx', '--extra 3', "Show extra, extra, extra data (only works 
+       with verbose or line output, not short form):" ],
+       ['2', '-A', '', "Specific vendor/product information (if relevant)." ],
+       ['2', '-B', '', "Chemistry, cycles, location (if available)." ],
+       ['2', '-C', '', "CPU boost (turbo) enabled/disabled, if present." ],
+       ['2', '-D', '', "Firmware rev. if available; partition scheme, in some cases; disk 
+       rotation speed (if detected)." ],
+       ['2', '-G', '', "Specific vendor/product information (if relevant)." ],
+       ['2', '-I', '', "For 'Shell:' adds ([su|sudo|login]) to shell name if present; 
+       for 'running in:' adds (SSH) if SSH session." ],
+       ['2', '-m', '', "Width of memory bus, data and total (if present and greater 
+       than data); Detail for Type, if present; module voltage, if available; serial 
+       number." ],
+       ['2', '-R', '', "zfs-raid: portion allocated (used) by RAID devices/arrays. 
+       md-raid: system md-raid support types (kernel support, read ahead, RAID events).
+       Hardware RAID rev, ports, specific vendor/product information." ],
+       ['2', '-S', '', "Panel/shell info in desktop output, if in X (like lxpanel, 
+       xfce4-panel, mate-panel); (if available) dm version number, window manager
+       version number." ]
+       );
+       push @data, @rows;
+       if ( $b_weather ){
+               @rows = (['2', '-w -W', '', "Location (uses -z/irc filter), weather observation 
+               time, altitude (shows extra lines for data where relevant)." ] );
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '', '--admin', "Adds advanced sys admin data (only works with 
+       verbose or line output, not short form):" ],
+       ['2', '-C', '', "If available: CPU errata (bugs); family, model-id, stepping - format:
+       hex (decimal) if greater than 9, otherwise hex; microcode - format: hex." ],
+       ['1', '-y', '--width', "Output line width max (integer >= 80). Overrides IRC/Terminal 
+       settings or actual widths. Example:^inxi^-y^130" ],
+       ['1', '-z', '--filter', "Adds security filters for IP/MAC addresses, serial numbers, 
+       location (-w), user home directory name. Default on for IRC clients." ],
+       ['1', '-Z', '--filter-override', "Absolute override for output filters. Useful for 
+       debugging networking issues in IRC, for example." ],
+       [0, '', '', "$line" ],
+       [0, '', '', "Additional Options:" ],
+       ['1', '-h', '--help', "This help menu." ],
+       ['1', '', '--recommends', "Checks $self_name application dependencies + recommends, 
+       and directories, then shows what package(s) you need to install to add support 
+       for that feature." ]
+       );
+       push @data, @rows;
+       if ( $b_update ){
+               @rows = (
+               ['1', '-U', '--update', "Auto-update $self_name. Will also install/update man 
+               page. Note: if you installed as root, you must be root to update, otherwise 
+               user is fine. Man page installs require root. No arguments downloads from 
+               main $self_name git repo." ],
+               ['1', '', '', "Use alternate sources for updating $self_name" ],
+               ['2', '1', '', "Get the git branch one version." ],
+               ['2', '2', '', "Get the git branch two version." ],
+               ['3', '3', '', "Get the dev server (smxi.org) version." ],
+               ['2', '<http>', '', "Get a version of $self_name from your own server. 
+               Use the full download path, e.g.^$self_name^-U^https://myserver.com/inxi" ]
+               );
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '-V', '--version', "Prints $self_name version info then exits." ],
+       ['0', '', '', "$line" ],
+       ['0', '', '', "Advanced Options:" ],
+       ['1', '', '--alt', "Trigger for various advanced options:" ],
+       ['2', '40', '', "Bypass Perl as a downloader option." ],
+       ['2', '41', '', "Bypass Curl as a downloader option." ],
+       ['2', '42', '', "Bypass Fetch as a downloader option." ],
+       ['2', '43', '', "Bypass Wget as a downloader option." ],
+       ['2', '44', '', "Bypass Curl, Fetch, and Wget as downloader options. Forces 
+       Perl if HTTP::Tiny present." ],
+       ['1', '', '--display', "[:[0-9]] Try to get display data out of X (default: display 0)." ],
+       ['1', '', '--dmidecode', "Force use of dmidecode data instead of /sys where relevant 
+       (e.g. -M, -B)." ],
+       ['1', '', '--downloader', "Force $self_name to use [curl|fetch|perl|wget] for downloads." ],
+       ['1', '', '--host', "Turn on hostname for -S." ],
+       ['1', '', '--indent-min', "Set point where $self_name autowraps line starters." ],
+       ['1', '', '--limit', "[-1; 1-x] Set max output limit of IP addresses for -i 
+       (default 10; -1 removes limit)." ],
+       );
+       push @data, @rows;
+       if ( $b_update ){
+               @rows = (
+               ['1', '', '--man', "Install correct man version for dev branch (-U 3) or pinxi using -U." ],
+               );
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '', '--no-host', "Turn off hostname for -S. Useful if showing output from servers etc." ],
+       );
+       push @data, @rows;
+       if ( $b_update ){
+               @rows = (
+               ['1', '', '--no-man', "Disable man install for all -U update actions." ],
+               );
+               push @data, @rows;
+       }
+       @rows = (
+       ['1', '', '--no-ssl', "Skip SSL certificate checks for all downloader actions 
+       (Wget/Fetch/Curl only)." ],
+       ['1', '', '--output', "[json|screen|xml] Change data output type. Requires --output-file 
+       if not screen." ],
+       ['1', '', '--output-file', "[Full filepath|print] Output file to be used for --output." ],
+       ['1', '', '--proc', "Force debugger parsing of /proc as sudo/root." ],
+       ['1', '', '--sleep', "[0-x.x] Change CPU sleep time, in seconds, for -C 
+       (default:^$cpu_sleep). Allows system to catch up and show a more accurate CPU 
+       use. Example:^$self_name^-Cxxx^--sleep^0.15" ],
+       ['1', '', '--wm', "Force wm: to use wmctrl as data source. Default uses ps." ],
+       ['0', '', '', $line ],
+       ['0', '', '', "Debugging Options:" ],
+       ['1', '', '--debug', "Triggers debugging modes." ],
+       ['2', '1-3', '', "On screen debugger output." ],
+       ['2', '10', '', "Basic logging." ],
+       ['2', '11', '', "Full file/system info logging." ],
+       ['1', '', ,'', "The following create a tar.gz file of system data, plus $self_name 
+       output. To automatically upload debugger data tar.gz file 
+       to ftp.techpatterns.com: $self_name^--debug^21" ],
+       ['2', '20', '', "Full system data collection: /sys; xorg conf and log data, xrandr, 
+       xprop, xdpyinfo, glxinfo etc.; data from dev, disks,  
+       ${partition_string}s, etc." ],
+       ['2', '21', '', "Upload debugger dataset to $self_name debugger server 
+       automatically, removes debugger data directory, leaves tar.gz debugger file." ],
+       ['2', '22', '', "Upload debugger dataset to $self_name debugger server 
+       automatically, removes debugger data directory and debugger tar.gz file." ],
+       ['1', '', '--ftp', "Use with --debugger 21 to trigger an alternate FTP server for upload. 
+       Format:^[ftp.xx.xx/yy]. Must include a remote directory to upload to. 
+       Example:^$self_name^--debug^21^--ftp^ftp.myserver.com/incoming" ],
+       ['0', '', '', "$line" ],
+       );
+       push @data, @rows;
+       print_basic(@data); 
+       exit 1;
+}
+
+sub show_version {
+       require Cwd;
+       import Cwd;
+       # if not in PATH could be either . or directory name, no slash starting
+       my $working_path=$self_path;
+       my (@data, @row, @rows, $link, $self_string);
+       if ( $working_path eq '.' ){
+               $working_path = getcwd();
+       }
+       elsif ( $working_path !~ /^\// ){
+               $working_path = getcwd() . "/$working_path";
+       }
+       # handle if it's a symbolic link, rare, but can happen with directories 
+       # in irc clients which would only matter if user starts inxi with -! 30 override 
+       # in irc client
+       if ( -l "$working_path/$self_name" ){
+               $link="$working_path/$self_name";
+               $working_path = readlink "$working_path/$self_name";
+               $working_path =~ s/[^\/]+$//;
+       }
+       # strange output /./ ending, but just trim it off, I don't know how it happens
+       $working_path =~ s%/\./%/%;
+       @row = ([ 0, '', '', "$self_name $self_version-$self_patch ($self_date)"],);
+       push @data, @row;
+       if ( ! $b_irc ){
+               @row = ([ 0, '', '', ""],);
+               push @data, @row;
+               my $year = (split/-/, $self_date)[0];
+               @row = [ 0, '', '', "Program Location: $working_path" ];
+               push @data, @row;
+               if ( $link ){
+                       @row = [ 0, '', '', "Started via symbolic link: $link" ];
+                       push @data, @row;
+               }
+               @rows = (
+               [ 0, '', '', "Website:^https://github.com/smxi/inxi^or^https://smxi.org/" ],
+               [ 0, '', '', "IRC:^irc.oftc.net channel:^#smxi" ],
+               [ 0, '', '', "Forums:^https://techpatterns.com/forums/forum-33.html" ],
+               [ 0, '', '', " " ],
+               [ 0, '', '', "$self_name - the universal, portable, system information tool 
+               for console and irc." ],
+               [ 0, '', '', "Using Perl version: $]"],
+               [ 0, '', '', " " ],
+               [ 0, '', '', "This program started life as a fork of Infobash 3.02: 
+               Copyright^(C)^2005-2007^Michiel^de^Boer^aka^locsmif." ],
+               [ 0, '', '', "Subsequent changes and modifications (after Infobash 3.02): 
+               Copyright^(C)^2008-$year^Harald^Hope^aka^h2. 
+               CPU/Konversation^fixes:^Scott^Rogers^aka^trash80.
+               USB^audio^fixes:^Steven^Barrett^aka^damentz." ],
+               [ 0, '', '', '' ],
+               [ 0, '', '', "This program is free software; you can redistribute it and/or modify 
+               it under the terms of the GNU General Public License as published by the Free Software 
+               Foundation; either version 3 of the License, or (at your option) any later version. 
+               (https://www.gnu.org/licenses/gpl.html)" ]
+               );
+               push @data, @rows;
+       }
+       print_basic(@data); 
+       exit 1;
+}
+
+########################################################################
+#### STARTUP DATA
+########################################################################
+
+# StartClient
+{
+package StartClient;
+
+# use warnings;
+# use strict;
+
+my $ppid = '';
+my $pppid = '';
+
+# NOTE: there's no reason to crete an object, we can just access
+# the features statically. 
+# args: none
+# sub new {
+#      my $class = shift;
+#      my $self = {};
+#      # print "$f\n";
+#      # print "$type\n";
+#      return bless $self, $class;
+# }
+
+sub get_client_data {
+       eval $start if $b_log;
+       $ppid = getppid();
+       main::set_ps_aux() if ! @ps_aux;
+       if (!$b_irc){
+               main::get_shell_data($ppid);
+       }
+       else {
+               $show{'filter'} = 1; 
+               get_client_name();
+               if ($client{'konvi'} == 1 || $client{'konvi'} == 3){
+                       set_konvi_data();
+               }
+       }
+       eval $end if $b_log;
+}
+
+sub get_client_name {
+       eval $start if $b_log;
+       my $client_name = '';
+       
+       # print "$ppid\n";
+       if ($ppid && -e "/proc/$ppid/exe" ){
+               $client_name = lc(readlink "/proc/$ppid/exe");
+               $client_name =~ s/^.*\///;
+               if ($client_name =~ /^bash|dash|sh|python.*|perl.*$/){
+                       $pppid = (main::grabber("ps -p $ppid -o ppid"))[1];
+                       #my @temp = (main::grabber("ps -p $ppid -o ppid 2>/dev/null"))[1];
+                       $pppid =~ s/^\s+|\s+$//g;
+                       $client_name =~ s/[0-9\.]+$//; # clean things like python2.7
+                       if ($pppid && -f "/proc/$pppid/exe" ){
+                               $client_name = lc(readlink "/proc/$pppid/exe");
+                               $client_name =~ s/^.*\///;
+                               $client{'native'} = 0;
+                       }
+               }
+               $client{'name'} = $client_name;
+               get_client_version();
+               # print "c:$client_name p:$pppid\n";
+       }
+       else {
+               if (! check_modern_konvi() ){
+                       $ppid = getppid();
+                       $client_name = (main::grabber("ps -p $ppid"))[1];
+                       if ($client_name){
+                               my @data = split /\s+/, $client_name if $client_name;
+                               if ($bsd_type){
+                                       $client_name = lc($data[5]);
+                               }
+                               # gnu/linux uses last value
+                               else {
+                                       $client_name = lc($data[-1]);
+                               }
+                               $client_name =~ s/.*\|-(|)//;
+                               $client_name =~ s/[0-9\.]+$//; # clean things like python2.7
+                               $client{'name'} = $client_name;
+                               $client{'native'} = 1;
+                               get_client_version();
+                       }
+                       else {
+                               $client{'name'} = "PPID='$ppid' - Empty?";
+                       }
+               }
+       }
+       if ($b_log){
+               my $string = "Client: $client{'name'} :: version: $client{'version'} :: konvi: $client{'konvi'} :: PPID: $ppid";
+               main::log_data('data', $string);
+       }
+       eval $end if $b_log;
+}
+sub get_client_version {
+       eval $start if $b_log;
+       @app = main::program_values($client{'name'});
+       my (@data,@working,$string);
+       if (@app){
+               $string = ($client{'name'} =~ /^gribble|limnoria|supybot$/) ? 'supybot' : $client{'name'};
+               $client{'version'} = main::program_version($string,$app[0],$app[1],$app[2],$app[4],$app[5],$app[6]);
+               $client{'name-print'} = $app[3];
+               $client{'console-irc'} = $app[4];
+       }
+       if ($client{'name'} =~ /^bash|dash|sh$/ ){
+               $client{'name-print'} = 'shell wrapper';
+               $client{'console-irc'} = 1;
+       }
+       elsif ($client{'name'} eq 'bitchx') {
+               @data = main::grabber("$client{'name'} -v");
+               $string = awk(\@data,'Version');
+               if ($string){
+                       $string =~ s/[()]|bitchx-//g; 
+                       @data = split /\s+/, $string;
+                       $_=lc for @data;
+                       $client{'version'} = ($data[1] eq 'version') ? $data[2] : $data[1];
+               }
+       }
+       # 'hexchat' => ['',0,'','HexChat',0,0], # special
+       # the hexchat author decided to make --version/-v return a gtk dialogue box, lol...
+       # so we need to read the actual config file for hexchat. Note that older hexchats
+       # used xchat config file, so test first for default, then legacy. Because it's possible
+       # for this file to be user edited, doing some extra checks here.
+       elsif ($client{'name'} eq 'hexchat') {
+               if ( -f '~/.config/hexchat/hexchat.conf' ){
+                       @data = main::reader('~/.config/hexchat/hexchat.conf','strip');
+               }
+               elsif ( -f '~/.config/hexchat/xchat.conf' ){
+                       @data = main::reader('~/.config/hexchat/xchat.conf','strip');
+               }
+               $client{'version'} = main::awk(\@data,'version',2,'\s*=\s*');
+               $client{'name-print'} = 'HexChat';
+       }
+       # note: see legacy inxi konvi logic if we need to restore any of the legacy code.
+       elsif ($client{'name'} eq 'konversation') {
+               $client{'konvi'} = ( ! $client{'native'} ) ? 2 : 1;
+       }
+       elsif ($client{'name'} =~ /quassel/) {
+               @data = main::grabber("$client{'name'} -v 2>/dev/null");
+               foreach (@data){
+                       if ($_ =~ /^Quassel IRC:/){
+                               $client{'version'} = (split /\s+/, $_ )[2];
+                               last;
+                       }
+                       elsif ($_ =~ /quassel\s[v]?[0-9]/){
+                               $client{'version'} = (split /\s+/, $_ )[1];
+                               last;
+                       }
+               }
+               $client{'version'} ||= '(pre v0.4.1)?'; 
+       }
+       # then do some perl type searches, do this last since it's a wildcard search
+       elsif ($client{'name'} =~ /^perl.*|ksirc|dsirc$/ ) {
+               my @cmdline = main::get_cmdline();
+               # Dynamic runpath detection is too complex with KSirc, because KSirc is started from
+               # kdeinit. /proc/<pid of the grandparent of this process>/exe is a link to /usr/bin/kdeinit
+               # with one parameter which contains parameters separated by spaces(??), first param being KSirc.
+               # Then, KSirc runs dsirc as the perl irc script and wraps around it. When /exec is executed,
+               # dsirc is the program that runs inxi, therefore that is the parent process that we see.
+               # You can imagine how hosed I am if I try to make inxi find out dynamically with which path
+               # KSirc was run by browsing up the process tree in /proc. That alone is straightjacket material.
+               # (KSirc sucks anyway ;)
+               foreach (@cmdline){
+                       if ( $_ =~ /dsirc/ ){
+                               $client{'version'} = main::program_version('ksirc','KSirc:',2,'-v',0,0);
+                               $client{'name'} = 'ksirc';
+                               $client{'name-print'} = 'KSirc';
+                       }
+               }
+               $client{'console-irc'} = 1;
+               perl_python_client();
+       }
+       elsif ($client{'name'} =~ /python/) {
+               perl_python_client();
+       }
+       if (!$client{'name-print'}) {
+               $client{'name-print'} = 'Unknown Client: ' . $client{'name'};
+       }
+       eval $end if $b_log;
+}
+sub get_cmdline {
+       eval $start if $b_log;
+       my @cmdline;
+       my $i = 0;
+       $ppid = getppid();
+       if (! -e "/proc/$ppid/cmdline" ){
+               return 1;
+       }
+       local $\ = '';
+       open( my $fh, '<', "/proc/$ppid/cmdline" ) or 
+         print_line("Open /proc/$ppid/cmdline failed: $!");
+       my @rows = <$fh>;
+       close $fh;
+       
+       foreach (@rows){
+               push @cmdline, $_;
+               $i++;
+               last if $i > 31;
+       }
+       if ( $i == 0 ){
+               $cmdline[0] = $rows[0];
+               $i = ($cmdline[0]) ? 1 : 0;
+       }
+       main::log_data('string',"cmdline: @cmdline count: $i") if $b_log;
+       eval $end if $b_log;
+       return @cmdline;
+}
+sub perl_python_client {
+       eval $start if $b_log;
+       return 1 if $client{'version'};
+       # this is a hack to try to show konversation if inxi is running but started via /cmd
+       # OR via program shortcuts, both cases in fact now
+       # main::print_line("konvi: " . scalar grep { $_ =~ /konversation/ } @ps_cmd);
+       if ( $b_display && main::check_program('konversation') && ( grep { $_ =~ /konversation/ } @ps_cmd )){
+               @app = main::program_values('konversation');
+               $client{'version'} = main::program_version('konversation',$app[0],$app[1],$app[2],$app[5],$app[6]);
+               $client{'name'} = 'konversation';
+               $client{'name-print'} = $app[3];
+               $client{'console-irc'} = $app[4];
+       }
+       ## NOTE: supybot only appears in ps aux using 'SHELL' command; the 'CALL' command
+       ## gives the user system irc priority, and you don't see supybot listed, so use SHELL
+       elsif ( !$b_display && 
+        (main::check_program('supybot') || main::check_program('gribble') || main::check_program('limnoria')) &&
+        ( grep { $_ =~ /supybot/ } @ps_cmd ) ){
+               @app = main::program_values('supybot');
+               $client{'version'} = main::program_version('supybot',$app[0],$app[1],$app[2],$app[5],$app[6]);
+               if ($client{'version'}){
+                       if ( grep { $_ =~ /gribble/ } @ps_cmd ){
+                               $client{'name'} = 'gribble';
+                               $client{'name-print'} = 'Gribble';
+                       }
+                       if ( grep { $_ =~ /limnoria/ } @ps_cmd){
+                               $client{'name'} = 'limnoria';
+                               $client{'name-print'} = 'Limnoria';
+                       }
+                       else {
+                               $client{'name'} = 'supybot';
+                               $client{'name-print'} = 'Supybot';
+                       }
+               }
+               else {
+                       $client{'name'} = 'supybot';
+                       $client{'name-print'} = 'Supybot';
+               }
+               $client{'console-irc'} = 1;
+       }
+       else {
+               $client{'name-print'} = "Unknown $client{'name'} client";
+       }
+       if ($b_log){
+               my $string = "namep: $client{'name-print'} name: $client{'name'} version: $client{'version'}";
+               main::log_data('data',$string);
+       }
+       eval $end if $b_log;
+}
+## try to infer the use of Konversation >= 1.2, which shows $PPID improperly
+## no known method of finding Konvi >= 1.2 as parent process, so we look to see if it is running,
+## and all other irc clients are not running. As of 2014-03-25 this isn't used in my cases
+sub check_modern_konvi {
+       eval $start if $b_log;
+       
+       return 0 if ! $client{'qdbus'};
+       my $b_modern_konvi = 0;
+       my $konvi_version = '';
+       my $konvi = '';
+       my $pid = '';
+       my (@temp);
+       # main::log_data('data',"name: $client{'name'} :: qdb: $client{'qdbus'} :: version: $client{'version'} :: konvi: $client{'konvi'} :: PPID: $ppid") if $b_log;
+       # sabayon uses /usr/share/apps/konversation as path
+       if ( -d '/usr/share/kde4/apps/konversation' || -d '/usr/share/apps/konversation' ){
+               $pid = main::awk(\@ps_aux,'konversation',2,'\s+');
+               main::log_data('data',"pid: $pid") if $b_log;
+               $konvi = readlink ("/proc/$pid/exe");
+               $konvi =~ s/^.*\///; # basename
+               @app = main::program_values('konversation');
+               if ($konvi){
+                       @app = main::program_values('konversation');
+                       $konvi_version = main::program_version($konvi,$app[0],$app[1],$app[2],$app[5],$app[6]);
+                       @temp = split /\./, $konvi_version;
+                       $client{'console-irc'} = $app[4];
+                       $client{'konvi'} = 3;
+                       $client{'name'} = 'konversation';
+                       $client{'name-print'} = $app[3];
+                       $client{'version'} = $konvi_version;
+                       # note: we need to change this back to a single dot number, like 1.3, not 1.3.2
+                       $konvi_version = $temp[0] . "." . $temp[1];
+                       if ($konvi_version > 1.1){
+                               $b_modern_konvi = 1;
+                       }
+               }
+       }
+       main::log_data('data',"name: $client{'name'} name print: $client{'name-print'} 
+       qdb: $client{'qdbus'} version: $konvi_version konvi: $konvi PID: $pid") if $b_log;
+       main::log_data('data',"b_is_qt4: $b_modern_konvi") if $b_log;
+       ## for testing this module
+#      my $ppid = getppid();
+#      system('qdbus org.kde.konversation', '/irc', 'say', $client{'dserver'}, $client{'dtarget'}, 
+#      "getpid_dir: $konvi_qt4 verNum: $konvi_version pid: $pid ppid: $ppid" );
+       eval $end if $b_log;
+       return $b_modern_konvi;
+}
+
+sub set_konvi_data {
+       eval $start if $b_log;
+       my $config_tool = '';
+       # https://userbase.kde.org/Konversation/Scripts/Scripting_guide
+       if ( $client{'konvi'} == 3 ){
+               $client{'dserver'} = shift @ARGV;
+               $client{'dtarget'} = shift @ARGV;
+               $client{'dobject'} = 'default';
+       }
+       elsif ( $client{'konvi'} == 1 ){
+               $client{'dport'} = shift @ARGV;
+               $client{'dserver'} = shift @ARGV;
+               $client{'dtarget'} = shift @ARGV;
+               $client{'dobject'} = 'Konversation';
+       }
+       # for some reason this logic hiccups on multiple spaces between args
+       @ARGV = grep { $_ ne '' } @ARGV;
+       # there's no current kde 5 konvi config tool that we're aware of. Correct if changes.
+       if ( main::check_program('kde4-config') ){
+               $config_tool = 'kde4-config';
+       }
+       elsif ( main::check_program('kde5-config') ){
+               $config_tool = 'kde5-config';
+       }
+       elsif ( main::check_program('kde-config') ){
+               $config_tool = 'kde-config';
+       }
+       # The section below is on request of Argonel from the Konversation developer team:
+       # it sources config files like $HOME/.kde/share/apps/konversation/scripts/inxi.conf
+       if ($config_tool){
+               my @data = main::grabber("$config_tool --path data 2>/dev/null",':');
+               main::get_configs(@data);
+       }
+       eval $end if $b_log;
+}
+}
+
+########################################################################
+#### OUTPUT
+########################################################################
+
+#### -------------------------------------------------------------------
+#### FILTERS AND TOOLS
+#### -------------------------------------------------------------------
+
+sub apply_filter {
+       my ($string) = @_;
+       if ($string){
+               $string = ( $show{'filter'} ) ? $filter_string : $string;
+       }
+       else {
+               $string = 'N/A';
+       }
+       return $string;
+}
+sub arm_cleaner {
+       my ($item) = @_;
+       $item =~ s/(\(?Device Tree\)?)//gi;
+       $item =~ s/\s\s+/ /g;
+       $item =~ s/^\s+|\s+$//g;
+       return $item;
+}
+
+sub clean_characters {
+       my ($data) = @_;
+       # newline, pipe, brackets, + sign, with space, then clear doubled
+       # spaces and then strip out trailing/leading spaces.
+       # etc/issue often has junk stuff like (\l)  \n \l
+       return if ! $data;
+       $data =~ s/[:\47]|\\[a-z]|\n|,|\"|\*|\||\+|\[\s\]|n\/a|\s\s+/ /g; 
+       $data =~ s/\(\)//;
+       $data =~ s/^\s+|\s+$//g;
+       return $data;
+}
+sub cleaner {
+       my ($item) = @_;
+       return $item if !$item;# handle cases where it was 0 or ''
+       # note: |nee trips engineering, but I don't know why nee was filtered
+       $item =~ s/chipset|company|components|computing|computer|corporation|communications|electronics|electrical|electric|gmbh|group|incorporation|industrial|international|\bnee\b|revision|semiconductor|software|technologies|technology|ltd\.|<ltd>|\bltd\b|inc\.|<inc>|\binc\b|intl\.|co\.|<co>|corp\.|<corp>|\(tm\)|\(r\)|®|\(rev ..\)|\'|\"|\sinc\s*$|\?//gi;
+       $item =~ s/,|\*/ /g;
+       $item =~ s/\s\s+/ /g;
+       $item =~ s/^\s+|\s+$//g;
+       return $item;
+}
+
+sub disk_cleaner {
+       my ($item) = @_;
+       return $item if !$item;
+       # <?unknown>?|
+       $item =~ s/vendor.*|product.*|O\.?E\.?M\.?//gi;
+       $item =~ s/\s\s+/ /g;
+       $item =~ s/^\s+|\s+$//g;
+       return $item;
+}
+
+sub dmi_cleaner {
+       my ($string) = @_;
+       my $cleaner = '^Base Board .*|^Chassis .*|empty|Undefined.*|.*O\.E\.M\..*|.*OEM.*|^Not .*';
+       $cleaner .= '|^System .*|.*unknow.*|.*N\/A.*|none|^To be filled.*|^0x[0]+$';
+       $cleaner .= '|\[Empty\]|<Bad Index>|Default string|^\.\.$|Manufacturer.*';
+       $cleaner .= '|AssetTagNum|Manufacturer| Or Motherboard|PartNum.*|SerNum';
+       $string =~ s/$cleaner//i;
+       $string =~ s/^\s+|\bbios\b|\bacpi\b|\s+$//gi;
+       $string =~ s/http:\/\/www.abit.com.tw\//Abit/i;
+       $string =~ s/\s\s+/ /g;
+       $string =~ s/^\s+|\s+$//g;
+       $string = remove_duplicates($string) if $string;
+       return $string;
+}
+
+sub remove_duplicates {
+       my ($string) = @_;
+       return if ! $string;
+       my $holder = '';
+       my (@temp);
+       my @data = split /\s+/, $string;
+       foreach (@data){
+               if ($holder ne $_){
+                       push @temp, $_;
+               }
+               $holder = $_;
+       }
+       $string = join ' ', @temp;
+       return $string;
+}
+
+# args: $1 - size in KB, return KB, MB, GB, TB, PB, EB
+sub get_size {
+       my ($size,$b_int) = @_;
+       my (@data);
+       return ('','') if ! defined $size;
+       if ($size !~ /^[0-9\.]+$/){
+               $data[0] = $size;
+               $data[1] = '';
+       }
+       elsif ($size > 1024**5){
+               $data[0] = sprintf("%.2f",$size/1024**5);
+               $data[1] = 'EiB';
+       }
+       elsif ($size > 1024**4){
+               $data[0] = sprintf("%.2f",$size/1024**4);
+               $data[1] = 'PiB';
+       }
+       elsif ($size > 1024**3){
+               $data[0] = sprintf("%.2f",$size/1024**3);
+               $data[1] = 'TiB';
+       }
+       elsif ($size > 1024**2){
+               $data[0] = sprintf("%.2f",$size/1024**2);
+               $data[1] = 'GiB';
+       }
+       elsif ($size > 1024){
+               $data[0] = sprintf("%.1f",$size/1024);
+               $data[1] = 'MiB';
+       }
+       else {
+               $data[0] = sprintf("%.0f",$size);
+               $data[1] = 'KiB';
+       }
+       $data[0] = int($data[0]) if $b_int && $data[0];
+       return @data;
+}
+
+# not used, but keeping logic for now
+sub increment_starters {
+       my ($key,$indexes) = @_;
+       my $result = $key;
+       if (defined $$indexes{$key} ){
+               $$indexes{$key}++;
+               $result = "$key-$$indexes{$key}";
+       }
+       return $result;
+}
+
+sub memory_data_full {
+       eval $start if $b_log;
+       my ($source) = @_;
+       my $num = 0;
+       my ($memory,@rows);
+       my ($gpu_ram,$percent,$total,$used) = (0,'','','');
+       if (!$show{'info'}){
+               $memory = get_memory_data('splits');
+               if ($memory){
+                       my @temp = split /:/, $memory;
+                       my @temp2 = get_size($temp[0]);
+                       $gpu_ram = $temp[3] if $temp[3];
+                       $total = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0];
+                       @temp2 = get_size($temp[1]);
+                       $used = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0];
+                       $used .= " ($temp[2]%)" if $temp[2];
+                       if ($gpu_ram){
+                               @temp2 = get_size($gpu_ram);
+                               $gpu_ram = $temp2[0] . ' ' . $temp2[1] if $temp2[1];
+                       }
+               }
+               my $key = ($source eq 'process') ? 'System RAM': 'RAM';
+               $rows[0]{main::key($num++,$key)} = '';
+               $rows[0]{main::key($num++,'total')} = $total;
+               $rows[0]{main::key($num++,'used')} = $used;
+               $rows[0]{main::key($num++,'gpu')} = $gpu_ram if $gpu_ram;
+       }
+       $b_mem = 1;
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub pci_cleaner {
+       my ($string,$type) = @_;
+       #print "st1 $type:$string\n";
+       my $filter = 'compatible\scontroller|\b(device|controller|connection|multimedia)\b|\([^)]+\)';
+       # \[[^\]]+\]$| not trimming off ending [...] initial type filters removes end
+       $filter = '\[[^\]]+\]$|' . $filter if $type eq 'pci';
+       $string =~ s/$filter//ig;
+       $string =~ s/\s\s+/ /g;
+       $string =~ s/^\s+|\s+$//g;
+       #print "st2 $type:$string\n";
+       $string = remove_duplicates($string) if $string;
+       return $string;
+}
+sub pci_cleaner_subsystem {
+       my ($string) = @_;
+       # we only need filters for features that might use vendor, -AGN
+       my $filter = 'adapter|(hd\s)?audio|definition|desktop|ethernet|gigabit|graphics|';
+       $filter .= 'hdmi(\/[\S]+)?|high|integrated|motherboard|network|onboard|';
+       $filter .= 'raid|pci\s?express';
+       $string =~ s/\b($filter)\b//gi;
+       $string =~ s/\s\s+/ /g;
+       $string =~ s/^\s+|\s+$//g;
+       return $string;
+}
+
+sub pci_long_filter {
+       my ($string) = @_;
+       if ($string =~ /\[AMD(\/ATI)?\]/){
+               $string =~ s/Advanced\sMicro\sDevices\s\[AMD(\/ATI)?\]/AMD/;
+       }
+       return $string;
+}
+
+sub row_defaults {
+       my ($type,$id) = @_;
+       $id ||= '';
+       my %unfound = (
+       'arm-cpu-f' => 'Use -f option to see features',
+       'arm-pci' => "No ARM data found for this feature.",
+       'battery-data' => "No system battery data found. Is one present?",
+       'battery-data-sys' => "No /sys data found. Old system?",
+       'cpu-model-null' => "Model N/A",
+       'cpu-speeds' => "No speed data found for $id cores.",
+       'darwin-feature' => "Feature not supported iu Darwin/OSX.",
+       'disk-data-bsd' => "No disk data found for this BSD system.",
+       'disk-data' => "No Disk data was found.",
+       'disk-size-0' => "Total N/A",
+       'display-console' => 'No advanced graphics data found on this system in console.',
+       'display-null' => 'No advanced graphics data found on this system.',
+       'display-root' => 'Advanced graphics data unavailable in console for root.',
+       'display-root-x' => 'Advanced graphics data unavailable for root. Old System?',
+       'display-server' => "No display server data found. Headless machine?",
+       'glxinfo-missing' => "Unable to show advanced data. Required tool glxinfo missing.",
+       'display-try' => 'Advanced graphics data unavailable in console. Try -G --display',
+       'dev' => 'Feature under development',
+       'dmesg-boot-permissions' => 'dmesg.boot permissions',
+       'dmesg-boot-missing' => 'dmesg.boot not found',
+       'IP' => "No $id data found. Connected to the web? SSL issues?",
+       'machine-data' => "No machine data: try newer kernel.",
+       'machine-data-bsd' => "No machine data: Is dmidecode installed? Try -M --dmidecode.",
+       'machine-data-dmidecode' => "No machine data: try newer kernel. Is dmidecode installed? Try -M --dmidecode.",
+       'machine-data-force-dmidecode' => "No machine data: try newer kernel. Is dmidecode installed? Try -M --dmidecode.",
+       'mips-pci' => "No MIPS data found for this feature.",
+       'optical-data' => "No Optical or Floppy data was found.",
+       'optical-data-bsd' => "No floppy or optical data found for this BSD system.",
+       'output-limit' => "Output throttled. IPs: $id; Limit: $limit; Override: --limit [1-x;-1 all]",
+       'partition-data' => "No Partition data was found.",
+       'pci-advanced-data' => 'bus/chip ids unavailable',
+       'pci-card-data' => "No PCI card data found.",
+       'pci-slot-data' => "No PCI slot data found. SBC?",
+       'raid-data' => "No RAID data was found.",
+       'ram-data' => "No RAM data was found. SBC?",
+       'root-required' => "<root required>",
+       'sensors-data-ipmi' => "No ipmi sensors data was found.",
+       'sensors-data-linux' => "No sensors data was found. Is sensors configured?",
+       'sensors-ipmi-root' => "Unable to run ipmi sensors. Are you root?",
+       'unmounted-data' => "No unmounted partitions found.",
+       'unmounted-data-bsd' => "No unmounted partition data found for this BSD system.",
+       'unmounted-file' => "No /proc/partitions file found.",
+       'usb-data' => "No USB data was found. Server?",
+       'unknown-desktop-version' => "ERR-101",
+       'unknown-dev' => "ERR-102",
+       'unknown-shell' => "ERR-100",
+       'weather-null' => "No $id found. Internet connection working?",
+       'xdpyinfo-missing' => '<xdpyinfo missing>',
+       );
+       return $unfound{$type};
+}
+
+# convert string passed to KB, based on GB/MB/TB id
+# NOTE: K 1024 KB 1000
+sub translate_size {
+       my ($working) = @_;
+       my $size = 0;
+       #print ":$working:\n";
+       return if ! defined $working;
+       my $math = ( $working =~ /B$/) ? 1000: 1024;
+       if ( $working =~ /^([0-9\.]+)M[B]?$/i){
+               $size = $1 * $math;
+       }
+       elsif ( $working =~ /^([0-9\.]+)G[B]?$/i){
+               $size = $1 * $math**2;
+       }
+       elsif ( $working =~ /^([0-9\.]+)T[B]?$/i){
+               $size = $1 * $math**3;
+       }
+       elsif ( $working =~ /^([0-9\.]+)P[B]?$/i){
+               $size = $1 * $math**4;
+       }
+       elsif ( $working =~ /^([0-9\.]+)E[B]?$/i){
+               $size = $1 * $math**5;
+       }
+       elsif ( $working =~ /^([0-9\.]+)K[B]?$/i){
+               $size = $1;
+       }
+       $size = int($size) if $size;
+       return $size;
+}
+
+#### -------------------------------------------------------------------
+#### GENERATE OUTPUT
+#### -------------------------------------------------------------------
+
+sub check_output_path {
+       my ($path) = @_;
+       my ($b_good,$dir,$file);
+       $dir = $path;
+       $dir =~ s/([^\/]+)$//;
+       $file = $1;
+       # print "file: $file : dir: $dir\n";
+       $b_good = 1 if (-d $dir && -w $dir && $dir =~ /^\// && $file);
+       return $b_good;
+}
+
+sub output_handler {
+       my (%data) = @_;
+       # print Dumper \%data;
+       if ($output_type eq 'screen'){
+               print_data(%data);
+       }
+       elsif ($output_type eq 'json'){
+               generate_json(%data);
+       }
+       elsif ($output_type eq 'xml'){
+               generate_xml(%data);
+       }
+}
+
+# NOTE: file has already been set and directory verified
+sub generate_json {
+       eval $start if $b_log;
+       my (%data) = @_;
+       my ($json);
+       my $b_debug = 1;
+       my ($b_cpanel,$b_valid);
+       error_handler('not-in-irc', 'help') if $b_irc;
+       #print Dumper \%data if $b_debug;
+       if (check_module('Cpanel::JSON::XS')){
+               import Cpanel::JSON::XS;
+               $json = Cpanel::JSON::XS::encode_json(\%data);
+       }
+       elsif (check_module('JSON::XS')){
+               import JSON::XS;
+               $json = JSON::XS::encode_json(\%data);
+       }
+       else {
+               error_handler('required-module', 'json', 'Cpanel::JSON::XS OR JSON::XS');
+       }
+       if ($json){
+               #$json =~ s/"[0-9]+#/"/g;
+               if ($output_file eq 'print'){
+                       #$json =~ s/\}/}\n/g;
+                       print "$json";
+               }
+               else {
+                       print_line("Writing JSON data to: $output_file\n");
+                       open(my $fh, '>', $output_file) or error_handler('open',$output_file,"$!");
+                       print $fh "$json";
+                       close $fh;
+                       print_line("Data written successfully.\n");
+               }
+       }
+       eval $end if $b_log;
+}
+
+# NOTE: So far xml is substantially more difficult than json, so 
+# using a crude dumper rather than making a nice xml file, but at
+# least xml has some output now.
+sub generate_xml {
+       eval $start if $b_log;
+       my (%data) = @_;
+       my ($xml);
+       my $b_debug = 0;
+       error_handler('not-in-irc', 'help') if $b_irc;
+       #print Dumper \%data if $b_debug;
+       if (check_module('XML::Dumper')){
+               import XML::Dumper;
+               $xml = XML::Dumper::pl2xml(\%data);
+               #$xml =~ s/"[0-9]+#/"/g;
+               if ($output_file eq 'print'){
+                       print "$xml";
+               }
+               else {
+                       print_line("Writing XML data to: $output_file\n");
+                       open(my $fh, '>', $output_file) or error_handler('open',$output_file,"$!");
+                       print $fh "$xml";
+                       close $fh;
+                       print_line("Data written successfully.\n");
+               }
+       }
+       else {
+               error_handler('required-module', 'xml', 'XML::Dumper');
+       }
+       eval $end if $b_log;
+}
+
+sub key {
+       return sprintf("%03d#%s", $_[0],$_[1]);
+}
+
+sub print_basic {
+       my (@data) = @_;
+       my $indent = 18;
+       my $indent_static = 18;
+       my $indent1_static = 5;
+       my $indent2_static = 8;
+       my $indent1 = 5;
+       my $indent2 = 8;
+       my $length =  @data;
+       my ($start,$aref,$i,$j,$line);
+       
+       if ( $size{'max'} > 110 ){
+               $indent_static = 22;
+       }
+       elsif ($size{'max'} < 90 ){
+               $indent_static = 15;
+       }
+       # print $length . "\n";
+       for my $i (0 .. $#data){
+               $aref = $data[$i];
+               #print "0: $data[$i][0]\n";
+               if ($data[$i][0] == 0 ){
+                       $indent = 0;
+                       $indent1 = 0;
+                       $indent2 = 0;
+               }
+               elsif ($data[$i][0] == 1 ){
+                       $indent = $indent_static;
+                       $indent1 = $indent1_static;
+                       $indent2= $indent2_static;
+               }
+               elsif ($data[$i][0] == 2 ){
+                       $indent = ( $indent_static + 7 );
+                       $indent1 = ( $indent_static + 5 );
+                       $indent2 = 0;
+               }
+               $data[$i][3] =~ s/\n/ /g;
+               $data[$i][3] =~ s/\s+/ /g;
+               if ($data[$i][1] && $data[$i][2]){
+                       $data[$i][1] = $data[$i][1] . ', ';
+               }
+               $start = sprintf("%${indent1}s%-${indent2}s",$data[$i][1],$data[$i][2]);
+               if ($indent > 1 && ( length($start) > ( $indent - 1) ) ){
+                       $line = sprintf("%-${indent}s\n", "$start");
+                       print_line($line);
+                       $start = '';
+                       #print "1-print.\n";
+               }
+               if ( ( $indent + length($data[$i][3]) ) < $size{'max'} ){
+                       $data[$i][3] =~ s/\^/ /g;
+                       $line = sprintf("%-${indent}s%s\n", "$start", $data[$i][3]);
+                       print_line($line);
+                       #print "2-print.\n";
+               }
+               else {
+                       my $holder = '';
+                       my $sep = ' ';
+                       foreach my $word (split / /, $data[$i][3]){
+                               #print "$word\n";
+                               if ( ( $indent + length($holder) + length($word) ) < $size{'max'} ) {
+                                       $word =~ s/\^/ /g;
+                                       $holder .= $word . $sep;
+                                       #print "3-hold.\n";
+                               }
+                               #elsif ( ( $indent + length($holder) + length($word) ) >= $size{'max'}){
+                               else {
+                                       $line = sprintf("%-${indent}s%s\n", "$start", $holder);
+                                       print_line($line);
+                                       $start = '';
+                                       $word =~ s/\^/ /g;
+                                       $holder = $word . $sep;
+                                       #print "4-print-hold.\n";
+                               }
+                       }
+                       if ($holder !~ /^[ ]*$/){
+                               $line = sprintf("%-${indent}s%s\n", "$start", $holder);
+                               print_line($line);
+                               #print "5-print-last.\n";
+                       }
+               }
+       }
+}
+
+# this has to get a hash of hashes, at least for now.
+# because perl does not retain insertion order, I use a prefix for each
+# hash key to force sorts. 
+sub print_data {
+       my (%data) = @_;
+       my $array = 0;
+       my $array_holder = 1;
+       my $counter=0;
+       my $split_count = 0;
+       my $hash = 0;
+       my $holder = '';
+       my $id_holder = 0;
+       my $start = '';
+       my $start2 = '';
+       my $length = 0;
+       my $indent = $size{'indent'};
+       my (@temp,@working,@values,%ids,$holder2,%row);
+       my ($key,$line,$val2,$val3);
+       # $size{'max'} = 88;
+       # NOTE: indent < 11 would break the output badly in some cases
+       if ($size{'max'} < $size{'indent-min'} || $size{'indent'} < 11 ){
+               $indent = 2;
+       }
+       #foreach my $key1 (sort { (split/#/, $a)[0] <=> (split/#/, $b)[0] } keys %data) {
+       foreach my $key1 (sort { substr($a,0,3) <=> substr($b,0,3) } keys %data) {
+       #foreach my $key1 (sort { $a cmp $b } keys %data) {
+               $key = (split/#/, $key1)[1];
+               if ($key ne 'SHORT' ) {
+                       $start = sprintf("$colors{'c1'}%-${indent}s$colors{'cn'}","$key$sep{'s1'}");
+                       if ($indent < 10){
+                               $line = "$start\n";
+                               print_line($line);
+                               $start = '';
+                               $line = '';
+                       }
+               }
+               else {
+                       $indent = 0;
+               }
+               if (ref($data{$key1}) eq 'ARRAY'){
+                       # @working = @{$data{$key1}};
+                       %ids = (
+                       'Array' => 1,
+                       'array' => 1,
+                       'Battery' => 1,
+                       'Card' => 1,
+                       'Device' => 1,
+                       'Floppy' => 1,
+                       'Hardware' => 1, # hardware raid report
+                       'ID' => 1,
+                       'IF-ID' => 1,
+                       'Optical' => 1,
+                       'variant' => 1, # arm > 1 cpu type
+                       );
+                       $array_holder = 1;
+                       foreach my $val1 (@{$data{$key1}}){
+                               $length = $indent;
+                               if (ref($val1) eq 'HASH'){
+                                       #%row = %$val1;
+                                       $counter=0;
+                                       $split_count = 0;
+                                       $hash = scalar %$val1;
+                                       #foreach my $key2 (sort { (split/#/, $a)[0] <=> (split/#/, $b)[0] } keys %$val1){
+                                       foreach my $key2 (sort { substr($a,0,3) <=> substr($b,0,3) } keys %$val1){
+                                       #foreach my $key2 (sort { $a cmp $b } keys %$val1){
+                                               $key = (split/#/, $key2)[1];
+                                               # for ram with > 1 system array, we want to reset device count to 1 for each
+                                               # new array
+                                               if ($key eq 'Array' && $array_holder != $ids{$key} ){
+                                                       $array_holder = $ids{$key};
+                                                       $ids{'Device'} = 1 if ($ids{'Device'} > 1);
+                                               }
+                                               if ($key eq 'Device' && $ids{'array'} > 1 && $id_holder != $ids{$key} ){
+                                                       $id_holder = $ids{$key};
+                                                       $ids{'array'} = 1 if ($ids{'array'} > 1);
+                                               }
+                                               if ($counter == 0 && defined $ids{$key}){
+                                                       $key .= '-' . $ids{$key}++;
+                                               }
+                                               $val2 = $$val1{$key2};
+                                               # we have to handle cases where $val2 is 0
+                                               if ($val2 || $val2 eq '0'){
+                                                       $val2 .= " ";
+                                               }
+                                               # see: Use of implicit split to @_ is deprecated. Only get this warning
+                                               # in Perl 5.08 oddly enough.
+                                               @temp = split/\s+/, $val2;
+                                               $split_count = scalar @temp;
+                                               if ( ( length( "$key$sep{'s2'} $val2" ) + $length ) < $size{'max'} ) {
+                                                       $length += length("$key$sep{'s2'} $val2");
+                                                       $holder .= "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2";
+                                                       #print "one\n";
+                                               }
+                                               # handle case where the opening key/value pair is > max, and where 
+                                               # there are a lot of terms, like cpu flags, raid types supported. Raid
+                                               # can have the last row have a lot of devices, or many raid types
+                                               elsif ( ( length( "$key$sep{'s2'} $val2" ) + $indent ) > $size{'max'} && 
+                                                                  !defined $ids{$key} && $split_count > 2 ) {
+                                                       @values = split/\s+/, $val2;
+                                                       $val3 = shift @values;
+                                                       # $length += length("$key$sep{'s2'} $val3 ") + $indent;
+                                                       $start2 = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val3 ";
+                                                       $holder2 = '';
+                                                       $length += length("$key$sep{'s2'} $val3 ");
+                                                       # print scalar @values,"\n";
+                                                       foreach (@values){
+                                                               # my $l =  (length("$_ ") + $length);
+                                                               #print "$l\n";
+                                                               if ( (length("$_ ") + $length) < $size{'max'} ){
+                                                                       #print "a\n";
+                                                                       if ($start2){
+                                                                               $holder2 .= "$start2$_ ";
+                                                                               $start2 = '';
+                                                                               #$length += $length2;
+                                                                               #$length2 = 0;
+                                                                       }
+                                                                       else {
+                                                                               $holder2 .= "$_ ";
+                                                                       }
+                                                                       $length += length("$_ ");
+                                                               }
+                                                               else {
+                                                                       #print "three\n";
+                                                                       if ($start2){
+                                                                               $holder2 = "$start2$holder2";
+                                                                       }
+                                                                       else {
+                                                                               $holder2 = "$colors{'c2'}$holder2";
+                                                                       }
+                                                                       #print "xx:$holder";
+                                                                       $line = sprintf("%-${indent}s%s$colors{'cn'}\n","$start","$holder$holder2");
+                                                                       print_line($line);
+                                                                       $holder = '';
+                                                                       
+                                                                       $holder2 = "$_ ";
+                                                                       #print "h2: $holder2\n";
+                                                                       $length = length($holder2) + $indent;
+                                                                       $start2 = '';
+                                                                       $start = '';
+                                                                       #$length2 = 0;
+                                                               }
+                                                       }
+                                                       if ($holder2 !~ /^\s*$/){
+                                                               #print "four\n";
+                                                               $holder2 = "$colors{'c2'}$holder2";
+                                                               $line = sprintf("%-${indent}s%s$colors{'cn'}\n","$start","$holder$holder2");
+                                                               print_line($line);
+                                                               $holder = '';
+                                                               $holder2 = '';
+                                                               $length = $indent;
+                                                               $start2 = '';
+                                                               $start = '';
+                                                               #$length2 = 0;
+                                                       }
+                                               }
+                                               else {
+                                                       #print "H: $counter $hash\n";
+                                                       if ($holder){
+                                                               #print "five\n";
+                                                               $line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$holder");
+                                                               $holder = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2";
+                                                               $length = length("$key$sep{'s2'} $val2") + $indent;
+                                                               print_line($line);
+                                                               $start = '';
+                                                       }
+                                                       else {
+                                                               #print "six\n";
+                                                               $holder = "$colors{'c1'}$key$sep{'s2'}$colors{'c2'} $val2";
+                                                               #$line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$holder");
+                                                               $length = $indent;
+                                                               #$holder = '';
+                                                       }
+                                               }
+                                               $counter++;
+                                       }
+                                       if ($holder !~ /^\s*$/){
+                                               #print "seven\n";
+                                               $line = sprintf("%-${indent}s%s$colors{'cn'}\n",$start,"$start2$holder");
+                                               print_line($line);
+                                               $holder = '';
+                                               $length = 0;
+                                               $start = '';
+                                       }
+                               }
+                               # only for repos?
+                               elsif (ref($val1) eq 'ARRAY'){
+                                       #print "eight\n";
+                                       $array=0;
+                                       foreach my $item (@$val1){
+                                               $array++;
+                                               $line = "$colors{'c1'}$array$sep{'s2'} $colors{'c2'}$item$colors{'cn'}";
+                                               $line = sprintf("%-${indent}s%s\n","","$line");
+                                               print_line($line);
+                                       }
+                               }
+                               else {
+                                       
+                               }
+                       }
+               }
+       }
+}
+
+sub print_line {
+       my ($line) = @_;
+       if ($b_irc && $client{'test-konvi'}){
+               $client{'konvi'} = 3;
+               $client{'dobject'} = 'Konversation';
+       }
+       if ($client{'konvi'} == 1 && $client{'dcop'} ){
+               # konvi doesn't seem to like \n characters, it just prints them literally
+               $line =~ s/\n//g;
+               #qx('dcop "$client{'dport'}" "$client{'dobject'}" say "$client{'dserver'}" "$client{'dtarget'}" "$line 1");
+               system('dcop', $client{'dport'}, $client{'dobject'}, 'say', $client{'dserver'}, $client{'dtarget'}, "$line 1");
+       }
+       elsif ($client{'konvi'} == 3 && $client{'qdbus'} ){
+               # print $line;
+               $line =~ s/\n//g;
+               #qx(qdbus org.kde.konversation /irc say "$client{'dserver'}" "$client{'dtarget'}" "$line");
+               system('qdbus', 'org.kde.konversation', '/irc', 'say', $client{'dserver'}, $client{'dtarget'}, $line);
+       }
+       else {
+               print $line;
+       }
+}
+
+########################################################################
+#### DATA PROCESSORS
+########################################################################
+
+#### -------------------------------------------------------------------
+#### PRIMARY DATA GENERATORS
+#### -------------------------------------------------------------------
+# 0 type
+# 1 type_id
+# 2 bus_id
+# 3 sub_id
+# 4 device
+# 5 vendor_id
+# 6 chip_id
+# 7 rev
+# 8 port
+# 9 driver
+# 10 modules
+
+## AudioData 
+{
+package AudioData;
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows);
+       my $num = 0;
+       if (($b_arm || $b_mips) && !$b_soc_audio && !$b_pci_tool){
+               my $key = ($b_arm) ? 'ARM' : 'MIPS';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults(lc($key) . '-pci',''),
+               },);
+               @rows = (@rows,@data);
+       }
+       else {
+               @data = card_data();
+               @rows = (@rows,@data);
+       }
+       if ( ( (($b_arm || $b_mips) && !$b_soc_audio && !$b_pci_tool) || !@rows ) && 
+          (my $file = main::system_files('asound-cards') ) ){
+               @data = asound_data($file);
+               @rows = (@rows,@data);
+       }
+       @data = usb_data();
+       @rows = (@rows,@data);
+       if (!@rows){
+               my $key = 'Message';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults('pci-card-data',''),
+               },);
+               @rows = (@rows,@data);
+       }
+       @data = sound_server_data();
+       @rows = (@rows,@data);
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub card_data {
+       eval $start if $b_log;
+       my (@rows,@data);
+       my ($j,$num) = (0,1);
+       foreach (@pci){
+               $num = 1;
+               my @row = @$_;
+               if ($row[0] =~ /^(audio|daudio|hdmi|multimedia)$/){
+                       $j = scalar @rows;
+                       my $driver = $row[9];
+                       $driver ||= 'N/A';
+                       my $card = $row[4];
+                       $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A';
+                       # have seen absurdly verbose card descriptions, with non related data etc
+                       if (length($card) > 85 || $size{'max'} < 110){
+                               $card = main::pci_long_filter($card);
+                       }
+                       @data = ({
+                       main::key($num++,'Card') => $card,
+                       },);
+                       @rows = (@rows,@data);
+                       if ($extra > 2 && $b_pci_tool && $row[11]){
+                               my $item = main::get_pci_vendor($row[4],$row[11]);
+                               $rows[$j]{main::key($num++,'vendor')} = $item if $item;
+                       }
+                       $rows[$j]{main::key($num++,'driver')} = $driver;
+                       if ($extra > 0 && !$bsd_type){
+                               if ($row[9] ){
+                                       my $version = main::get_module_version($row[9]);
+                                       $rows[$j]{main::key($num++,'v')} = $version if $version;
+                               }
+                       }
+                       if ($extra > 0){
+                               $rows[$j]{main::key($num++,'bus ID')} = (!$row[2] && !$row[3]) ? 'N/A' : "$row[2].$row[3]";
+                       }
+                       if ($extra > 1){
+                               $rows[$j]{main::key($num++,'chip ID')} = ($row[5]) ? "$row[5]:$row[6]" : $row[6];
+                       }
+               }
+               #print "$row[0]\n";
+       }
+       #my $ref = $pci[-1];
+       #print $$ref[0],"\n";
+       eval $end if $b_log;
+       return @rows;
+}
+# this handles fringe cases where there is no card on pcibus,
+# but there is a card present. I don't know the exact architecture
+# involved but I know this situation exists on at least one old machine.
+sub asound_data {
+       eval $start if $b_log;
+       my ($file) = @_;
+       my (@asound,@rows,@data);
+       my ($card,$driver,$j,$num) = ('','',0,1);
+       @asound = main::reader($file);
+       foreach (@asound){
+               # filtering out modems and usb devices like webcams, this might get a
+               # usb audio card as well, this will take some trial and error
+               if ( !/modem|usb/i && /^\s*[0-9]/ ) {
+                       $num = 1;
+                       my @working = split /:\s*/, $_;
+                       # now let's get 1 2
+                       $working[1] =~ /(.*)\s+-\s+(.*)/;
+                       $card = $2;
+                       $driver = $1;
+                       if ( $card ){
+                               $j = scalar @rows;
+                               $driver ||= 'N/A';
+                               @data = ({
+                               main::key($num++,'Card') => $card,
+                               main::key($num++,'driver') => $driver,
+                               },);
+                               @rows = (@rows,@data);
+                               if ($extra > 0){
+                                       my $version = main::get_module_version($driver);
+                                       $rows[$j]{main::key($num++,'v')} = $version if $version;
+                                       $rows[$j]{main::key($num++,'message')} = main::row_defaults('pci-advanced-data','');
+                               }
+                       }
+               }
+       }
+       # print Data::Dumper:Dumper \s@rows;
+       eval $end if $b_log;
+       return @rows;
+}
+sub usb_data {
+       eval $start if $b_log;
+       my (@rows,@data,@ids,$driver,$product,$product2,@temp2,$vendor,$vendor2);
+       my ($j,$num) = (0,1);
+       if (-d '/proc/asound') {
+               # note: this will double the data, but it's easier this way.
+               # inxi tested for -L in the /proc/asound files, and used only those.
+               my @files = main::globber('/proc/asound/*/usbid');
+               foreach (@files){
+                       my $id = (main::reader($_))[0];
+                       push @ids, $id if ($id && ! grep {/$id/} @ids);
+               }
+               # lsusb is a very expensive operation
+               if (@ids){
+                       if (!$bsd_type && !$b_usb_check){
+                               main::set_usb_data();
+                               $b_usb_check = 1;
+                       }
+               }
+               main::log_data('dump','@ids',\@ids) if $b_log;
+               return if !@usb;
+               foreach my $id (@ids){
+                       $j = scalar @rows;
+                       foreach my $ref (@usb){
+                               my @row = @$ref;
+                               # a device will always be the second or > device on the bus
+                               if ($row[1] > 1 && $row[2] eq $id){
+                                       $num = 1;
+                                       # makre sure to reset, or second device trips last flag
+                                       ($product,$product2,$vendor,$vendor2) = ('','','','');
+                                       if ($usb_level == 1){
+                                               $product = main::cleaner($row[3]);
+                                       }
+                                       else {
+                                               foreach my $line (@row){
+                                                       my @working = split /:/, $line;
+                                                       if ($working[0] eq 'idVendor' && $working[2]){
+                                                               $vendor = main::cleaner($working[2]);
+                                                       }
+                                                       if ($working[0] eq 'idProduct' && $working[2]){
+                                                               $product = main::cleaner($working[2]);
+                                                       }
+                                                       if ($working[0] eq 'iManufacturer' && $working[2]){
+                                                               $vendor2 = main::cleaner($working[2]);
+                                                       }
+                                                       if ($working[0] eq 'iProduct' && $working[2]){
+                                                               $product2 = main::cleaner($working[2]);
+                                                       }
+                                                       if ($working[0] eq 'Descriptor_Configuration'){
+                                                               last;
+                                                       }
+                                               }
+                                       }
+                                       if ($vendor && $product){
+                                               $product = ($product =~ /$vendor/) ? $product: "$vendor $product" ;
+                                       }
+                                       elsif (!$product) {
+                                               if ($vendor && $product2){
+                                                       $product = ($product2 =~ /$vendor/) ? $product2: "$vendor $product2" ;
+                                               }
+                                               elsif ($vendor2 && $product2){
+                                                       $product = ($product2 =~ /$vendor2/) ? $product2: "$vendor2 $product2" ;
+                                               }
+                                               elsif ($vendor){
+                                                       $product = $vendor;
+                                               }
+                                               elsif ($vendor2){
+                                                       $product = $vendor2;
+                                               }
+                                               else {
+                                                       $product = 'N/A';
+                                               }
+                                       }
+                                       @temp2 = main::get_usb_drivers($row[0],$row[2]) if !$bsd_type && -d "/sys/devices";
+                                       if (@temp2 && $temp2[0]){
+                                               $driver = $temp2[0];
+                                       }
+                                       $driver ||= 'snd-usb-audio';
+                                       @data = ({
+                                       main::key($num++,'Card') => $product,
+                                       main::key($num++,'type') => 'USB',
+                                       main::key($num++,'driver') => $driver,
+                                       },);
+                                       @rows = (@rows,@data);
+                                       if ($extra > 0){
+                                               $rows[$j]{main::key($num++,'bus ID')} = "$row[0]:$row[1]";
+                                       }
+                                       if ($extra > 1){
+                                               $rows[$j]{main::key($num++,'chip ID')} = $row[2];
+                                       }
+                               }
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub sound_server_data {
+       eval $start if $b_log;
+       my (@data,$server,$version);
+       my $num = 0;
+       if (my $file = main::system_files('asound-version') ){
+               my $content = (main::reader($file))[0];
+               # some alsa strings have the build date in (...)
+               # remove trailing . and remove possible second line if compiled by user
+#              foreach (@content){
+#                      if (!/compile/i){
+                               #$_ =~ s/Advanced Linux Sound Architecture/ALSA/;
+                               $version = (split /\s+/, $content)[-1];
+                               $version =~ s/\.$//; # trim off period
+                               $server = 'ALSA';
+#                      }
+#              }
+       }
+       elsif (my $program = main::check_program('oss')){
+               $server = 'OSS';
+               $version = main::program_version('oss','\S',2);
+               $version ||= 'N/A';
+       }
+       if ($server){
+               @data = ({
+               main::key($num++,'Sound Server') => $server,
+               main::key($num++,'v') => $version,
+               },);
+       }
+       eval $end if $b_log;
+       return @data;
+}
+}
+
+## BatteryData
+{
+package BatteryData;
+my (@upower_items,$b_upower,$upower);
+sub get {
+       eval $start if $b_log;
+       my (@rows,%battery,$key1,$val1);
+       my $num = 0;
+       if ($bsd_type || $b_dmidecode_force){
+               my $ref = $alerts{'dmidecode'};
+               if ( $$ref{'action'} ne 'use'){
+                       $key1 = $$ref{'action'};
+                       $val1 = $$ref{$key1};
+                       $key1 = ucfirst($key1);
+                       @rows = ({main::key($num++,$key1) => $val1,});
+               }
+               else {
+                       %battery = battery_data_dmi();
+                       if (!%battery){
+                               if ($show{'battery-forced'}){
+                                       $key1 = 'Message';
+                                       $val1 = main::row_defaults('battery-data','');
+                                       @rows = ({main::key($num++,$key1) => $val1,});
+                               }
+                       }
+                       else {
+                               @rows = create_output(%battery);
+                       }
+               }
+       }
+       elsif (-d '/sys/class/power_supply/'){
+               %battery = battery_data_sys();
+               if (!%battery){
+                       if ($show{'battery-forced'}){
+                               $key1 = 'Message';
+                               $val1 = main::row_defaults('battery-data','');
+                               @rows = ({main::key($num++,$key1) => $val1,});
+                       }
+               }
+               else {
+                       @rows = create_output(%battery);
+               }
+       }
+       else {
+               if ($show{'battery-forced'}){
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('battery-data-sys','');
+                       @rows = ({main::key($num++,$key1) => $val1,});
+               }
+       }
+       (@upower_items,$b_upower,$upower) = undef;
+       eval $end if $b_log;
+       return @rows;
+}
+# alarm capacity capacity_level charge_full charge_full_design charge_now 
+#      cycle_count energy_full energy_full_design energy_now location manufacturer model_name 
+#      power_now present serial_number status technology type voltage_min_design voltage_now
+# 0  name - battery id, not used
+# 1  status
+# 2  present
+# 3  technology
+# 4  cycle_count
+# 5  voltage_min_design
+# 6  voltage_now
+# 7  power_now
+# 8  energy_full_design
+# 9  energy_full
+# 10 energy_now
+# 11 capacity
+# 12 capacity_level
+# 13 of_orig
+# 14 model_name
+# 15 manufacturer
+# 16 serial_number
+# 17 location
+sub create_output {
+       eval $start if $b_log;
+       my (%battery) = @_;
+       my ($key,@data,@rows);
+       my $num = 0;
+       my $j = 0;
+       # print Data::Dumper::Dumper \%battery;
+       foreach $key (sort keys %battery){
+               $num = 0;
+               my ($charge,$condition,$model,$serial,$status,$volts) = ('','','','','','');
+               my ($chemistry,$cycles,$location) = ('','','');
+               next if !$battery{$key}{'purpose'} || $battery{$key}{'purpose'} ne 'primary';
+               # $battery{$key}{''};
+               # we need to handle cases where charge or energy full is 0
+               $charge = (defined $battery{$key}{'energy_now'} && $battery{$key}{'energy_now'} ne '') ? "$battery{$key}{'energy_now'} Wh" : 'N/A';
+               if ($battery{$key}{'energy_full'} || $battery{$key}{'energy_full_design'}){
+                       $battery{$key}{'energy_full_design'} ||= 'N/A';
+                       $battery{$key}{'energy_full'}= (defined $battery{$key}{'energy_full'} && $battery{$key}{'energy_full'} ne '') ? $battery{$key}{'energy_full'} : 'N/A';
+                       $condition = "$battery{$key}{'energy_full'}/$battery{$key}{'energy_full_design'} Wh";
+                       if ($battery{$key}{'of_orig'}){
+                               $condition .= " ($battery{$key}{'of_orig'}%)"; 
+                       }
+               }
+               $condition ||= 'N/A';
+               $j = scalar @rows;
+               @data = ({
+               main::key($num++,'ID') => $key,
+               main::key($num++,'charge') => $charge,
+               main::key($num++,'condition') => $condition,
+               },);
+               @rows = (@rows,@data);
+               if ($extra > 0){
+                       if ($extra > 1){
+                               if ($battery{$key}{'voltage_min_design'} || $battery{$key}{'voltage_now'}){
+                                       $battery{$key}{'voltage_min_design'} ||= 'N/A';
+                                       $battery{$key}{'voltage_now'} ||= 'N/A';
+                                       $volts = "$battery{$key}{'voltage_now'}/$battery{$key}{'voltage_min_design'}";
+                               }
+                               $volts ||= 'N/A';
+                               $rows[$j]{main::key($num++,'volts')} = $volts;
+                       }
+                       if ($battery{$key}{'manufacturer'} || $battery{$key}{'model_name'}) {
+                               if ($battery{$key}{'manufacturer'} && $battery{$key}{'model_name'}){
+                                       $model = "$battery{$key}{'manufacturer'} $battery{$key}{'model_name'}";
+                               }
+                               elsif ($battery{$key}{'manufacturer'}){
+                                       $model = $battery{$key}{'manufacturer'};
+                               }
+                               elsif ($battery{$key}{'model_name'}){
+                                       $model = $battery{$key}{'model_name'};
+                               }
+                       }
+                       else {
+                               $model = 'N/A';
+                       }
+                       $rows[$j]{main::key($num++,'model')} = $model;
+                       if ($extra > 2){
+                               $chemistry = ( $battery{$key}{'technology'} ) ? $battery{$key}{'technology'}: 'N/A';
+                               $rows[$j]{main::key($num++,'type')} = $chemistry;
+                       }
+                       if ($extra > 1){
+                               $serial = main::apply_filter($battery{$key}{'serial_number'});
+                               $rows[$j]{main::key($num++,'serial')} = $serial;
+                       }
+                       $status = ($battery{$key}{'status'}) ? $battery{$key}{'status'}: 'N/A';
+                       $rows[$j]{main::key($num++,'status')} = $status;
+                       if ($extra > 2){
+                               if ($battery{$key}{'cycle_count'}){
+                                       $rows[$j]{main::key($num++,'cycles')} = $battery{$key}{'cycle_count'};
+                               }
+                               if ($battery{$key}{'location'}){
+                                       $rows[$j]{main::key($num++,'location')} = $battery{$key}{'location'};
+                               }
+                       }
+               }
+               $battery{$key} = undef;
+       }
+       # print Data::Dumper::Dumper \%battery;
+       # now if there are any devices left, print them out, excluding Mains
+       if ($extra > 0){
+               $upower = main::check_program('upower');
+               foreach $key (sort keys %battery){
+                       $num = 0;
+                       next if !defined $battery{$key} || $battery{$key}{'purpose'} eq 'mains';
+                       my ($charge,$model,$serial,$percent,$status,$vendor) = ('','','','','','');
+                       my (%upower_data);
+                       $j = scalar @rows;
+                       %upower_data = upower_data($key) if $upower;
+                       if ($upower_data{'percent'}){
+                               $charge = $upower_data{'percent'};
+                       }
+                       elsif ($battery{$key}{'capacity_level'} && lc($battery{$key}{'capacity_level'}) ne 'unknown'){
+                               $charge = $battery{$key}{'capacity_level'};
+                       }
+                       else {
+                               $charge = 'N/A';
+                       }
+                       $model = $battery{$key}{'model_name'} if $battery{$key}{'model_name'};
+                       $status = ($battery{$key}{'status'} && lc($battery{$key}{'status'}) ne 'unknown') ? $battery{$key}{'status'}: 'N/A' ;
+                       $vendor = $battery{$key}{'manufacturer'} if $battery{$key}{'manufacturer'};
+                       if ($vendor || $model){
+                               if ($vendor && $model){
+                                       $model = "$vendor $model";
+                               }
+                               elsif ($vendor){
+                                       $model = $vendor;
+                               }
+                       }
+                       else {
+                               $model = 'N/A';
+                       }
+                       @data = ({
+                       main::key($num++,'Device') => $key,
+                       main::key($num++,'model') => $model,
+                       },);
+                       @rows = (@rows,@data);
+                       if ($extra > 1){
+                               $serial = main::apply_filter($battery{$key}{'serial_number'});
+                               $rows[$j]{main::key($num++,'serial')} = $serial;
+                       }
+                       $rows[$j]{main::key($num++,'charge')} = $charge;
+                       if ($extra > 2 && $upower_data{'rechargeable'}){
+                               $rows[$j]{main::key($num++,'rechargeable')} = $upower_data{'rechargeable'};
+                       }
+                       $rows[$j]{main::key($num++,'status')} = $status;
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+# charge: mAh energy: Wh
+sub battery_data_sys {
+       eval $start if $b_log;
+       my ($b_ma,%battery,$file,$id,$item,$path,$value);
+       my $num = 0;
+       my @batteries = main::globber("/sys/class/power_supply/*");
+       # note: there is no 'location' file, but dmidecode has it
+       # 'type' is generic, like: Battery, Mains
+       # capacity_level is a string, like: Normal
+       my @items = qw(alarm capacity capacity_level charge_full charge_full_design charge_now 
+       cycle_count energy_full energy_full_design energy_now location manufacturer model_name 
+       power_now present serial_number status technology type voltage_min_design voltage_now);
+       foreach $item (@batteries){
+               $b_ma = 0;
+               $id = $item;
+               $id =~ s%/sys/class/power_supply/%%g;
+               my $purpose = ($id =~ /^(BAT|CMB).*$/) ? 'primary': 'device';
+               # don't create arrays of device data if it's not going to show
+               next if $extra == 0 && $purpose ne 'primary';
+               $battery{$id} = ({});
+               # NOTE: known ids: BAT[0-9] CMB[0-9]
+               $battery{$id}{'purpose'} = $purpose;
+               foreach $file (@items){
+                       $path = "$item/$file";
+                       $value = (-f $path) ? (main::reader($path))[0]: '';
+                       # mains
+                       if ($file eq 'type' && $value && lc($value) ne 'battery' ){
+                               $battery{$id}{'purpose'} = 'mains';
+                       }
+                       if ($value){
+                               if ($file eq 'voltage_min_design'){
+                                       $value = sprintf("%.1f", $value/1000000);
+                               }
+                               elsif ($file eq 'voltage_now'){
+                                       $value = sprintf("%.1f", $value/1000000);
+                               }
+                               elsif ($file eq 'energy_full_design'){
+                                       $value = $value/1000000;
+                               }
+                               elsif ($file eq 'energy_full'){
+                                       $value = $value/1000000;
+                               }
+                               elsif ($file eq 'energy_now'){
+                                       $value = sprintf("%.1f", $value/1000000);
+                               }
+                               # note: the following 3 were off, 100000 instead of 1000000
+                               # why this is, I do not know. I did not document any reason for that
+                               # so going on assumption it is a mistake. CHARGE is mAh, which are converted
+                               # to Wh by: mAh x voltage. Note: voltage fluctuates so will make results vary slightly.
+                               elsif ($file eq 'charge_full_design'){
+                                       $value = $value/1000000;
+                                       $b_ma = 1;
+                               }
+                               elsif ($file eq 'charge_full'){
+                                       $value = $value/1000000;
+                                       $b_ma = 1;
+                               }
+                               elsif ($file eq 'charge_now'){
+                                       $value = $value/1000000;
+                                       $b_ma = 1;
+                               }
+                               elsif ($file eq 'manufacturer'){
+                                       $value = main::dmi_cleaner($value);
+                               }
+                               elsif ($file eq 'model_name'){
+                                       $value = main::dmi_cleaner($value);
+                               }
+                       }
+                       elsif ($b_root && -e $path && ! -r $path ){
+                               $value = main::row_defaults('root-required');
+                       }
+                       $battery{$id}{$file} = $value;
+                       # print "$battery{$id}{$file}\n";
+               }
+               # note:voltage_now fluctuates, which will make capacity numbers change a bit
+               # if any of these values failed, the math will be wrong, but no way to fix that
+               # tests show more systems give right capacity/charge with voltage_min_design 
+               # than with voltage_now
+               if ($b_ma && $battery{$id}{'voltage_min_design'}){
+                       if ($battery{$id}{'charge_now'}){
+                               $battery{$id}{'energy_now'} = $battery{$id}{'charge_now'} * $battery{$id}{'voltage_min_design'};
+                       }
+                       if ($battery{$id}{'charge_full'}){
+                               $battery{$id}{'energy_full'} = $battery{$id}{'charge_full'}*$battery{$id}{'voltage_min_design'};
+                       }
+                       if ($battery{$id}{'charge_full_design'}){
+                               $battery{$id}{'energy_full_design'} = $battery{$id}{'charge_full_design'} * $battery{$id}{'voltage_min_design'};
+                       }
+               }
+               if ( $battery{$id}{'energy_now'} && $battery{$id}{'energy_full'} ){
+                       $battery{$id}{'capacity'} = 100 * $battery{$id}{'energy_now'}/$battery{$id}{'energy_full'};
+                       $battery{$id}{'capacity'} = sprintf( "%.1f", $battery{$id}{'capacity'} );
+               }
+               if ( $battery{$id}{'energy_full_design'} && $battery{$id}{'energy_full'} ){
+                       $battery{$id}{'of_orig'} = 100 * $battery{$id}{'energy_full'}/$battery{$id}{'energy_full_design'};
+                       $battery{$id}{'of_orig'} = sprintf( "%.0f", $battery{$id}{'of_orig'} );
+               }
+               if ( $battery{$id}{'energy_now'} ){
+                       $battery{$id}{'energy_now'} = sprintf( "%.1f", $battery{$id}{'energy_now'} );
+               }
+               if ( $battery{$id}{'energy_full_design'} ){
+                       $battery{$id}{'energy_full_design'} = sprintf( "%.1f",$battery{$id}{'energy_full_design'} );
+               }
+               if ( $battery{$id}{'energy_full'} ){
+                       $battery{$id}{'energy_full'} = sprintf( "%.1f", $battery{$id}{'energy_full'} );
+               }
+       }
+       eval $end if $b_log;
+       return %battery;
+}
+# note, dmidecode does not have charge_now or charge_full
+sub battery_data_dmi {
+       eval $start if $b_log;
+       my (%battery,$id);
+       my $i = 0;
+       foreach (@dmi){
+               my @ref = @$_;
+               # Portable Battery
+               if ($ref[0] == 22){
+                       $id = "BAT$i";
+                       $i++;
+                       $battery{$id} = ({});
+                       $battery{$id}{'purpose'} = 'primary';
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       foreach my $item (@ref){
+                               my @value = split /:\s+/, $item;
+                               next if !$value[0];
+                               if ($value[0] eq 'Location') {$battery{$id}{'location'} = $value[1] }
+                               elsif ($value[0] eq 'Manufacturer') {$battery{$id}{'manufacturer'} = main::dmi_cleaner($value[1]) }
+                               elsif ($value[0] =~ /Chemistry/) {$battery{$id}{'technology'} = $value[1] }
+                               elsif ($value[0] =~ /Serial Number/) {$battery{$id}{'serial_number'} = $value[1] }
+                               elsif ($value[0] =~ /^Name/) {$battery{$id}{'model_name'} = main::dmi_cleaner($value[1]) }
+                               elsif ($value[0] eq 'Design Capacity') {
+                                       $value[1] =~ s/\s*mwh$//i;
+                                       $battery{$id}{'energy_full_design'} = sprintf( "%.1f", $value[1]/1000);
+                               }
+                               elsif ($value[0] eq 'Design Voltage') {
+                                       $value[1] =~ s/\s*mv$//i;
+                                       $battery{$id}{'voltage_min_design'} = sprintf( "%.1f", $value[1]/1000);
+                               }
+                       }
+                       if ($battery{$id}{'energy_now'} && $battery{$id}{'energy_full'} ){
+                               $battery{$id}{'capacity'} = 100 * $battery{$id}{'energy_now'} / $battery{$id}{'energy_full'};
+                               $battery{$id}{'capacity'} = sprintf( "%.1f%", $battery{$id}{'capacity'} );
+                       }
+                       if ($battery{$id}{'energy_full_design'} && $battery{$id}{'energy_full'} ){
+                               $battery{$id}{'of_orig'} = 100 * $battery{$id}{'energy_full'} / $battery{$id}{'energy_full_design'};
+                               $battery{$id}{'of_orig'} = sprintf( "%.0f%", $battery{$id}{'of_orig'} );
+                       }
+               }
+               elsif ($ref[0] > 22){
+                       last;
+               }
+       }
+       # print Data::Dumper::Dumper \%battery;
+       eval $end if $b_log;
+       return %battery;
+}
+sub upower_data {
+       my ($id) = @_;
+       eval $start if $b_log;
+       my (%data);
+       if (!$b_upower && $upower){
+               @upower_items = main::grabber("$upower -e",'','strip');
+               $b_upower = 1;
+       }
+       if ($upower && @upower_items){
+               foreach (@upower_items){
+                       if ($_ =~ /$id/){
+                               my @working = main::grabber("$upower -i $_",'','strip');
+                               foreach my $row (@working){
+                                       my @temp = split /\s*:\s*/, $row;
+                                       if ($temp[0] eq 'percentage'){
+                                               $data{'percent'} = $temp[1];
+                                       }
+                                       elsif ($temp[0] eq 'rechargeable'){
+                                               $data{'rechargeable'} = $temp[1];
+                                       }
+                               }
+                               last;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return %data;
+}
+
+}
+
+## CpuData
+{
+package CpuData;
+
+sub get {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my (@data,@rows,$single,$key1,$val1);
+       my $num = 0;
+       if ($type eq 'short' || $type eq 'basic'){
+               @rows = data_short($type);
+       }
+       else {
+               @rows = create_output_full();
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output_full {
+       eval $start if $b_log;
+       my $num = 0;
+       my ($b_flags,$b_speeds,$core_speeds_value,$flag_key,@flags,%cpu,@data,@rows);
+       my $sleep = $cpu_sleep * 1000000;
+       if ($b_hires){
+               eval 'Time::HiRes::usleep( $sleep )';
+       }
+       else {
+               select(undef, undef, undef, $cpu_sleep);
+       }
+       if (my $file = main::system_files('cpuinfo')){
+               %cpu = data_cpuinfo($file,'full');
+       }
+       elsif ($bsd_type ){
+               my ($key1,$val1) = ('','');
+               if ( $alerts{'sysctl'} ){
+                       if ( $alerts{'sysctl'}{'action'} eq 'use' ){
+#                              $key1 = 'Status';
+#                              $val1 = main::row_defaults('dev');
+                               %cpu = data_sysctl('full');
+                       }
+                       else {
+                               $key1 = ucfirst($alerts{'sysctl'}{'action'});
+                               $val1 = $alerts{'sysctl'}{$alerts{'sysctl'}{'action'}};
+                               @data = ({main::key($num++,$key1) => $val1,});
+                               return @data;
+                       }
+               }
+       }
+       my %properties = cpu_properties(%cpu);
+       my $type = ($properties{'cpu-type'}) ? $properties{'cpu-type'}: '';
+       my $ref = $cpu{'processors'};
+       my @processors = @$ref;
+       my @speeds = cpu_speeds(@processors);
+       my $j = scalar @rows;
+       $cpu{'model_name'} ||= 'N/A'; 
+       @data = ({
+       main::key($num++,'Topology') => $properties{'cpu-layout'},
+       main::key($num++,'model') => $cpu{'model_name'},
+       },);
+       @rows = (@rows,@data);
+       if ($cpu{'arm-cpus'}){
+               my $ref = $cpu{'arm-cpus'};
+               my %arm_cpus = %$ref;
+               my $i = 1;
+               my $counter = ( %arm_cpus && scalar keys %arm_cpus > 1 ) ? '-' : '';
+               foreach my $key (keys %arm_cpus){
+                       $counter = '-' . $i++ if $counter;
+                       $rows[$j]{main::key($num++,'variant'.$counter)} = $key;
+               }
+       }
+       $properties{'bits-sys'} ||= 'N/A';
+       $rows[$j]{main::key($num++,'bits')} = $properties{'bits-sys'};
+       if ($type){
+               $rows[$j]{main::key($num++,'type')} = $type;
+       }
+       if ($extra > 0){
+               $cpu{'arch'} ||= 'N/A';
+               $rows[$j]{main::key($num++,'arch')} = $cpu{'arch'};
+               if ( !$b_admin && $cpu{'arch'} ne 'N/A' && $cpu{'rev'} ){
+                       $rows[$j]{main::key($num++,'rev')} = $cpu{'rev'};
+               }
+       }
+       if ($b_admin){
+               $rows[$j]{main::key($num++,'family')} = hex_and_decimal($cpu{'family'});
+               $rows[$j]{main::key($num++,'model-id')} = hex_and_decimal($cpu{'model_id'});
+               $rows[$j]{main::key($num++,'stepping')} = hex_and_decimal($cpu{'rev'});
+               $cpu{'microcode'} ||= 'N/A';
+               $rows[$j]{main::key($num++,'microcode')} = $cpu{'microcode'};
+       }
+       $properties{'l2-cache'} ||= 'N/A';
+       if (!$b_arm || ($b_arm && $properties{'l2-cache'} ne 'N/A')){
+               $rows[$j]{main::key($num++,'L2 cache')} = $properties{'l2-cache'};
+       }
+       if ($extra > 0 && !$show{'cpu-flag'}){
+               $j = scalar @rows;
+               @flags = split /\s+/, $cpu{'flags'} if $cpu{'flags'};
+               $flag_key = ($b_arm || $bsd_type) ? 'features': 'flags';
+               my $flag = 'N/A';
+               if (@flags){
+                       # failure to read dmesg.boot: dmesg.boot permissions
+                       @flags = grep {/^(dmesg.boot|lm|nx|pae|permissions|pni|svm|vmx|(sss|ss)e([2-9])?([a-z])?(_[0-9])?)$/} @flags;
+                       @flags = map {s/pni/sse3/; $_} @flags;
+                       @flags = sort(@flags);
+                       $flag = join ' ', @flags if @flags;
+               }
+               if ($b_arm && $flag eq 'N/A'){
+                       $flag = main::row_defaults('arm-cpu-f');
+               }
+               @data = ({
+               main::key($num++,$flag_key) => $flag,
+               },);
+               @rows = (@rows,@data);
+               $b_flags = 1;
+       }
+       if ($extra > 0 && !$bsd_type){
+               my $bogomips = ($cpu{'bogomips'}) ? int($cpu{'bogomips'}) : 'N/A';
+               $rows[$j]{main::key($num++,'bogomips')} = $bogomips;
+       }
+       $j = scalar @rows;
+       my $core_key = (scalar @speeds > 1) ? 'Core speeds (MHz)' : 'Core speed (MHz)';
+       my $speed_key = ($properties{'speed-key'}) ? $properties{'speed-key'}: 'Speed';
+       my $min_max = ($properties{'min-max'}) ? $properties{'min-max'}: 'N/A';
+       my $min_max_key = ($properties{'min-max-key'}) ? $properties{'min-max-key'}: 'min/max';
+       my $speed = (defined $properties{'speed'}) ? $properties{'speed'}: 'N/A';
+       # aren't able to get per core speeds in bsds yet
+       if (@speeds){
+               if (grep {$_ ne '0'} @speeds){
+                       $core_speeds_value = '';
+                       $b_speeds = 1;
+               }
+               else {
+                       $core_speeds_value = main::row_defaults('cpu-speeds',scalar @speeds);
+               }
+       }
+       else {
+               $core_speeds_value = 'N/A';
+       }
+       $j = scalar @rows;
+       @data = ({
+       main::key($num++,$speed_key) => $speed,
+       main::key($num++,$min_max_key) => $min_max,
+       });
+       @rows = (@rows,@data);
+       if ($extra > 2){
+               my $boost = get_boost_status();
+               $rows[$j]{main::key($num++,'boost')} = $boost if $boost;
+       }
+       $rows[$j]{main::key($num++,$core_key)} = $core_speeds_value;
+       my $i = 1;
+       # if say 96 0 speed cores, no need to print all those 0s
+       if ($b_speeds){
+               foreach (@speeds){
+                       $rows[$j]{main::key($num++,$i++)} = $_;
+               }
+       }
+       if ($show{'cpu-flag'} && !$b_flags){
+               $flag_key = ($b_arm || $bsd_type) ? 'Features': 'Flags';
+               @flags = split /\s+/, $cpu{'flags'} if $cpu{'flags'};
+               my $flag = 'N/A';
+               if (@flags){
+                       @flags = sort(@flags);
+                       $flag = join ' ', @flags if @flags;
+               }
+               @data = ({
+               main::key($num++,$flag_key) => $flag,
+               },);
+               @rows = (@rows,@data);
+       }
+       if ($b_admin && $cpu{'bugs'}){
+               my @bugs = split /\s+/, $cpu{'bugs'};
+               @bugs = sort(@bugs);
+               my $bug = join ' ', @bugs;
+               @data = ({
+               main::key($num++,'Errata') => $bug,
+               },);
+               @rows = (@rows,@data);
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output_short {
+       eval $start if $b_log;
+       my (@cpu) = @_;
+       my @data;
+       my $num = 0;
+       $cpu[1] ||= main::row_defaults('cpu-model-null');
+       $cpu[2] ||= 'N/A';
+       @data = ({
+       main::key($num++,$cpu[0]) => $cpu[1],
+       main::key($num++,'type') => $cpu[2],
+       },);
+       if ($extra > 0){
+               $data[0]{main::key($num++,'arch')} = $cpu[7];
+       }
+       $data[0]{main::key($num++,$cpu[3])} = $cpu[4];
+       if ($cpu[6]){
+               $data[0]{main::key($num++,$cpu[5])} = $cpu[6];
+       }
+       eval $end if $b_log;
+       return @data;
+}
+sub data_short {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my $num = 0;
+       my (%cpu,@data,%speeds);
+       my $sys = '/sys/devices/system/cpu/cpufreq/policy0';
+       my $sleep = $cpu_sleep * 1000000;
+       if ($b_hires){
+               eval 'Time::HiRes::usleep( $sleep )';
+       }
+       else {
+               select(undef, undef, undef, $cpu_sleep);
+       }
+       # NOTE: : Permission denied, ie, this is not always readable
+       # /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
+       if (my $file = main::system_files('cpuinfo')){
+               %cpu = data_cpuinfo($file,$type);
+       }
+       elsif ($bsd_type ){
+               my ($key1,$val1) = ('','');
+               if ( $alerts{'sysctl'} ){
+                       if ( $alerts{'sysctl'}{'action'} eq 'use' ){
+#                              $key1 = 'Status';
+#                              $val1 = main::row_defaults('dev');
+                               %cpu = data_sysctl($type);
+                       }
+                       else {
+                               $key1 = ucfirst($alerts{'sysctl'}{'action'});
+                               $val1 = $alerts{'sysctl'}{$alerts{'sysctl'}{'action'}};
+                               @data = ({main::key($num++,$key1) => $val1,});
+                               return @data;
+                       }
+               }
+       }
+       # $cpu{'cur-freq'} = $cpu[0]{'core-id'}[0]{'speed'};
+       if ($type eq 'short' || $type eq 'basic'){
+               @data = prep_short_data(%cpu);
+       }
+       if ($type eq 'basic'){
+               @data = create_output_short(@data);
+       }
+       eval $end if $b_log;
+       return @data;
+}
+
+sub prep_short_data {
+       eval $start if $b_log;
+       my (%cpu) = @_;
+       my %properties = cpu_properties(%cpu);
+       my ($cpu,$speed_key,$speed,$type) = ('','speed',0,'');
+       $cpu = $cpu{'model_name'} if $cpu{'model_name'};
+       $type = $properties{'cpu-type'} if $properties{'cpu-type'};
+       $speed_key = $properties{'speed-key'} if $properties{'speed-key'};
+       $speed = $properties{'speed'} if $properties{'speed'};
+       my @result = (
+       $properties{'cpu-layout'},
+       $cpu,
+       $type,
+       $speed_key,
+       $speed,
+       $properties{'min-max-key'},
+       $properties{'min-max'},
+       );
+       if ($extra > 0){
+               $cpu{'arch'} ||= 'N/A';
+               $result[7] = $cpu{'arch'};
+       }
+       eval $end if $b_log;
+       return @result;
+}
+
+sub data_cpuinfo {
+       eval $start if $b_log;
+       my ($file,$type)=  @_;
+       my ($arch,@ids,@line,$b_first,$b_proc_int,$starter);
+       # use --arm flag when testing arm cpus
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-4-core-pinebook-1.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv6-single-core-1.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv7-dual-core-1.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/armv7-new-format-model-name-single-core.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-2-die-96-core-rk01.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/16-core-32-mt-ryzen.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/2-16-core-epyc-abucodonosor.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/2-core-probook-antix.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-jean-antix.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-althlon-mjro.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-apu-vc-box.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/amd/4-core-a10-5800k-1.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-core-ht-atom-bruh.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/core-2-i3.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/8-core-i7-damentz64.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-10-core-xeon-ht.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-core-xeon-fake-dual-die-zyanya.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-core-i5-fake-dual-die-hek.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/2-1-core-xeon-vm-vs2017.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-1-core-xeon-vps-frodo1.txt";
+       # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/intel/4-6-core-xeon-no-mt-lathander.txt";
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/mips/mips-mainusg-cpuinfo.txt";
+       my %speeds = set_cpu_speeds_sys();
+       my @cpuinfo = main::reader($file);
+       my @phys_cpus = (0);# start with 1 always
+       my ($cache,$core_count,$die_holder,$die_id,$phys_id,$proc_count,$speed) = (0,0,0,0,0,0,0);
+       my ($phys_holder) = (undef);
+       # need to prime for arm cpus, which do not have physical/core ids usually
+       # level 0 is phys id, level 1 is die id, level 2 is core id
+       #$ids[0] = ([(0)]);
+       $ids[0] = ([]);
+       $ids[0][0] = ([]);
+       my %cpu =  set_cpu_data();
+       # note, there con be a lot of processors, 32 core HT would have 64, for example.
+       foreach (@cpuinfo){
+               next if /^\s*$/;
+               @line = split /\s*:\s*/, $_;
+               next if !$line[0];
+               $starter = $line[0]; # preserve case for one specific ARM issue
+               $line[0] = lc($line[0]);
+               if ($b_arm && !$b_first && $starter eq 'Processor' && $line[1] !~ /^\d+$/){
+                       #print "l1:$line[1]\n";
+                       $cpu{'model_name'} = main::cleaner($line[1]);
+                       $cpu{'model_name'} = cpu_cleaner($cpu{'model_name'});
+                       $cpu{'type'} = 'arm';
+                       # Processor   : AArch64 Processor rev 4 (aarch64)
+                       # Processor : Feroceon 88FR131 rev 1 (v5l)
+                       if ($cpu{'model_name'} && $cpu{'model_name'} =~ /(.*)\srev\s([\S]+)\s(\(([\S]+)\))?/){
+                               $cpu{'model_name'} = $1;
+                               $cpu{'rev'} = $2;
+                               if ($4){
+                                       $cpu{'arch'} = $4;
+                                       $cpu{'model_name'} .= ' ' . $cpu{'arch'} if $cpu{'model_name'} !~ /$cpu{'arch'}/i; 
+                               }
+                               $cpu{'processors'}[$proc_count] = 0;
+                               $b_proc_int = 0;
+                               $b_first = 1;
+                               #print "p0:\n";
+                       }
+               }
+               elsif ($line[0] eq 'processor'){
+                       # this protects against double processor lines, one int, one string
+                       if ($line[1] =~ /^\d+$/){
+                               $b_proc_int = 1;
+                               $b_first = 1;
+                               $cpu{'processors'}[$proc_count] = 0;
+                               $proc_count++;
+                               #print "p1: $proc_count\n";
+                       }
+                       else {
+                               if (!$b_proc_int){
+                                       $cpu{'processors'}[$proc_count] = 0;
+                                       $proc_count++;
+                                       #print "p2a: $proc_count\n";
+                               }
+                               if (!$b_first ){
+                                       # note: alternate: 
+                                       # Processor     : AArch64 Processor rev 4 (aarch64)
+                                       # but no model name type
+                                       if ( $b_arm || $line[1] =~ /ARM|AArch/i){
+                                               $b_arm = 1;
+                                               $cpu{'type'} = 'arm';
+                                       }
+                                       $cpu{'model_name'} = main::cleaner($line[1]);
+                                       $cpu{'model_name'} = cpu_cleaner($cpu{'model'});
+                                       #print "p2b:\n";
+                               }
+                               $b_first = 1;
+                       }
+               }
+               elsif (!$cpu{'family'} && 
+                      ($line[0] eq 'architecture' || $line[0] eq 'cpu family' || $line[0] eq 'cpu architecture' )){
+                       if ($line[1] =~ /^\d+$/){
+                               # translate integers to hex
+                               $cpu{'family'} = uc(sprintf("%x", $line[1]));
+                       }
+                       elsif ($b_arm) {
+                               $cpu{'arch'} = $line[1];
+                       }
+               }
+               elsif (!$cpu{'rev'} && ($line[0] eq 'stepping' || $line[0] eq 'cpu revision' )){
+                       $cpu{'rev'} = uc(sprintf("%x", $line[1]));
+               }
+               # this is hex so uc for cpu arch id
+               elsif (!$cpu{'model_id'} && $line[0] eq 'model' ){
+                       $cpu{'model_id'} = uc(sprintf("%x", $line[1]));
+               }
+               elsif (!$cpu{'model_id'} && $line[0] eq 'cpu variant' ){
+                       $cpu{'model_id'} = uc($line[1]);
+                       $cpu{'model_id'} =~ s/^0X//;
+               }
+               # cpu can show in arm
+               elsif (!$cpu{'model_name'} && ( $line[0] eq 'model name' || $line[0] eq 'cpu' || $line[0] eq 'cpu model' )){
+                       $cpu{'model_name'} = main::cleaner($line[1]);
+                       $cpu{'model_name'} = cpu_cleaner($cpu{'model_name'});
+                       if ( $b_arm || $line[1] =~ /ARM|AArch/i){
+                               $b_arm = 1;
+                               $cpu{'type'} = 'arm';
+                               if ($cpu{'model_name'} && $cpu{'model_name'} =~ /(.*)\srev\s([\S]+)\s(\(([\S]+)\))?/){
+                                       $cpu{'model_name'} = $1;
+                                       $cpu{'rev'} = $2;
+                                       if ($4){
+                                               $cpu{'arch'} = $4;
+                                               $cpu{'model_name'} .= ' ' . $cpu{'arch'} if $cpu{'model_name'} !~ /$cpu{'arch'}/i;
+                                       }
+                                       #$cpu{'processors'}[$proc_count] = 0;
+                               }
+                       }
+                       elsif ($b_mips || $line[1] =~ /mips/i){
+                               $b_mips = 1;
+                               $cpu{'type'} = 'mips';
+                       }
+               }
+               elsif ( $line[0] eq 'cpu mhz' ){
+                       $speed = speed_cleaner($line[1]);
+                       $cpu{'processors'}[$proc_count-1] = $speed;
+                       #$ids[$phys_id][$die_id] = ([($speed)]);
+               }
+               elsif (!$cpu{'siblings'} && $line[0] eq 'siblings' ){
+                       $cpu{'siblings'} = $line[1];
+               }
+               elsif (!$cpu{'cores'} && $line[0] eq 'cpu cores' ){
+                       $cpu{'cores'} = $line[1];
+               }
+               # increment by 1 for every new physical id we see. These are in almost all cases
+               # separate cpus, not separate dies within a single cpu body.
+               elsif ( $line[0] eq 'physical id' ){
+                       if ( !defined $phys_holder || $phys_holder != $line[1] ){
+                               # only increment if not in array counter
+                               push @phys_cpus, $line[1] if ! grep {/$line[1]/} @phys_cpus;
+                               $phys_holder = $line[1];
+                               $ids[$phys_holder] = ([]) if ! exists $ids[$phys_holder];
+                               $ids[$phys_holder][$die_id] = ([]) if ! exists $ids[$phys_holder][$die_id];
+                               #print "pid: $line[1] ph: $phys_holder did: $die_id\n";
+                               $die_id = 0;
+                               #$die_holder = 0;
+                       }
+               }
+               elsif ( $line[0] eq 'core id' ){
+                       #print "ph: $phys_holder did: $die_id l1: $line[1] s: $speed\n";
+                       # https://www.pcworld.com/article/3214635/components-processors/ryzen-threadripper-review-we-test-amds-monster-cpu.html
+                       if ($line[1] > 0 ){
+                               $die_holder = $line[1];
+                               $core_count++;
+                       }
+                       # NOTE: this logic won't work for die detections, unforutnately.
+                       # ARM uses a different /sys based method, and ryzen relies on math on the cores
+                       # in process_data
+                       elsif ($line[1] == 0 && $die_holder > 0 ){
+                               $die_holder = $line[1];
+                               $core_count = 0;
+                               $die_id++ if ($cpu{'type'} ne 'intel' && $cpu{'type'} ne 'amd' );
+                       }
+                       $phys_holder = 0 if ! defined $phys_holder;
+                       $ids[$phys_holder][$die_id][$line[1]] = $speed;
+                       #print "ph: $phys_holder did: $die_id l1: $line[1] s: $speed\n";
+               }
+               if (!$cpu{'type'} && $line[0] eq 'vendor_id' ){
+                       $cpu{'type'} = cpu_vendor($line[1]);
+               }
+               ## this is only for -C full cpu output
+               if ( $type eq 'full' ){
+                       if (!$cpu{'l2-cache'} && $line[0] eq 'cache size'){
+                               if ($line[1] =~ /(\d+)\sKB$/){
+                                       $cpu{'l2-cache'} = $1;
+                               }
+                               elsif ($line[1] =~ /(\d+)\sMB$/){
+                                       $cpu{'l2-cache'} = ($1*1024);
+                               }
+                       }
+                       if (!$cpu{'flags'} && ($line[0] eq 'flags' || $line[0] eq 'features' )){
+                               $cpu{'flags'} = $line[1];
+                       }
+               }
+               if ( $extra > 0 && $type eq 'full' ){
+                       if ($line[0] eq 'bogomips'){
+                               # new arm shows bad bogomip value, so don't use it
+                               $cpu{'bogomips'} += $line[1] if $line[1] > 50;
+                       }
+               }
+               if ($b_admin ){
+                       if ( !$cpu{'bugs'} && $line[0] eq 'bugs'){
+                               $cpu{'bugs'} = $line[1];
+                       }
+                       # unlike family and model id, microcode appears to be hex already
+                       if ( !$cpu{'microcode'} && $line[0] eq 'microcode'){
+                               if ($line[1] =~ /0x/){
+                                       $cpu{'microcode'} = uc($line[1]);
+                                       $cpu{'microcode'} =~ s/^0X//;
+                               }
+                               else {
+                                       $cpu{'microcode'} = uc(sprintf("%x", $line[1]));
+                               }
+                       }
+               }
+       }
+       $cpu{'phys'} = scalar @phys_cpus;
+       $cpu{'dies'} = $die_id++; # count starts at 0, all cpus have 1 die at least
+       if ($b_arm){
+               if ($cpu{'dies'} <= 1){
+                       my $arm_dies = cpu_dies_sys();
+                       # case were 4 core arm returned 4 sibling lists, obviously wrong
+                       $cpu{'dies'} = $arm_dies if $arm_dies && $proc_count != $arm_dies;
+               }
+               $cpu{'type'} = 'arm' if !$cpu{'type'};
+               if (!$bsd_type){
+                       my %arm_cpus = arm_cpu_name();
+                       $cpu{'arm-cpus'} = \%arm_cpus if %arm_cpus;
+               }
+       }
+       $cpu{'ids'} = (\@ids);
+       if ( $extra > 0 && !$cpu{'arch'} && $type ne 'short' ){
+               $cpu{'arch'} = cpu_arch($cpu{'type'},$cpu{'family'},$cpu{'model_id'});
+               $cpu{'arch'} = $cpu_arch if (!$cpu{'arch'} && $cpu_arch && ($b_mips || $b_arm))
+               #print "$cpu{'type'},$cpu{'family'},$cpu{'model_id'},$cpu{'arch'}\n";
+       }
+       if (!$speeds{'cur-freq'}){
+               $cpu{'cur-freq'} = $cpu{'processors'}[0];
+               $speeds{'min-freq'} = 0;
+               $speeds{'max-freq'} = 0;
+       }
+       else {
+               $cpu{'cur-freq'} = $speeds{'cur-freq'};
+               $cpu{'min-freq'} = $speeds{'min-freq'};
+               $cpu{'max-freq'} = $speeds{'max-freq'};
+       }
+       main::log_data('dump','%cpu',\%cpu) if $b_log;
+       print Data::Dumper::Dumper \%cpu if $test[8];
+       eval $end if $b_log;
+       return %cpu;
+}
+
+sub data_sysctl {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my %cpu = set_cpu_data();
+       my (@ids,@line,%speeds,@working);
+       my ($sep) = ('');
+       my ($cache,$die_holder,$die_id,$phys_holder,$phys_id,$proc_count,$speed) = (0,0,0,0,0,0,0);
+       foreach (@sysctl){
+               @line = split /\s*:\s*/, $_;
+               next if ! $line[0];
+               # darwin shows machine, like MacBook7,1, not cpu
+               # machdep.cpu.brand_string: Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz
+               if ( ($bsd_type ne 'darwin' && $line[0] eq 'hw.model' ) || $line[0] eq 'machdep.cpu.brand_string' ){
+                       # cut L2 cache/cpu max speed out of model string, if available
+                       # openbsd 5.6: AMD Sempron(tm) Processor 3400+ ("AuthenticAMD" 686-class, 256KB L2 cache)
+                       # freebsd 10: hw.model: AMD Athlon(tm) II X2 245 Processor
+                       $line[1] = main::cleaner($line[1]);
+                       $line[1] = cpu_cleaner($line[1]);
+                       if ( $line[1] =~ /([0-9]+)[\-[:space:]]*([KM]B)\s+L2 cache/) {
+                               my $multiplier = ($2 eq 'MB') ? 1024: 1;
+                               $cpu{'l2-cache'} = $1 * $multiplier;
+                       }
+                       if ( $line[1] =~ /([^0-9\.][0-9\.]+)[\-[:space:]]*[MG]Hz/) {
+                               $cpu{'max-freq'} = $1;
+                               if ($cpu{'max-freq'} =~ /MHz/i) {
+                                       $cpu{'max-freq'} =~ s/[\-[:space:]]*MHz//;
+                                       $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz');
+                               }
+                               elsif ($cpu{'max-freq'} =~ /GHz/) {
+                                       $cpu{'max-freq'} =~ s/[\-[:space:]]*GHz//i;
+                                       $cpu{'max-freq'} = $cpu{'max-freq'} / 1000;
+                                       $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz');
+                               }
+                       }
+                       if ( $line[1] =~ /\)$/ ){
+                               $line[1] =~ s/\s*\(.*\)$//;
+                       }
+                       $cpu{'model_name'} = $line[1];
+                       $cpu{'type'} = cpu_vendor($line[1]);
+               }
+               # NOTE: hw.l1icachesize: hw.l1dcachesize:
+               elsif ($line[0] eq 'hw.l1icachesize') {
+                       $cpu{'l1-cache'} = $line[1]/1024;
+               }
+               elsif ($line[0] eq 'hw.l2cachesize') {
+                       $cpu{'l2-cache'} = $line[1]/1024;
+               }
+               # this is in mghz in samples
+               elsif ($line[0] eq 'hw.clockrate' || $line[0] eq 'hw.cpuspeed') {
+                       $cpu{'cur-freq'} = $line[1];
+               }
+               # these are in hz: 2400000000
+               elsif ($line[0] eq 'hw.cpufrequency') {
+                       $cpu{'cur-freq'} = $line[1]/1000000;
+               }
+               elsif ($line[0] eq 'hw.busfrequency_min') {
+                       $cpu{'min-freq'} = $line[1]/1000000;
+               }
+               elsif ($line[0] eq 'hw.busfrequency_max') {
+                       $cpu{'max-freq'} = $line[1]/1000000;
+               }
+               elsif ($line[0] eq 'machdep.cpu.vendor') {
+                       $cpu{'type'} = cpu_vendor($line[1]);
+               }
+               # darwin only?
+               elsif ($line[0] eq 'machdep.cpu.features') {
+                       $cpu{'flags'} = lc($line[1]);
+               }
+               elsif ($line[0] eq 'hw.ncpu' ) {
+                       $cpu{'cores'} = $line[1];
+               }
+               # Freebsd does some voltage hacking to actually run at lowest listed frequencies.
+               # The cpu does not actually support all the speeds output here but works in freebsd. 
+               elsif ($line[0] eq 'dev.cpu.0.freq_levels') {
+                       $line[1] =~ s/^\s+|\/[0-9]+|\s+$//g;
+                       if ( $line[1] =~ /[0-9]+\s+[0-9]+/ ) {
+                               my @temp = split /\s+/, $line[1];
+                               $cpu{'max-freq'} = $temp[0];
+                               $cpu{'min-freq'} = $temp[-1];
+                               $cpu{'scalings'} = \@temp;
+                       }
+               }
+               elsif (!$cpu{'cur-freq'} && $line[0] eq 'dev.cpu.0.freq' ) {
+                       $cpu{'cur-freq'} = $line[1];
+               }
+               # the following have only been seen in DragonflyBSD data but thumbs up!
+               elsif ($line[0] eq 'hw.cpu_topology.members' ) {
+                       my @temp = split /\s+/, $line[1];
+                       my $count = scalar @temp;
+                       $count-- if $count > 0;
+                       $cpu{'processors'}[$count] = 0;
+                       # no way to get per processor speeds yet, so assign 0 to each
+                       foreach (0 .. $count){
+                               $cpu{'processors'}[$_] = 0;
+                       }
+               }
+               elsif ($line[0] eq 'hw.cpu_topology.cpu1.physical_siblings' ) {
+                       # string, like: cpu0 cpu1
+                       my @temp = split /\s+/, $line[1];
+                       $cpu{'siblings'} = scalar @temp;
+               }
+               # increment by 1 for every new physical id we see. These are in almost all cases
+               # separate cpus, not separate dies within a single cpu body.
+               elsif ( $line[0] eq 'hw.cpu_topology.cpu0.physical_id' ){
+                       if ($phys_holder != $line[1] ){
+                               $phys_id++;
+                               $phys_holder = $line[1];
+                               $ids[$phys_id] = ([(0)]);
+                               $ids[$phys_id][$die_id] = ([(0)]);
+                       }
+               }
+               elsif ( $line[0] eq 'hw.cpu_topology.cpu0.core_id' ){
+                       if ($line[1] > 0 ){
+                               $die_holder = $line[1];
+                       }
+                       # this handles multi die cpus like 16 core ryzen
+                       elsif ($line[1] == 0 && $die_holder > 0 ){
+                               $die_id++ ;
+                               $die_holder = $line[1];
+                       }
+                       $ids[$phys_id][$die_id][$line[1]] = $speed;
+                       $cpu{'dies'} = $die_id;
+               }
+       }
+       if (!$cpu{'flags'}){
+               $cpu{'flags'} = cpu_flags_bsd();
+       }
+       main::log_data('dump','%cpu',\%cpu) if $b_log;
+       print Data::Dumper::Dumper \%cpu if $test[8];
+       eval $end if $b_log;
+       return %cpu;
+}
+
+sub cpu_properties {
+       my (%cpu) = @_;
+       my ($b_amd_zen,$b_epyc,$b_ht,$b_intel,$b_ryzen,$b_xeon);
+       if ($cpu{'type'} ){
+               if ($cpu{'type'} eq 'intel'){
+                       $b_intel = 1;
+                       $b_xeon = 1 if $cpu{'model_name'} =~ /Xeon/i;
+               }
+               elsif ($cpu{'type'} eq 'amd' ){
+                       if ( $cpu{'family'} && $cpu{'family'} eq '17' ) {
+                               $b_amd_zen = 1;
+                               if ($cpu{'model_name'} ){
+                                       if ($cpu{'model_name'} =~ /Ryzen/i ){ 
+                                               $b_ryzen = 1;
+                                       }
+                                       elsif ($cpu{'model_name'} =~ /EPYC/i){
+                                               $b_epyc = 1;
+                                       }
+                               }
+                       }
+               }
+       }
+       #my @dies = $phys[0][0];
+       my $ref = $cpu{'ids'};
+       my @phys = @$ref;
+       my $phyical_count = 0;
+       #my $phyical_count = scalar @phys;
+       my @processors;
+       my ($speed,$speed_key);
+       # handle case where cpu reports say, phys id 0, 2, 4, 6 [yes, seen it]
+       foreach (@phys) {
+               $phyical_count++ if $_;
+       }
+       $phyical_count ||= 1; # assume 1 if no id found, as with ARM
+       # count unique processors ##
+       # note, this fails for intel cpus at times
+       $ref = $cpu{'processors'};
+       @processors = @$ref;
+       #print ref $cpu{'processors'}, "\n";
+       my $processors_count = scalar @processors;
+       #print "p count:$processors_count\n";
+       #print Data::Dumper::Dumper \@processors;
+       # $cpu_cores is per physical cpu
+       my ($cpu_layout,$cpu_type,$min_max,$min_max_key) = ('','','','');
+       my ($cache,$core_count,$cpu_cores,$die_count) = (0,0,0,0);
+       foreach my $die_ref ( @phys ){
+               next if ! $die_ref;
+               my @dies = @$die_ref;
+               $core_count = 0;
+               $die_count = scalar @dies;
+               #$cpu{'dies'} = $die_count;
+               foreach my $core_ref (@dies){
+                       next if ref $core_ref ne 'ARRAY';
+                       my @cores = @$core_ref;
+                       $core_count = 0;# reset for each die!!
+                       # NOTE: the counters can be undefined because the index comes from 
+                       # core id: which can be 0 skip 1 then 2, which leaves index 1 undefined
+                       # arm cpus do not actually show core id so ignore that counter
+                       foreach my $id (@cores){
+                               $core_count++ if defined $id && !$b_arm;
+                       }
+                       #print 'cores: ' . $core_count, "\n";
+               }
+       }
+       # this covers potentially cases where ARM cpus have > 1 die 
+       $cpu{'dies'} = ($b_arm && $die_count <= 1 && $cpu{'dies'} > 1) ? $cpu{'dies'}: $die_count;
+       # this is an attempt to fix the amd family 15 bug with reported cores vs actual cores
+       # NOTE: amd A6-4400M APU 2 core reports: cores: 1 siblings: 2
+       # NOTE: AMD A10-5800K APU 4 core reports: cores: 2 siblings: 4
+       if ($cpu{'cores'} && ! $core_count || $cpu{'cores'} >= $core_count){
+               $cpu_cores = $cpu{'cores'};
+       }
+       elsif ($core_count > $cpu{'cores'}){
+               $cpu_cores = $core_count;
+       }
+       #print "cpu-c:$cpu_cores\n";
+       #$cpu_cores = $cpu{'cores'}; 
+       # like, intel core duo
+       # NOTE: sadly, not all core intel are HT/MT, oh well...
+       # xeon may show wrong core / physical id count, if it does, fix it. A xeon
+       # may show a repeated core id : 0 which gives a fake num_of_cores=1
+       if ($b_intel){
+               if ($cpu{'siblings'} && $cpu{'siblings'} > 1 && $cpu{'cores'} && $cpu{'cores'} > 1 ){
+                       if ( $cpu{'siblings'}/$cpu{'cores'} == 1 ){
+                               $b_intel = 0;
+                               $b_ht = 0;
+                       }
+                       else {
+                               $cpu_cores = ($cpu{'siblings'}/2); 
+                               $b_ht = 1;
+                       }
+               }
+       }
+       # ryzen is made out of blocks of 8 core dies
+       elsif ($b_ryzen){
+               $cpu_cores = $cpu{'cores'}; 
+                # note: posix ceil isn't present in Perl for some reason, deprecated?
+               my $working = $cpu_cores / 8;
+               my @temp = split /\./, $working;
+               $cpu{'dies'} = ($temp[1] && $temp[1] > 0) ? $temp[0]++ : $temp[0];
+       }
+       # these always have 4 dies
+       elsif ($b_epyc) {
+               $cpu_cores = $cpu{'cores'}; 
+               $cpu{'dies'} = 4;
+       }
+       # final check, override the num of cores value if it clearly is wrong
+       # and use the raw core count and synthesize the total instead of real count
+       if ( $cpu_cores == 0 && ($cpu{'cores'} * $phyical_count > 1)){
+               $cpu_cores = ($cpu{'cores'} * $phyical_count);
+       }
+       # last check, seeing some intel cpus and vms with intel cpus that do not show any
+       # core id data at all, or siblings.
+       if ($cpu_cores == 0 && $processors_count > 0){
+               $cpu_cores = $processors_count;
+       }
+       # this happens with BSDs which have very little cpu data available
+       if ( $processors_count == 0 && $cpu_cores > 0 ){
+               $processors_count = $cpu_cores;
+               if ($bsd_type && ($b_ht || $b_amd_zen) && $cpu_cores > 2 ){
+                       $cpu_cores = $cpu_cores/2;;
+               }
+               my $count = $processors_count;
+               $count-- if $count > 0;
+               $cpu{'processors'}[$count] = 0;
+               # no way to get per processor speeds yet, so assign 0 to each
+               # must be a numeric value. Could use raw speed from core 0, but 
+               # that would just be a hack.
+               foreach (0 .. $count){
+                       $cpu{'processors'}[$_] = 0;
+               }
+       }
+       # last test to catch some corner cases 
+       # seen a case where a xeon vm in a dual xeon system actually had 2 cores, no MT
+       # so it reported 4 siblings, 2 cores, but actually only had 1 core per virtual cpu
+       #print "prc: $processors_count phc: $phyical_count coc: $core_count cpc: $cpu_cores\n";
+       if (!$b_arm && $processors_count == $phyical_count*$core_count && $cpu_cores > $core_count){
+               $b_ht = 0;
+               #$b_xeon = 0;
+               $b_intel = 0;
+               $cpu_cores = 1;
+               $core_count = 1;
+               $cpu{'siblings'} = 1;
+       }
+       #print "pc: $processors_count s: $cpu{'siblings'} cpuc: $cpu_cores corec: $core_count\n";
+       # Algorithm:
+       # if > 1 processor && processor id (physical id) == core id then Multi threaded (MT)
+       # if siblings > 1 && siblings ==  2 * num_of_cores ($cpu{'cores'}) then Multi threaded (MT)
+       # if > 1 processor && processor id (physical id) != core id then Multi-Core Processors (MCP)
+       # if > 1 processor && processor ids (physical id) > 1 then Symmetric Multi Processing (SMP)
+       # if = 1 processor then single core/processor Uni-Processor (UP)
+       if ( $processors_count > 1 || ( $b_intel && $cpu{'siblings'} > 0 ) ) {
+               # non-multicore MT
+               if ($processors_count == ($phyical_count * $cpu_cores * 2)){
+                       #print "mt:1\n";
+                       $cpu_type .= 'MT'; 
+               }
+#              elsif ($b_xeon && $cpu{'siblings'} > 1){
+#                      #print "mt:2\n";
+#                      $cpu_type .= 'MT'; 
+#              }
+               elsif ($cpu{'siblings'} > 1 && ($cpu{'siblings'} == 2 * $cpu_cores )){
+                       #print "mt:3\n";
+                       $cpu_type .= 'MT'; 
+               }
+               # non-MT multi-core or MT multi-core
+               if ( ($processors_count == $cpu_cores ) || ($phyical_count < $cpu_cores)){
+                       my $sep = ($cpu_type) ? ' ' : '' ;
+                       $cpu_type .= $sep . 'MCP'; 
+               }
+               # only solidly known > 1 die cpus will use this, ryzen and arm for now
+               if ( $cpu{'dies'} > 1 ){
+                       my $sep = ($cpu_type) ? ' ' : '' ;
+                       $cpu_type .= $sep . 'MCM'; 
+               }
+               # >1 cpu sockets active: Symetric Multi Processing
+               if ($phyical_count > 1){
+                       my $sep = ($cpu_type) ? ' ' : '' ;
+                       $cpu_type .= $sep . 'SMP'; 
+               }
+       }
+       else {
+               $cpu_type = 'UP';
+       }
+       if ($phyical_count > 1){
+               $cpu_layout = $phyical_count . 'x ';
+       }
+       $cpu_layout .= count_alpha($cpu_cores) . 'Core';
+       $cpu_layout .= ' (' . $cpu{'dies'}. '-Die)' if !$bsd_type && $cpu{'dies'} > 1;
+       # the only possible change for bsds is if we can get phys counts in the future
+       if ($bsd_type){
+               $cache = $cpu{'l2-cache'} * $phyical_count;
+       }
+       # AMD SOS chips appear to report full L2 cache per core
+       elsif ($cpu{'type'} eq 'amd' && ($cpu{'family'} eq '14' || $cpu{'family'} eq '15' || $cpu{'family'} eq '16')){
+               $cache = $cpu{'l2-cache'} * $phyical_count;
+       }
+       elsif ($cpu{'type'} ne 'intel'){
+               $cache = $cpu{'l2-cache'} * $cpu_cores * $phyical_count;
+       }
+       ## note: this handles how intel reports L2, total instead of per core like AMD does
+       # note that we need to multiply by number of actual cpus here to get true cache size
+       else {
+               $cache = $cpu{'l2-cache'} * $phyical_count;
+       }
+       if ($cache > 10000){
+               $cache = sprintf("%.01f MiB",$cache/1024); # trim to no decimals?
+       }
+       elsif ($cache > 0){
+               $cache = "$cache KiB";
+       }
+       if ($cpu{'cur-freq'} && $cpu{'min-freq'} && $cpu{'max-freq'} ){
+               $min_max = "$cpu{'min-freq'}/$cpu{'max-freq'} MHz";
+               $min_max_key = "min/max";
+               $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed';
+               $speed = "$cpu{'cur-freq'} MHz";
+       }
+       elsif ($cpu{'cur-freq'} && $cpu{'max-freq'}){
+               $min_max = "$cpu{'max-freq'} MHz";
+               $min_max_key = "max";
+               $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed';
+               $speed = "$cpu{'cur-freq'} MHz";
+       }
+#      elsif ($cpu{'cur-freq'} && $cpu{'max-freq'} && $cpu{'cur-freq'} == $cpu{'max-freq'}){
+#              $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed';
+#              $speed = "$cpu{'cur-freq'} MHz (max)";
+#      }
+       elsif ($cpu{'cur-freq'} && $cpu{'min-freq'}){
+               $min_max = "$cpu{'min-freq'} MHz";
+               $min_max_key = "min";
+               $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed';
+               $speed = "$cpu{'cur-freq'} MHz";
+       }
+       elsif ($cpu{'cur-freq'} && !$cpu{'max-freq'}){
+               $speed_key = ($show{'short'} || $show{'cpu-basic'}) ? 'speed' : 'Speed';
+               $speed = "$cpu{'cur-freq'} MHz";
+       }
+       
+       if ( !$bits_sys && !$b_arm && $cpu{'flags'} ){
+               $bits_sys = ($cpu{'flags'} =~ /\blm\b/) ? 64 : 32;
+       }
+       my %cpu_properties = (
+       'bits-sys' => $bits_sys,
+       'cpu-layout' => $cpu_layout,
+       'cpu-type' => $cpu_type,
+       'min-max-key' => $min_max_key,
+       'min-max' => $min_max,
+       'speed-key' => $speed_key,
+       'speed' => $speed,
+       'l2-cache' => $cache,
+       );
+       main::log_data('dump','%cpu_properties',\%cpu_properties) if $b_log;
+       #print Data::Dumper::Dumper \%cpu;
+       #print Data::Dumper::Dumper \%cpu_properties;
+       #my $dc = scalar @dies;
+       #print 'phys: ' . $pc . ' dies: ' . $dc, "\n";
+       eval $end if $b_log;
+       return %cpu_properties;
+}
+sub cpu_speeds {
+       eval $start if $b_log;
+       my (@processors) = @_;
+       my (@speeds);
+       my @files = main::globber('/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq');
+       foreach (@files){
+               my $speed = (main::reader($_))[0];
+               if ($speed || $speed eq '0'){
+                       $speed = sprintf "%.0f", $speed/1000;
+                       push @speeds, $speed;
+               }
+       }
+       if (!@speeds){
+               foreach (@processors){
+                       if ($_ || $_ eq '0'){
+                               $_ = sprintf "%.0f", $_;
+                               push @speeds, $_;
+                       }
+               }
+       }
+       #print join '; ', @speeds, "\n";
+       eval $end if $b_log;
+       return @speeds;
+}
+sub set_cpu_speeds_sys {
+       eval $start if $b_log;
+       my (@arm,%speeds);
+       my $sys = '/sys/devices/system/cpu/cpufreq/policy0';
+       my $sys2 = '/sys/devices/system/cpu/cpu0/cpufreq/';
+       my ($cur,$min,$max) = ('scaling_cur_freq','scaling_min_freq','scaling_max_freq');
+       if (!-d $sys && -d $sys2){
+               $sys = $sys2;
+               ($cur,$min,$max) = ('scaling_cur_freq','cpuinfo_min_freq','cpuinfo_max_freq');
+       }
+       if (-d $sys){
+               $speeds{'cur-freq'} = (main::reader("$sys/$cur"))[0];
+               $speeds{'cur-freq'} = speed_cleaner($speeds{'cur-freq'},'khz');
+               $speeds{'min-freq'} = (main::reader("$sys/$min"))[0];
+               $speeds{'min-freq'} = speed_cleaner($speeds{'min-freq'},'khz');
+               $speeds{'max-freq'} = (main::reader("$sys/$max"))[0];
+               $speeds{'max-freq'} = speed_cleaner($speeds{'max-freq'},'khz');
+               if ($b_arm){
+                       @arm = main::globber('/sys/devices/system/cpu/cpufreq/policy*/');
+                       # there are arm chips with two dies, that run at different min max speeds!!
+                       # see: https://github.com/smxi/inxi/issues/128
+                       # it would be slick to show both die min/max/cur speeds, but this is
+                       # ok for now.
+                       if (scalar @arm > 1){
+                               my ($current,$max,$min) = (0,0,0);
+                               foreach (@arm){
+                                       $_ =~ s/\/$//; # strip off last slash in case globs have them
+                                       my $max_temp = main::reader("$_/cpuinfo_max_freq");
+                                       $max_temp = speed_cleaner($max_temp,'khz');
+                                       if ($max_temp > $max){
+                                               $max = $max_temp;
+                                       }
+                                       my $min_temp = main::reader("$_/cpuinfo_min_freq");
+                                       $min_temp = speed_cleaner($min_temp,'khz');
+                                       if ($min_temp < $min || $min == 0){
+                                               $max = $min_temp;
+                                       }
+                                       my $cur_temp = main::reader("$_/cpuinfo_max_freq");
+                                       $cur_temp = speed_cleaner($cur_temp,'khz');
+                                       if ($cur_temp > $current){
+                                               $current = $cur_temp;
+                                       }
+                               }
+                               $speeds{'cur-freq'} = $current if $current;
+                               $speeds{'max-freq'} = $max if $max;
+                               $speeds{'min-freq'} = $min if $min;
+                       }
+               }
+               # policy4/cpuinfo_max_freq:["2000000"]
+               # policy4/cpuinfo_min_freq:["200000"]
+               if ($speeds{'min-freq'} > $speeds{'max-freq'} || $speeds{'min-freq'} == $speeds{'max-freq'}){
+                       $speeds{'min-freq'} = 0;
+               }
+       }
+       main::log_data('dump','%speeds',\%speeds) if $b_log;
+       eval $end if $b_log;
+       return %speeds;
+}
+
+# right now only using this for ARM cpus, this is not the same in intel/amd
+sub cpu_dies_sys {
+       eval $start if $b_log;
+       my @data = main::globber('/sys/devices/system/cpu/cpu*/topology/core_siblings_list');
+       my (@dies);
+       foreach (@data){
+               my $siblings = (main::reader($_))[0];
+               if (! grep {/$siblings/} @dies){
+                       push @dies, $siblings;
+               }
+       }
+       my $die_count = scalar @dies;
+       eval $end if $b_log;
+       return $die_count;
+}
+sub cpu_flags_bsd {
+       eval $start if $b_log;
+       my ($flags,$sep) = ('','');
+       # this will be null if it was not readable
+       my $file = main::system_files('dmesg-boot');
+       if ( @dmesg_boot){
+               foreach (@dmesg_boot){
+                       if ( /Features/ || ( $bsd_type eq "openbsd" && /^cpu0:\s*[a-z0-9]{2,3}(\s|,)[a-z0-9]{2,3}(\s|,)/i ) ) {
+                               my @line = split /:\s*/, lc($_);
+                               # free bsd has to have weird syntax: <....<b23>,<b34>>
+                               # Features2=0x1e98220b<SSE3,PCLMULQDQ,MON,SSSE3,CX16,SSE4.1,SSE4.2,POPCNT,AESNI,XSAVE,OSXSAVE,AVX>
+                               $line[1] =~ s/^[^<]*<|>[^>]*$//g;
+                               # then get rid of <b23> stuff
+                               $line[1] =~ s/<[^>]+>//g;
+                               # and replace commas with spaces
+                               $line[1] =~ s/,/ /g;
+                               $flags .= $sep . $line[1];
+                               $sep = ' ';
+                       }
+                       elsif (/real mem/){
+                               last;
+                       }
+               }
+               if ($flags){
+                       $flags =~ s/\s+/ /g;
+                       $flags =~ s/^\s+|\s+$//g;
+               }
+       }
+       else {
+               if ( $file && ! -r $file ){
+                       $flags = main::row_defaults('dmesg-boot-permissions');
+               }
+       }
+       eval $end if $b_log;
+       return $flags;
+}
+
+sub cpu_vendor {
+       eval $start if $b_log;
+       my ($string) = @_;
+       my ($vendor) = ('');
+       $string = lc($string);
+       if ($string =~ /intel/) {
+               $vendor = "intel"
+       }
+       elsif ($string =~ /amd/){
+               $vendor = "amd"
+       }
+       # via
+       elsif ($string =~ /centaur/){
+               $vendor = "centaur"
+       }
+       eval $end if $b_log;
+       return $vendor;
+}
+sub get_boost_status {
+       eval $start if $b_log;
+       my ($boost);
+       my $path = '/sys/devices/system/cpu/cpufreq/boost';
+       if (-f $path){
+               $boost = (main::reader($path))[0];
+               if (defined $boost && $boost =~/^[01]$/){
+                       $boost = ($boost) ? 'enabled' : 'disabled';
+               }
+       }
+       eval $end if $b_log;
+       return $boost;
+}
+sub arm_cpu_name {
+       eval $start if $b_log;
+       my (%cpus,$compat);
+       if ( -e '/sys/firmware/devicetree/base/cpus/cpu@1/compatible' ){
+               my @working = main::globber('/sys/firmware/devicetree/base/cpus/cpu@*/compatible');
+               foreach my $file (@working){
+                       $compat = (main::reader($file))[0];
+                       # these can have non printing ascii... why? As long as we only have the 
+                       # splits for: null 00/start header 01/start text 02/end text 03
+                       $compat = (split /\x01|\x02|\x03|\x00/, $compat)[0] if $compat;
+                       $compat = (split /,\s*/, $compat)[-1] if $compat;
+                       $cpus{$compat} = ($cpus{$compat}) ? ++$cpus{$compat}: 1;
+               }
+       }
+       main::log_data('dump','%cpus',\%cpus) if $b_log;
+       eval $end if $b_log;
+       return %cpus;
+}
+
+sub cpu_arch {
+       eval $start if $b_log;
+       my ($type,$family,$model) = @_;
+       my $arch = '';
+       # https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
+       # print "$type;$family;$model\n";
+       if ( $type eq 'amd'){
+               if ($family eq '4'){
+                       if ( $model =~ /^(3|7|8|9|A)$/ ) {$arch = 'Am486'}
+                       elsif ( $model =~ /^(E|F)$/ ) {$arch = 'Am5x86'}
+               }
+               elsif ($family eq '5'){
+                       if ( $model =~ /^(0|1|2|3)$/ ) {$arch = 'K5'}
+                       elsif ( $model =~ /^(6|7)$/ ) {$arch = 'K6'}
+                       elsif ( $model =~ /^(8)$/ ) {$arch = 'K6-2'}
+                       elsif ( $model =~ /^(9|D)$/ ) {$arch = 'K6-3'}
+                       elsif ( $model =~ /^(A)$/ ) {$arch = 'Geode'}
+                       }
+               elsif ($family eq '6'){
+                       if ( $model =~ /^(1|2)$/ ) {$arch = 'K7'}
+                       elsif ( $model =~ /^(3|4)$/ ) {$arch = 'K7 Thunderbird'}
+                       elsif ( $model =~ /^(6|7|8|A)$/ ) {$arch = 'K7 Palomino+'}
+                       else {$arch = 'K7'}
+               }
+               elsif ($family eq 'F'){
+                       if ( $model =~ /^(4|5|7|8|B|C|E|F|14|15|17|18|1B|1C|1F)$/ ) {$arch = 'K8'}
+                       elsif ( $model =~ /^(21|23|24|25|27|28|2C|2F)$/ ) {$arch = 'K8 rev.E'}
+                       elsif ( $model =~ /^(41|43|48|4B|4C|4F|5D|5F|68|6B|6C|6F|7C|7F|C1)$/ ) {$arch = 'K8 rev.F+'}
+                       else {$arch = 'K8'}
+               }
+               elsif ($family eq '10'){
+                       if ( $model =~ /^(2|4|5|6|8|9|A)$/ ) {$arch = 'K10'}
+                       else {$arch = 'K10'}
+               }
+               elsif ($family eq '11'){
+                       if ( $model =~ /^(3)$/ ) {$arch = 'Turion X2 Ultra'}
+               }
+               # might also need cache handling like 14/16
+               elsif ($family eq '12'){
+                       if ( $model =~ /^(1)$/ ) {$arch = 'Fusion'}
+                       else {$arch = 'Fusion'}
+               }
+               # SOC, apu
+               elsif ($family eq '14'){
+                       if ( $model =~ /^(1|2)$/ ) {$arch = 'Bobcat'}
+                       else {$arch = 'Bobcat'}
+               }
+               elsif ($family eq '15'){
+                       if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)$/ ) {$arch = 'Bulldozer'}
+                       elsif ( $model =~ /^(10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F)$/ ) {$arch = 'Piledriver'}
+                       elsif ( $model =~ /^(30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)$/ ) {$arch = 'Steamroller'}
+                       elsif ( $model =~ /^(60|61|62|63|64|65|66|67|68|69|6A|6B|6C|6D|6E|6F|70|71|72|73|74|75|76|77|78|79|7A|7B|7C|7D|7E|7F)$/ ) {$arch = 'Excavator'}
+                       else {$arch = 'Bulldozer'}
+               }
+               # SOC, apu
+               elsif ($family eq '16'){
+                       if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)$/ ) {$arch = 'Jaguar'}
+                       elsif ( $model =~ /^(30|31|32|33|34|35|36|37|38|39|3A|3B|3C|3D|3E|3F)$/ ) {$arch = 'Puma'}
+                       else {$arch = 'Jaguar'}
+               }
+               elsif ($family eq '17'){
+                       if ( $model =~ /^(1)$/ ) {$arch = 'Zen'}
+                       else {$arch = 'Zen'}
+               }
+       }
+       elsif ( $type eq 'arm'){
+               if ($family ne ''){$arch="ARMv$family";}
+               else {$arch='ARM';}
+       }
+       # aka VIA
+       elsif ( $type eq 'centaur'){ 
+               if ($family eq '5'){
+                       if ( $model =~ /^(4)$/ ) {$arch = 'WinChip C6'}
+                       elsif ( $model =~ /^(8)$/ ) {$arch = 'WinChip 2'}
+                       elsif ( $model =~ /^(9)$/ ) {$arch = 'WinChip 3'}
+               }
+               elsif ($family eq '6'){
+                       if ( $model =~ /^(6)$/ ) {$arch = 'WinChip-based'}
+                       elsif ( $model =~ /^(7|8)$/ ) {$arch = 'C3'}
+                       elsif ( $model =~ /^(9)$/ ) {$arch = 'C3-2'}
+                       elsif ( $model =~ /^(A|D)$/ ) {$arch = 'C7'}
+                       elsif ( $model =~ /^(F)$/ ) {$arch = 'Isaiah'}
+               }
+       }
+       # https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers
+       elsif ( $type eq 'intel'){
+               if ($family eq '4'){
+                       if ( $model =~ /^(0|1|2|3|4|5|6|7|8|9)$/ ) {$arch = '486'}
+               }
+               elsif ($family eq '5'){
+                       if ( $model =~ /^(1|2|3|7)$/ ) {$arch = 'P5'}
+                       elsif ( $model =~ /^(4|8)$/ ) {$arch = 'P5'} # MMX
+                       elsif ( $model =~ /^(9)$/ ) {$arch = 'Quark'}
+               }
+               elsif ($family eq '6'){
+                       if ( $model =~ /^(1)$/ ) {$arch = 'P6 Pro'}
+                       elsif ( $model =~ /^(15)$/ ) {$arch = 'Dothan Tolapai'} # pentium M system on chip
+                       elsif ( $model =~ /^(3)$/ ) {$arch = 'P6 II Klamath'}
+                       elsif ( $model =~ /^(5)$/ ) {$arch = 'P6 II Deschutes'}
+                       elsif ( $model =~ /^(6)$/ ) {$arch = 'P6 II Mendocino'}
+                       elsif ( $model =~ /^(7)$/ ) {$arch = 'P6 III Katmai'}
+                       elsif ( $model =~ /^(8)$/ ) {$arch = 'P6 III Coppermine'}
+                       elsif ( $model =~ /^(9)$/ ) {$arch = 'Banias'} # pentium M
+                       elsif ( $model =~ /^(A)$/ ) {$arch = 'P6 III Xeon'}
+                       elsif ( $model =~ /^(B)$/ ) {$arch = 'P6 III Tualitin'}
+                       elsif ( $model =~ /^(D)$/ ) {$arch = 'Dothan'} # Pentium M
+                       elsif ( $model =~ /^(E)$/ ) {$arch = 'Yonah'}
+                       elsif ( $model =~ /^(F|16)$/ ) {$arch = 'Merom'}
+                       elsif ( $model =~ /^(17|1D)$/ ) {$arch = 'Penryn'}
+                       elsif ( $model =~ /^(1A|1E|1F|2E|25|2C|2F)$/ ) {$arch = 'Nehalem'}
+                       elsif ( $model =~ /^(1C)$/ ) {$arch = 'Bonnell'} # atom Bonnell? 27?
+                       elsif ( $model =~ /^(27|35)$/ ) {$arch = 'Saltwell'}
+                       elsif ( $model =~ /^(25|2C|2F)$/ ) {$arch = 'Westmere'}
+                       elsif ( $model =~ /^(26)$/ ) {$arch = 'Atom Lincroft'}
+                       elsif ( $model =~ /^(2A|2D)$/ ) {$arch = 'Sandy Bridge'}
+                       elsif ( $model =~ /^(36)$/ ) {$arch = 'Atom Cedarview'}
+                       elsif ( $model =~ /^(37|4A|4D|5A)$/ ) {$arch = 'Silvermont'}
+                       elsif ( $model =~ /^(3A|3E)$/ ) {$arch = 'Ivy Bridge'}
+                       elsif ( $model =~ /^(3C|3F|45|46)$/ ) {$arch = 'Haswell'}
+                       elsif ( $model =~ /^(3D|47|4F|56)$/ ) {$arch = 'Broadwell'}
+                       elsif ( $model =~ /^(4E|55|9E)$/ ) {$arch = 'Skylake'}
+                       elsif ( $model =~ /^(5E)$/ ) {$arch = 'Skylake-S'}
+                       elsif ( $model =~ /^(4C|5D)$/ ) {$arch = 'Airmont'}
+                       elsif ( $model =~ /^(8E|9E)$/ ) {$arch = 'Kaby Lake'}
+                       elsif ( $model =~ /^(57)$/ ) {$arch = 'Knights Landing'}
+                       elsif ( $model =~ /^(85)$/ ) {$arch = 'Knights Mill'}
+                       # product codes: https://en.wikipedia.org/wiki/List_of_Intel_microprocessors
+                       # coming: coffee lake; cannonlake; icelake; tigerlake
+               }
+               # itanium 1 family 7 all recalled
+               elsif ($family eq 'B'){
+                       if ( $model =~ /^(1)$/ ) {$arch = 'Knights Corne'}
+               }
+               elsif ($family eq 'F'){
+                       if ( $model =~ /^(0|1)$/ ) {$arch = 'Netburst Willamette'}
+                       elsif ( $model =~ /^(2)$/ ) {$arch = 'Netburst Northwood'}
+                       elsif ( $model =~ /^(3)$/ ) {$arch = 'Prescott'} # 6? Nocona
+                       elsif ( $model =~ /^(4)$/ ) {$arch = 'Smithfield'} # 6? Nocona
+                       elsif ( $model =~ /^(6)$/ ) {$arch = 'Presler'}
+                       else {$arch = 'Netburst'}
+               }
+       }
+       eval $end if $b_log;
+       return $arch;
+}
+
+sub count_alpha {
+       my ($count) = @_;
+       #print "$count\n";
+       my @alpha = qw(Single Dual Triple Quad);
+       if ($count > 4){
+               $count .= '-';
+       }
+       else {
+               $count = $alpha[$count-1] . ' ' if $count > 0;
+       }
+       return $count;
+}
+sub set_cpu_data {
+       my %cpu =  (
+       'arch' => '',
+       'bogomips' => 0,
+       'cores' => 0,
+       'cur-freq' => 0,
+       'dies' => 0,
+       'family' => '',
+       'flags' => '',
+       'ids' => [],
+       'l1-cache' => 0, # store in KB
+       'l2-cache' => 0, # store in KB
+       'l3-cache' => 0, # store in KB
+       'max-freq' => 0,
+       'min-freq' => 0,
+       'model_id' => '',
+       'model_name' => '',
+       'processors' => [],
+       'rev' => '',
+       'scalings' => [],
+       'siblings' => 0,
+       'type' => '',
+       );
+       return %cpu;
+}
+# MHZ - cell cpus
+sub speed_cleaner {
+       my ($speed,$opt) = @_;
+       return if ! $speed || $speed eq '0';
+       $speed =~ s/[GMK]HZ$//gi;
+       $speed = ($speed/1000) if $opt && $opt eq 'khz';
+       $speed = sprintf "%.0f", $speed;
+       return $speed;
+}
+sub cpu_cleaner {
+       my ($cpu) = @_;
+       return if ! $cpu;
+       my $filters = '@|cpu |cpu deca|([0-9]+|single|dual|two|triple|three|tri|quad|four|';
+       $filters .= 'penta|five|hepta|six|hexa|seven|octa|eight|multi)[ -]core|';
+       $filters .= 'ennea|genuine|multi|processor|single|triple|[0-9\.]+ *[MmGg][Hh][Zz]';
+       $cpu =~ s/$filters//ig;
+       $cpu =~ s/\s\s+/ /g;
+       $cpu =~ s/^\s+|\s+$//g;
+       return $cpu;
+}
+sub hex_and_decimal {
+       my ($data) = @_; 
+       if ($data){
+               $data .=  ' (' . hex($data) . ')' if hex($data) ne $data;
+       }
+       else {
+               $data = 'N/A';
+       }
+       return $data;
+}
+}
+
+## DiskData
+{
+package DiskData;
+my ($b_hddtemp,$b_nvme);
+my ($hddtemp,$nvme) = ('','');
+my (@by_id,@by_path);
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows,$key1,$val1);
+       my ($type) = @_;
+       $type ||= 'standard';
+       my $num = 0;
+       @data = disk_data($type);
+       # NOTE: 
+       if (@data){
+               if ($type eq 'standard'){
+                       @data = create_output(@data);
+                       @rows = (@rows,@data);
+                       if ( $bsd_type && !@dm_boot_disk && $type eq 'standard' && $show{'disk'} ){
+                               $key1 = 'Drive Report';
+                               my $file = main::system_files('dmesg-boot');
+                               if ( $file && ! -r $file){
+                                       $val1 = main::row_defaults('dmesg-boot-permissions');
+                               }
+                               elsif (!$file){
+                                       $val1 = main::row_defaults('dmesg-boot-missing');
+                               }
+                               else {
+                                       $val1 = main::row_defaults('disk-data-bsd');
+                               }
+                               @data = ({main::key($num++,$key1) => $val1,});
+                               @rows = (@rows,@data);
+                       }
+               }
+               else {
+                       @rows = @data;
+                       # print Data::Dumper::Dumper \@rows;
+               }
+       }
+       else {
+               $key1 = 'Message';
+               $val1 = main::row_defaults('disk-data');
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       if (!@rows){
+               $key1 = 'Message';
+               $val1 = main::row_defaults('disk-data');
+               @data = ({main::key($num++,$key1) => $val1,});
+       }
+       #@rows = (@rows,@data);
+       @data = ();
+       if ($show{'optical'} || $show{'optical-basic'}){
+               @data = OpticalData::get();
+               @rows = (@rows,@data);
+       }
+       ($b_hddtemp,$b_nvme,$hddtemp,$nvme) = (undef,undef,undef,undef);
+       (@by_id,@by_path) = (undef,undef);
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my (@disks) = @_;
+       #print Data::Dumper::Dumper \@disks;
+       my (@data,@rows);
+       my ($num,$j) = (0,0);
+       my ($id,$model,$size,$used,$percent,$size_holder,$used_holder) = ('','','','','','','');
+       my @sizing = main::get_size($disks[0]{'size'}) if $disks[0]{'size'};
+       #print Data::Dumper::Dumper \@disks;
+       if (@sizing){
+               $size = $sizing[0];
+               # note: if a string is returned there will be no Size unit so just use string.
+               if (defined $sizing[0] && $sizing[1]){
+                       $size .= ' ' . $sizing[1];
+               }
+       }
+       $size ||= 'N/A';
+       @sizing = main::get_size($disks[0]{'used'}) if $disks[0]{'used'};
+       if (@sizing){
+               $used = $sizing[0];
+               if (defined $sizing[0] && $sizing[1]){
+                       $used .= ' ' . $sizing[1];
+                       if (( $disks[0]{'size'} && $disks[0]{'size'} =~ /^[0-9]/ ) && 
+                           ( $disks[0]{'used'} =~ /^[0-9]/ ) ){
+                               $used = $used . ' (' . sprintf("%0.1f", $disks[0]{'used'}/$disks[0]{'size'}*100) . '%)';
+                       }
+               }
+       }
+       $used ||= 'N/A';
+       @data = ({
+       main::key($num++,'Local Storage') => '',
+       main::key($num++,'total') => $size,
+       main::key($num++,'used') => $used,
+       });
+       @rows = (@rows,@data);
+       shift @disks;
+       if ( $show{'disk'} && @disks){
+               @disks = sort { $a->{'id'} cmp $b->{'id'} } @disks;
+               foreach my $ref (@disks){
+                       ($id,$model,$size) = ('','','');
+                       my %row = %$ref;
+                       $num = 1;
+                       $model = ($row{'model'}) ? $row{'model'}: 'N/A';
+                       $id =  ($row{'id'}) ? "/dev/$row{'id'}":'N/A';
+                       my @sizing = main::get_size($row{'size'});
+                       #print Data::Dumper::Dumper \@disks;
+                       if (@sizing){
+                               $size = $sizing[0];
+                               # note: if a string is returned there will be no Size unit so just use string.
+                               if (defined $sizing[0] && $sizing[1]){
+                                       $size .= ' ' . $sizing[1];
+                                       $size_holder = $sizing[0];
+                               }
+                               $size ||= 'N/A';
+                       }
+                       else {
+                               $size = 'N/A';
+                       }
+                       $j = scalar @rows;
+                       @data = ({
+                       main::key($num++,'ID') => $id,
+                       });
+                       @rows = (@rows,@data);
+                       if ($row{'type'}){
+                               $rows[$j]{main::key($num++,'type')} = $row{'type'},
+                       }
+                       if ($row{'vendor'}){
+                               $rows[$j]{main::key($num++,'vendor')} = $row{'vendor'},
+                       }
+                       $rows[$j]{main::key($num++,'model')} = $model;
+                       $rows[$j]{main::key($num++,'size')} = $size;
+                       if ($extra > 1 && $row{'speed'}){
+                               $rows[$j]{main::key($num++,'speed')} = $row{'speed'};
+                               $rows[$j]{main::key($num++,'lanes')} = $row{'lanes'} if $row{'lanes'};
+                       }
+                       if ($extra > 2 && $row{'rotation'}){
+                               $rows[$j]{main::key($num++,'rotation')} = $row{'rotation'};
+                       }
+                       if ($extra > 1){
+                               my $serial = main::apply_filter($row{'serial'});
+                               $rows[$j]{main::key($num++,'serial')} = $serial;
+                               if ($row{'firmware'}){
+                                       $rows[$j]{main::key($num++,'rev')} = $row{'firmware'};
+                               }
+                       }
+                       if ($extra > 0 && $row{'temp'}){
+                               $rows[$j]{main::key($num++,'temp')} = $row{'temp'} . ' C';
+                       }
+                       # extra level tests already done
+                       if (defined $row{'partition-table'}){
+                               $rows[$j]{main::key($num++,'scheme')} = $row{'partition-table'};
+                       }
+               }
+       }
+
+       eval $end if $b_log;
+       return @rows;
+}
+sub disk_data {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my (@rows,@data,@devs);
+       my $num = 0;
+       my ($used) = (0);
+       PartitionData::partition_data() if !$b_partitions;
+       foreach my $ref (@partitions){
+               my %row = %$ref;
+               # don't count remote used, also, some cases mount 
+               # panfs is parallel NAS volume manager, need more data
+               next if ($row{'fs'} && $row{'fs'} =~ /nfs|panfs|sshfs|smbfs|unionfs/);
+               # in some cases, like redhat, mounted cdrom/dvds show up in partition data
+               next if ($row{'dev-base'} && $row{'dev-base'} =~ /^sr[0-9]+$/);
+               # this is used for specific cases where bind, or incorrect multiple mounts 
+               # to same partitions, or btrfs sub volume mounts, is present. The value is 
+               # searched for an earlier appearance of that partition and if it is present, 
+               # the data is not added into the partition used size.
+               if ( $row{'dev-base'} !~ /^\/\/|:\// && ! (grep {/$row{'dev-base'}/} @devs) ){
+                       $used += $row{'used'} if  $row{'used'};
+                       push @devs, $row{'dev-base'};
+               }
+       }
+       if (!$bsd_type && (my $file = main::system_files('partitions'))){
+               @data = proc_data($used,$file);
+       }
+       elsif ($bsd_type) {
+               @data = dmesg_boot_data($used);
+       }
+       #print Data::Dumper::Dumper \@data;
+       main::log_data('data',"used: $used") if $b_log;
+       eval $end if $b_log;
+       return @data;
+}
+sub proc_data {
+       eval $start if $b_log;
+       my ($used,$file) = @_;
+       my (@data,@drives);
+       my ($b_hdx,$size,$drive_size) = (0,0,0);
+       my @proc_partitions = main::reader($file,'strip');
+       shift @proc_partitions;
+       foreach (@proc_partitions){
+               next if (/^\s*$/);
+               my @row = split /\s+/, $_;
+               if ( $row[-1] =~ /^([hsv]d[a-z]+|(ada|mmcblk|n[b]?d|nvme[0-9]+n)[0-9]+)$/) {
+                       $drive_size = $row[2];
+                       $b_hdx = 1 if $row[-1] =~ /^hd[a-z]/;
+                       @data = ({
+                       'firmware' => '',
+                       'id' => $row[-1],
+                       'model' => '',
+                       'serial' => '',
+                       'size' => $drive_size,
+                       'spec' => '',
+                       'speed' => '',
+                       'temp' => '',
+                       'type' => '',
+                       'vendor' => '',
+                       });
+                       @drives = (@drives,@data);
+               }
+               # See http://lanana.org/docs/device-list/devices-2.6+.txt for major numbers used below
+               # See https://www.mjmwired.net/kernel/Documentation/devices.txt for kernel 4.x device numbers
+               # if ( $row[0] =~ /^(3|22|33|8)$/ && $row[1] % 16 == 0 )  {
+               #        $size += $row[2];
+               # }
+               # special case from this data: 8     0  156290904 sda
+               # 43        0   48828124 nbd0
+               # note: known starters: vm: 252/253/254; grsec: 202; nvme: 259 mmcblk: 179
+               if ( $row[0] =~ /^(3|8|22|33|43|179|202|252|253|254|259)$/ && 
+                    $row[-1] =~ /(mmcblk[0-9]+|n[b]?d[0-9]+|nvme[0-9]+n[0-9]+|[hsv]d[a-z]+)$/ && 
+                    ( $row[1] % 16 == 0 || $row[1] % 16 == 8 ) ) {
+                       $size += $row[2];
+               }
+       }
+       # print Data::Dumper::Dumper \@drives;
+       main::log_data('data',"size: $size") if $b_log;
+       @data = ({
+       'size' => $size,
+       'used' => $used,
+       });
+       #print Data::Dumper::Dumper \@data;
+       if ( $show{'disk'} ){
+               @drives = (@data,@drives);
+               # print 'drives:', Data::Dumper::Dumper \@drives;
+               @data = proc_data_advanced($b_hdx,@drives);
+       }
+       main::log_data('dump','@data',\@data) if $b_log;
+       # print Data::Dumper::Dumper \@data;
+       eval $end if $b_log;
+       return @data;
+}
+sub proc_data_advanced {
+       eval $start if $b_log;
+       my ($b_hdx,@drives) = @_;
+       my ($i) = (0);
+       my (@data,@disk_data,@rows,@scsi,@temp,@working);
+       my ($pt_cmd) = ('unset');
+       my ($block_type,$file,$firmware,$model,$path,$partition_scheme,
+       $serial,$vendor,$working_path);
+       @by_id = main::globber('/dev/disk/by-id/*');
+       # these do not contain any useful data, no serial or model name
+       # wwn-0x50014ee25fb50fc1 and nvme-eui.0025385b71b07e2e 
+       # scsi-SATA_ST980815A_ simply repeats ata-ST980815A_; same with scsi-0ATA_WDC_WD5000L31X
+       # we also don't need the partition items
+       my $pattern = '^\/dev\/disk\/by-id\/(md-|lvm-|dm-|wwn-|nvme-eui|raid-|scsi-([0-9]ATA|SATA))|-part[0-9]+$';
+       @by_id = grep {!/$pattern/} @by_id if @by_id;
+       # print join "\n", @by_id, "\n";
+       @by_path = main::globber('/dev/disk/by-path/*');
+       ## check for all ide type drives, non libata, only do it if hdx is in array
+       ## this is now being updated for new /sys type paths, this may handle that ok too
+       ## skip the first rows in the loops since that's the basic size/used data
+       if ($b_hdx){
+               for ($i = 1; $i < scalar @drives; $i++){
+                       $file = "/proc/ide/$drives[$i]{'id'}/model";
+                       if ( $drives[$i]{'id'} =~ /^hd[a-z]/ && -e $file){
+                               $model = (main::reader($file,'strip'))[0];
+                               $drives[$i]{'model'} = $model;
+                       }
+               }
+       }
+       # scsi stuff
+       if ($file = main::system_files('scsi')){
+               @scsi = scsi_data($file);
+       }
+       # print 'drives:', Data::Dumper::Dumper \@drives;
+       for ($i = 1; $i < scalar @drives; $i++){
+               #next if $drives[$i]{'id'} =~ /^hd[a-z]/;
+               ($block_type,$firmware,$model,$partition_scheme,
+               $serial,$vendor,$working_path) = ('','','','','','','');
+               if ($extra > 2){
+                       @data = advanced_disk_data($pt_cmd,$drives[$i]{'id'});
+                       $pt_cmd = $data[0];
+                       $drives[$i]{'partition-table'} = uc($data[1]) if $data[1];
+                       $drives[$i]{'rotation'} = "$data[2] rpm" if $data[2];
+               }
+               #print "$drives[$i]{'id'}\n";
+               @disk_data = disk_data_by_id("/dev/$drives[$i]{'id'}");
+               main::log_data('dump','@disk_data', \@disk_data) if $b_log;
+               if ($drives[$i]{'id'} =~ /[sv]d[a-z]/){
+                       $block_type = 'sdx';
+                       $working_path = "/sys/block/$drives[$i]{'id'}/device/";
+               }
+               elsif ($drives[$i]{'id'} =~ /mmcblk/){
+                       $block_type = 'mmc';
+                       $working_path = "/sys/block/$drives[$i]{'id'}/device/";
+               }
+               elsif ($drives[$i]{'id'} =~ /nvme/){
+                       $block_type = 'nvme';
+                       # this results in:
+                       # /sys/devices/pci0000:00/0000:00:03.2/0000:06:00.0/nvme/nvme0/nvme0n1
+                       # but we want to go one level down so slice off trailing nvme0n1
+                       $working_path = Cwd::abs_path("/sys/block/$drives[$i]{'id'}");
+                       $working_path =~ s/nvme[^\/]*$//;
+               }
+               main::log_data('data',"working path: $working_path") if $b_log;
+               if ($block_type && @scsi && @by_id && ! -e "${working_path}model" && ! -e "${working_path}name"){
+                       ## ok, ok, it's incomprehensible, search /dev/disk/by-id for a line that contains the
+                       # discovered disk name AND ends with the correct identifier, sdx
+                       # get rid of whitespace for some drive names and ids, and extra data after - in name
+                       SCSI:
+                       foreach my $ref (@scsi){
+                               my %row = %$ref;
+                               if ($row{'model'}){
+                                       $row{'model'} = (split /\s*-\s*/,$row{'model'})[0];
+                                       foreach my $id (@by_id){
+                                               if ($id =~ /$row{'model'}/ && "/dev/$drives[$i]{'id'}" eq Cwd::abs_path($id)){
+                                                       $drives[$i]{'firmware'} = $row{'firmware'};
+                                                       $drives[$i]{'model'} = $row{'model'};
+                                                       $drives[$i]{'vendor'} = $row{'vendor'};
+                                                       last SCSI;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               # note: an entire class of model names gets truncated by /sys so that should be the last 
+               # in priority re tests.
+               elsif ( (!@disk_data || !$disk_data[0] ) && $block_type){
+                       # NOTE: while path ${working_path}vendor exists, it contains junk value, like: ATA
+                       $path = "${working_path}model";
+                       if ( -e $path){
+                               $model = (main::reader($path,'strip'))[0];
+                               if ($model){
+                                       $drives[$i]{'model'} = $model;
+                               }
+                       }
+                       elsif ($block_type eq 'mmc' && -e "${working_path}name"){
+                               $path = "${working_path}name";
+                               $model = (main::reader($path,'strip'))[0];
+                               if ($model){
+                                       $drives[$i]{'model'} = $model;
+                               }
+                       }
+               }
+               if (!$drives[$i]{'model'} && @disk_data){
+                       $drives[$i]{'model'} = $disk_data[0] if $disk_data[0];
+                       $drives[$i]{'vendor'} = $disk_data[1] if $disk_data[1];
+               }
+               # maybe rework logic if find good scsi data example, but for now use this
+               elsif ($drives[$i]{'model'} && !$drives[$i]{'vendor'}) {
+                       $drives[$i]{'model'} = main::disk_cleaner($drives[$i]{'model'});
+                       my @device_data = device_vendor($drives[$i]{'model'},'');
+                       $drives[$i]{'model'} = $device_data[1] if $device_data[1];
+                       $drives[$i]{'vendor'} = $device_data[0] if $device_data[0];
+               }
+               if ($working_path){
+                       $path = "${working_path}removable";
+                       $drives[$i]{'type'} = 'Removable' if -e $path && (main::reader($path,'strip'))[0]; # 0/1 value
+               }
+               my $peripheral = peripheral_data($drives[$i]{'id'});
+               # note: we only want to update type if we found a peripheral, otherwise preserve value
+               $drives[$i]{'type'} = $peripheral if $peripheral;
+               # print "type:$drives[$i]{'type'}\n";
+               if ($extra > 0){
+                       $drives[$i]{'temp'} = hdd_temp("/dev/$drives[$i]{'id'}");
+                       if ($extra > 1){
+                               my @speed_data = device_speed($drives[$i]{'id'});
+                               $drives[$i]{'speed'} = $speed_data[0] if $speed_data[0];
+                               $drives[$i]{'lanes'} = $speed_data[1] if $speed_data[1];
+                               if (@disk_data && $disk_data[2]){
+                                       $drives[$i]{'serial'} = $disk_data[2];
+                               }
+                               else {
+                                       $path = "${working_path}serial";
+                                       if ( -e $path){
+                                               $serial = (main::reader($path,'strip'))[0];
+                                               $drives[$i]{'serial'} = $serial if $serial;
+                                       }
+                               }
+                               if ($extra > 2 && !$drives[$i]{'firmware'} ){
+                                       my @fm = ('rev','fmrev','firmware_rev'); # 0 ~ default; 1 ~ mmc; 2 ~ nvme
+                                       foreach my $firmware (@fm){
+                                               $path = "${working_path}$firmware";
+                                               if ( -e $path){
+                                                       $drives[$i]{'firmware'} = (main::reader($path,'strip'))[0];
+                                                       last;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       # print Data::Dumper::Dumper \@drives;
+       eval $end if $b_log;
+       return @drives;
+}
+# camcontrol identify <device> |grep ^serial (this might be (S)ATA specific)
+# smartcl -i <device> |grep ^Serial
+# see smartctl; camcontrol devlist; gptid status;
+sub dmesg_boot_data {
+       eval $start if $b_log;
+       my ($used) = @_;
+       my (@data,@drives,@temp);
+       my ($id_holder,$i,$size,$working) = ('',0,0,0);
+       my $file = main::system_files('dmesg-boot');
+       if (@dm_boot_disk){
+               foreach (@dm_boot_disk){
+                       my @row = split /:\s*/, $_;
+                       next if ! defined $row[1];
+                       if ($id_holder ne $row[0]){
+                               $i++ if $id_holder;
+                               # print "$i $id_holder $row[0]\n";
+                               $id_holder = $row[0];
+                       }
+                       # no dots, note: ada2: 2861588MB BUT: ada2: 600.000MB/s 
+                       if (! exists $drives[$i]){
+                               $drives[$i] = ({});
+                               $drives[$i]{'id'} = $row[0];
+                               $drives[$i]{'firmware'} = '';
+                               $drives[$i]{'temp'} = '';
+                               $drives[$i]{'type'} = '';
+                               $drives[$i]{'vendor'} = '';
+                       }
+                       #print "$i\n";
+                       if ($bsd_type eq 'openbsd'){
+                               if ($row[1] =~ /,\s*([0-9\.]+[MGTPE][B]?),.*\ssectors$|^</){
+                                       $working = main::translate_size($1);
+                                       $size += $working if $working;
+                                       $drives[$i]{'size'} = $working;
+                               }
+                               if ($row[2] && $row[2] =~ /<([^>]+)>/){
+                                       $drives[$i]{'model'} = $1 if $1;
+                                       $drives[$i]{'type'} = 'removable' if $_ =~ /removable$/;
+                                       # <Generic-, Compact Flash, 1.00>
+                                       my $count = ($drives[$i]{'model'} =~ tr/,//);
+                                       if ($count && $count > 1){
+                                               @temp = split /,\s*/, $drives[$i]{'model'};
+                                               $drives[$i]{'model'} = $temp[1];
+                                       }
+                               }
+                               # print "openbsd\n";
+                       }
+                       else {
+                               if ($row[1] =~ /^([0-9]+[KMGTPE][B]?)\s/){
+                                       $working = main::translate_size($1);
+                                       $size += $working if $working;
+                                       $drives[$i]{'size'} = $working;
+                               }
+                               if ($row[1] =~ /device$|^</){
+                                       $row[1] =~ s/\sdevice$//g;
+                                       $row[1] =~ /<([^>]*)>\s(.*)/;
+                                       $drives[$i]{'model'} = $1 if $1;
+                                       $drives[$i]{'spec'} = $2 if $2;
+                               }
+                               if ($row[1] =~ /^Serial\sNumber\s(.*)/){
+                                       $drives[$i]{'serial'} = $1;
+                               }
+                               if ($row[1] =~ /^([0-9\.]+[MG][B]?\/s)/){
+                                       $drives[$i]{'speed'} = $1;
+                                       $drives[$i]{'speed'} =~ s/\.[0-9]+// if $drives[$i]{'speed'};
+                               }
+                       }
+                       $drives[$i]{'model'} = main::disk_cleaner($drives[$i]{'model'});
+                       my @device_data = device_vendor($drives[$i]{'model'},'');
+                       $drives[$i]{'vendor'} = $device_data[0] if $device_data[0];
+                       $drives[$i]{'model'} = $device_data[1] if $device_data[1];
+               }
+               if (!$size){
+                       $size = main::row_defaults('data-bsd');
+               }
+       }
+       elsif ( $file && ! -r $file ){
+               $size = main::row_defaults('dmesg-boot-permissions');
+       }
+       elsif (!$file ){
+               $size = main::row_defaults('dmesg-boot-missing');
+       }
+       @data = ({
+       'size' => $size,
+       'used' => $used,
+       });
+       #main::log_data('dump','@data',\@data) if $b_log;
+       if ( $show{'disk'} ){
+               @data = (@data,@drives);
+               # print 'drives:', Data::Dumper::Dumper \@drives;
+       }
+       # print Data::Dumper::Dumper \@data;
+       eval $end if $b_log;
+       return @data;
+}
+
+# check for usb/firewire/[and thunderwire when data found]
+sub peripheral_data {
+       eval $start if $b_log;
+       my ($id) = @_;
+       my ($type) = ('');
+       # print "$id here\n";
+       if (@by_id){
+               foreach (@by_id) {
+                       if ("/dev/$id" eq Cwd::abs_path($_)){
+                               #print "$id here\n";
+                               if (/usb-/i){
+                                       $type = 'USB';
+                               }
+                               elsif (/ieee1394--/i){
+                                       $type = 'FireWire';
+                               }
+                               last;
+                       }
+               }
+       }
+       # note: sometimes with wwn- numbering usb does not appear in by-id but it does in by-path
+       if (!$type && @by_path){
+               foreach (@by_path) {
+                       if ("/dev/$id" eq Cwd::abs_path($_)){
+                               if (/usb-/i){
+                                       $type = 'USB';
+                               }
+                               elsif (/ieee1394--/i){
+                                       $type = 'FireWire';
+                               }
+                               last;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return $type;
+}
+sub advanced_disk_data {
+       eval $start if $b_log;
+       my ($set_cmd,$id) = @_;
+       my ($cmd,$pt,$program,@data,@return);
+       if ($set_cmd ne 'unset'){
+               $return[0] = $set_cmd;
+       }
+       else {
+               # runs as user, but is SLOW: udisksctl info -b /dev/sda
+               # line: org.freedesktop.UDisks2.PartitionTable:
+               # Type:               dos
+               if ($program = main::check_program('udevadm')){
+                       $return[0] = "$program info -q property -n ";
+               }
+               elsif ($b_root && -e "/lib/udev/udisks-part-id") {
+                       $return[0] = "/lib/udev/udisks-part-id /dev/";
+               }
+               elsif ($b_root && ($program = main::check_program('fdisk'))) {
+                       $return[0] = "$program -l /dev/";
+               }
+               if (!$return[0]) {
+                       $return[0] = 'na'
+               }
+       }
+       if ($return[0] ne 'na'){
+               $cmd = "$return[0]$id 2>&1";
+               main::log_data('cmd',$cmd) if $b_log;
+               @data = main::grabber($cmd);
+               # for pre ~ 2.30 fdisk did not show gpt, but did show gpt scheme error, so
+               # if no gpt match, it's dos = mbr
+               if ($cmd =~ /fdisk/){
+                       foreach (@data){
+                               if (/^WARNING:\s+GPT/){
+                                       $return[1] = 'gpt';
+                                       last;
+                               }
+                               elsif (/^Disklabel\stype:\s*(.+)/i){
+                                       $return[1] = $1;
+                                       last;
+                               }
+                       }
+                       $return[1] = 'dos' if !$return[1];
+               }
+               else {
+                       foreach (@data){
+                               if ( /^(UDISKS_PARTITION_TABLE_SCHEME|ID_PART_TABLE_TYPE)/ ){
+                                       my @working = split /=/, $_;
+                                       $return[1] = $working[1];
+                               }
+                               elsif (/^ID_ATA_ROTATION_RATE_RPM/){
+                                       my @working = split /=/, $_;
+                                       $return[2] = $working[1];
+                               }
+                               last if $return[1] && $return[2];
+                       }
+               }
+               $return[1] = 'mbr' if $return[1] && lc($return[1]) eq 'dos';
+       }
+       eval $end if $b_log;
+       return @return;
+}
+sub scsi_data {
+       eval $start if $b_log;
+       my ($file) = @_;
+       my @temp = main::reader($file);
+       my (@scsi);
+       my ($firmware,$model,$vendor) = ('','','');
+       foreach (@temp){
+               if (/Vendor:\s*(.*)\s+Model:\s*(.*)\s+Rev:\s*(.*)/i){
+                       $vendor = $1;
+                       $model = $2;
+                       $firmware = $3;
+               }
+               if (/Type:/i){
+                       if (/Type:\s*Direct-Access/i){
+                               my @working = ({
+                               'vendor' => $vendor,
+                               'model' => $model,
+                               'firmware' => $firmware,
+                               });
+                               @scsi = (@scsi,@working);
+                       }
+                       else {
+                               ($firmware,$model,$vendor) = ('','','');
+                       }
+               }
+       }
+       main::log_data('dump','@scsi', \@scsi) if $b_log;
+       eval $end if $b_log;
+       return @scsi;
+}
+# @b_id has already been cleaned of partitions, wwn-, nvme-eui
+sub disk_data_by_id {
+       eval $start if $b_log;
+       my ($device) = @_;
+       my ($model,$serial,$vendor) = ('','','');
+       my (@disk_data);
+       foreach (@by_id){
+               if ($device eq Cwd::abs_path($_)){
+                       my @data = split /_/, $_;
+                       my @device_data = ();
+                       last if scalar @data < 2; # scsi-3600508e000000000876995df43efa500
+                       $serial = pop @data if @data;
+                       # usb-PNY_USB_3.0_FD_3715202280-0:0
+                       $serial =~ s/-[0-9]+:[0-9]+$//;
+                       $model = join ' ', @data;
+                       # get rid of the ata-|nvme-|mmc- etc
+                       $model =~ s/^\/dev\/disk\/by-id\/([^-]+-)?//;
+                       $model = main::disk_cleaner($model);
+                       @device_data = device_vendor($model,$serial);
+                       $vendor = $device_data[0] if $device_data[0];
+                       $model = $device_data[1] if $device_data[1];
+                       # print $device, '::', Cwd::abs_path($_),'::', $model, '::', $vendor, '::', $serial, "\n";
+                       (@disk_data) = ($model,$vendor,$serial);
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return @disk_data;
+}
+# receives space separated string that may or may not contain vendor data
+sub device_vendor {
+       eval $start if $b_log;
+       my ($model,$serial) = @_;
+       my ($vendor) = ('');
+       my (@data);
+       return if !$model;
+       # 0 - match pattern; 1 - replace pattern; 2 - vendor print; 3 - serial pattern
+       # Data URLs: inxi-resources.txt Section: DiskData device_vendor()
+       my @vendors = (
+       ## These go first because they are the most likely and common ##
+       ['(Crucial|^CT|-CT|^M4-)','Crucial','Crucial',''],
+       ['^INTEL','^INTEL','Intel',''],
+       ['(KINGSTON|DataTraveler|^SMS|^SHS|^SUV)','KINGSTON','Kingston',''], # maybe SHS: SHSS37A SKC SUV
+       # must come before samsung MU. NOTE: toshiba can have: TOSHIBA_MK6475GSX: mush: MKNSSDCR120GB_
+       ['(^MKN|Mushkin)','Mushkin','Mushkin',''], # MKNS
+       # MU = Multiple_Flash_Reader too risky: |M[UZ][^L]
+       ['(SAMSUNG|^MCG[0-9]+GC)','SAMSUNG','Samsung',''], # maybe ^SM
+       ['(SanDisk|^SDS[S]?[DQ]|^SL([0-9]+)G|^AFGCE|ULTRA\sFIT|Cruzer)','SanDisk','SanDisk',''],
+       ['(^ST[^T]|[S]?SEAGATE|^X[AFP]|^BUP|Expansion Desk)','[S]?SEAGATE','Seagate',''], # real, SSEAGATE Backup+; XP1600HE30002
+       ['^(WD|Western Digital|My (Book|Passport)|00LPCX|Elements)','(^WDC|Western Digital)','Western Digital',''],
+       ## Then better known ones ##
+       ['^(A-DATA|ADATA|AXN)','^(A-DATA|ADATA)','A-Data',''],
+       ['^ADTRON','^(ADTRON)','Adtron',''],
+       ['^ASUS','^ASUS','ASUS',''],
+       ['^ATP','^ATP[\s\-]','ATP',''],
+       ['^Corsair','^Corsair','Corsair',''],
+       ['^(FUJITSU|MP)','^FUJITSU','Fujitsu',''],
+       # note: 2012:  wdc bought hgst
+       ['^(HGST)','^HGST','HGST (Hitachi)',''], # HGST HUA
+       ['^(Hitachi|HDS|IC|HT|HU)','^Hitachi','Hitachi',''], 
+       ['^Hoodisk','^Hoodisk','Hoodisk',''],
+       ['^(HP\b)','^HP','HP',''], # vb: VB0250EAVER but clashes with vbox; HP_SSD_S700_120G
+       ['^(LSD|Lexar)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c
+       # OCZSSD2-2VTXE120G is OCZ-VERTEX2_3.5
+       ['^(OCZ|APOC|D2|DEN|DEN|DRSAK|EC188|FTNC|GFGC|MANG|MMOC|NIMC|NIMR|PSIR|TALOS2|TMSC|TRSAK)','^OCZ[\s\-]','OCZ',''],
+       ['^OWC','^OWC[\s\-]','OWC',''],
+       ['^Philips','^Philips','Philips',''],
+       ['^PIONEER','^PIONEER','Pioneer',''],
+       ['^PNY','^PNY\s','PNY','','^PNY'],
+       # note: get rid of: M[DGK] becasue mushkin starts with MK
+       # note: seen: KXG50ZNV512G NVMe TOSHIBA 512GB | THNSN51T02DUK NVMe TOSHIBA 1024GB
+       ['(^[S]?TOS|^THN|TOSHIBA)','[S]?TOSHIBA','Toshiba',''], # scsi-STOSHIBA_STOR.E_EDITION_
+       ## These go last because they are short and could lead to false ID, or are unlikely ##
+       ['^Android','^Android','Android',''],
+       # must come before AP|Apacer
+       ['^APPLE','^APPLE','Apple',''],
+       ['^(AP|Apacer)','^Apacer','Apacer',''],
+       ['^BUFFALO','^BUFFALO','Buffalo',''],
+       ['^CHN\b','','Zheino',''],
+       ['^Colorful\b','^Colorful','Colorful',''],
+       ['^DREVO\b','','Drevo',''],
+       ['^EXCELSTOR','^EXCELSTOR( TECHNOLOGY)?','Excelstor',''],
+       ['^FASTDISK','^FASTDISK','FASTDISK',''],
+       ['^FORESEE','^FORESEE','Foresee',''],
+       ['^GALAX\b','^GALAX','GALAX',''],
+       ['^Generic','^Generic','Generic',''],
+       ['^GOODRAM','^GOODRAM','GOODRAM',''],
+       # supertalent also has FM: |FM
+       ['^(G[\.]?SKILL)','^G[\.]?SKILL','G.SKILL',''],
+       ['^HUAWEI','^HUAWEI','Huawei',''],
+       ['^(IBM|DT)','^IBM','IBM',''], 
+       ['^Imation','^Imation(\sImation)?','Imation',''], # Imation_ImationFlashDrive
+       ['^(InnoDisk|Innolite)','^InnoDisk( Corp.)?','InnoDisk',''],
+       ['^Innostor','^Innostor','Innostor',''],
+       ['^Intenso','^Intenso','Intenso',''],
+       ['^KingDian','^KingDian','KingDian',''],
+       ['^(LITE[\-]?ON[\s\-]?IT)','^LITE[\-]?ON[\s\-]?IT','LITE-ON IT',''], # LITEONIT_LSS-24L6G
+       ['^(LITE[\-]?ON|PH6)','^LITE[\-]?ON','LITE-ON',''], # PH6-CE240-L
+       ['^M-Systems','^M-Systems','M-Systems',''],
+       ['^MAXTOR','^MAXTOR','Maxtor',''],
+       ['^(MT|M5|Micron)','^Micron','Micron',''],
+       ['^MARVELL','^MARVELL','Marvell',''],
+       ['^Medion','^Medion','Medion',''],
+       ['^Motorola','^Motorola','Motorola',''],
+       ['^(PS[8F]|Patriot)','^Patriot','Patriot',''],
+       ['^PIX[\s]?JR','^PIX[\s]?JR','Disney',''],
+       ['^(PLEXTOR|PX-)','^PLEXTOR','Plextor',''],
+       ['(^Quantum|Fireball)','^Quantum','Quantum',''],
+       ['^R3','','AMD Radeon',''], # ssd 
+       ['^RENICE','^RENICE','Renice',''],
+       ['^RIM[\s]','^RIM','RIM',''],
+       ['^SigmaTel','^SigmaTel','SigmaTel',''],
+       ['^SPPC','','Silicon Power',''],
+       ['^(SK\s?HYNIX|HFS)','^SK\s?HYNIX','SK Hynix',''], # HFS128G39TND-N210A
+       ['^hynix','hynix','Hynix',''],# nvme middle of string, must be after sk hynix
+       ['^SH','','Smart Modular Tech.',''],
+       ['^(SMART( Storage Systems)?|TX)','^(SMART( Storage Systems)?)','Smart Storage Systems',''],
+       ['^(S[FR]-|Sony)','^Sony','Sony',''],
+       ['^STE[CK]','^STE[CK]','sTec',''], # wd bought this one
+       ['^STORFLY','^STORFLY','StorFly',''],
+       # NOTE: F[MNETU] not reliable, g.skill starts with FM too: 
+       # Seagate ST skips STT. 
+       ['^(STT)','','Super Talent',''], 
+       ['^(SF|Swissbit)','^Swissbit','Swissbit',''],
+       # ['^(SUPERSPEED)','^SUPERSPEED','SuperSpeed',''], # superspeed is a generic term
+       ['^TANDBERG','^TANDBERG','Tanberg',''],
+       ['^TEAC','^TEAC','TEAC',''],
+       ['^(TS|Transcend|JetFlash)','^Transcend','Transcend',''],
+       ['^TrekStor','^TrekStor','TrekStor',''],
+       ['^UDinfo','^UDinfo','UDinfo',''],
+       ['^(UG|Unigen)','^Unigen','Unigen',''],
+       ['^VBOX','','VirtualBox',''],
+       ['^(Verbatim|STORE N GO)','^Verbatim','Verbatim',''],
+       ['^VISIONTEK','^VISIONTEK','VisionTek',''],
+       ['^VMware','^VMware','VMware',''],
+       ['^(Vseky|Vaseky)','^Vaseky','Vaseky',''], # ata-Vseky_V880_350G_
+       );
+       foreach my $ref (@vendors){
+               my @row = @$ref;
+               if ($model =~ /$row[0]/i || ($row[3] && $serial && $serial =~ /$row[3]/)){
+                       $vendor = $row[2];
+                       $model =~ s/$row[1]//i if $row[1] && lc($model) ne lc($row[1]);
+                       $model =~ s/^[\s\-_]+|[\s\-_]+$//g;
+                       $model =~ s/\s\s/ /g;
+                       @data = ($vendor,$model);
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return @data;
+}
+# Normally hddtemp requires root, but you can set user rights in /etc/sudoers.
+# args: $1 - /dev/<disk> to be tested for
+sub hdd_temp {
+       eval $start if $b_log;
+       my ($device) = @_;
+       my ($path) = ('');
+       my (@data,$hdd_temp);
+       if ($device =~ /nvme/i){
+               if (!$b_nvme){
+                       $b_nvme = 1;
+                       if ($path = main::check_program('nvme')) {
+                               $nvme = $path;
+                       }
+               }
+               if ($nvme){
+                       $device =~ s/n[0-9]//;
+                       @data = main::grabber("$sudo$nvme smart-log $device 2>/dev/null");
+                       foreach (@data){
+                               my @row = split /\s*:\s*/, $_;
+                               # other rows may have: Temperature sensor 1 :
+                               if ( $row[0] eq 'temperature') {
+                                       $row[1] =~ s/\s*C//;
+                                       $hdd_temp = $row[1];
+                                       last;
+                               }
+                       }
+               }
+       }
+       else {
+               if (!$b_hddtemp){
+                       $b_hddtemp = 1;
+                       if ($path = main::check_program('hddtemp')) {
+                               $hddtemp = $path;
+                       }
+               }
+               if ($hddtemp){
+                       $hdd_temp = (main::grabber("$sudo$hddtemp -nq -u C $device 2>/dev/null"))[0];
+               }
+       }
+       eval $end if $b_log;
+       return $hdd_temp;
+}
+sub device_speed {
+       eval $start if $b_log;
+       my ($device) = @_;
+       my ($b_nvme,$lanes,$speed,@data);
+       my $working = Cwd::abs_path("/sys/class/block/$device");
+       #print "$working\n";
+       if ($working){
+               my ($id);
+               # slice out the ata id:
+               # /sys/devices/pci0000:00:11.0/ata1/host0/target0:
+               if ($working =~ /^.*\/ata([0-9]+)\/.*/){
+                       $id = $1;
+               }
+               # /sys/devices/pci0000:00/0000:00:05.0/virtio1/block/vda
+               elsif ($working =~ /^.*\/virtio([0-9]+)\/.*/){
+                       $id = $1;
+               }
+               # /sys/devices/pci0000:10/0000:10:01.2/0000:13:00.0/nvme/nvme0/nvme0n1
+               elsif ($working =~ /^.*\/(nvme[0-9]+)\/.*/){
+                       $id = $1;
+                       $b_nvme = 1;
+               }
+               # do host last because the strings above might have host as well as their search item
+               # 0000:00:1f.2/host3/target3: increment by 1 sine ata starts at 1, but host at 0
+               elsif ($working =~ /^.*\/host([0-9]+)\/.*/){
+                       $id = $1 + 1 if defined $1;
+               }
+               # print "$working $id\n";
+               if (defined $id){
+                       if ($b_nvme){
+                               $working = "/sys/class/nvme/$id/device/max_link_speed";
+                               $speed = (main::reader($working))[0] if -f $working;
+                               if ($speed =~ /([0-9\.]+)\sGT\/s/){
+                                       $speed = $1;
+                                       # pcie1: 2.5 GT/s; pcie2: 5.0 GT/s; pci3: 8 GT/s
+                                       # NOTE: PCIe 3 stopped using the 8b/10b encoding but a sample pcie3 nvme has 
+                                       # rated speed of GT/s * .8 anyway. GT/s * (128b/130b)
+                                       $speed = ($speed <= 5 ) ? $speed * .8 : $speed * 128/130; 
+                                       $speed = sprintf("%.1f",$speed) if $speed;
+                                       $working = "/sys/class/nvme/$id/device/max_link_width";
+                                       $lanes = (main::reader($working))[0] if -f $working;
+                                       $lanes = 1 if !$lanes;
+                                       # https://www.edn.com/electronics-news/4380071/What-does-GT-s-mean-anyway-
+                                       # https://www.anandtech.com/show/2412/2
+                                       # http://www.tested.com/tech/457440-theoretical-vs-actual-bandwidth-pci-express-and-thunderbolt/
+                                       # PCIe 1,2 use “8b/10b” encoding: eight bits are encoded into a 10-bit symbol
+                                       # PCIe 3,4,5 use "128b/130b" encoding: 128 bits are encoded into a 130 bit symbol
+                                       $speed = ($speed * $lanes) . " Gb/s";
+                               }
+                       }
+                       else {
+                               $working = "/sys/class/ata_link/link$id/sata_spd";
+                               $speed = (main::reader($working))[0] if -f $working;
+                               $speed = main::disk_cleaner($speed) if $speed;
+                               $speed =~ s/Gbps/Gb\/s/ if $speed;
+                       }
+               }
+       }
+       @data = ($speed,$lanes);
+       #print "$working $speed\n";
+       eval $end if $b_log;
+       return @data;
+}
+# gptid/c5e940f1-5ce2-11e6-9eeb-d05099ac4dc2     N/A  ada0p1
+sub match_glabel {
+       eval $start if $b_log;
+       my ($gptid) = @_;
+       return if !@glabel || ! $gptid;
+       #$gptid =~ s/s[0-9]+$//;
+       my ($dev_id) = ('');
+       foreach (@glabel){
+               my @temp = split /\s+/, $_;
+               my $gptid_trimmed = $gptid;
+               # slice off s[0-9] from end in case they use slice syntax
+               $gptid_trimmed =~ s/s[0-9]+$//;
+               if (defined $temp[0] && ($temp[0] eq $gptid || $temp[0] eq $gptid_trimmed ) ){
+                       $dev_id = $temp[2];
+                       last;
+               }
+       }
+       $dev_id ||= $gptid; # no match? return full string
+       eval $end if $b_log;
+       return $dev_id;
+}
+sub set_glabel {
+       eval $start if $b_log;
+       $b_glabel = 1;
+       if (my $path = main::check_program('glabel')){
+               @glabel = main::grabber("$path status 2>/dev/null");
+       }
+       main::log_data('dump','@glabel:with Headers',\@glabel) if $b_log;
+       # get rid of first header line
+       shift @glabel;
+       eval $end if $b_log;
+}
+}
+
+## GraphicData 
+{
+package GraphicData;
+my $driver = ''; # we need this as a fallback in case no xorg.0.log
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows);
+       my $num = 0;
+       if (($b_arm || $b_mips) && !$b_soc_gfx && !$b_pci_tool){
+               my $key = ($b_arm) ? 'ARM' : 'MIPS';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults(lc($key) . '-pci',''),
+               },);
+               @rows = (@rows,@data);
+       }
+       else {
+               @data = card_data();
+               @rows = (@rows,@data);
+               if (!@rows){
+                       my $key = 'Message';
+                       @data = ({
+                       main::key($num++,$key) => main::row_defaults('pci-card-data',''),
+                       },);
+                       @rows = (@rows,@data);
+               }
+       }
+       @data = display_data();
+       @rows = (@rows,@data);
+       @data = gl_data();
+       @rows = (@rows,@data);
+       eval $end if $b_log;
+       return @rows;
+}
+# 0 type
+# 1 type_id
+# 2 bus_id
+# 3 sub_id
+# 4 device
+# 5 vendor_id
+# 6 chip_id
+# 7 rev
+# 8 port
+# 9 driver
+# 10 modules
+# not using 3D controller yet, needs research: |3D controller |display controller
+# note: this is strange, but all of these can be either a separate or the same
+# card. However, by comparing bus id, say: 00:02.0 we can determine that the
+# cards are  either the same or different. We want only the .0 version as a valid
+# card. .1 would be for example: Display Adapter with bus id x:xx.1, not the right one
+sub card_data {
+       eval $start if $b_log;
+       my (@rows,@data);
+       my ($j,$num) = (0,1);
+       foreach (@pci){
+               $num = 1;
+               my @row = @$_;
+               #print "$row[0] $row[3]\n";
+               if ($row[3] == 0 && ( $row[0] =~ /^(vga|disp|display|3d|fb|gpu|hdmi)$/ ) ){
+                       #print "$row[0] $row[3]\n";
+                       $j = scalar @rows;
+                       $driver = $row[9];
+                       $driver ||= 'N/A';
+                       my $card = main::trimmer($row[4]);
+                       $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A';
+                       #$card ||= 'N/A';
+                       # have seen absurdly verbose card descriptions, with non related data etc
+                       if (length($card) > 85 || $size{'max'} < 110){
+                               $card = main::pci_long_filter($card);
+                       }
+                       @data = ({
+                       main::key($num++,'Card') => $card,
+                       },);
+                       @rows = (@rows,@data);
+                       if ($extra > 2 && $b_pci_tool && $row[11]){
+                               my $item = main::get_pci_vendor($row[4],$row[11]);
+                               $rows[$j]{main::key($num++,'vendor')} = $item if $item;
+                       }
+                       $rows[$j]{main::key($num++,'driver')} = $driver;
+                       if ($row[9] && !$bsd_type){
+                               my $version = main::get_module_version($row[9]);
+                               $version ||= 'N/A';
+                               $rows[$j]{main::key($num++,'v')} = $version;
+                       }
+                       if ($extra > 0){
+                               $rows[$j]{main::key($num++,'bus ID')} = (!$row[2] && !$row[3]) ? 'N/A' : "$row[2].$row[3]";
+                       }
+                       if ($extra > 1){
+                               $rows[$j]{main::key($num++,'chip ID')} = ($row[5]) ? "$row[5]:$row[6]" : $row[6];
+                       }
+               }
+               #print "$row[0]\n";
+       }
+       #my $ref = $pci[-1];
+       #print $$ref[0],"\n";
+       eval $end if $b_log;
+       return @rows;
+}
+sub display_data(){
+       eval $start if $b_log;
+       my (%graphics,@row);
+       my @xdpyinfo;
+       my $num = 0;
+       my ($protocol,$server) = ('','');
+       # note: these may not always be set, they won't be out of X, for example
+       $protocol = $ENV{'XDG_SESSION_TYPE'} if $ENV{'XDG_SESSION_TYPE'};
+       $protocol = $ENV{'WAYLAND_DISPLAY'} if (!$protocol && $ENV{'WAYLAND_DISPLAY'});
+       # need to confirm that there's a point to this test, I believe no, fails out of x
+       # loginctl also results in the session id
+       if (!$protocol && $b_display && $b_force_display){
+               if (my $program = main::check_program('loginctl')){
+                       my $id = '';
+                       # $id = $ENV{'XDG_SESSION_ID'}; # returns tty session in console
+                       my @data = main::grabber("$program --no-pager --no-legend 2>/dev/null",'','strip');
+                       foreach (@data){
+                               next if /tty[v]?[0-6]$/; # freebsd: ttyv3
+                               $id = (split /\s+/, $_)[0];
+                               last; # multiuser? too bad, we'll go for the first one
+                       }
+                       if ($id ){
+                               my $temp = (main::grabber("$program show-session $id -p Type --no-pager --no-legend 2>/dev/null"))[0];
+                               $temp =~ s/Type=// if $temp;
+                               # ssh will not show /dev/ttyx so would have passed the first test
+                               $protocol = $temp if $temp && $temp ne 'tty';
+                       }
+               }
+       }
+       if ($extra > 1){
+               # initial tests, if wayland, it is certainly a compositor
+               $protocol = lc($protocol) if $protocol;
+               $graphics{'compositor'} = display_compositor($protocol);
+       }
+       if ( $b_display){
+               # X vendor and version detection.
+               # new method added since radeon and X.org and the disappearance of 
+               # <X server name> version : ...etc. Later on, the normal textual version string 
+               # returned, e.g. like: X.Org version: 6.8.2 
+               # A failover mechanism is in place: if $version empty, release number parsed instead
+               if (my $program = main::check_program('xdpyinfo')){
+                       my @xdpyinfo = main::grabber("$program $display_opt 2>/dev/null","\n",'strip');
+                       #@xdpyinfo = map {s/^\s+//;$_} @xdpyinfo if @xdpyinfo;
+                       #print join "\n",@xdpyinfo, "\n";
+                       foreach (@xdpyinfo){
+                               my @working = split /:\s+/, $_;
+                               next if ( ($graphics{'dimensions'} && $working[0] ne 'dimensions' ) || !$working[0] );
+                               #print "$_\n";
+                               if ($working[0] eq 'vendor string'){
+                                       $working[1] =~ s/The\s|\sFoundation//g;
+                                       # some distros, like fedora, report themselves as the xorg vendor, 
+                                       # so quick check here to make sure the vendor string includes Xorg in string
+                                       if ($working[1] !~ /x/i){
+                                               $working[1] .= ' X.org';
+                                       }
+                                       $graphics{'vendor'} = $working[1];
+                               }
+                               elsif ($working[0] eq 'version number'){
+                                       $graphics{'version-id'} = $working[1];
+                               }
+                               elsif ($working[0] eq 'vendor release number'){
+                                       $graphics{'vendor-release'} = $working[1];
+                               }
+                               elsif ($working[0] eq 'X.Org version'){
+                                       $graphics{'xorg-version'} = $working[1];
+                               }
+                               elsif ($working[0] eq 'dimensions'){
+                                       $working[1] =~ s/\spixels//;
+                                       $working[1] =~ s/\smillimeters/ mm/;
+                                       if ($graphics{'dimensions'}){
+                                               $graphics{'dimensions'} = ([@{$graphics{'dimensions'}},$working[1]]);
+                                       }
+                                       else {
+                                               $graphics{'dimensions'} = ([$working[1]]);
+                                       }
+                               }
+                       }
+                       #$graphics{'dimensions'} = (\@dimensions);
+                       # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle
+                       # multiple screens from different video cards
+                       my $ref = $graphics{'dimensions'};
+                       if (defined $ref){
+                               my @screens = @$ref;
+                               if (scalar @screens == 1){
+                                       if (my $program = main::check_program('xrandr')){
+                                               my @xrandr = main::grabber("$program $display_opt 2>/dev/null",'','strip');
+                                               foreach (@xrandr){
+                                                       my @working = split /\s+/,$_;
+                                                       # print join "$_\n";
+                                                       if ($working[1] =~ /\*/){
+                                                               $working[1] =~ s/\*|\+//g;
+                                                               $working[1] = sprintf("%.0f",$working[1]);
+                                                               $working[1] =  ($working[1]) ? "$working[1]Hz" : 'N/A';
+                                                               my $screen = "$working[0]~$working[1]";
+                                                               if ($graphics{'screens'}){
+                                                                       $graphics{'screens'} = ([@{$graphics{'screens'}},$screen]);
+                                                               }
+                                                               else {
+                                                                       $graphics{'screens'} = ([$screen]);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       else {
+                               $graphics{'tty'} = tty_data();
+                       }
+               }
+               else {
+                       $graphics{'screens'} = ([main::row_defaults('xdpyinfo-missing')]);
+               }
+       }
+       else {
+               $graphics{'tty'} = tty_data();
+       }
+       # this gives better output than the failure last case, which would only show:
+       # for example: X.org: 1.9 instead of: X.org: 1.9.0
+       $graphics{'version'} = $graphics{'xorg-version'} if $graphics{'xorg-version'};;
+       $graphics{'version'} = x_version() if !$graphics{'version'};
+       $graphics{'version'} = $graphics{'version-id'} if !$graphics{'version'};
+       
+       undef @xdpyinfo;
+       #print Data::Dumper::Dumper \%graphics;
+       if (%graphics){
+               my $resolution = '';
+               my $server_string = '';
+               if ($graphics{'vendor'}){
+                       my $version = ($graphics{'version'}) ? " $graphics{'version'}" : '';
+                       $server_string = "$graphics{'vendor'}$version";
+               }
+               elsif ($graphics{'version'}) {
+                       $server_string = "X.org $graphics{'version'}";
+               }
+               if ($graphics{'screens'}){
+                       my $ref = $graphics{'screens'};
+                       my @screens = @$ref;
+                       my $sep = '';
+                       foreach (@screens){
+                               $resolution .= $sep . $_;
+                               $sep = ', ';
+                       }
+               }
+               my @drivers = x_drivers();
+               if (!$protocol && !$server_string && !$graphics{'vendor'} && !@drivers){
+                       $server_string = main::row_defaults('display-server');
+                       @row = ({
+                       main::key($num++,'Display') => '',
+                       main::key($num++,'server') => $server_string,
+                       });
+               }
+               else {
+                       $server_string ||= 'N/A';
+                       # note: if no xorg log, and if wayland, there will be no xorg drivers, 
+                       # obviously, so we use the last driver found on the card section in that case.
+                       # those come from lscpi kernel drivers so there should be no xorg/wayland issues.
+                       $driver = ($drivers[0]) ? $drivers[0]: $driver;
+                       @row = ({
+                       main::key($num++,'Display') => $protocol,
+                       main::key($num++,'server') => $server_string,
+                       main::key($num++,'driver') => $driver,
+                       });
+                       if ($drivers[2]){
+                               $row[0]{main::key($num++,'FAILED')} = $drivers[2];
+                       }
+                       if ($drivers[1]){
+                               $row[0]{main::key($num++,'unloaded')} = $drivers[1];
+                       }
+                       if ($extra > 1 && $drivers[3]){
+                               $row[0]{main::key($num++,'alternate')} = $drivers[3];
+                       }
+                       if ($graphics{'compositor'}){
+                               $row[0]{main::key($num++,'compositor')} = $graphics{'compositor'};
+                       }
+               }
+               if ($resolution){
+                       $row[0]{main::key($num++,'resolution')} = $resolution;
+               }
+               else {
+                       $graphics{'tty'} ||= 'N/A';
+                       $row[0]{main::key($num++,'tty')} = $graphics{'tty'};
+               }
+       }
+       eval $end if $b_log;
+       return @row;
+}
+sub gl_data(){
+       eval $start if $b_log;
+       my $num = 0;
+       my (@row,$arg);
+       #print ("$b_display : $b_root\n");
+       if ( $b_display){
+               if (my $program = main::check_program('glxinfo')){
+                       # NOTE: glxinfo -B is not always available, unforunately
+                       my @glxinfo = main::grabber("$program $display_opt 2>/dev/null");
+                       if (!@glxinfo){
+                               my $type = 'display-console';
+                               if ($b_root){
+                                       $type = 'display-root-x';
+                               }
+                               else {
+                                       $type = 'display-null';
+                               }
+                               @row = ({
+                               main::key($num++,'Message') => main::row_defaults($type),
+                               });
+                               return @row;
+                       }
+                       #print join "\n",@glxinfo,"\n";
+                       my $compat_version = '';
+                       my ($b_compat,@core_profile_version,@direct_render,@renderer,@opengl_version,@working);
+                       foreach (@glxinfo){
+                               next if /^\s/;
+                               if (/^opengl renderer/i){
+                                       @working = split /:\s*/, $_;
+                                       $working[1] = main::cleaner($working[1]);
+                                       # Allow all mesas
+                                       #if ($working[1] =~ /mesa/i){
+                                       #       
+                                       #}
+                                       push @renderer, $working[1];
+                               }
+                               # dropping all conditions from this test to just show full mesa information
+                               # there is a user case where not f and mesa apply, atom mobo
+                               # /opengl version/ && ( f || $2 !~ /mesa/ ) {
+                               elsif (/^opengl version/i){
+                                       # fglrx started appearing with this extra string, does not appear 
+                                       # to communicate anything of value
+                                       @working = split /:\s*/, $_;
+                                       $working[1] =~ s/(Compatibility Profile Context|\(Compatibility Profile\))//;
+                                       $working[1] =~ s/\s\s/ /g;
+                                       $working[1] =~ s/^\s+|\s+$//;
+                                       push @opengl_version, $working[1];
+                                       # note: this is going to be off if ever multi opengl versions appear, never seen one
+                                       @working = split /\s+/, $working[1];
+                                       $compat_version = $working[0];
+                               }
+                               elsif (/^opengl core profile version/i){
+                                       # fglrx started appearing with this extra string, does not appear 
+                                       # to communicate anything of value
+                                       @working = split /:\s*/, $_;
+                                       $working[1] =~ s/(Compatibility Profile Context|\((Compatibility|Core) Profile\))//;
+                                       $working[1] =~ s/\s\s/ /g;
+                                       $working[1] =~ s/^\s+|\s+$//;
+                                       push @core_profile_version, $working[1];
+                               }
+                               elsif (/direct rendering/){
+                                       @working = split /:\s*/, $_;
+                                       push @direct_render, $working[1];
+                               }
+                               # if -B was always available, we could skip this, but it is not
+                               elsif (/GLX Visuals/){
+                                       last;
+                               }
+                       }
+                       my ($direct_render,$renderer,$version) = ('N/A','N/A','N/A');
+                       $direct_render = join ', ',  @direct_render if @direct_render;
+                       # non free drivers once filtered and cleaned show the same for core and compat
+                       # but this stopped for some reason at 4.5/4.6 nvidia
+                       if (@core_profile_version && @opengl_version && 
+                         join ('', @core_profile_version) ne join( '', @opengl_version) &&
+                         !(grep {/nvidia/i} @opengl_version ) ){
+                               @opengl_version = @core_profile_version;
+                               $b_compat = 1;
+                       }
+                       $version = join ', ', @opengl_version if @opengl_version;
+                       $renderer = join ', ', @renderer if @renderer;
+                       @row = ({
+                       main::key($num++,'OpenGL') => '',
+                       main::key($num++,'renderer') => $renderer,
+                       main::key($num++,'v') => $version,
+                       });
+                       
+                       if ($b_compat && $extra > 1 && $compat_version){
+                               $row[0]{main::key($num++,'compat-v')} = $compat_version;
+                       }
+                       if ($extra > 0){
+                               $row[0]{main::key($num++,'direct render')} = $direct_render;
+                       }
+               }
+               else {
+                       @row = ({
+                       main::key($num++,'Message') => main::row_defaults('glxinfo-missing'),
+                       });
+               }
+       }
+       else {
+               my $type = 'display-console';
+               if (!main::check_program('glxinfo')){
+                       $type = 'glxinfo-missing';
+               }
+               else {
+                       if ($b_root){
+                               $type = 'display-root';
+                       }
+                       else {
+                               $type = 'display-try';
+                       }
+               }
+               @row = ({
+               main::key($num++,'Message') => main::row_defaults($type),
+               });
+       }
+       eval $end if $b_log;
+       return @row;
+}
+sub tty_data(){
+       eval $start if $b_log;
+       my ($tty);
+       if ($size{'term-cols'}){
+               $tty = "$size{'term-cols'}x$size{'term-lines'}";
+       }
+       elsif ($b_irc && $client{'console-irc'}){
+               my $tty_working = main::get_tty_console_irc('tty');
+               if (my $program = main::check_program('stty')){
+                       my $tty_arg = ($bsd_type) ? '-f' : '-F';
+                       $tty = (main::grabber("$program $tty_arg /dev/pts/$tty_working size 2>/dev/null"))[0];
+                       if ($tty){
+                               my @temp = split /\s+/, $tty;
+                               $tty = "$temp[1]x$temp[0]";
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return $tty;
+}
+sub x_drivers {
+       eval $start if $b_log;
+       my ($driver,@driver_data,,%drivers);
+       my ($alternate,$failed,$loaded,$sep,$unloaded) = ('','','','','');
+       if (my $log = main::system_files('xorg-log')){
+               # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/Xorg.0-voyager-serena.log";
+               # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/loading-unload-failed-all41-mint.txt";
+               # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/loading-unload-failed-phd21-mint.txt";
+               # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/Xorg.0-gm10.log";
+               my @xorg = main::reader($log);
+               # list is from sgfxi plus non-free drivers, plus ARM drivers
+               my $list = 'amdgpu|apm|ark|armsoc|ati|chips|cirrus|cyrix|fbdev|fbturbo|fglrx|glint|';
+               $list .= 'i128|i740|i810|iftv|imstt|intel|ivtv|mach64|mesa|mga|modesetting|';
+               $list .= 'neomagic|newport|nouveau|nsc|nvidia|nv|openchrome|radeonhd|radeon|';
+               $list .= 'rendition|s3virge|s3|savage|siliconmotion|sisimedia|sisusb|sis|tdfx|';
+               $list .= 'tga|trident|tseng|unichrome|v4l|vboxvideo|vesa|vga|via|vmware|voodoo';
+               # it's much cheaper to grab the simple pattern match then do the expensive one 
+               # in the main loop.
+               #@xorg = grep {/Failed|Unload|Loading/} @xorg;
+               foreach (@xorg){
+                       next if !/Failed|Unload|Loading/;
+                       # print "$_\n";
+                       # note that in file names, driver is always lower case
+                       if (/\sLoading.*($list)_drv.so$/i ) {
+                               $driver=lc($1);
+                               # we get all the actually loaded drivers first, we will use this to compare the
+                               # failed/unloaded, which have not always actually been truly loaded
+                               $drivers{$driver}='loaded';
+                       }
+                       # openbsd uses UnloadModule: 
+                       elsif (/(Unloading\s|UnloadModule).*\"?($list)(_drv.so)?\"?$/i ) {
+                               $driver=lc($2);
+                               # we get all the actually loaded drivers first, we will use this to compare the
+                               # failed/unloaded, which have not always actually been truly loaded
+                               if (exists $drivers{$driver} && $drivers{$driver} ne 'alternate'){
+                                       $drivers{$driver}='unloaded';
+                               }
+                       }
+                       # verify that the driver actually started the desktop, even with false failed messages 
+                       # which can occur. This is the driver that is actually driving the display.
+                       # note that xorg will often load several modules, like modesetting,fbdev,nouveau
+                       # NOTE:
+                       #(II) UnloadModule: "nouveau"
+                       #(II) Unloading nouveau
+                       #(II) Failed to load module "nouveau" (already loaded, 0)
+                       #(II) LoadModule: "modesetting"
+                       elsif (/Failed.*($list)\"?.*$/i ) {
+                               # Set driver to lower case because sometimes it will show as 
+                               # RADEON or NVIDIA in the actual x start
+                               $driver=lc($1);
+                               # we need to make sure that the driver has already been truly loaded, 
+                               # not just discussed
+                               if (exists $drivers{$driver} && $drivers{$driver} ne 'alternate'){
+                                       if ( $_ !~ /\(already loaded/){
+                                               $drivers{$driver}='failed';
+                                       }
+                                       # reset the previous line's 'unloaded' to 'loaded' as well
+                                       else {
+                                               $drivers{$driver}='loaded';
+                                       }
+                               }
+                               elsif ($_ =~ /module does not exist/){
+                                       $drivers{$driver}='alternate';
+                               }
+                       }
+               }
+               my $sep = '';
+               foreach (sort keys %drivers){
+                       if ($drivers{$_} eq 'loaded') {
+                               $sep = ($loaded) ? ',' : '';
+                               $loaded .= $sep . $_;
+                       }
+                       elsif ($drivers{$_} eq 'unloaded') {
+                               $sep = ($unloaded) ? ',' : '';
+                               $unloaded .= $sep . $_;
+                       }
+                       elsif ($drivers{$_} eq 'failed') {
+                               $sep = ($failed) ? ',' : '';
+                               $failed .= $sep . $_;
+                       }
+                       elsif ($drivers{$_} eq 'alternate') {
+                               $sep = ($alternate) ? ',' : '';
+                               $alternate .= $sep . $_;
+                       }
+               }
+               $loaded ||= 'none';
+               @driver_data = ($loaded,$unloaded,$failed,$alternate);
+       }
+       eval $end if $b_log;
+       return @driver_data;
+}
+sub x_version {
+       eval $start if $b_log;
+       my ($version,@data,$program);
+       # IMPORTANT: both commands send version data to stderr!
+       if ($program = main::check_program('Xorg')){
+               @data = main::grabber("$program -version 2>&1");
+       }
+       elsif ($program = main::check_program('X')){
+               @data = main::grabber("$program -version 2>&1");
+       }
+       #print Data::Dumper::Dumper \@data;
+       if (@data){
+               foreach (@data){
+                       if (/^X.org X server/i){
+                               my @working = split /\s+/, $_;
+                               $version = $working[3];
+                               last;
+                       }
+                       elsif (/^X Window System Version/i) {
+                               my @working = split /\s+/, $_;
+                               $version = $working[4];
+                               last;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return $version;
+}
+# $1 - protocol: wayland|x11
+sub display_compositor {
+       eval $start if $b_log;
+       my ($protocol) = @_; 
+       my ($compositor) = ('');
+       main::set_ps_gui() if ! $b_ps_gui;
+       if (@ps_gui){
+               # 1 check program; 2 search; 3 unused version; 4 print
+               my @compositors = (
+               ['budgie-wm','budgie-wm','','budgie-wm'],
+               ['compton','compton','','compton'],
+               ['enlightenment','enlightenment','','enlightenment'],
+               ['gnome-shell','gnome-shell','','gnome-shell'],
+               ['kwin_wayland','kwin_wayland','','kwin wayland'],
+               ['kwin_x11','kwin_x11','','kwin x11'],
+               #['kwin','kwin','','kwin'],
+               ['marco','marco','','marco'],
+               ['muffin','muffin','','muffin'],
+               ['mutter','mutter','','mutter'],
+               ['weston','weston','','weston'],
+               # owned by: compiz-core in debian
+               ['compiz','compiz','','compiz'],
+               # did not find follwing in debian apt
+               ['3dwm','3dwm','','3dwm'],
+               ['dwc','dwc','','dwc'],
+               ['grefson','grefson','','grefson'],
+               ['ireplace','ireplace','','ireplace'],
+               ['kmscon','kmscon','','kmscon'],
+               ['metisse','metisse','','metisse'],
+               ['mir','mir','','mir'],
+               ['moblin','moblin','','moblin'],
+               ['rustland','rustland','','rustland'],
+               ['sway','sway','','sway'],
+               ['swc','swc','','swc'],
+               ['unagi','unagi','','unagi'],
+               ['wayhouse','wayhouse','','wayhouse'],
+               ['westford','westford','','westford'],
+               ['xcompmgr','xcompmgr','','xcompmgr'],
+               );
+               foreach my $ref (@compositors){
+                       my @item = @$ref;
+                       # no need to use check program with short list of ps_gui
+                       # if (main::check_program($item[0]) && (grep {/^$item[1]$/} @ps_gui ) ){
+                       if (grep {/^$item[1]$/} @ps_gui){
+                               $compositor = $item[3];
+                               last;
+                       }
+               }
+       }
+       main::log_data('data',"compositor: $compositor") if $b_log;
+       eval $end if $b_log;
+       return $compositor;
+}
+}
+
+## MachineData
+{
+package MachineData;
+
+sub get {
+       eval $start if $b_log;
+       my (%soc_machine,@data,@rows,$key1,$val1,$which);
+       my $num = 0;
+       if ($bsd_type && @sysctl_machine && !$b_dmidecode_force ){
+               @data = machine_data_sysctl();
+               if (!@data && !$key1){
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('machine-data-force-dmidecode','');
+               }
+       }
+       elsif ($bsd_type || $b_dmidecode_force){
+               my $ref = $alerts{'dmidecode'};
+               if ( $$ref{'action'} ne 'use'){
+                       $key1 = $$ref{'action'};
+                       $val1 = $$ref{$key1};
+                       $key1 = ucfirst($key1);
+               }
+               else {
+                       @data = machine_data_dmi();
+                       if (!@data && !$key1){
+                               $key1 = 'Message';
+                               $val1 = main::row_defaults('machine-data','');
+                       }
+               }
+       }
+       elsif (-d '/sys/class/dmi/id/') {
+               @data = machine_data_sys();
+               if (!@data){
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('machine-data-dmidecode','');
+               }
+       }
+       elsif (!$bsd_type) {
+               # this uses /proc/cpuinfo so only GNU/Linux
+               if ($b_arm || $b_mips){
+                       %soc_machine = machine_data_soc();
+                       @data = create_output_soc(%soc_machine) if %soc_machine;
+               }
+               if (!@data){
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('machine-data-force-dmidecode','');
+               }
+       }
+       # if error case, null data, whatever
+       if ($key1) {
+               @data = ({main::key($num++,$key1) => $val1,});
+       }
+       eval $end if $b_log;
+       return @data;
+}
+## keys for machine data are:
+# 0-sys_vendor 1-product_name 2-product_version 3-product_serial 4-product_uuid 
+# 5-board_vendor 6-board_name 7-board_version 8-board_serial 
+# 9-bios_vendor 10-bios_version 11-bios_date
+## with extra data: 
+# 12-chassis_vendor 13-chassis_type 14-chassis_version 15-chassis_serial
+## unused: 16-bios_rev  17-bios_romsize 18 - firmware type
+sub create_output {
+       eval $start if $b_log;
+       my ($ref) = @_;
+       my (%data,@row,@rows);
+       %data = %$ref;
+       my $firmware = 'BIOS';
+       my $num = 0;
+       my $j = 0;
+       my ($b_chassis,$b_skip_chassis,$b_skip_system);
+       my ($bios_date,$bios_rev,$bios_romsize,$bios_vendor,$bios_version,$chassis_serial,
+       $chassis_type,$chassis_vendor,$chassis_version, $mobo_model,$mobo_serial,$mobo_vendor,
+       $mobo_version,$product_name,$product_serial,$product_version,$system_vendor);
+#      foreach my $key (keys %data){
+#              print "$key: $data{$key}\n";
+#      }
+       if (!$data{'sys_vendor'} || ($data{'board_vendor'} && 
+       $data{'sys_vendor'} eq $data{'board_vendor'} && !$data{'product_name'} && 
+           !$data{'product_version'} && !$data{'product_serial'})){
+               $b_skip_system = 1;
+       }
+       # found a case of battery existing but having nothing in it on desktop mobo
+       # not all laptops show the first. /proc/acpi/battery is deprecated.
+       elsif ( !glob('/proc/acpi/battery/*') && !glob('/sys/class/power_supply/*') ){
+               # ibm / ibm can be true; dell / quantum is false, so in other words, only do this
+               # in case where the vendor is the same and the version is the same and not null, 
+               # otherwise the version information is going to be different in all cases I think
+               if ( ($data{'sys_vendor'} && $data{'sys_vendor'} eq $data{'board_vendor'} ) &&
+                       ( ($data{'product_version'} && $data{'product_version'} eq $data{'board_version'} ) ||
+                       (!$data{'product_version'} && $data{'product_name'} eq $data{'board_name'} ) ) ){
+                       $b_skip_system = 1;
+               }
+       }
+       $data{'device'} ||= 'N/A';
+       $j = scalar @rows;
+       @row = ({
+       main::key($num++,'Type') => ucfirst($data{'device'}),
+       },);
+       @rows = (@rows,@row);
+       if (!$b_skip_system){
+               # this has already been tested for above so we know it's not null
+               $system_vendor = main::cleaner($data{'sys_vendor'});
+               $product_name = ($data{'product_name'}) ? $data{'product_name'}:'N/A';
+               $product_version = ($data{'product_version'}) ? $data{'product_version'}:'N/A';
+               $product_serial = main::apply_filter($data{'product_serial'});
+               $rows[$j]{main::key($num++,'System')} = $system_vendor;
+               $rows[$j]{main::key($num++,'product')} = $product_name;
+               $rows[$j]{main::key($num++,'v')} = $product_version;
+               $rows[$j]{main::key($num++,'serial')} = $product_serial;
+               # no point in showing chassis if system isn't there, it's very unlikely that 
+               # would be correct
+               if ($extra > 1){
+                       if ($data{'board_version'} && $data{'chassis_version'} eq $data{'board_version'}){
+                               $b_skip_chassis = 1;
+                       }
+                       if (!$b_skip_chassis && $data{'chassis_vendor'} ){
+                               if ($data{'chassis_vendor'} ne $data{'sys_vendor'} ){
+                                       $chassis_vendor = $data{'chassis_vendor'};
+                               }
+                               # dmidecode can have these be the same
+                               if ($data{'chassis_type'} && $data{'device'} ne $data{'chassis_type'} ){
+                                       $chassis_type = $data{'chassis_type'};
+                               }
+                               if ($data{'chassis_version'}){
+                                       $chassis_version = $data{'chassis_version'};
+                                       $chassis_version =~ s/^v([0-9])/$1/i;
+                               }
+                               $chassis_serial = main::apply_filter($data{'chassis_serial'});
+                               $chassis_vendor ||= '';
+                               $chassis_type ||= '';
+                               $rows[$j]{main::key($num++,'Chassis')} = $chassis_vendor;
+                               if ($chassis_type){
+                                       $rows[$j]{main::key($num++,'type')} = $chassis_type;
+                               }
+                               if ($chassis_version){
+                                       $rows[$j]{main::key($num++,'v')} = $chassis_version;
+                               }
+                               $rows[$j]{main::key($num++,'serial')} = $chassis_serial;
+                       }
+               }
+               $j++; # start new row
+       }
+       if ($data{'firmware'}){
+               $firmware = $data{'firmware'};
+       }
+       $mobo_vendor = ($data{'board_vendor'}) ? main::cleaner($data{'board_vendor'}) : 'N/A';
+       $mobo_model = ($data{'board_name'}) ? $data{'board_name'}: 'N/A';
+       $mobo_version = ($data{'board_version'})? $data{'board_version'} : '';
+       $mobo_serial = main::apply_filter($data{'board_serial'});
+       $bios_vendor = ($data{'bios_vendor'}) ? main::cleaner($data{'bios_vendor'}) : 'N/A';
+       if ($data{'bios_version'}){
+               $bios_version = $data{'bios_version'};
+               $bios_version =~ s/^v([0-9])/$1/i;
+               if ($data{'bios_rev'}){
+                       $bios_rev = $data{'bios_rev'};
+               }
+       }
+       $bios_version ||= 'N/A';
+       if ($data{'bios_date'}){
+               $bios_date = $data{'bios_date'};
+       }
+       $bios_date ||= 'N/A';
+       if ($extra > 1 && $data{'bios_romsize'}){
+               $bios_romsize = $data{'bios_romsize'};
+       }
+       $rows[$j]{main::key($num++,'Mobo')} = $mobo_vendor;
+       $rows[$j]{main::key($num++,'model')} = $mobo_model;
+       if ($mobo_version){
+               $rows[$j]{main::key($num++,'v')} = $mobo_version;
+       }
+       $rows[$j]{main::key($num++,'serial')} = $mobo_serial;
+       if ($extra > 2 && $data{'board_uuid'}){
+               $rows[$j]{main::key($num++,'uuid')} = $data{'board_uuid'};
+       }
+       $rows[$j]{main::key($num++,$firmware)} = $bios_vendor;
+       $rows[$j]{main::key($num++,'v')} = $bios_version;
+       if ($bios_rev){
+               $rows[$j]{main::key($num++,'rev')} = $bios_rev;
+       }
+       $rows[$j]{main::key($num++,'date')} = $bios_date;
+       if ($bios_romsize){
+               $rows[$j]{main::key($num++,'rom size')} = $bios_romsize;
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output_soc {
+       my (%data,@row,@rows);
+       my (%soc_machine) = @_;
+       my $num = 0;
+       my $j = 0;
+       #print Data::Dumper::Dumper \%soc_machine;
+       # this is sketchy, /proc/device-tree/model may be similar to Hardware value from /proc/cpuinfo
+       # raspi: Hardware       : BCM2835 model: Raspberry Pi Model B Rev 2
+       if ($soc_machine{'device'} || $soc_machine{'model'}){
+               my $key = ($b_arm) ? 'ARM Device': 'MIPS Device';
+               $rows[$j]{main::key($num++,'Type')} = $key;
+               my $system = 'System';
+               if (defined $soc_machine{'model'}){
+                       $rows[$j]{main::key($num++,'System')} = $soc_machine{'model'};
+                       $system = 'details';
+               }
+               my $device = $soc_machine{'device'};
+               $device ||= 'N/A';
+               $rows[$j]{main::key($num++,$system)} = $device;
+       }
+       # we're going to print N/A for 0000 values sine the item was there.
+       if ($soc_machine{'firmware'}){
+               # most samples I've seen are like: 0000
+               $soc_machine{'firmware'} =~ s/^[0]+$//;
+               $soc_machine{'firmware'} ||= 'N/A';
+               $rows[$j]{main::key($num++,'rev')} = $soc_machine{'firmware'};
+       }
+       # sometimes has value like: 0000
+       if (defined $soc_machine{'serial'}){
+               # most samples I've seen are like: 0000
+               $soc_machine{'serial'} =~ s/^[0]+$//;
+               $rows[$j]{main::key($num++,'serial')} = main::apply_filter($soc_machine{'serial'});
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub machine_data_sys {
+       eval $start if $b_log;
+       my (%data,$path,$vm);
+       my $sys_dir = '/sys/class/dmi/id/';
+       my $sys_dir_alt = '/sys/devices/virtual/dmi/id/';
+       my @sys_files = qw(bios_vendor bios_version bios_date 
+       board_name board_serial board_vendor board_version chassis_type 
+       product_name product_serial product_uuid product_version sys_vendor
+       );
+       if ($extra > 1){
+               splice @sys_files, 0, 0, qw( chassis_serial chassis_vendor chassis_version);
+       }
+       $data{'firmware'} = 'BIOS';
+       # print Data::Dumper::Dumper \@sys_files;
+       if (!-d $sys_dir ){
+               if ( -d $sys_dir_alt){
+                       $sys_dir = $sys_dir_alt;
+               }
+               else {
+                       return 0;
+               }
+       }
+       if ( -d '/sys/firmware/efi'){
+               $data{'firmware'} = 'UEFI';
+       }
+       elsif ( glob('/sys/firmware/acpi/tables/UEFI*') ){
+               $data{'firmware'} = 'UEFI [Legacy]';
+       }
+       foreach (@sys_files){
+               $path = "$sys_dir$_";
+               if (-r $path){
+                       $data{$_} = (main::reader($path))[0];
+                       $data{$_} = ($data{$_}) ? main::dmi_cleaner($data{$_}) : '';
+               }
+               elsif (!$b_root && -e $path && !-r $path ){
+                       $data{$_} = main::row_defaults('root-required');
+               }
+               else {
+                       $data{$_} = '';
+               }
+       }
+       if ($data{'chassis_type'}){
+               if ( $data{'chassis_type'} == 1){
+                       $data{'device'} = get_device_vm($data{'sys_vendor'},$data{'product_name'});
+                       $data{'device'} ||= 'other-vm?';
+               }
+               else {
+                       $data{'device'} = get_device_sys($data{'chassis_type'});
+               }
+       }
+#      print "sys:\n";
+#      foreach (keys %data){
+#              print "$_: $data{$_}\n";
+#      }
+       main::log_data('dump','%data',\%data) if $b_log;
+       my @rows = create_output(\%data);
+       eval $end if $b_log;
+       return @rows;
+}
+# this will create an alternate machine data source
+# which will be used for alt ARM machine data in cases 
+# where no dmi data present, or by cpu data to guess at 
+# certain actions for arm only.
+sub machine_data_soc {
+       eval $end if $b_log;
+       my (%soc_machine,@temp);
+       if (my $file = main::system_files('cpuinfo')){
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/cpu/arm/arm-shevaplug-1.2ghz.txt";
+               my @data = main::reader($file);
+               foreach (@data){
+                       if (/^(Hardware|machine)\s*:/i){
+                               @temp = split /\s*:\s*/, $_;
+                               $temp[1] = main::arm_cleaner($temp[1]);
+                               $temp[1] = main::dmi_cleaner($temp[1]);
+                               $soc_machine{'device'} = main::cleaner($temp[1]);
+                       }
+                       elsif (/^(system type)\s*:/i){
+                               @temp = split /\s*:\s*/, $_;
+                               $temp[1] = main::dmi_cleaner($temp[1]);
+                               $soc_machine{'model'} = main::cleaner($temp[1]);
+                       }
+                       elsif (/^Revision/i){
+                               @temp = split /\s*:\s*/, $_;
+                               $soc_machine{'firmware'} = $temp[1];
+                       }
+                       elsif (/^Serial/i){
+                               @temp = split /\s*:\s*/, $_;
+                               $soc_machine{'serial'} = $temp[1];
+                       }
+               }
+       }
+       if (!$soc_machine{'model'} && -f '/proc/device-tree/model'){
+               my $model  = (main::reader('/proc/device-tree/model'))[0];
+               main::log_data('data',"device-tree-model: $model") if $b_log;
+               if ( $model ){
+                       $model = main::dmi_cleaner($model);
+                       $model = (split /\x01|\x02|\x03|\x00/, $model)[0] if $model;
+                       # idea was to use only first part of string, but now try using all
+                       #my (@result) = ();
+                       #@result = split(/\s+/, $soc_machine{'device'}) if $soc_machine{'device'};
+                       if ( !$soc_machine{'device'} || ($model && $model !~ /$soc_machine{'device'}/i) ){
+                               $model = main::arm_cleaner($model);
+                               $soc_machine{'model'} = $model;
+                       }
+               }
+       }
+       if (!$soc_machine{'serial'} && -f '/proc/device-tree/serial-number'){
+               my $serial  = (main::reader('/proc/device-tree/serial-number'))[0];
+               $serial = (split /\x01|\x02|\x03|\x00/, $serial)[0] if $serial;
+               main::log_data('data',"device-tree-serial: $serial") if $b_log;
+               $soc_machine{'serial'} = $serial if $serial;
+       }
+       #print Data::Dumper::Dumper \%soc_machine;
+       eval $end if $b_log;
+       return %soc_machine;
+}
+
+# bios_date: 09/07/2010
+# bios_romsize: dmi only
+# bios_vendor: American Megatrends Inc.
+# bios_version: P1.70
+# bios_rev: 8.14:  dmi only
+# board_name: A770DE+
+# board_serial: 
+# board_vendor: ASRock
+# board_version: 
+# chassis_serial: 
+# chassis_type: 3
+# chassis_vendor: 
+# chassis_version: 
+# firmware: 
+# product_name: 
+# product_serial: 
+# product_uuid: 
+# product_version: 
+# sys_uuid: dmi/sysctl only
+# sys_vendor:
+sub machine_data_dmi {
+       eval $start if $b_log;
+       my (%data,$vm);
+       return if ! @dmi;
+       $data{'firmware'} = 'BIOS';
+       # dmi types:
+       # 0 bios; 1 system info; 2 board|base board info; 3 chassis info; 
+       # 4 processor info, use to check for hypervisor
+       foreach (@dmi){
+               my @ref = @$_;
+               # bios/firmware
+               if ($ref[0] == 0){
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       foreach my $item (@ref){
+                               if ($item !~ /^~/){ # skip the indented rows
+                                       my @value = split /:\s+/, $item;
+                                       if ($value[0] eq 'Release Date') {$data{'bios_date'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Vendor') {$data{'bios_vendor'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Version') {$data{'bios_version'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'ROM Size') {$data{'bios_romsize'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'BIOS Revision') {$data{'bios_rev'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] =~ /^UEFI is supported/) {$data{'firmware'} = 'UEFI';}
+                               }
+                       }
+                       next;
+               }
+               # system information
+               elsif ($ref[0] == 1){
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       foreach my $item (@ref){
+                               if ($item !~ /^~/){ # skip the indented rows
+                                       my @value = split /:\s+/, $item;
+                                       if ($value[0] eq 'Product Name') {$data{'product_name'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Version') {$data{'product_version'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Serial Number') {$data{'product_serial'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Manufacturer') {$data{'sys_vendor'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'UUID') {$data{'sys_uuid'} = main::dmi_cleaner($value[1]) }
+                               }
+                       }
+                       next;
+               }
+               # baseboard information
+               elsif ($ref[0] == 2){
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       foreach my $item (@ref){
+                               if ($item !~ /^~/){ # skip the indented rows
+                                       my @value = split /:\s+/, $item;
+                                       if ($value[0] eq 'Product Name') {$data{'board_name'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Serial Number') {$data{'board_serial'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Manufacturer') {$data{'board_vendor'} = main::dmi_cleaner($value[1]) }
+                               }
+                       }
+                       next;
+               }
+               # chassis information
+               elsif ($ref[0] == 3){
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       foreach my $item (@ref){
+                               if ($item !~ /^~/){ # skip the indented rows
+                                       my @value = split /:\s+/, $item;
+                                       if ($value[0] eq 'Serial Number') {$data{'chassis_serial'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Type') {$data{'chassis_type'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Manufacturer') {$data{'chassis_vendor'} = main::dmi_cleaner($value[1]) }
+                                       elsif ($value[0] eq 'Version') {$data{'chassis_version'} = main::dmi_cleaner($value[1]) }
+                               }
+                       }
+                       if ( $data{'chassis_type'} && $data{'chassis_type'} ne 'Other' ){
+                               $data{'device'} = $data{'chassis_type'};
+                       }
+                       next;
+               }
+               # this may catch some BSD and fringe Linux cases
+               # processor information: check for hypervisor
+               elsif ($ref[0] == 4){
+                       # skip first three row, we don't need that data
+                       splice @ref, 0, 3 if @ref;
+                       if (!$data{'device'}){
+                               if (grep {/hypervisor/i} @ref){
+                                       $data{'device'} = 'virtual-machine';
+                               }
+                       }
+                       last;
+               }
+               elsif ($ref[0] > 4){
+                       last;
+               }
+       }
+       if (!$data{'device'}){
+               $data{'device'} = get_device_vm($data{'sys_vendor'},$data{'product_name'});
+               $data{'device'} ||= 'other-vm?';
+       }
+#      print "dmi:\n";
+#      foreach (keys %data){
+#              print "$_: $data{$_}\n";
+#      }
+       main::log_data('dump','%data',\%data) if $b_log;
+       my @rows = create_output(\%data);
+       eval $end if $b_log;
+       return @rows;
+}
+# As far as I know, only OpenBSD supports this method.
+# it uses hw. info from sysctl -a and bios info from dmesg.boot
+sub machine_data_sysctl {
+       eval $start if $b_log;
+       my (%data,$vm);
+       # ^hw\.(vendor|product|version|serialno|uuid)
+       foreach (@sysctl_machine){
+               next if ! $_;
+               my @item = split /:/, $_;
+               next if ! $item[1];
+               if ($item[0] eq 'hw.vendor'){
+                       $data{'board_vendor'} = main::dmi_cleaner($item[1]);
+               }
+               elsif ($item[0] eq 'hw.product'){
+                       $data{'board_name'} = main::dmi_cleaner($item[1]);
+               }
+               elsif ($item[0] eq 'hw.version'){
+                       $data{'board_version'} = $item[1];
+               }
+               elsif ($item[0] eq 'hw.serialno'){
+                       $data{'board_serial'} = $item[1];
+               }
+               elsif ($item[0] eq 'hw.serial'){
+                       $data{'board_serial'} = $item[1];
+               }
+               elsif ($item[0] eq 'hw.uuid'){
+                       $data{'board_uuid'} = $item[1];
+               }
+               # bios0:at mainbus0: AT/286+ BIOS, date 06/30/06, BIOS32 rev. 0 @ 0xf2030, SMBIOS rev. 2.4 @ 0xf0000 (47 entries)
+               # bios0:vendor Phoenix Technologies, LTD version "3.00" date 06/30/2006
+               elsif ($item[0] =~ /^bios[0-9]/){
+                       if ($_ =~ /^^bios[0-9]:at\s.*\srev\.\s([\S]+)\s@.*/){
+                               $data{'bios_rev'} = $1;
+                               $data{'firmware'} = 'BIOS' if $_ =~ /BIOS/;
+                       }
+                       elsif ($item[1] =~ /^vendor\s(.*)\sversion\s"?([\S]+)"?\sdate\s([\S]+)/ ){
+                               $data{'bios_vendor'} = $1;
+                               $data{'bios_version'} = $2;
+                               $data{'bios_date'} = $3;
+                               $data{'bios_version'} =~ s/^v//i if $data{'bios_version'} && $data{'bios_version'} !~ /vi/i;
+                       }
+               }
+       }
+       my @rows = create_output(\%data);
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub get_device_sys {
+       eval $start if $b_log;
+       my ($chasis_id) = @_;
+       my ($device) = ('');
+       my @chassis;
+       # https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.8.0.pdf
+       $chassis[2] = 'unknown';
+       # note: 13 is all-in-one which we take as a mac type system
+       $chassis[3] = 'desktop';
+       $chassis[4] = 'desktop';
+       $chassis[6] = 'desktop';
+       $chassis[7] = 'desktop';
+       $chassis[13] = 'desktop';
+       $chassis[15] = 'desktop';
+       $chassis[24] = 'desktop';
+       # 5 - pizza box was a 1 U desktop enclosure, but some old laptops also id this way
+       $chassis[5] = 'pizza-box';
+       $chassis[9] = 'laptop';
+       # note: lenovo T420 shows as 10, notebook,  but it's not a notebook
+       $chassis[10] = 'laptop';
+       $chassis[16] = 'laptop';
+       $chassis[14] = 'notebook';
+       $chassis[8] = 'portable';
+       $chassis[11] = 'portable';
+       $chassis[17] = 'server';
+       $chassis[23] = 'server';
+       $chassis[25] = 'server';
+       $chassis[27] = 'blade';
+       $chassis[25] = 'blade';
+       $chassis[29] = 'blade';
+       $chassis[12] = 'docking-station';
+       $chassis[18] = 'expansion-chassis';
+       $chassis[19] = 'sub-chassis';
+       $chassis[20] = 'bus-expansion';
+       $chassis[21] = 'peripheral';
+       $chassis[22] = 'RAID';
+       $chassis[26] = 'compact-PCI';
+       $device = $chassis[$chasis_id] if $chassis[$chasis_id];
+       eval $end if $b_log;
+       return $device;
+}
+
+sub get_device_vm {
+       eval $start if $b_log;
+       my ($manufacturer,$product_name) = @_;
+       my $vm;
+       if ( my $program = main::check_program('systemd-detect-virt') ){
+               my $vm_test = (main::grabber("$program 2>/dev/null"))[0];
+               if ($vm_test){
+                       # kvm vbox reports as oracle, usually, unless they change it
+                       if (lc($vm_test) eq 'oracle'){
+                               $vm = 'virtualbox';
+                       }
+                       elsif ( $vm_test ne 'none'){
+                               $vm = $vm_test;
+                       }
+               }
+       }
+       if (!$vm || lc($vm) eq 'bochs') {
+               if (-e '/proc/vz'){$vm = 'openvz'}
+               elsif (-e '/proc/xen'){$vm = 'xen'}
+               elsif (-e '/dev/vzfs'){$vm = 'virtuozzo'}
+               elsif (my $program = main::check_program('lsmod')){
+                       my @vm_data = main::grabber("$program 2>/dev/null");
+                       if (@vm_data){
+                               if (grep {/kqemu/i} @vm_data){$vm = 'kqemu'}
+                               elsif (grep {/kvm/i} @vm_data){$vm = 'kvm'}
+                               elsif (grep {/qemu/i} @vm_data){$vm = 'qemu'}
+                       }
+               }
+       }
+       # this will catch many Linux systems and some BSDs
+       if (!$vm || lc($vm) eq 'bochs' ) {
+               my @vm_data = (@pci,@sysctl,@dmesg_boot);
+               if (-e '/dev/disk/by-id'){
+                       my @dev = glob('/dev/disk/by-id/*');
+                       @vm_data = (@vm_data,@dev);
+               }
+               if ( grep {/innotek|vbox|virtualbox/i} @vm_data){
+                       $vm = 'virtualbox';
+               }
+               elsif (grep {/vmware/i} @vm_data){
+                       $vm = 'vmware';
+               }
+               elsif (grep {/Virtual HD/i} @vm_data){
+                       $vm = 'hyper-v';
+               }
+               if (!$vm && (my $file = main::system_files('cpuinfo'))){
+                       my @info = main::reader($file);
+                       $vm = 'virtual-machine' if grep {/^flags.*hypervisor/} @info;
+               }
+               if (!$vm && -e '/dev/vda' || -e '/dev/vdb' || -e '/dev/xvda' || -e '/dev/xvdb' ){
+                       $vm = 'virtual-machine';
+               }
+       }
+       if (!$vm  && $product_name){
+               if ($product_name eq 'VMware'){
+                       $vm = 'vmware';
+               }
+               elsif ($product_name eq 'VirtualBox'){
+                       $vm = 'virtualbox';
+               }
+               elsif ($product_name eq 'KVM'){
+                       $vm = 'kvm';
+               }
+               elsif ($product_name eq 'Bochs'){
+                       $vm = 'qemu';
+               }
+       }
+       if (!$vm && $manufacturer && $manufacturer eq 'Xen'){
+               $vm = 'xen';
+       }
+       eval $end if $b_log;
+       return $vm;
+}
+
+}
+
+## NetworkData 
+{
+package NetworkData;
+my ($b_ip_run,@ifs_found);
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows);
+       my $num = 0;
+       if (($b_arm || $b_mips) && !$b_soc_net && !$b_pci_tool){
+               # do nothing, but keep the test conditions to force 
+               # the non arm case to always run
+       }
+       else {
+               @data = card_data();
+               @rows = (@rows,@data) if @data;
+       }
+       @data = usb_data();
+       @rows = (@rows,@data) if @data;
+       # note: rasberry pi uses usb networking only 
+       if (!@rows && ($b_arm || $b_mips)){
+               my $key = ($b_arm) ? 'ARM' : 'MIPS';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults(lc($key) . '-pci',''),
+               },);
+               @rows = (@rows,@data);
+       }
+       if ($show{'network-advanced'}){
+               # @ifs_found = ();
+               # shift @ifs_found;
+               # pop @ifs_found;
+               if (!$bsd_type){
+                       @data = advanced_data_sys('check','',0,'','');
+                       @rows = (@rows,@data) if @data;
+               }
+               else {
+                       @data = advanced_data_bsd('check');
+                       @rows = (@rows,@data) if @data;
+               }
+       }
+       if ($show{'ip'}){
+               @data = wan_ip();
+               @rows = (@rows,@data);
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+# 1 type_id
+# 2 bus_id
+# 3 sub_id
+# 4 device
+# 5 vendor_id
+# 6 chip_id
+# 7 rev
+# 8 port
+# 9 driver
+# 10 modules
+# 11 driver nu (bsds)
+sub card_data {
+       eval $start if $b_log;
+       my ($b_wifi,@rows,@data,%holder);
+       my ($j,$num) = (0,1);
+       foreach (@pci){
+               $num = 1;
+               my @row = @$_;
+               #print "$row[0] $row[3]\n"; 
+               # NOTE: class 06 subclass 80 
+               # https://www-s.acm.illinois.edu/sigops/2007/roll_your_own/7.c.1.html
+               if (($row[0] && $row[0] =~ /^(eth|ethernet|ethernet-phy|network|wifi|wlan)$/ )|| ($row[1] && $row[1] eq '0680' ) ){
+                       #print "$row[0] $row[3]\n";
+                       $j = scalar @rows;
+                       my $driver = $row[9];
+                       my $chip_id = "$row[5]:$row[6]";
+                       # working around a virtuo bug same chip id is used on two nics
+                       if (!defined $holder{$chip_id}){
+                               $holder{$chip_id} = 0;
+                       }
+                       else {
+                               $holder{$chip_id}++; 
+                       }
+                       # first check if it's a known wifi id'ed card, if so, no print of duplex/speed
+                       $b_wifi = check_wifi($row[4]);
+                       my $card = $row[4];
+                       $card = ($card) ? main::pci_cleaner($card,'output') : 'N/A';
+                       #$card ||= 'N/A';
+                       $driver ||= 'N/A';
+                       @data = ({
+                       main::key($num++,'Card') => $card,
+                       },);
+                       @rows = (@rows,@data);
+                       #if ($extra > 2 && $b_pci_tool && $row[11]){
+                       #       my $item = main::get_pci_vendor($row[4],$row[11]);
+                       #       $rows[$j]{main::key($num++,'model')} = $item if $item;
+                       #}
+                       if ($row[1] eq '0680'){
+                               $rows[$j]{main::key($num++,'type')} = 'network bridge';
+                       }
+                       $rows[$j]{main::key($num++,'driver')} = $driver;
+                       if ($extra > 0){
+                               if ($row[9] && !$bsd_type){
+                                       my $version = main::get_module_version($row[9]);
+                                       $version ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'v')} = $version;
+                               }
+                               $row[8] ||= 'N/A';
+                               # as far as I know, wifi has no port, but in case it does in future, use it
+                               $rows[$j]{main::key($num++,'port')} = $row[8] if (!$b_wifi || ( $b_wifi && $row[8] ne 'N/A') );
+                               my $bus_id = 'N/A';
+                               # note: for arm/mips we want to see the single item bus id, why not?
+                               if ($row[2] && $row[3]){$bus_id = "$row[2].$row[3]"}
+                               elsif ($row[2]){$bus_id = $row[2]}
+                               elsif ($row[3]){$bus_id = $row[3]}
+                               $rows[$j]{main::key($num++,'bus ID')} = $bus_id;
+                       }
+                       if ($extra > 1){
+                               $rows[$j]{main::key($num++,'chip ID')} = $chip_id;
+                       }
+                       if ($show{'network-advanced'}){
+                               if (!$bsd_type){
+                                       @data = advanced_data_sys($row[5],$row[6],$holder{$chip_id},$b_wifi,'');
+                               }
+                               else {
+                                       @data = advanced_data_bsd("$row[9]$row[11]",$b_wifi);
+                               }
+                               @rows = (@rows,@data);
+                       }
+               }
+               #print "$row[0]\n";
+       }
+       # @rows = ();
+       # we want to handle ARM errors in main get
+       if (!@rows && !$b_arm){
+               my $key = 'Message';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults('pci-card-data',''),
+               },);
+               @rows = (@rows,@data);
+               
+       }
+       #my $ref = $pci[-1];
+       #print $$ref[0],"\n";
+       eval $end if $b_log;
+       return @rows;
+}
+sub usb_data {
+       eval $start if $b_log;
+       my (@data,@rows,@temp2,$b_wifi,$driver,$path,$product,$product2,$test,$vendor,$vendor2);
+       my ($j,$num) = (0,1);
+       return if !@usb;
+       foreach my $ref (@usb){
+               my @row = @$ref;
+               # a device will always be the second or > device on the bus
+               if ($row[1] > 1){
+                       $num = 1;
+                       ($product,$product2,$test,$vendor,$vendor2) = ('','','','','');
+                       if ($usb_level == 1){
+                               $product = main::cleaner($row[3]);
+                       }
+                       else {
+                               foreach my $line (@row){
+                                       my @working = split /:/, $line;
+                                       if ($working[0] eq 'idVendor' && $working[2]){
+                                               $vendor = main::cleaner($working[2]);
+                                       }
+                                       if ($working[0] eq 'idProduct' && $working[2]){
+                                               $product = main::cleaner($working[2]);
+                                       }
+                                       if ($working[0] eq 'iVendor' && $working[2]){
+                                               $product2 = main::cleaner($working[2]);
+                                       }
+                                       if ($working[0] eq 'iProduct' && $working[2]){
+                                               $product2 = main::cleaner($working[2]);
+                                       }
+                                       if ($working[0] eq 'Descriptor_Configuration'){
+                                               last;
+                                       }
+                               }
+                               if ($vendor && $product){
+                                       $product = ($product =~ /$vendor/) ? $product: "$vendor $product";
+                               }
+                               elsif ($vendor && $product2){
+                                       $product = ($product2 =~ /$vendor/) ? $product2: "$vendor $product2";
+                               }
+                               elsif ($vendor2 && $product){
+                                       $product = ($product =~ /$vendor2/) ? $product: "$vendor2 $product";
+                               }
+                               elsif ($vendor2 && $product2){
+                                       $product = ($product2 =~ /$vendor2/) ? $product2: "$vendor2 $product2";
+                               }
+                               elsif ($vendor){
+                                       $product = $vendor;
+                               }
+                               elsif ($vendor2){
+                                       $product = $vendor2;
+                               }
+                               $test = "$vendor $product $vendor2 $vendor2";
+                       }
+                       if ($product && network_device($test)){
+                               @temp2 = main::get_usb_drivers($row[0],$row[2]) if !$bsd_type && -d "/sys/devices";
+                               if (@temp2){
+                                       $driver = $temp2[0] if $temp2[0];
+                                       $path = $temp2[1] if $temp2[1];
+                               }
+                               $driver ||= 'usb-network';
+                               @data = ({
+                               main::key($num++,'Card') => $product,
+                               main::key($num++,'type') => 'USB',
+                               main::key($num++,'driver') => $driver,
+                               },);
+                               $b_wifi = check_wifi($product);
+                               @rows = (@rows,@data);
+                               if ($extra > 0){
+                                       $rows[$j]{main::key($num++,'bus ID')} = "$row[0]:$row[1]";
+                               }
+                               if ($extra > 1){
+                                       $rows[$j]{main::key($num++,'chip ID')} = $row[2];
+                               }
+                               if ($show{'network-advanced'}){
+                                       if (!$bsd_type){
+                                               my (@temp,$vendor,$chip);
+                                               @temp = split (/:/, $row[2]) if $row[2];
+                                               ($vendor,$chip) = ($temp[0],$temp[1]) if @temp;
+                                               @data = advanced_data_sys($vendor,$chip,0,$b_wifi,$path);
+                                       }
+                                       # NOTE: we need the driver.number, like wlp0 to get a match, and 
+                                       # we can't get that from usb data, so we have to let it fall back down 
+                                       # to the check function for BSDs.
+                                       #else {
+                                       #       @data = advanced_data_bsd($row[2],$b_wifi);
+                                       #}
+                                       @rows = (@rows,@data) if @data;
+                               }
+                               $j = scalar @rows;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub advanced_data_sys {
+       eval $start if $b_log;
+       return if ! -d '/sys/class/net';
+       my ($vendor,$chip,$count,$b_wifi,$path_usb) = @_;
+       my $num = 0;
+       my $key = 'IF';
+       my ($b_check,$b_usb,$if,$path,@paths,@row,@rows);
+       # ntoe: we've already gotten the base path, now we 
+       # we just need to get the IF path, which is one level in:
+       # usb1/1-1/1-1:1.0/net/enp0s20f0u1/
+       if ($path_usb){
+               $b_usb = 1;
+               @paths = main::globber("${path_usb}*/net/*");
+       }
+       else {
+               @paths = main::globber('/sys/class/net/*');
+       }
+       @paths = grep {!/\/lo$/} @paths;
+       if ( $count > 0 && $count < scalar @paths ){
+               @paths = splice @paths, $count, scalar @paths;
+       }
+       if ($vendor eq 'check'){
+               $b_check = 1;
+               $key = 'IF-ID';
+       }
+       #print join '; ', @paths,  $count, "\n";
+       foreach (@paths){
+               my ($data1,$data2,$duplex,$mac,$speed,$state);
+               # for usb, we already know where we are
+               if (!$b_usb){
+                       if (!$b_arm || $b_pci_tool ){
+                               $path = "$_/device/vendor";
+                               $data1 = (main::reader($path))[0] if -e $path;
+                               $data1 =~ s/^0x// if $data1;
+                               $path = "$_/device/device";
+                               $data2 = (main::reader($path))[0] if -e $path;
+                               $data2 =~ s/^0x// if $data2;
+                               # this is a fix for a redhat bug in virtio 
+                               $data2 = (defined $data2 && $data2 eq '0001' && defined $chip && $chip eq '1000') ? '1000' : $data2;
+                       }
+                       elsif ($b_arm) {
+                               $path = Cwd::abs_path($_);
+                               $path =~ /($chip)/;
+                               if ($1){
+                                       $data1 = $vendor;
+                                       $data2 = $chip;
+                               }
+                       }
+               }
+               #print "d1:$data1 v:$vendor d2:$data2 c:$chip\n";
+               if ( $b_usb || $b_check || ( $data1 && $data2 && $data1 eq $vendor && $data2 eq $chip )) {
+                       $if = $_;
+                       $if =~ s/^\/.+\///;
+                       # print "top: if: $if ifs: @ifs_found\n";
+                       next if ($b_check && grep {/$if/} @ifs_found);
+                       $path = "$_/duplex";
+                       $duplex = (main::reader($path))[0] if -e $path;
+                       $duplex ||= 'N/A';
+                       $path = "$_/address";
+                       $mac = (main::reader($path))[0] if -e $path;
+                       $mac = main::apply_filter($mac);
+                       $path = "$_/speed";
+                       $speed = (main::reader($path))[0] if -e $path;
+                       $speed ||= 'N/A';
+                       $path = "$_/operstate";
+                       $state = (main::reader($path))[0] if -e $path;
+                       $state ||= 'N/A';
+                       #print "$speed \n";
+                       @row = ({
+                       main::key($num++,$key) => $if,
+                       main::key($num++,'state') => $state,
+                       },);
+                       #my $j = scalar @row - 1;
+                       push (@ifs_found, $if) if (!$b_check && (! grep {/$if/} @ifs_found));
+                       # print "push: if: $if ifs: @ifs_found\n";
+                       # no print out for wifi since it doesn't have duplex/speed data available
+                       # note that some cards show 'unknown' for state, so only testing explicitly
+                       # for 'down' string in that to skip showing speed/duplex
+                       # /sys/class/net/$if/wireless : nont always there, but worth a try: wlan/wl/ww/wlp
+                       $b_wifi = 1 if !$b_wifi && ( -e "$_$if/wireless" || $if =~ /^(wl|ww)/);
+                       if (!$b_wifi && $state ne 'down' && $state ne 'no'){
+                               # make sure the value is strictly numeric before appending Mbps
+                               $speed = ($speed =~ /^[0-9]+$/) ? "$speed Mbps" : $speed;
+                               $row[0]{main::key($num++,'speed')} = $speed;
+                               $row[0]{main::key($num++,'duplex')} = $duplex;
+                       }
+                       $row[0]{main::key($num++,'mac')} = $mac;
+                       if ($b_check){
+                               @rows = (@rows,@row);
+                       }
+                       else {
+                               @rows = @row;
+                       }
+                       if ($show{'ip'}){
+                               @row = if_ip($if);
+                               @rows = (@rows,@row);
+                       }
+                       last if !$b_check;
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub advanced_data_bsd {
+       eval $start if $b_log;
+       return if ! @ifs_bsd;
+       my ($if,$b_wifi) = @_;
+       my (@data,@row,@rows,$working_if);
+       my ($b_check,$state,$speed,$duplex,$mac);
+       my $num = 0;
+       my $key = 'IF';
+       my $j = 0;
+       if ($if eq 'check'){
+               $b_check = 1;
+               $key = 'IF-ID';
+       }
+       foreach my $ref (@ifs_bsd){
+               if (ref $ref ne 'ARRAY'){
+                       $working_if = $ref;
+                       # print "$working_if\n";
+                       next;
+               } 
+               else {
+                       @data = @$ref;
+               }
+               if ( $b_check || $working_if eq $if){
+                       $if = $working_if if $b_check;
+                       # print "top: if: $if ifs: @ifs_found\n";
+                       next if ($b_check && grep {/$if/} @ifs_found);
+                       foreach my $line (@data){
+                               # ($state,$speed,$duplex,$mac)
+                               $duplex = $data[2];
+                               $duplex ||= 'N/A';
+                               $mac = main::apply_filter($data[3]);
+                               $speed = $data[1];
+                               $speed ||= 'N/A';
+                               $state = $data[0];
+                               $state ||= 'N/A';
+                               #print "$speed \n";
+                               @row = ({
+                               main::key($num++,$key) => $if,
+                               main::key($num++,'state') => $state,
+                               },);
+                               push (@ifs_found, $if) if (!$b_check && (! grep {/$if/} @ifs_found ));
+                               # print "push: if: $if ifs: @ifs_found\n";
+                               # no print out for wifi since it doesn't have duplex/speed data available
+                               # note that some cards show 'unknown' for state, so only testing explicitly
+                               # for 'down' string in that to skip showing speed/duplex
+                               if (!$b_wifi && $state ne 'down' && $state ne 'no'){
+                                       # make sure the value is strictly numeric before appending Mbps
+                                       $speed = ($speed =~ /^[0-9]+$/) ? "$speed Mbps" : $speed;
+                                       $row[0]{main::key($num++,'speed')} = $speed;
+                                       $row[0]{main::key($num++,'duplex')} = $duplex;
+                               }
+                               $row[0]{main::key($num++,'mac')} = $mac;
+                       }
+                       @rows = (@rows,@row);
+                       if ($show{'ip'}){
+                               @row = if_ip($if) if $if;
+                               @rows = (@rows,@row) if @row;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+## values:
+# 0 - ipv 
+# 1 - ip 
+# 2 - broadcast, if found 
+# 3 - scope, if found 
+# 4 - scope if, if different from if
+sub if_ip {
+       eval $start if $b_log;
+       my ($if) = @_;
+       my (@data,@row,@rows,$working_if);
+       my $num = 0;
+       my $j = 0;
+       $b_ip_run = 1;
+       OUTER:
+       foreach my $ref (@ifs){
+               if (ref $ref ne 'ARRAY'){
+                       $working_if = $ref;
+                       # print "if:$if wif:$working_if\n";
+                       next;
+               } 
+               else {
+                       @data = @$ref;
+                       # print "ref:$ref\n";
+               }
+               if ($working_if eq $if){
+                       foreach my $ref2 (@data){
+                               $j = scalar @rows;
+                               $num = 1;
+                               if ($limit > 0 && $j >= $limit){
+                                       @row  = ({
+                                       main::key($num++,'Message') => main::row_defaults('output-limit',scalar @data),
+                                       },);
+                                       @rows = (@rows,@row);
+                                       last OUTER;
+                               }
+                               my @data2 = @$ref2;
+                               #print "$data2[0] $data2[1]\n";
+                               my ($ipv,$ip,$broadcast,$scope,$scope_id);
+                               $ipv = ($data2[0])? $data2[0]: 'N/A';
+                               $ip = main::apply_filter($data2[1]);
+                               $scope = ($data2[3])? $data2[3]: 'N/A';
+                               if ($if ne 'all'){
+                                       if (defined $data2[4] && $working_if ne $data2[4]){
+                                               # scope global temporary deprecated dynamic 
+                                               # scope global dynamic 
+                                               # scope global temporary deprecated dynamic 
+                                               # scope site temporary deprecated dynamic 
+                                               # scope global dynamic noprefixroute enx403cfc00ac68
+                                               # scope global eth0
+                                               # scope link
+                                               # scope site dynamic 
+                                               # scope link 
+                                               # trim off if at end of multi word string if found
+                                               $data2[4] =~ s/\s$if$// if $data2[4] =~ /[^\s]+\s$if$/;
+                                               my $key = ($data2[4] =~ /deprecated|dynamic|temporary|noprefixroute/ ) ? 'type':'virtual' ;
+                                               @row  = ({
+                                               main::key($num++,"IP v$ipv") => $ip,
+                                               main::key($num++,$key) => $data2[4],
+                                               main::key($num++,'scope') => $scope,
+                                               },);
+                                       }
+                                       else {
+                                               @row  = ({
+                                               main::key($num++,"IP v$ipv") => $ip,
+                                               main::key($num++,'scope') => $scope,
+                                               },);
+                                       }
+                               }
+                               else {
+                                       @row  = ({
+                                       main::key($num++,'IF') => $if,
+                                       main::key($num++,"IP v$ipv") => $ip,
+                                       main::key($num++,'scope') => $scope,
+                                       },);
+                               }
+                               @rows = (@rows,@row);
+                               if ($extra > 1 && $data2[2]){
+                                       $broadcast = main::apply_filter($data2[2]);
+                                       $rows[$j]{main::key($num++,'broadcast')} = $broadcast;
+                               }
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+# get ip using downloader to stdout. This is a clean, text only IP output url,
+# single line only, ending in the ip address. May have to modify this in the future
+# to handle ipv4 and ipv6 addresses but should not be necessary.
+# ip=$( echo  2001:0db8:85a3:0000:0000:8a2e:0370:7334 | gawk  --re-interval '
+# ip=$( wget -q -O - $WAN_IP_URL | gawk  --re-interval '
+# this generates a direct dns based ipv4 ip address, but if opendns.com goes down, 
+# the fall backs will still work. 
+# note: consistently slower than domain based: 
+# dig +short +time=1 +tries=1 myip.opendns.com. A @208.67.222.222
+sub wan_ip {
+       eval $start if $b_log;
+       my (@data,$ip);
+       my $num = 0;
+       # time: 0.06 - 0.07 seconds
+       if (my $program = main::check_program('dig')){
+               $ip = (main::grabber("$program +short +time=1 +tries=1 myip.opendns.com \@resolver1.opendns.com 2>/dev/null"))[0];
+       }
+       else {
+               # note: tests: akamai: 0.055 - 0.065 icanhazip.com: 0.177 0.164
+               # smxi: 0.525, so almost 10x slower. Dig is fast too
+               # leaving smxi as last test because I know it will always be up.
+               my @urls = qw( http://whatismyip.akamai.com/ http://icanhazip.com/ https://smxi.org/opt/ip.php);
+               foreach (@urls){
+                       $ip = main::download_file('stdout',$_);
+                       if ($ip){
+                               # print "$_\n";
+                               chomp $ip;
+                               $ip = (split /\s+/, $ip)[-1];
+                               last;
+                       }
+               }
+       }
+       if ($ip && $show{'filter'}){
+               $ip = $filter_string;
+       }
+       $ip ||= main::row_defaults('IP', 'WAN IP');
+       @data = ({
+       main::key($num++,'WAN IP') => $ip,
+       },);
+       eval $end if $b_log;
+       return @data;
+}
+
+### USB networking search string data, because some brands can have other products than
+### wifi/nic cards, they need further identifiers, with wildcards.
+### putting the most common and likely first, then the less common, then some specifics
+
+# Wi-Fi.*Adapter Wireless.*Adapter Ethernet.*Adapter WLAN.*Adapter 
+# Network.*Adapter 802\.11 Atheros Atmel D-Link.*Adapter D-Link.*Wireless Linksys 
+# Netgea Ralink Realtek.*Network Realtek.*Wireless Realtek.*WLAN Belkin.*Wireless 
+# Belkin.*WLAN Belkin.*Network Actiontec.*Wireless Actiontec.*Network AirLink.*Wireless 
+# Asus.*Network Asus.*Wireless Buffalo.*Wireless Davicom DWA-.*RangeBooster DWA-.*Wireless 
+# ENUWI-.*Wireless LG.*Wi-Fi Rosewill.*Wireless RNX-.*Wireless Samsung.*LinkStick 
+# Samsung.*Wireless Sony.*Wireless TEW-.*Wireless TP-Link.*Wireless 
+# WG[0-9][0-9][0-9].*Wireless WNA[0-9][0-9][0-9] WNDA[0-9][0-9][0-9] 
+# Zonet.*ZEW.*Wireless
+sub network_device {
+       eval $start if $b_log;
+       my ($device_string) = @_;
+       my ($b_network);
+       # belkin=050d; d-link=07d1; netgear=0846; ralink=148f; realtek=0bda; 
+       # Atmel makes other stuff
+       my @tests = qw(wifi Wi-Fi.*Adapter Ethernet \bLAN\b WLAN Network 802\.11 
+       Wireless.*Adapter 54\sMbps Network 100\/1000 Mobile\sBroadband Atheros D-Link.*Adapter 
+       Dell.*Wireless D-Link.*Wireless Linksys Netgea Ralink Realtek.*Network Realtek.*Wireless
+       Belkin.*Wireless Actiontec.*Wireless AirLink.*Wireless Asus.*Wireless 
+       Buffalo.*Wireless Davicom DWA-.*RangeBooster DWA-.*Wireless
+       ENUWI-.*Wireless LG.*Wi-Fi Rosewill.*Wireless RNX-.*Wireless Samsung.*LinkStick 
+       Samsung.*Wireless Sony.*Wireless TEW-.*Wireless TP-Link.*Wireless 
+       WG[0-9][0-9][0-9].*Wireless WNA[0-9][0-9][0-9] WNDA[0-9][0-9][0-9] 
+       Zonet.*ZEW.*Wireless 050d:935b 0bda:8189 0bda:8197
+       );
+       foreach (@tests){
+               if ($device_string =~ /$_/i ){
+                       $b_network = 1;
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return $b_network;
+}
+sub check_wifi {
+       my ($item) = @_;
+       my $b_wifi = ($item =~ /wireless|wifi|wi-fi|wlan|802\.11|centrino/i) ? 1 : 0;
+       return $b_wifi;
+}
+}
+
+## OpticalData
+{
+package OpticalData;
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows,$key1,$val1);
+       my $num = 0;
+       if ($bsd_type){
+               #@data = optical_data_bsd();
+               $key1 = 'Optical Report';
+               $val1 = main::row_defaults('optical-data-bsd');
+               @data = ({main::key($num++,$key1) => $val1,});
+               if ( @dm_boot_optical){
+                       @data = optical_data_bsd();
+               }
+               else{
+                       my $file = main::system_files('dmesg-boot');
+                       if ( $file && ! -r $file ){
+                               $val1 = main::row_defaults('dmesg-boot-permissions');
+                       }
+                       elsif (!$file){
+                               $val1 = main::row_defaults('dmesg-boot-missing');
+                       }
+                       else {
+                               $val1 = main::row_defaults('optical-data-bsd');
+                       }
+                       $key1 = 'Optical Report';
+                       @data = ({main::key($num++,$key1) => $val1,});
+               }
+       }
+       else {
+               @data = optical_data_linux();
+       }
+       if (!@data){
+               $key1 = 'Message';
+               $val1 = main::row_defaults('optical-data');
+               @data = ({main::key($num++,$key1) => $val1,});
+       }
+       @rows = (@rows,@data);
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my (%devices) = @_;
+       my (@data,@rows);
+       my $num = 0;
+       my $j = 0;
+       # build floppy if any
+       foreach my $key (sort keys %devices){
+               if ($devices{$key}{'type'} eq 'floppy'){
+                       @data = ({ main::key($num++,ucfirst($devices{$key}{'type'})) => "/dev/$key"});
+                       @rows = (@rows,@data);
+                       delete $devices{$key};
+               }
+       }
+       foreach my $key (sort keys %devices){
+               $j = scalar @rows;
+               $num = 1;
+               my $vendor = $devices{$key}{'vendor'};
+               $vendor ||= 'N/A';
+               my $model = $devices{$key}{'model'};
+               $model ||= 'N/A';
+               @data = ({ 
+               main::key($num++,ucfirst($devices{$key}{'type'})) => "/dev/$key",
+               main::key($num++,'vendor') => $vendor,
+               main::key($num++,'model') => $model,
+               });
+               @rows = (@rows,@data);
+               if ($extra > 0){
+                       my $rev = $devices{$key}{'rev'};
+                       $rev ||= 'N/A';
+                       $rows[$j]{ main::key($num++,'rev')} = $rev;
+               }
+               if ($extra > 1 && $devices{$key}{'serial'}){
+                       $rows[$j]{ main::key($num++,'serial')} = main::apply_filter($devices{$key}{'serial'});
+               }
+               my $ref = $devices{$key}{'links'};
+               my $links = (@$ref) ? join ',', sort @$ref: 'N/A' ;
+               $rows[$j]{ main::key($num++,'dev-links')} = $links;
+               if ($show{'optical'}){
+                       $j = scalar @rows;
+                       my $speed = $devices{$key}{'speed'};
+                       $speed ||= 'N/A';
+                       my ($audio,$multisession) = ('','');
+                       if (defined $devices{$key}{'multisession'}){
+                               $multisession = ( $devices{$key}{'multisession'} == 1 ) ? 'yes' : 'no' ;
+                       }
+                       $multisession ||= 'N/A';
+                       if (defined $devices{$key}{'audio'}){
+                               $audio = ( $devices{$key}{'audio'} == 1 ) ? 'yes' : 'no' ;
+                       }
+                       $audio ||= 'N/A';
+                       my $dvd = 'N/A';
+                       my (@rw,$rws);
+                       if (defined $devices{$key}{'dvd'}){
+                               $dvd = ( $devices{$key}{'dvd'} == 1 ) ? 'yes' : 'no' ;
+                       }
+                       if ($devices{$key}{'cdr'}){
+                               push @rw, 'cd-r';
+                       }
+                       if ($devices{$key}{'cdrw'}){
+                               push @rw, 'cd-rw';
+                       }
+                       if ($devices{$key}{'dvdr'}){
+                               push @rw, 'dvd-r';
+                       }
+                       if ($devices{$key}{'dvdram'}){
+                               push @rw, 'dvd-ram';
+                       }
+                       $rws = (@rw) ? join ',', @rw: 'none' ;
+                       @data = ({
+                       main::key($num++,'Features') => '',
+                       main::key($num++,'speed') => $speed,
+                       main::key($num++,'multisession') => $multisession,
+                       main::key($num++,'audio') => $audio,
+                       main::key($num++,'dvd') => $dvd,
+                       main::key($num++,'rw') => $rws,
+                       });
+                       @rows = (@rows,@data);
+                       
+                       if ($extra > 0 ){
+                               my $state = $devices{$key}{'state'};
+                               $state ||= 'N/A';
+                               $rows[$j]{ main::key($num++,'state')} = $state;
+                       }
+               }
+       }
+       #print Data::Dumper::Dumper \%devices;
+       eval $end if $b_log;
+       return @rows;
+}
+sub optical_data_bsd {
+       eval $start if $b_log;
+       my (@data,%devices,@rows,@temp);
+       my ($count,$i,$working) = (0,0,'');
+       foreach (@dm_boot_optical){
+               $_ =~ s/(cd[0-9]+)\(([^:]+):([0-9]+):([0-9]+)\):/$1:$2-$3.$4,/;
+               my @row = split /:\s*/, $_;
+               next if ! defined $row[1];
+               if ($working ne $row[0]){
+                       # print "$id_holder $row[0]\n";
+                       $working = $row[0];
+               }
+               # no dots, note: ada2: 2861588MB BUT: ada2: 600.000MB/s 
+               if (! exists $devices{$working}){
+                       $devices{$working} = ({});
+                       $devices{$working}{'links'} = ([]);
+                       $devices{$working}{'model'} = '';
+                       $devices{$working}{'rev'} = '';
+                       $devices{$working}{'state'} = '';
+                       $devices{$working}{'vendor'} = '';
+                       $devices{$working}{'temp'} = '';
+                       $devices{$working}{'type'} = ($working =~ /^cd/) ? 'optical' : 'unknown';
+               }
+               #print "$_\n";
+               if ($bsd_type ne 'openbsd'){
+                       if ($row[1] && $row[1] =~ /^<([^>]+)>/){
+                               $devices{$working}{'model'} = $1;
+                               $count = ($devices{$working}{'model'} =~ tr/ //);
+                               if ($count && $count > 1){
+                                       @temp = split /\s+/, $devices{$working}{'model'};
+                                       $devices{$working}{'vendor'} = $temp[0];
+                                       my $index = ($#temp > 2 ) ? ($#temp - 1): $#temp;
+                                       $devices{$working}{'model'} = join ' ', @temp[1..$index];
+                                       $devices{$working}{'rev'} = $temp[-1] if $count > 2;
+                               }
+                               if ($show{'optical'}){
+                                       if (/\bDVD\b/){
+                                               $devices{$working}{'dvd'} = 1;
+                                       }
+                                       if (/\bRW\b/){
+                                               $devices{$working}{'cdrw'} = 1;
+                                               $devices{$working}{'dvdr'} = 1 if $devices{$working}{'dvd'};
+                                       }
+                               }
+                       }
+                       if ($row[1] && $row[1] =~ /^Serial/){
+                               @temp = split /\s+/,$row[1];
+                               $devices{$working}{'serial'} = $temp[-1];
+                       }
+                       if ($show{'optical'}){
+                               if ($row[1] =~ /^([0-9\.]+[MGTP][B]?\/s)/){
+                                       $devices{$working}{'speed'} = $1;
+                                       $devices{$working}{'speed'} =~ s/\.[0-9]+//;
+                               }
+                               if (/\bDVD[-]?RAM\b/){
+                                       $devices{$working}{'cdr'} = 1;
+                                       $devices{$working}{'dvdram'} = 1;
+                               }
+                               if ($row[2] && $row[2] =~ /,\s(.*)$/){
+                                       $devices{$working}{'state'} = $1;
+                                       $devices{$working}{'state'} =~ s/\s+-\s+/, /;
+                               }
+                       }
+               }
+               else {
+                       if ($row[2] && $row[2] =~ /<([^>]+)>/){
+                               $devices{$working}{'model'} = $1;
+                               $count = ($devices{$working}{'model'} =~ tr/,//);
+                               #print "c: $count $row[2]\n";
+                               if ($count && $count > 1){
+                                       @temp = split /,\s*/, $devices{$working}{'model'};
+                                       $devices{$working}{'vendor'} = $temp[0];
+                                       $devices{$working}{'model'} = $temp[1];
+                                       $devices{$working}{'rev'} = $temp[2];
+                               }
+                               if ($show{'optical'}){
+                                       if (/\bDVD\b/){
+                                               $devices{$working}{'dvd'} = 1;
+                                       }
+                                       if (/\bRW\b/){
+                                               $devices{$working}{'cdrw'} = 1;
+                                               $devices{$working}{'dvdr'} = 1 if $devices{$working}{'dvd'};
+                                       }
+                                       if (/\bDVD[-]?RAM\b/){
+                                               $devices{$working}{'cdr'} = 1;
+                                               $devices{$working}{'dvdram'} = 1;
+                                       }
+                               }
+                       }
+                       if ($show{'optical'}){
+                               #print "$row[1]\n";
+                               if (($row[1] =~ tr/,//) > 1){
+                                       @temp = split /,\s*/, $row[1];
+                                       $devices{$working}{'speed'} = $temp[2];
+                               }
+                               
+                       }
+               }
+       }
+       
+       main::log_data('dump','%devices',\%devices) if $b_log;
+       #print Data::Dumper::Dumper \%devices;
+       @rows = create_output(%devices) if %devices;
+       eval $end if $b_log;
+       return @rows;
+}
+sub optical_data_linux {
+       eval $start if $b_log;
+       my (@data,%devices,@info,@rows);
+       @data = main::globber('/dev/dvd* /dev/cdr* /dev/scd* /dev/sr* /dev/fd[0-9]');
+       # Newer kernel is NOT linking all optical drives. Some, but not all.
+       # Get the actual disk dev location, first try default which is easier to run, 
+       # need to preserve line breaks
+       foreach (@data){
+               my $working = readlink($_);
+               $working = ($working) ? $working: $_;
+               next if $working =~ /random/;
+               # possible fix: puppy has these in /mnt not /dev they say
+               $working =~ s/\/(dev|media|mnt)\///;
+               $_ =~ s/\/(dev|media|mnt)\///;
+               if  (! defined $devices{$working}){
+                       my @temp = ($_ ne $working) ? ([$_]) : ([]);
+                       $devices{$working} = ({'links' => @temp});
+                       $devices{$working}{'type'} = ($working =~ /^fd/) ? 'floppy' : 'optical' ;
+               }
+               else {
+                       my $ref = $devices{$working}{'links'};
+                       push @$ref, $_ if $_ ne $working;
+               }
+               #print "$working\n";
+       }
+       if ($show{'optical'} && -e '/proc/sys/dev/cdrom/info'){
+               @info = main::reader('/proc/sys/dev/cdrom/info','strip');
+       }
+       #print join '; ', @data, "\n";
+       foreach my $key (keys %devices){
+               next if $devices{$key}{'type'} eq 'floppy';
+               my $device = "/sys/block/$key/device";
+               if ( -d $device){
+                       if (-e "$device/vendor"){
+                               $devices{$key}{'vendor'} = (main::reader("$device/vendor"))[0];
+                               $devices{$key}{'vendor'} = main::cleaner($devices{$key}{'vendor'});
+                               $devices{$key}{'state'} = (main::reader("$device/state"))[0];
+                               $devices{$key}{'model'} = (main::reader("$device/model"))[0];
+                               $devices{$key}{'model'} = main::cleaner($devices{$key}{'model'});
+                               $devices{$key}{'rev'} = (main::reader("$device/rev"))[0];
+                       }
+               }
+               elsif ( -e "/proc/ide/$_/model"){
+                       $devices{$key}{'vendor'} = (main::reader("/proc/ide/$_/model"))[0];
+                       $devices{$key}{'vendor'} = main::cleaner($devices{$key}{'vendor'});
+               }
+               if ($show{'optical'} && @info){
+                       my $index = 0;
+                       foreach my $item (@info){
+                               next if $item =~ /^\s*$/;
+                               my @split = split '\s+', $item;
+                               if ($item =~ /^drive name:/){
+                                       foreach my $id (@split){
+                                               last if ($id eq $key);
+                                               $index++;
+                                       }
+                                       last if ! $index; # index will be > 0 if it was found
+                               }
+                               elsif ($item =~/^drive speed:/) {
+                                       $devices{$key}{'speed'} = $split[$index];
+                               }
+                               elsif ($item =~/^Can read multisession:/) {
+                                       $devices{$key}{'multisession'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can read MCN:/) {
+                                       $devices{$key}{'mcn'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can play audio:/) {
+                                       $devices{$key}{'audio'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can write CD-R:/) {
+                                       $devices{$key}{'cdr'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can write CD-RW:/) {
+                                       $devices{$key}{'cdrw'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can read DVD:/) {
+                                       $devices{$key}{'dvd'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can write DVD-R:/) {
+                                       $devices{$key}{'dvdr'}=$split[$index+1];
+                               }
+                               elsif ($item =~/^Can write DVD-RAM:/) {
+                                       $devices{$key}{'dvdram'}=$split[$index+1];
+                               }
+                       }
+               }
+       }
+       main::log_data('dump','%devices',\%devices) if $b_log;
+       #print Data::Dumper::Dumper \%devices;
+       @rows = create_output(%devices) if %devices;
+       eval $end if $b_log;
+       return @rows;
+}
+
+}
+
+## PartitionData
+{
+package PartitionData;
+
+sub get {
+       eval $start if $b_log;
+       my (@rows,$key1,$val1);
+       my $num = 0;
+       partition_data() if !$b_partitions;
+       if (!@partitions) {
+               $key1 = 'Message';
+               #$val1 = ($bsd_type && $bsd_type eq 'darwin') ? 
+               # main::row_defaults('darwin-feature') : main::row_defaults('partition-data');
+               $val1 = main::row_defaults('partition-data');
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       else {
+               @rows = create_output();
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my $num = 0;
+       my $j = 0;
+       my (@data,@data2,%part,@rows,$dev,$dev_type,$fs);
+       @partitions = sort { $a->{'id'} cmp $b->{'id'} } @partitions;
+       foreach my $ref (@partitions){
+               my %row = %$ref;
+               $num = 1;
+               next if $row{'type'} eq 'secondary' && $show{'partition'};
+               @data2 = main::get_size($row{'size'}) if (defined $row{'size'});
+               my $size = (@data2) ? $data2[0] . ' ' . $data2[1]: 'N/A';
+               @data2 = main::get_size($row{'used'}) if (defined $row{'used'});
+               my $used = (@data2) ? $data2[0] . ' ' . $data2[1]: 'N/A';
+               my $percent = (defined $row{'percent-used'}) ? ' (' . $row{'percent-used'} . '%)' : '';
+               %part = ();
+               if (defined $row{'dev-base'}){
+                       if ($row{'dev-base'} =~ /^non-dev-/){
+                               $row{'dev-base'} =~ s/^non-dev-//;
+                               $dev_type = 'raid';
+                               $dev = $row{'dev-base'};
+                       }
+                       # note: I have seen this: beta:data/ for sshfs path
+                       elsif ($row{'dev-base'} =~ /^\/\/|:\//){
+                               $dev_type = 'remote';
+                               $dev = $row{'dev-base'};
+                       }
+                       # an error has occurred almost for sure
+                       elsif (!$row{'dev-base'}){
+                               $dev_type = 'dev';
+                               $dev = main::row_defaults('unknown-dev');
+                       }
+                       else {
+                               $dev_type = 'dev';
+                               $dev = '/dev/' . $row{'dev-base'};
+                       }
+               }
+               else {
+                       $dev_type = 'dev';
+               }
+               $fs = ($row{'fs'}) ? lc($row{'fs'}): 'N/A';
+               $dev ||= 'N/A';
+               $j = scalar @rows;
+               @data = ({
+               main::key($num++,'ID') => $row{'id'},
+               main::key($num++,'size') => $size,
+               main::key($num++,'used') => $used . $percent,
+               main::key($num++,'fs') => $fs,
+               main::key($num++,$dev_type) => $dev,
+               });
+               @rows = (@rows,@data);
+               if ($show{'label'}){
+                       $rows[$j]{main::key($num++,'label')} = ($row{'label'}) ? $row{'label'}: 'N/A';
+               }
+               if ($show{'uuid'}){
+                       $rows[$j]{main::key($num++,'uuid')} = ($row{'uuid'}) ? $row{'uuid'}: 'N/A';
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub partition_data {
+       eval $start if $b_log;
+       #return if $bsd_type && $bsd_type eq 'darwin'; # darwin has muated output, of course
+       my (@data,@rows,@mapper,@mount,@partitions_working,%part);
+       my ($b_fake_map,$b_fs,$b_load,$cols,$roots) = (0,1,0,6,0);
+       my ($back_size,$back_used) = (4,3);
+       my ($dev_base,$fs,$id,$label,$percent_used,$size,$type,$uuid,$used);
+       $b_partitions = 1;
+       set_lsblk() if !$bsd_type && !$b_lsblk;
+       # set labels, uuid, gpart
+       set_label_uuid() if !$b_label_uuid;
+       # most current OS support -T and -k, but -P means different things
+       # in freebsd. However since most use is from linux, we make that default
+       if (!$bsd_type){
+               @partitions_working = main::grabber("df -P -T -k 2>/dev/null");
+               if (-d '/dev/mapper'){
+                       @mapper = main::globber('/dev/mapper/*');
+               }
+       }
+       else {
+               # this is missing the file system data
+               if ($bsd_type ne 'darwin'){
+                       @partitions_working = main::grabber("df -T -k 2>/dev/null");
+               }
+               #Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on
+               else {
+                       $cols = 8;
+                       $b_fake_map = 1;
+                       ($back_size,$back_used) = (7,6);
+               }
+       }
+       # busybox only supports -k and -P, openbsd, darwin
+       if (!@partitions_working){
+               @partitions_working = main::grabber("df -k 2>/dev/null");
+               $b_fs = 0;
+               $cols = 5 if !$bsd_type || $bsd_type ne 'darwin';
+               if (my $path = main::check_program('mount')){
+                       @mount = main::grabber("$path 2>/dev/null");
+               }
+       }
+       # determine positions
+       my $row1 = shift @partitions_working;
+       # new kernels/df have rootfs and / repeated, creating two entries for the same partition
+       # so check for two string endings of / then slice out the rootfs one, I could check for it
+       # before slicing it out, but doing that would require the same action twice re code execution
+       foreach (@partitions_working){
+               if (/\s\/$/){
+                       $roots++;
+               }
+       }
+       @partitions_working = grep {!/^rootfs/} @partitions_working if $roots > 1;
+       my $filters = '^(aufs|cgroup.*|cgmfs|configfs|debugfs|\/dev|dev|\/dev/loop[0-9]*|';
+       $filters .= 'devfs|devtmpfs|fdescfs|iso9660|linprocfs|none|procfs|\/run(\/.*)?|';
+       $filters .= 'run|shm|squashfs|sys|\/sys\/.*|sysfs|tmpfs|type|udev|unionfs|vartmp)$';
+       foreach (@partitions_working){
+               # stupid apple bullshit
+               $_ =~ s/^map\s+([\S]+)/map:\/$1/ if $b_fake_map;
+               my @row = split /\s+/, $_;
+               if ($row[0] =~ /$filters/ || $row[0] =~ /^ROOT/i || ($b_fs && $row[1] eq 'tmpfs')){
+                       next;
+               }
+               $dev_base = '';
+               $fs = '';
+               $id = '';
+               $label = '';
+               $size = 0;
+               $used = 0;
+               %part = ();
+               $percent_used = 0;
+               $type = '';
+               $uuid = '';
+               $b_load = 0;
+               # NOTE: using -P for linux fixes line wraps, and for bsds, assuming they don't use such long file names
+               if ($row[0] =~ /^\/dev\/|:\/|\/\//){
+                       # this could point to by-label or by-uuid so get that first. In theory, abs_path should 
+                       # drill down to get the real path, but it isn't always working.
+                       if ($row[0] eq '/dev/root'){
+                               $row[0] = get_root();
+                       }
+                       # sometimes paths are set using /dev/disk/by-[label|uuid] so we need to get the /dev/xxx path
+                       if ($row[0] =~ /by-label|by-uuid/){
+                               $row[0] = Cwd::abs_path($row[0]);
+                       }
+                       elsif ($row[0] =~ /mapper\// && @mapper){
+                               $row[0] = get_mapper($row[0],@mapper);
+                       }
+                       $dev_base = $row[0];
+                       $dev_base =~ s/^\/dev\///;
+                       %part = check_lsblk($dev_base,0) if @lsblk;
+               }
+               # this handles zfs type devices/partitions, which do not start with / but contain /
+               # note: Main/jails/transmission_1 path can be > 1 deep 
+               # Main zfs 3678031340 8156 3678023184 0% /mnt/Main
+               if (!$dev_base && ($row[0] =~ /^([^\/]+\/)(.+)/ || ($row[0] =~ /^[^\/]+$/ && $row[1] =~ /^(btrfs|zfs)$/ ) ) ){
+                       $dev_base = "non-dev-$row[0]";
+               }
+               # this handles yet another fredforfaen special case where a mounted drive
+               # has the search string in its name
+               if ($row[-1] =~ /^\/$|^\/boot$|^\/var$|^\/var\/tmp$|^\/var\/log$|^\/home$|^\/opt$|^\/tmp$|^\/usr$/){
+                       $b_load = 1;
+                       # note, older df in bsd do not have file system column
+                       $type = 'main';
+               }
+               elsif ($row[$cols] !~ /^\/$|^\/boot$|^\/var$|^\/var\/tmp$|^\/var\/log$|^\/home$|^\/opt$|^\/tmp$|^\/usr$|^filesystem/){
+                       $b_load = 1;
+                       $type = 'secondary';
+               }
+               if ($b_load){
+                       if (!$bsd_type){
+                               if ($b_fs){
+                                       $fs = (%part && $part{'fs'}) ? $part{'fs'} : $row[1];
+                               }
+                               else {
+                                       $fs = get_mounts_fs($row[0],@mount);
+                               }
+                               if ($show{'label'}) {
+                                       if (%part && $part{'label'}) {
+                                               $label = $part{'label'};
+                                       }
+                                       elsif ( @labels){
+                                               $label = get_label($row[0]);
+                                       }
+                               }
+                               if ($show{'uuid'}) {
+                                       if (%part && $part{'uuid'}) {
+                                               $uuid = $part{'uuid'};
+                                       }
+                                       elsif ( @uuids){
+                                               $uuid = get_uuid($row[0]);
+                                       }
+                               }
+                       }
+                       else {
+                               $fs = ($b_fs) ? $row[1]: get_mounts_fs($row[0],@mount);
+                               if (@gpart && ($show{'label'} || $show{'uuid'} ) ){
+                                       my @extra = get_bsd_label_uuid("$dev_base");
+                                       if (@extra){
+                                               $label = $extra[0];
+                                               $uuid = $extra[1];
+                                       }
+                               }
+                       }
+                       $id = join ' ', @row[$cols .. $#row];
+                       $id =~ s/\/home\/[^\/]+\/(.*)/\/home\/$filter_string\/$1/ if $show{'filter'};
+                       $size = $row[$cols - $back_size];
+                       $used = $row[$cols - $back_used];
+                       $percent_used = sprintf( "%.1f", ( $used/$size )*100 ) if ($size);
+                       @data = ({
+                       'id' => $id,
+                       'dev-base' => $dev_base,
+                       'fs' => $fs,
+                       'label' => $label,
+                       'size' => $size,
+                       'type' => $type,
+                       'used' => $used,
+                       'uuid' => $uuid,
+                       'percent-used' => $percent_used,
+                       });
+                       @partitions = (@partitions,@data);
+               }
+       }
+       @data = swap_data();
+       @partitions = (@partitions,@data);
+       main::log_data('dump','@partitions',\@partitions) if $b_log;
+       # print Data::Dumper::Dumper \@partitions;
+       eval $end if $b_log;
+}
+
+sub swap_data {
+       eval $start if $b_log;
+       my (@swap,@working,$path,$label,$uuid);
+       my ($s,$j,$size_id,$used_id) = (1,0,2,3);
+       if (!$bsd_type){
+               # faster, avoid subshell, same as swapon -s
+               if ( -r '/proc/swaps'){
+                       @working = main::reader("/proc/swaps");
+               }
+               elsif ( $path = main::check_program('swapon') ){
+                       # note: while -s is deprecated, --show --bytes is not supported
+                       # on older systems
+                       @working = main::grabber("$path -s 2>/dev/null");
+               }
+       }
+       else {
+               if ( $path = main::check_program('swapctl') ){
+                       # output in in KB blocks
+                       @working = main::grabber("$path -l -k 2>/dev/null");
+               }
+               ($size_id,$used_id) = (1,2);
+       }
+       # now add the swap partition data, don't want to show swap files, just partitions,
+       # though this can include /dev/ramzswap0. Note: you can also use /proc/swaps for this
+       # data, it's the same exact output as swapon -s
+       foreach (@working){
+               next if ! /^\/dev/ || /^\/dev\/(ramzwap|zram)/;
+               my @data = split /\s+/, $_;
+               my $dev_base = $data[0];
+               $dev_base =~ s/^\/dev\///;
+               my $size = $data[$size_id];
+               my $used = $data[$used_id];
+               my $percent_used = sprintf( "%.1f", ( $used/$size )*100 );
+               if ($show{'label'} && @labels){
+                       $label = get_label($data[0]);
+               }
+               if ($show{'uuid'} && @uuids){
+                       $uuid = get_uuid($data[0]);
+               }
+               if ($bsd_type && @gpart && ($show{'label'} || $show{'uuid'} ) ){
+                       my @extra = get_bsd_label_uuid("$dev_base");
+                       if (@extra){
+                               $label = $extra[0];
+                               $uuid = $extra[1];
+                       }
+               }
+               @data = ({
+               'id' => "swap-$s",
+               'dev-base' => $dev_base,
+               'fs' => 'swap',
+               'label' => $label,
+               'size' => $size,
+               'type' => 'main',
+               'used' => $used,
+               'uuid' => $uuid,
+               'percent-used' => $percent_used,
+               });
+               @swap = (@swap,@data);
+               $s++;
+       }
+       eval $end if $b_log;
+       return @swap;
+}
+sub get_mounts_fs {
+       eval $start if $b_log;
+       my ($item,@mount) = @_;
+       $item =~ s/map:\/(\S+)/map $1/ if $bsd_type && $bsd_type eq 'darwin';
+       return 'N/A' if ! @mount;
+       my ($fs) = ('');
+       # linux: /dev/sdb6 on /var/www/m type ext4 (rw,relatime,data=ordered)
+       # /dev/sda3 on /root.dev/ugw type ext3 (rw,relatime,errors=continue,user_xattr,acl,barrier=1,data=journal)
+       # bsd: /dev/ada0s1a on / (ufs, local, soft-updates)
+       foreach (@mount){
+               if ($bsd_type && $_ =~ /^$item\son.*\(([^,\s\)]+)[,\s]*.*\)/){
+                       $fs = $1;
+                       last;
+               }
+               elsif (!$bsd_type && $_ =~ /^$item\son.*\stype\s([\S]+)\s\([^\)]+\)/){
+                       $fs = $1;
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       main::log_data('data',"fs: $fs") if $b_log;
+       return $fs;
+}
+# 1. Name: ada1p1
+#   label: (null)
+#   label: ssd-root
+#   rawuuid: b710678b-f196-11e1-98fd-021fc614aca9
+sub get_bsd_label_uuid {
+       eval $start if $b_log;
+       my ($item) = @_;
+       my (@data,$b_found);
+       foreach (@gpart){
+               my @working = split /\s*:\s*/, $_;
+               if ($_ =~ /^[0-9]+\.\sName:/ && $working[1] eq $item){
+                       $b_found = 1;
+               }
+               elsif ($_ =~ /^[0-9]+\.\sName:/ && $working[1] ne $item){
+                       $b_found = 0;
+               }
+               if ($b_found){
+                       if ($working[0] eq 'label'){
+                               $data[0] = $working[1];
+                               $data[0] =~ s/\(|\)//g; # eg: label:(null) - we want to show null
+                       }
+                       if ($working[0] eq 'rawuuid'){
+                               $data[1] = $working[1];
+                               $data[0] =~ s/\(|\)//g; 
+                       }
+               }
+       }
+       main::log_data('dump','@data',\@data) if $b_log;
+       eval $end if $b_log;
+       return @data;
+}
+sub set_label_uuid {
+       eval $start if $b_log;
+       $b_label_uuid = 1;
+       if ( $show{'unmounted'} || $show{'label'} || $show{'uuid'} ){
+               if (!$bsd_type){
+                       if (-d '/dev/disk/by-label'){
+                               @labels = main::globber('/dev/disk/by-label/*');
+                       }
+                       if (-d '/dev/disk/by-uuid'){
+                               @uuids = main::globber('/dev/disk/by-uuid/*');
+                       }
+               }
+               else {
+                       if ( my $path = main::check_program('gpart')){
+                               @gpart = main::grabber("$path list 2>/dev/null",'strip');
+                       }
+               }
+       }
+       eval $end if $b_log;
+}
+sub set_lsblk {
+       eval $start if $b_log;
+       $b_lsblk = 1;
+       my (@temp,@working);
+       if (my $program = main::check_program('lsblk')){
+               @working = main::grabber("$program -bP --output NAME,TYPE,RM,FSTYPE,SIZE,LABEL,UUID,SERIAL,MOUNTPOINT 2>/dev/null");
+               foreach (@working){
+                       if (/NAME="([^"]*)"\s+TYPE="([^"]*)"\s+RM="([^"]*)"\s+FSTYPE="([^"]*)"\s+SIZE="([^"]*)"\s+LABEL="([^"]*)"\s+UUID="([^"]*)"\s+SERIAL="([^"]*)"\s+MOUNTPOINT="([^"]*)"/){
+                               my $size = ($5) ? $5/1024: 0;
+                               # some versions of lsblk do not return serial, fs, uuid, or label
+                               my @temp = ({
+                               'name' => $1, 
+                               'type' => $2,
+                               'rm' => $3, 
+                               'fs' => $4, 
+                               'size' => $size,
+                               'label' => $6,
+                               'uuid' => $7,
+                               'serial' => $8,
+                               'mount' => $9,
+                               });
+                               @lsblk = (@lsblk,@temp);
+                       }
+               }
+       }
+       # print Data::Dumper::Dumper \@lsblk;
+       main::log_data('dump','@lsblk',\@lsblk) if $b_log;
+       eval $end if $b_log;
+}
+sub check_lsblk {
+       eval $start if $b_log;
+       my ($name,$b_size) = @_;
+       my (%part,@row);
+       foreach my $ref (@lsblk){
+               my %row = %$ref;
+               next if ! $row{'name'};
+               if ($name eq $row{'name'}){
+                       %part = %row;
+                       last;
+               }
+       }
+       # print Data::Dumper::Dumper \%part;
+       main::log_data('dump','%part',\%part) if $b_log;
+       eval $end if $b_log;
+       return %part;
+}
+sub get_label {
+       eval $start if $b_log;
+       my ($item) = @_;
+       my $label = '';
+       foreach (@labels){
+               if ($item eq Cwd::abs_path($_)){
+                       $label = $_;
+                       $label =~ s/\/dev\/disk\/by-label\///;
+                       $label =~ s/\\x20/ /g;
+                       $label =~ s%\\x2f%/%g;
+                       last;
+               }
+       }
+       $label ||= 'N/A';
+       eval $end if $b_log;
+       return $label;
+}
+# args: $1 - dev item $2 - @mapper
+# check for mapper, then get actual dev item if mapped
+# /dev/mapper/ will usually be a symbolic link to the real /dev id
+sub get_mapper {
+       eval $start if $b_log;
+       my ($item,@mapper) = @_;
+       my $mapped = '';
+       foreach (@mapper){
+               if ($item eq $_){
+                       my $temp = Cwd::abs_path($_);
+                       $mapped = $temp if $temp;
+                       last;
+               }
+       }
+       $mapped ||= $item;
+       eval $end if $b_log;
+       return $mapped;
+}
+sub get_root {
+       eval $start if $b_log;
+       my ($path) = ('/dev/root');
+       # note: the path may be a symbolic link to by-label/by-uuid but not 
+       # sure how far in abs_path resolves the path.
+       my $temp = Cwd::abs_path($path);
+       $path = $temp if $temp;
+       # note: it's a kernel config option to have /dev/root be a sym link 
+       # or not, if it isn't, path will remain /dev/root, if so, then try mount
+       if ($path eq '/dev/root' && (my $program = main::check_program('mount'))){
+               my @data = main::grabber("$program 2>/dev/null");
+               # /dev/sda2 on / type ext4 (rw,noatime,data=ordered)
+               foreach (@data){
+                       if (/^([\S]+)\son\s\/\s/){
+                               $path = $1;
+                               # note: we'll be handing off any uuid/label paths to the next 
+                               # check tools after get_root() above, so don't trim those.
+                               $path =~ s/.*\/// if $path !~ /by-uuid|by-label/;
+                               last;
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return $path;
+}
+
+sub get_uuid {
+       eval $start if $b_log;
+       my ($item) = @_;
+       my $uuid = '';
+       foreach (@uuids){
+               if ($item eq Cwd::abs_path($_)){
+                       $uuid = $_;
+                       $uuid =~ s/\/dev\/disk\/by-uuid\///;
+                       last;
+               }
+       }
+       $uuid ||= 'N/A';
+       eval $end if $b_log;
+       return $uuid;
+}
+}
+
+## ProcessData 
+{
+package ProcessData;
+
+sub get {
+       eval $start if $b_log;
+       my (@processes,@rows);
+       if ($show{'ps-cpu'}){
+               @rows = cpu_processes();
+               @processes = (@processes,@rows);
+       }
+       if ($show{'ps-mem'}){
+               @rows = mem_processes();
+               @processes = (@processes,@rows);
+       }
+       return @processes;
+       eval $end if $b_log;
+}
+sub cpu_processes {
+       eval $start if $b_log;
+       my ($j,$num,$cpu,$cpu_mem,$mem) = (0,0,'','','');
+       my (@processes);
+       my $count = ($b_irc)? 5: $ps_count;
+       my @rows = sort { 
+               my @a = split(/\s+/,$a); 
+               my @b = split(/\s+/,$b); 
+               $b[2] <=> $a[2] } @ps_aux;
+       # if there's a count limit, for irc, etc, only use that much of the data
+       @rows = splice @rows,0,$count;
+       
+       $j = scalar @rows;
+       # $cpu_mem = ' - Memory: MiB / % used' if $extra > 0;
+       my $throttled = throttled($ps_count,$count,$j);
+       #my $header = "CPU  % used - Command - pid$cpu_mem - top";
+       #my $header = "Top $count by CPU";
+       my @data = ({
+       main::key($num++,'CPU top') => "$count$throttled",
+       },);
+       @processes = (@processes,@data);
+       my $i = 1;
+       foreach (@rows){
+               $num = 1;
+               $j = scalar @processes;
+               my @row = split /\s+/, $_;
+               my @command = process_starter(scalar @row, $row[10],$row[11]);
+               @data = ({
+               main::key($num++,$i++) => '',
+               main::key($num++,'cpu') => $row[2] . '%',
+               main::key($num++,'command') => $command[0],
+               },);
+               @processes = (@processes,@data);
+               if ($command[1]) {
+                       $processes[$j]{main::key($num++,'started by')} = $command[1];
+               }
+               $processes[$j]{main::key($num++,'pid')} = $row[1];
+               if ($extra > 0){
+                       my $decimals = ($row[5]/1024 > 10 ) ? 1 : 2;
+                       $mem = (defined $row[5]) ? sprintf( "%.${decimals}f", $row[5]/1024 ) . ' MiB' : 'N/A';
+                       $mem .= ' (' . $row[3] . '%)';
+                       $processes[$j]{main::key($num++,'mem')} = $mem;
+               }
+               #print Data::Dumper::Dumper \@processes, "i: $i; j: $j ";
+       }
+       eval $end if $b_log;
+       return @processes;
+}
+sub mem_processes {
+       eval $start if $b_log;
+       my ($j,$num,$cpu,$cpu_mem,$mem) = (0,0,'','','');
+       my (@data,@processes,$memory);
+       my $count = ($b_irc)? 5: $ps_count;
+       my @rows = sort { 
+               my @a = split(/\s+/,$a); 
+               my @b = split(/\s+/,$b); 
+               $b[5] <=> $a[5] } @ps_aux;
+       @rows = splice @rows,0,$count;
+       #print Data::Dumper::Dumper \@rows;
+       @processes = main::memory_data_full('process') if !$b_mem;
+       $j = scalar @rows;
+       my $throttled = throttled($ps_count,$count,$j);
+       #$cpu_mem = ' - CPU: % used' if $extra > 0;
+       #my $header = "Memory MiB/% used - Command - pid$cpu_mem - top";
+       #my $header = "Top $count by Memory";
+       @data = ({
+       main::key($num++,'Memory top') => "$count$throttled",
+       },);
+       @processes = (@processes,@data);
+       my $i = 1;
+       foreach (@rows){
+               $num = 1;
+               $j = scalar @processes;
+               my @row = split /\s+/, $_;
+               my $decimals = ($row[5]/1024 > 10 ) ? 1 : 2;
+               $mem = ($row[5]) ? sprintf( "%.${decimals}f", $row[5]/1024 ) . ' MiB' : 'N/A';
+               my @command = process_starter(scalar @row, $row[10],$row[11]);
+               $mem .= " (" . $row[3] . "%)"; 
+               @data = ({
+               main::key($num++,$i++) => '',
+               main::key($num++,'mem') => $mem,
+               main::key($num++,'command') => $command[0],
+               },);
+               @processes = (@processes,@data);
+               if ($command[1]) {
+                       $processes[$j]{main::key($num++,'started by')} = $command[1];
+               }
+               $processes[$j]{main::key($num++,'pid')} = $row[1];
+               if ($extra > 0){
+                       $cpu = $row[2] . '%';
+                       $processes[$j]{main::key($num++,'cpu')} = $cpu;
+               }
+               #print Data::Dumper::Dumper \@processes, "i: $i; j: $j ";
+       }
+       eval $end if $b_log;
+       return @processes;
+}
+sub process_starter {
+       my ($count, $row10, $row11) = @_;
+       my (@return);
+       # note: [migration/0] would clear with a simple basename
+       if ($count > 11 && $row11 =~ /^\//){
+               $row11 =~ s/^\/.*\///;
+               $return[0] = $row11;
+               $row10 =~ s/^\/.*\///;
+               $return[1] = $row10;
+       }
+       else {
+               $row10 =~ s/^\/.*\///;
+               $return[0] = $row10;
+               $return[1] = '';
+       }
+       return @return;
+}
+sub throttled {
+       my ($ps_count,$count,$j) = @_;
+       my $throttled = '';
+       if ($count > $j){
+               $throttled = " ( $j processes)";
+       }
+       elsif ($count < $ps_count){
+               $throttled = " (throttled from $ps_count)";
+       }
+       return $throttled;
+}
+}
+
+## RaidData
+{
+package RaidData;
+# debugger switches
+my ($b_md,$b_zfs);
+
+sub get {
+       eval $start if $b_log;
+       my (@rows,$key1,$val1);
+       my $num = 0;
+       raid_data() if !$b_raid;
+       #print 'get: ', Data::Dumper::Dumper \@raid;
+       if (!@raid && !@hardware_raid){
+               if ($show{'raid-forced'}){
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('raid-data');
+               }
+       }
+       else {
+               @rows = create_output();
+       }
+       if (!@rows && $key1){
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       eval $end if $b_log;
+       ($b_md,$b_zfs,@hardware_raid) = undef;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my (@arrays,@arrays_holder,@components,@components_good,@data,@failed,@rows,
+       @sizes,@spare,@temp);
+       my ($allocated,$available,$blocks_avail,$chunk_raid,$component_string,$raid,
+       $ref2,$ref3,$report_size,$size,$status);
+       my ($b_row_1_sizes);
+       my ($i,$j,$num,$status_id) = (0,0,0,0);
+       #print Data::Dumper::Dumper \@raid;
+       if (@hardware_raid){
+               foreach my $ref (@hardware_raid){
+                       my %row = %$ref;
+                       $num = 1;
+                       my $device = ($row{'device'}) ? $row{'device'}: 'N/A';
+                       my $driver = ($row{'driver'}) ? $row{'driver'}: 'N/A';
+                       @data = ({
+                       main::key($num++,'Hardware') => $device,
+                       });
+                       @rows = (@rows,@data);
+                       $j = scalar @rows - 1;
+                       $rows[$j]{main::key($num++,'vendor')} = $row{'vendor'} if $row{'vendor'};
+                       $rows[$j]{main::key($num++,'driver')} = $driver;
+                       if ($extra > 0){
+                               my $driver_version = ($row{'driver-version'}) ?  $row{'driver-version'}: 'N/A' ;
+                               $rows[$j]{main::key($num++,'v')} = $driver_version;
+                               if ($extra > 2){
+                                       my $port= ($row{'port'}) ? $row{'port'}: 'N/A' ;
+                                       $rows[$j]{main::key($num++,'port')} = $port;
+                               }
+                               my $bus_id = (defined $row{'bus-id'} && defined $row{'sub-id'}) ?  "$row{'bus-id'}.$row{'sub-id'}": 'N/A' ;
+                               $rows[$j]{main::key($num++,'bus ID')} = $bus_id;
+                       }
+                       if ($extra > 1){
+                               my $chip_id = (defined $row{'vendor-id'} && defined $row{'chip-id'}) ?  "$row{'vendor-id'}.$row{'chip-id'}": 'N/A' ;
+                               $rows[$j]{main::key($num++,'chip ID')} = $chip_id;
+                       }
+                       if ($extra > 2){
+                               my $rev= (defined $row{'rev'} && $row{'rev'}) ? $row{'rev'}: 'N/A' ;
+                               $rows[$j]{main::key($num++,'rev')} = $rev;
+                       }
+               }
+       }
+       if ($extra > 2 && $raid[0]{'system-supported'}){
+               @data = ({
+               main::key($num++,'Supported md-raid types') => $raid[0]{'system-supported'},
+               });
+               @rows = (@rows,@data);
+       }
+       foreach my $ref (@raid){
+               $j = scalar @rows;
+               my %row = %$ref;
+               $b_row_1_sizes = 0;
+               next if !%row;
+               $num = 1;
+               @data = ({
+               main::key($num++,'Device') => $row{'id'},
+               main::key($num++,'type') => $row{'type'},
+               main::key($num++,'status') => $row{'status'},
+               });
+               @rows = (@rows,@data);
+               if ($row{'type'} eq 'mdraid'){
+                       $blocks_avail = 'blocks';
+                       $chunk_raid = 'chunk size';
+                       $report_size = 'report';
+                       if ($extra > 0){
+                               $available = ($row{'blocks'}) ? $row{'blocks'} : 'N/A';
+                       }
+                       $size = ($row{'report'}) ? $row{'report'}: '';
+                       $size .= " $row{'u-data'}" if $size; 
+                       $size ||= 'N/A';
+                       $status_id = 2;
+               }
+               else {
+                       $blocks_avail = 'free';
+                       $chunk_raid = 'allocated';
+                       $report_size = 'size';
+                       @sizes = ($row{'size'}) ? main::get_size($row{'size'}) : ();
+                       $size = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                       @sizes = ($row{'free'}) ? main::get_size($row{'free'}) : ();
+                       $available = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                       if ($extra > 2){
+                               @sizes = ($row{'allocated'}) ? main::get_size($row{'allocated'}) : ();
+                               $allocated = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                       }
+                       $status_id = 1;
+               }
+               $ref2 = $row{'arrays'};
+               @arrays = @$ref2;
+               @arrays = grep {defined $_} @arrays;
+               @arrays_holder = @arrays;
+               if (($row{'type'} eq 'mdraid' && $extra == 0 ) || !defined $arrays[0]{'raid'} ){
+                       $raid = (defined $arrays[0]{'raid'}) ? $arrays[0]{'raid'}: 'no-raid';
+                       $rows[$j]{main::key($num++,'raid')} = $raid;
+               }
+               if ( ( $row{'type'} eq 'zfs' || ($row{'type'} eq 'mdraid' && $extra == 0 ) ) && $size){
+                       #print "here 0\n";
+                       $rows[$j]{main::key($num++,$report_size)} = $size;
+                       $size = '';
+                       $b_row_1_sizes = 1;
+               }
+               if ( $row{'type'} eq 'zfs' && $available){
+                       $rows[$j]{main::key($num++,$blocks_avail)} = $available;
+                       $available = '';
+                       $b_row_1_sizes = 1;
+               }
+               if ( $row{'type'} eq 'zfs' && $allocated){
+                       $rows[$j]{main::key($num++,$chunk_raid)} = $allocated;
+                       $allocated = '';
+               }
+               $i = 0;
+               my $count = scalar @arrays;
+               foreach $ref3 (@arrays){
+                       my %row2 = %$ref3;
+                       if ($count > 1){
+                               $j = scalar @rows;
+                               $num = 1;
+                               @sizes = ($row2{'size'}) ? main::get_size($row2{'size'}) : ();
+                               $size = (@sizes) ? "$sizes[0] $sizes[1]" : 'N/A';
+                               @sizes = ($row2{'free'}) ? main::get_size($row2{'free'}) : ();
+                               $available = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                               $raid = (defined $row2{'raid'}) ? $row2{'raid'}: 'no-raid';
+                               $status = ($row2{'status'}) ? $row2{'status'}: 'N/A';
+                               @data = ({
+                               main::key($num++,'array') => $raid,
+                               main::key($num++,'status') => $status,
+                               main::key($num++,'size') => $size,
+                               main::key($num++,'free') => $available,
+                               });
+                               @rows = (@rows,@data);
+                       }
+                       # items like cache may have one component, with a size on that component
+                       elsif (!$b_row_1_sizes && $row{'type'} eq 'zfs'){
+                               #print "here $count\n";
+                               @sizes = ($row2{'size'}) ? main::get_size($row2{'size'}) : ();
+                               $size = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                               @sizes = ($row2{'free'}) ? main::get_size($row2{'free'}) : ();
+                               $available = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                               $rows[$j]{main::key($num++,'size')} = $size;
+                               $rows[$j]{main::key($num++,'free')} = $available;
+                               if ($extra > 2){
+                                       @sizes = ($row{'allocated'}) ? main::get_size($row2{'allocated'}) : ();
+                                       $allocated = (@sizes) ? "$sizes[0] $sizes[1]" : '';
+                                       if ($allocated){
+                                               $rows[$j]{main::key($num++,$chunk_raid)} = $allocated;
+                                       }
+                               }
+                       }
+                       $ref3 = $row2{'components'};
+                       @components = (ref $ref3 eq 'ARRAY') ? @$ref3 : ();
+                       @failed = ();
+                       @spare = ();
+                       @components_good = ();
+                       # @spare = split(/\s+/, $row{'unused'}) if $row{'unused'};
+                       foreach my $item (@components){
+                               @temp = split /~/, $item;
+                               if (defined $temp[$status_id] && $temp[$status_id] =~ /^(F|DEGRADED|FAULTED|UNAVAIL)$/){
+                                       $temp[0] = "$temp[0]~$temp[1]" if $status_id == 2;
+                                       push @failed, $temp[0];
+                               }
+                               elsif (defined $temp[$status_id] && $temp[$status_id] =~ /(S|OFFLINE)$/){
+                                       $temp[0] = "$temp[0]~$temp[1]" if $status_id == 2;
+                                       push @spare, $temp[0];
+                               }
+                               else {
+                                       $temp[0] = ($status_id == 2) ? "$temp[0]~$temp[1]" : $temp[0];
+                                       push @components_good, $temp[0];
+                               }
+                       }
+                       $component_string = (@components_good) ? join ' ', @components_good : 'N/A';
+                       $rows[$j]{main::key($num++,'Components')} = '';
+                       $rows[$j]{main::key($num++,'online')} = $component_string;
+                       if (@failed){
+                               $rows[$j]{main::key($num++,'FAILED')} = join ' ', @failed;
+                       }
+                       if (@spare){
+                               $rows[$j]{main::key($num++,'spare')} = join ' ', @spare;
+                       }
+                       if ($row{'type'} eq 'mdraid' && $extra > 0 ){
+                               $j = scalar @rows;
+                               $num = 1;
+                               #print Data::Dumper::Dumper \@arrays_holder;
+                               $rows[$j]{main::key($num++,'Info')} = '';
+                               $raid = (defined $arrays_holder[0]{'raid'}) ? $arrays_holder[0]{'raid'}: 'no-raid';
+                               $rows[$j]{main::key($num++,'raid')} = $raid;
+                               $rows[$j]{main::key($num++,$blocks_avail)} = $available;
+                               if ($size){
+                                       $rows[$j]{main::key($num++,$report_size)} = $size;
+                               }
+                               my $chunk = ($row{'chunk-size'}) ? $row{'chunk-size'}: 'N/A';
+                               $rows[$j]{main::key($num++,$chunk_raid)} = $chunk;
+                               if ($extra > 1){
+                                       if ($row{'bitmap'}){
+                                               $rows[$j]{main::key($num++,'bitmap')} = $row{'bitmap'};
+                                       }
+                                       if ($row{'super-block'}){
+                                               $rows[$j]{main::key($num++,'super blocks')} = $row{'super-block'};
+                                       }
+                                       if ($row{'algorithm'}){
+                                               $rows[$j]{main::key($num++,'algorithm')} = $row{'algorithm'};
+                                       }
+                               }
+                       }
+                       $i++;
+               }
+               if ($row{'recovery-percent'}){
+                       $j = scalar @rows;
+                       $num = 1;
+                       my $percent = $row{'recovery-percent'};
+                       if ($extra > 1 && $row{'progress-bar'}){
+                               $percent .= " $row{'progress-bar'}"
+                       }
+                       $rows[$j]{main::key($num++,'Recovering')} = $percent;
+                       my $finish = ($row{'recovery-finish'})?$row{'recovery-finish'} : 'N/A';
+                       $rows[$j]{main::key($num++,'time remaining')} = $finish;
+                       if ($extra > 0){
+                               if ($row{'sectors-recovered'}){
+                                       $rows[$j]{main::key($num++,'sectors')} = $row{'sectors-recovered'};
+                               }
+                       }
+                       if ($extra > 1 && $row{'recovery-speed'}){
+                               $rows[$j]{main::key($num++,'speed')} = $row{'recovery-speed'};
+                       }
+               }
+       }
+       eval $end if $b_log;
+       #print Data::Dumper::Dumper \@rows;
+       return @rows;
+}
+sub raid_data {
+       eval $start if $b_log;
+       my (@data);
+       $b_raid = 1;
+       if ($b_hardware_raid){
+               hardware_raid();
+       }
+       if ($b_md || (my $file = main::system_files('mdstat') )){
+               @data = mdraid_data($file);
+               @raid = (@raid,@data) if @data;
+       }
+       if ($b_zfs || (my $path = main::check_program('zpool') )){
+               @data = zfs_data($path);
+               @raid = (@raid,@data) if @data;
+       }
+       main::log_data('dump','@raid',\@raid) if $b_log;
+       #print Data::Dumper::Dumper \@raid;
+       eval $end if $b_log;
+}
+# 0 type
+# 1 type_id
+# 2 bus_id
+# 3 sub_id
+# 4 device
+# 5 vendor_id
+# 6 chip_id
+# 7 rev
+# 8 port
+# 9 driver
+# 10 modules
+sub hardware_raid {
+       eval $start if $b_log;
+       my ($driver,$vendor,@data,@working);
+       foreach my $ref (@pci){
+               @working = @$ref;
+               next if $working[1] ne '0104';
+               $driver = ($working[9]) ? lc($working[9]): '';
+               $driver =~ s/-/_/g if $driver;
+               my $driver_version = ($driver) ? main::get_module_version($driver): '';
+               if ($extra > 2 && $b_pci_tool && $working[11]){
+                       $vendor = main::get_pci_vendor($working[4],$working[11]);
+               }
+               @data = ({
+               'bus-id' => $working[2],
+               'chip-id' => $working[6],
+               'device' => $working[4],
+               'driver' => $driver,
+               'driver-version' => $driver_version,
+               'port' => $working[8],
+               'rev' => $working[7],
+               'sub-id' => $working[3],
+               'vendor-id' => $working[5],
+               'vendor' => $vendor,
+               });
+               @hardware_raid = (@hardware_raid,@data);
+       }
+       # print Data::Dumper::Dumper \@hardware_raid;
+       main::log_data('dump','@hardware_raid',\@hardware_raid) if $b_log;
+       eval $end if $b_log;
+}
+sub mdraid_data {
+       eval $start if $b_log;
+       my ($mdstat) = @_;
+       my $j = 0;
+       #$mdstat = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/md-4-device-1.txt";
+       #$mdstat = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/md-rebuild-1.txt";
+       #$mdstat = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/md-2-mirror-fserver2-1.txt";
+       #$mdstat = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/md-2-raid10-abucodonosor.txt";
+       my @working = main::reader($mdstat,'strip');
+       #print Data::Dumper::Dumper \@working;
+       my (@data,@mdraid,@temp,$b_found,$system,$unused);
+       # NOTE: a system with empty mdstat will still show these values
+       if ($working[0] && $working[0] =~ /^Personalities/){
+               $system = ( split /:\s*/,  $working[0])[1];
+               $system =~ s/\[|\]//g if $system;
+               shift @working;
+       }
+       if ($working[-1] && $working[-1] =~ /^used\sdevices/){
+               $unused = ( split /:\s*/,  $working[0])[1];
+               $unused =~ s/<|>|none//g if $unused;
+               pop @working;
+       }
+       foreach (@working){
+               $_ =~ s/\s*:\s*/:/;
+               # print "$_\n";
+               #md126 : active (auto-read-only) raid1 sdq1[0]
+               if (/^(md[0-9]+)\s*:\s*([^\s]+)(\s\([^)]+\))?\s([^\s]+)\s(.*)/){
+                       my $id = $1;
+                       my $status = $2;
+                       my $raid = $4;
+                       my $component_string = $5;
+                       @temp = ();
+                       $raid =~ s/^raid1$/mirror/;
+                       $raid =~ s/^raid/raid-/;
+                       $raid = 'mirror' if $raid eq '1';
+                       # remember, these include the [x] id, so remove that for disk/unmounted
+                       my @components = split /\s+/, $component_string;
+                       foreach my $component (@components){
+                               $component =~ /([\S]+)\[([0-9]+)\]\(?([SF])?\)?/;
+                               my $string = "$1~";
+                               $string .= (defined $2) ? "c$2" : '';
+                               $string .= (defined $3) ? "~$3" : '';
+                               push @temp, $string;
+                       }
+                       @components = @temp;
+                       #print "$component_string\n";
+                       $j = scalar @mdraid;
+                       @data = ({
+                       'id' => $id,
+                       'arrays' => ([],),
+                       'status' => $status,
+                       'type' => 'mdraid',
+                       });
+                       @mdraid = (@mdraid,@data);
+                       $mdraid[$j]{'arrays'}[0]{'raid'} = $raid;
+                       $mdraid[$j]{'arrays'}[0]{'components'} = \@components;
+               }
+               #print "$_\n";
+               if ($_ =~ /^([0-9]+)\sblocks/){
+                       $mdraid[$j]{'blocks'} = $1;
+               }
+               if ($_ =~ /super\s([0-9\.]+)\s/){
+                       $mdraid[$j]{'super-block'} = $1;
+               }
+               if ($_ =~ /algorithm\s([0-9\.]+)\s/){
+                       $mdraid[$j]{'algorithm'} = $1;
+               }
+               if ($_ =~ /\[([0-9]+\/[0-9]+)\]\s\[([U_]+)\]/){
+                       $mdraid[$j]{'report'} = $1;
+                       $mdraid[$j]{'u-data'} = $2;
+               }
+               if ($_ =~ /resync=([\S]+)/){
+                       $mdraid[$j]{'resync'} = $1;
+               }
+               if ($_ =~ /([0-9]+[km])\schunk/i){
+                       $mdraid[$j]{'chunk-size'} = $1;
+               }
+               if ($_ =~ /(\[[=]*>[\.]*\]).*(resync|recovery)\s*=\s*([0-9\.]+%)?(\s\(([0-9\/]+)\))?/){
+                       $mdraid[$j]{'progress-bar'} = $1;
+                       $mdraid[$j]{'recovery-percent'} = $3 if $3;
+                       $mdraid[$j]{'sectors-recovered'} = $5 if $5;
+               }
+               if ($_ =~ /finish\s*=\s*([\S]+)\s+speed\s*=\s*([\S]+)/){
+                       $mdraid[$j]{'recovery-finish'} = $1;
+                       $mdraid[$j]{'recovery-speed'} = $2;
+               }
+               #print 'mdraid loop: ', Data::Dumper::Dumper \@mdraid;
+       }
+       if (@mdraid){
+               $mdraid[0]{'system-supported'} = $system if $system;
+               $mdraid[0]{'unused'} = $unused if $unused;
+       }
+       #print Data::Dumper::Dumper \@mdraid;
+       eval $end if $b_log;
+       return @mdraid;
+}
+
+sub zfs_data {
+       eval $start if $b_log;
+       my ($zpool) = @_;
+       my (@components,@data,@zfs);
+       my ($allocated,$free,$ref,$size,$status);
+       my $b_v = 1;
+       my ($i,$j,$k) = (0,0,0);
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-list-1-mirror-main-solestar.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-list-2-mirror-main-solestar.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-list-v-tank-1.txt";
+       #my @working = main::reader($file);$zpool = '';
+       my @working = main::grabber("$zpool list -v 2>/dev/null");
+       DiskData::set_glabel() if $bsd_type && !$b_glabel;
+       # bsd sed does not support inserting a true \n so use this trick
+       # some zfs does not have -v
+       if (!@working){
+               @working = main::grabber("$zpool list 2>/dev/null");
+               $b_v = 0;
+       }
+       #print Data::Dumper::Dumper \@working;
+       main::log_data('dump','@working',\@working) if $b_log;
+       if (!@working){
+               main::log_data('data','no zpool list data') if $b_log;
+               eval $end if $b_log;
+               return ();
+       }
+       my ($status_i) = (0);
+       # NAME   SIZE  ALLOC   FREE  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
+       my $test = shift @working; # get rid of first header line
+       if ($test){
+               foreach (split /\s+/, $test){
+                       last if $_ eq 'HEALTH';
+                       $status_i++;
+               }
+       }
+       foreach (@working){
+               my @row = split /\s+/, $_;
+               if (/^[\S]+/){
+                       @components = ();
+                       $i = 0;
+                       $size = ($row[1] && $row[1] ne '-')? main::translate_size($row[1]): '';
+                       $allocated = ($row[2] && $row[2] ne '-')? main::translate_size($row[2]): '';
+                       $free = ($row[3] && $row[3] ne '-')? main::translate_size($row[3]): '';
+                       $status = (defined $row[$status_i] && $row[$status_i] ne '') ? $row[$status_i]: 'no-status';
+                       $j = scalar @zfs;
+                       @data = ({
+                       'id' => $row[0],
+                       'allocated' => $allocated,
+                       'arrays' => ([],),
+                       'free' => $free,
+                       'size' => $size,
+                       'status' => $status,
+                       'type' => 'zfs',
+                       });
+                       @zfs = (@zfs,@data);
+               }
+               #print Data::Dumper::Dumper \@zfs;
+               # raid level is the second item in the output, unless it is not, sometimes it is absent
+               if ($row[1] =~ /raid|mirror/){
+                       $row[1] =~ s/^raid1/mirror/;
+                       #$row[1] =~ s/^raid/raid-/; # need to match in zpool status <device>
+                       $ref = $zfs[$j]{'arrays'};
+                       $k = scalar @$ref;
+                       $zfs[$j]{'arrays'}[$k]{'raid'} = $row[1];
+                       $i = 0;
+                       $zfs[$j]{'arrays'}[$k]{'size'} = ($row[2] && $row[2] ne '-') ? main::translate_size($row[2]) : '';
+                       $zfs[$j]{'arrays'}[$k]{'allocated'} = ($row[3] && $row[3] ne '-') ? main::translate_size($row[3]) : '';
+                       $zfs[$j]{'arrays'}[$k]{'free'} = ($row[4] && $row[4] ne '-') ? main::translate_size($row[4]) : '';
+               }
+               # https://blogs.oracle.com/eschrock/entry/zfs_hot_spares
+               elsif ($row[1] =~ /spares/){
+                       next;
+               }
+               # the first is a member of a raid array
+               #    ada2        -      -      -         -      -      -
+               # this second is a single device not in an array
+               #  ada0s2    25.9G  14.6G  11.3G         -     0%    56%
+               #    gptid/3838f796-5c46-11e6-a931-d05099ac4dc2      -      -      -         -      -      -
+               elsif ($row[1] =~ /^([a-z0-9]+[0-9]+|([\S]+)\/.*)$/ && 
+                      ($row[2] eq '-' || $row[2] =~ /^[0-9\.]+[MGTP]$/ )){
+                       $row[1] =~ /^([a-z0-9]+[0-9]+|([\S]+)\/.*)\s*(DEGRADED|FAULTED|OFFLINE)?$/;
+                       my $working = $1;
+                       my $state = ($3) ? $3 : '';
+                       if ($working =~ /[\S]+\// && @glabel){
+                               $working = DiskData::match_glabel($working);
+                       }
+                       # kind of a hack, things like cache may not show size/free
+                       # data since they have no array row, but they might show it in 
+                       # component row:
+                       #   ada0s2    25.9G  19.6G  6.25G         -     0%    75%
+                       if (!$zfs[$j]{'size'} && $row[2] && $row[2] ne '-') {
+                               $size = ($row[2])? main::translate_size($row[2]): '';
+                               $zfs[$j]{'arrays'}[$k]{'size'} = $size;
+                       }
+                       if (!$zfs[$j]{'allocated'} && $row[3] && $row[3] ne '-') {
+                               $allocated = ($row[3])? main::translate_size($row[3]): '';
+                               $zfs[$j]{'arrays'}[$k]{'allocated'} = $allocated;
+                       }
+                       if (!$zfs[$j]{'free'} && $row[4] && $row[4] ne '-') {
+                               $free = ($row[4])? main::translate_size($row[4]): '';
+                               $zfs[$j]{'arrays'}[$k]{'free'} = $free;
+                       }
+                       $zfs[$j]{'arrays'}[$k]{'components'}[$i] = $working . '~' . $state;
+                       $i++;
+               }
+       }
+       # print Data::Dumper::Dumper \@zfs;
+       # clear out undefined arrrays values
+       $j = 0;
+       foreach $ref (@zfs){
+               my %row = %$ref;
+               my $ref2 = $row{'arrays'};
+               my @arrays = (ref $ref2 eq 'ARRAY' ) ? @$ref2 : ();
+               @arrays = grep {defined $_} @arrays;
+               $zfs[$j]{'arrays'} = \@arrays;
+               $j++;
+       }
+       @zfs = zfs_status($zpool,@zfs);
+       # print Data::Dumper::Dumper \@zfs;
+       eval $end if $b_log;
+       return @zfs;
+}
+sub zfs_status {
+       eval $start if $b_log;
+       my ($zpool,@zfs) = @_;
+       my ($cmd,$status,$file,$raid,@arrays,@pool_status,@temp);
+       my ($i,$j,$k,$l) = (0,0,0,0);
+       foreach my $ref (@zfs){
+               my %row = %$ref;
+               $i = 0;
+               $k = 0;
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-status-1-mirror-main-solestar.txt";
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-status-2-mirror-main-solestar.txt";
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/raid/zpool-status-tank-1.txt";
+               #@pool_status = main::reader($file,'strip');
+               $cmd = "$zpool status $row{'id'} 2>/dev/null";
+               @pool_status = main::grabber($cmd,"\n",'strip');
+               main::log_data('cmd',$cmd) if $b_log;
+               my $ref2 = $row{'arrays'};
+               @arrays = (ref $ref2 eq 'ARRAY' ) ? @$ref2 : ();
+               #print "$row{'id'} rs:$row{'status'}\n";
+               $status = ($row{'status'} && $row{'status'} eq 'no-status') ? check_status($row{'id'},@pool_status): $row{'status'};
+               $zfs[$j]{'status'} = $status if $status;
+               #@arrays = grep {defined $_} @arrays;
+               #print "$row{id} $#arrays\n";
+               #print Data::Dumper::Dumper \@arrays;
+               foreach my $array (@arrays){
+                       #print 'ref: ', ref $array, "\n";
+                       #next if ref $array ne 'HASH';
+                       my %row2 = %$array;
+                       my $ref3 = $row2{'components'};
+                       my @components = (ref $ref3 eq 'ARRAY') ? @$ref3 : ();
+                       $l = 0;
+                       # zpool status: mirror-0  ONLINE       2     0     0
+                       $raid = ($row2{'raid'}) ? "$row2{'raid'}-$i": $row2{'raid'};
+                       $status = ($raid) ? check_status($raid,@pool_status): '';
+                       $zfs[$j]{'arrays'}[$k]{'status'} = $status;
+                       #print "$raid i:$i j:$j k:$k $status\n";
+                       foreach my $component (@components){
+                               my @temp = split /~/, $component;
+                               $status = ($temp[0]) ? check_status($temp[0],@pool_status): '';
+                               $zfs[$j]{'arrays'}[$k]{'components'}[$l] .= $status if $status;
+                               $l++;
+                       }
+                       $k++;
+                       # haven't seen a raid5/6 type array yet
+                       $i++ if $row2{'raid'}; # && $row2{'raid'} eq 'mirror';
+               }
+               $j++;
+       }
+       eval $end if $b_log;
+       return @zfs;
+}
+sub check_status {
+       eval $start if $b_log;
+       my ($item,@pool_status) = @_;
+       my ($status) = ('');
+       foreach (@pool_status){
+               my @temp = split /\s+/, $_;
+               if ($temp[0] eq $item){
+                       last if !$temp[1]; 
+                       $status = $temp[1];
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return $status;
+}
+}
+
+## RamData
+{
+package RamData;
+
+sub get {
+       my (@data,@rows,$key1,@ram,$val1);
+       my $num = 0;
+       my $ref = $alerts{'dmidecode'};
+       @rows = main::memory_data_full('ram') if !$b_mem;
+       if ( $$ref{'action'} ne 'use'){
+               $key1 = $$ref{'action'};
+               $val1 = $$ref{$key1};
+               @data = ({
+               main::key($num++,'RAM Report') => '',
+               main::key($num++,$key1) => $val1,
+               });
+               @rows = (@rows,@data);
+       }
+       else {
+               @ram = dmidecode_data();
+               if (@ram){
+                       @data = create_output(@ram);
+               }
+               else {
+                       $key1 = 'message';
+                       $val1 = main::row_defaults('ram-data');
+                       @data = ({
+                       main::key($num++,'RAM Report') => '',
+                       main::key($num++,$key1) => $val1,
+                       });
+               }
+               @rows = (@rows,@data);
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub create_output {
+       eval $start if $b_log;
+       my (@ram) = @_;
+       return if !@ram;
+       my $num = 0;
+       my $j = 0;
+       my (@data,@rows);
+       foreach (@ram){
+               $j = scalar @rows;
+               my %ref = %$_;
+               $num = 1;
+               @data = ({
+               main::key($num++,'Array') => '',
+               main::key($num++,'capacity') => process_size($ref{'capacity'}),
+               });
+               @rows = (@rows,@data);
+               if ($ref{'cap-qualifier'}){
+                       $rows[$j]{main::key($num++,'note')} = $ref{'cap-qualifier'};
+               }
+               $rows[$j]{main::key($num++,'slots')} = $ref{'slots'};
+               $rows[$j]{main::key($num++,'EC')} = $ref{'eec'};
+               if ($extra > 0 ){
+                       $rows[$j]{main::key($num++,'max module size')} = process_size($ref{'max-module-size'});
+                       if ($ref{'mod-qualifier'}){
+                               $rows[$j]{main::key($num++,'note')} = $ref{'mod-qualifier'};
+                       }
+               }
+               foreach my $ref2 ($ref{'modules'}){
+                       my @modules = @$ref2;
+                       # print Data::Dumper::Dumper \@modules;
+                       foreach my $ref3 ( @modules){
+                               $num = 1;
+                               $j = scalar @rows;
+                               # multi array setups will start index at next from previous array
+                               next if ref $ref3 ne 'HASH';
+                               my %mod = %$ref3;
+                               $mod{'locator'} ||= 'N/A';
+                               @data = ({
+                               main::key($num++,'Device') => $mod{'locator'},
+                               main::key($num++,'size') => process_size($mod{'size'}),
+                               });
+                               @rows = (@rows,@data);
+                               next if ($mod{'size'} =~ /\D/);
+                               if ($extra > 1 && $mod{'type'} ){
+                                       $rows[$j]{main::key($num++,'info')} = $mod{'type'};
+                               }
+                               $rows[$j]{main::key($num++,'speed')} = $mod{'speed'};
+                               if ($extra > 0 ){
+                                       $mod{'device-type'} ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'type')} = $mod{'device-type'};
+                                       if ($extra > 2 && $mod{'device-type'} ne 'N/A'){
+                                               $mod{'device-type-detail'} ||= 'N/A';
+                                               $rows[$j]{main::key($num++,'detail')} = $mod{'device-type-detail'};
+                                       }
+                               }
+                               if ($extra > 2 ){
+                                       $mod{'data-width'} ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'bus width')} = $mod{'data-width'};
+                                       $mod{'total-width'} ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'total')} = $mod{'total-width'};
+                               }
+                               if ($extra > 1 ){
+                                       $mod{'manufacturer'} ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'manufacturer')} = $mod{'manufacturer'};
+                                       $mod{'part-number'} ||= 'N/A';
+                                       $rows[$j]{main::key($num++,'part-no')} = $mod{'part-number'};
+                               }
+                               if ($extra > 2 ){
+                                       $mod{'serial'} = main::apply_filter($mod{'serial'});
+                                       $rows[$j]{main::key($num++,'serial')} = $mod{'serial'};
+                               }
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+
+sub dmidecode_data {
+       eval $start if $b_log;
+       my ($b_5,$handle,@ram,@temp);
+       my ($derived_module_size,$max_cap_5,$max_cap_16,$max_module_size) = (0,0,0,0);
+       my ($i,$j,$k) = (0,0,0);
+       foreach (@dmi){
+               my @ref = @$_;
+               # Portable Battery
+               if ($ref[0] == 5){
+                       $ram[$k] = ({}) if !$ram[$k];
+                       foreach my $item (@ref){
+                               @temp = split /:\s*/, $item;
+                               next if ! $temp[1];
+                               if ($temp[0] eq 'Maximum Memory Module Size'){
+                                       $max_module_size = calculate_size($temp[1],$max_module_size);
+                                       $ram[$k]{'max-module-size'} = $max_module_size;
+                               }
+                               elsif ($temp[0] eq 'Maximum Total Memory Size'){
+                                       $max_cap_5 = calculate_size($temp[1],$max_cap_5);
+                                       $ram[$k]{'max-capacity-5'} = $max_cap_5;
+                               }
+                               elsif ($temp[0] eq 'Memory Module Voltage'){
+                                       $temp[1] =~ s/\s*V.*$//;
+                                       $ram[$k]{'voltage'} = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Associated Memory Slots'){
+                                       $ram[$k]{'slots-5'} = $temp[1];
+                               }
+                       }
+                       $ram[$k]{'modules'} = ([],);
+                       #print Data::Dumper::Dumper \@ram;
+                       $b_5 = 1;
+               }
+               elsif ($ref[0] == 6){
+                       my ($size,$speed,$type) = (0,0,0);
+                       foreach my $item (@ref){
+                               @temp = split /:\s*/, $item;
+                               next if ! $temp[1];
+                               if ($temp[0] eq 'Installed Size'){
+                                       # get module size
+                                       
+                                       $size = calculate_size($temp[1],0);
+                                       # get data after module size
+                                       $temp[1] =~ s/ Connection\)?//;
+                                       $temp[1] =~ s/^[0-9]+\s*[MGTP]B\s*\(?//;
+                                       $type = lc($temp[1]);
+                               }
+                               elsif ($temp[0] eq 'Current Speed'){
+                                       $speed = $temp[1];
+                               }
+                       }
+                       $ram[$k]{'modules'}[$j] = ({
+                       'size' => $size,
+                       'speed-ns' => $speed,
+                       'type' => $type,
+                       });
+                       #print Data::Dumper::Dumper \@ram;
+                       $j++;
+               }
+               elsif ($ref[0] == 16){
+                       $handle = $ref[1];
+                       $ram[$handle] = $ram[$k] if $ram[$k];
+                       $ram[$k] = undef;
+                       $ram[$handle] = ({}) if !$ram[$handle];
+                       foreach my $item (@ref){
+                               @temp = split /:\s*/, $item;
+                               next if ! $temp[1];
+                               if ($temp[0] eq 'Maximum Capacity'){
+                                       $max_cap_16 = calculate_size($temp[1],$max_cap_16);
+                                       $ram[$handle]{'max-capacity-16'} = $max_cap_16;
+                               }
+                               # note: these 3 have cleaned data in set_dmidecode_data, so replace stuff manually
+                               elsif ($temp[0] eq 'Location'){
+                                       $temp[1] =~ s/\sOr\sMotherboard//;
+                                       $temp[1] ||= 'System Board';
+                                       $ram[$handle]{'location'} = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Use'){
+                                       $temp[1] ||= 'System Memory';
+                                       $ram[$handle]{'use'} = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Error Correction Type'){
+                                       $temp[1] ||= 'None';
+                                       $ram[$handle]{'eec'} = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Number Of Devices'){
+                                       $ram[$handle]{'slots-16'} = $temp[1];
+                               }
+                               #print "0: $temp[0]\n";
+                       }
+                       $ram[$handle]{'derived-module-size'} = 0;
+                       $ram[$handle]{'device-count-found'} = 0;
+                       $ram[$handle]{'used-capacity'} = 0;
+                       #print "s16: $ram[$handle]{'slots-16'}\n";
+               }
+               elsif ($ref[0] == 17){
+                       my ($bank_locator,$configured_clock_speed,$data_width) = ('','','');
+                       my ($device_type,$device_type_detail,$form_factor,$locator,$main_locator) = ('','','','','');
+                       my ($manufacturer,$part_number,$serial,$speed,$total_width) = ('','','','','');
+                       my ($device_size,$i_data,$i_total,$working_size) = (0,0,0,0);
+                       foreach my $item (@ref){
+                               @temp = split /:\s*/, $item;
+                               next if ! $temp[1];
+                               if ($temp[0] eq 'Array Handle'){
+                                       $handle = hex($temp[1]);
+                               }
+                               elsif ($temp[0] eq 'Data Width'){
+                                       $data_width = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Total Width'){
+                                       $total_width = $temp[1];
+                               }
+                               # do not try to guess from installed modules, only use this to correct type 5 data
+                               elsif ($temp[0] eq 'Size'){
+                                       # we want any non real size data to be preserved
+                                       if ( $temp[1] =~ /^[0-9]+\s*[MTPG]B/ ) {
+                                               $derived_module_size = calculate_size($temp[1],$derived_module_size);
+                                               $working_size = calculate_size($temp[1],0);
+                                               $device_size = $working_size;
+                                       }
+                                       else {
+                                               $device_size = $temp[1];
+                                       }
+                               }
+                               elsif ($temp[0] eq 'Locator'){
+                                       $temp[1] =~ s/RAM slot #/Slot/;
+                                       $locator = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Bank Locator'){
+                                       $bank_locator = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Form Factor'){
+                                       $form_factor = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Type'){
+                                       $device_type = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Type Detail'){
+                                       $device_type_detail = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Speed'){
+                                       $speed = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Configured Clock Speed'){
+                                       $configured_clock_speed = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Manufacturer'){
+                                       $temp[1] = main::dmi_cleaner($temp[1]);
+                                       $manufacturer = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Part Number'){
+                                       $temp[1] =~ s/(^[0]+$||.*Module.*|Undefined.*|PartNum.*|\[Empty\]|^To be filled.*)//g;
+                                       $part_number = $temp[1];
+                               }
+                               elsif ($temp[0] eq 'Serial Number'){
+                                       $temp[1] =~ s/(^[0]+$|Undefined.*|SerNum.*|\[Empty\]|^To be filled.*)//g;
+                                       $serial = $temp[1];
+                               }
+                       }
+                       # because of the wide range of bank/slot type data, we will just use
+                       # the one that seems most likely to be right. Some have: Bank: SO DIMM 0 slot: J6A
+                       # so we dump the useless data and use the one most likely to be visibly correct
+                       if ( $bank_locator =~ /DIMM/ ) {
+                               $main_locator = $bank_locator;
+                       }
+                       else {
+                               $main_locator = $locator;
+                       }
+                       if ($working_size =~ /^[0-9][0-9]+$/) {
+                               $ram[$handle]{'device-count-found'}++;
+                               # build up actual capacity found for override tests
+                               $ram[$handle]{'used-capacity'} += $working_size;
+                       }
+                       # sometimes the data is just wrong, they reverse total/data. data I believe is
+                       # used for the actual memory bus width, total is some synthetic thing, sometimes missing.
+                       # note that we do not want a regular string comparison, because 128 bit memory buses are
+                       # in our future, and 128 bits < 64 bits with string compare
+                       $data_width =~ /(^[0-9]+).*/;
+                       $i_data = $1;
+                       $total_width =~ /(^[0-9]+).*/;
+                       $i_total = $1;
+                       if ($i_data && $i_total && $i_data > $i_total){
+                               my $temp_width = $data_width;
+                               $data_width = $total_width;
+                               $total_width = $temp_width;
+                       }
+                       $ram[$handle]{'derived-module-size'} = $derived_module_size;
+                       $ram[$handle]{'modules'}[$i]{'configured-clock-speed'} = $configured_clock_speed;
+                       $ram[$handle]{'modules'}[$i]{'data-width'} = $data_width;
+                       $ram[$handle]{'modules'}[$i]{'size'} = $device_size;
+                       $ram[$handle]{'modules'}[$i]{'device-type'} = $device_type;
+                       $ram[$handle]{'modules'}[$i]{'device-type-detail'} = lc($device_type_detail);
+                       $ram[$handle]{'modules'}[$i]{'form-factor'} = $form_factor;
+                       $ram[$handle]{'modules'}[$i]{'locator'} = $main_locator;
+                       $ram[$handle]{'modules'}[$i]{'manufacturer'} = $manufacturer;
+                       $ram[$handle]{'modules'}[$i]{'part-number'} = $part_number;
+                       $ram[$handle]{'modules'}[$i]{'serial'} = $serial;
+                       $ram[$handle]{'modules'}[$i]{'speed'} = $speed;
+                       $ram[$handle]{'modules'}[$i]{'total-width'} = $total_width;
+                       $i++
+               }
+               elsif ($ref[0] < 17 ){
+                       next;
+               }
+               elsif ($ref[0] > 17 ){
+                       last;
+               }
+       }
+       @ram = data_processor(@ram) if @ram;
+       main::log_data('dump','@ram',\@ram) if $b_log;
+       # print Data::Dumper::Dumper \@ram;
+       eval $end if $b_log;
+       return @ram;
+}
+sub data_processor {
+       eval $start if $b_log;
+       my (@ram) = @_;
+       my $b_debug = 0;
+       my (@return,@temp);
+       my $est = 'est.';
+       
+       foreach (@ram){
+               # because we use the actual array handle as the index, 
+               # there will be many undefined keys
+               next if ! defined $_;
+               my %ref = %$_;
+               my ($max_cap,$max_mod_size) = (0,0);
+               my ($alt_cap,$est_cap,$est_mod,$unit) = (0,'','','');
+               $max_cap = $ref{'max-capacity-16'};
+               # make sure they are integers not string if empty
+               $ref{'slots-5'} ||= 0; 
+               $ref{'slots-16'} ||= 0; 
+               $ref{'max-capacity-5'} ||= 0;
+               $ref{'max-module-size'} ||= 0;
+               #$ref{'max-module-size'} = 0;# debugger
+               # 1: if max cap 1 is null, and max cap 2 not null, use 2
+               if ($b_debug){
+                       print "1: mms: $ref{'max-module-size'} :dms: $ref{'derived-module-size'} :mc: $max_cap :uc: $ref{'used-capacity'}\n";
+                       print "1a: s5: $ref{'slots-5'} s16: $ref{'slots-16'}\n";
+               }
+               if (!$max_cap && $ref{'max-capacity-5'}) {
+                       $max_cap = $ref{'max-capacity-5'};
+               }
+               if ($b_debug){
+                       print "2: mms: $ref{'max-module-size'} :dms: $ref{'derived-module-size'} :mc: $max_cap :uc: $ref{'used-capacity'}\n";
+               }
+               # 2: now check to see if actually found module sizes are > than listed max module, replace if >
+               if ( $ref{'max-module-size'} && $ref{'derived-module-size'} && 
+                    $ref{'derived-module-size'} > $ref{'max-module-size'} ){
+                       $ref{'max-module-size'} = $ref{'derived-module-size'};
+                       $est_mod = $est;
+               }
+               if ($b_debug){
+                       print "3: dcf: $ref{'device-count-found'} :dms: $ref{'derived-module-size'} :mc: $max_cap :uc: $ref{'used-capacity'}\n";
+               }
+               # note: some cases memory capacity == max module size, so one stick will fill it
+               # but I think only with cases of 2 slots does this happen, so if > 2, use the count of slots.
+               if ($max_cap && ($ref{'device-count-found'} || $ref{'slots-16'}) ){
+                       # first check that actual memory found is not greater than listed max cap, or
+                       # checking to see module count * max mod size is not > used capacity
+                       if ($ref{'used-capacity'} && $ref{'max-capacity-16'}){
+                               if ($ref{'used-capacity'} > $max_cap){
+                                       if ($ref{'max-module-size'} && 
+                                         $ref{'used-capacity'} < ($ref{'slots-16'} * $ref{'max-module-size'} )){
+                                               $max_cap = $ref{'slots-16'} * $ref{'max-module-size'};
+                                               $est_cap = $est;
+                                               print "A\n" if $b_debug;
+                                       }
+                                       elsif ($ref{'derived-module-size'} && 
+                                         $ref{'used-capacity'} < ($ref{'slots-16'} * $ref{'derived-module-size'}) ){
+                                               $max_cap = $ref{'slots-16'} * $ref{'derived-module-size'};
+                                               $est_cap = $est;
+                                               print "B\n" if $b_debug;
+                                       }
+                                       else {
+                                               $max_cap = $ref{'used-capacity'};
+                                               $est_cap = $est;
+                                               print "C\n" if $b_debug;
+                                       }
+                               }
+                       }
+                       # note that second case will never really activate except on virtual machines and maybe
+                       # mobile devices
+                       if (!$est_cap){
+                               # do not do this for only single modules found, max mod size can be equal to the array size
+                               if ($ref{'slots-16'} > 1 && $ref{'device-count-found'} > 1 && 
+                                 $max_cap < ($ref{'derived-module-size'} * $ref{'slots-16'} ) ){
+                                       $max_cap = $ref{'derived-module-size'} * $ref{'slots-16'};
+                                       $est_cap = $est;
+                                       print "D\n" if $b_debug;
+                               }
+                               elsif ($ref{'device-count-found'} > 0 && $max_cap < ( $ref{'derived-module-size'} * $ref{'device-count-found'} )){
+                                       $max_cap = $ref{'derived-module-size'} * $ref{'device-count-found'};
+                                       $est_cap = $est;
+                                       print "E\n" if $b_debug;
+                               }
+                               ## handle cases where we have type 5 data: mms x device count equals type 5 max cap
+                               # however do not use it if cap / devices equals the derived module size
+                               elsif ($ref{'max-module-size'} > 0 &&
+                                 ($ref{'max-module-size'} * $ref{'slots-16'}) == $ref{'max-capacity-5'} &&
+                                 $ref{'max-capacity-5'} != $ref{'max-capacity-16'} &&
+                                 $ref{'derived-module-size'} != ($ref{'max-capacity-16'}/$ref{'slots-16'}) ){
+                                       $max_cap = $ref{'max-capacity-5'};
+                                       $est_cap = $est;
+                                       print "F\n" if $b_debug;
+                               }
+                               
+                       }
+                       if ($b_debug){
+                               print "4: mms: $ref{'max-module-size'} :dms: $ref{'derived-module-size'} :mc: $max_cap :uc: $ref{'used-capacity'}\n";
+                       }
+                       # some cases of type 5 have too big module max size, just dump the data then since
+                       # we cannot know if it is valid or not, and a guess can be wrong easily
+                       if ($ref{'max-module-size'} && $max_cap && $ref{'max-module-size'} > $max_cap){
+                               $ref{'max-module-size'} = 0;
+                       }
+                       if ($b_debug){
+                               print "5: dms: $ref{'derived-module-size'} :s16: $ref{'slots-16'} :mc: $max_cap\n";
+                       }
+                       
+                       # now prep for rebuilding the ram array data 
+                       if (!$ref{'max-module-size'}){
+                               # ie: 2x4gB
+                               if (!$est_cap && $ref{'derived-module-size'} > 0 && $max_cap > ($ref{'derived-module-size'} * $ref{'slots-16'} * 4) ){
+                                       $est_cap = 'check';
+                                       print "G\n" if $b_debug;
+                               }
+                               if ($max_cap && ($ref{'slots-16'} || $ref{'slots-5'})){
+                                       my $slots = 0;
+                                       if ($ref{'slots-16'} && $ref{'slots-16'} >= $ref{'slots-5'}){
+                                               $slots = $ref{'slots-16'};
+                                       }
+                                       elsif ($ref{'slots-5'} && $ref{'slots-5'} > $ref{'slots-16'}){
+                                               $slots = $ref{'slots-5'};
+                                       }
+                                       if ($ref{'derived-module-size'} * $slots > $max_cap){
+                                               $ref{'max-module-size'} = $ref{'derived-module-size'};
+                                       }
+                                       else {
+                                               $ref{'max-module-size'} = sprintf("%.f",$max_cap/$slots);
+                                       }
+                                       $est_mod = $est;
+                               }
+                       }
+                       # case where listed max cap is too big for actual slots x max cap, eg:
+                       # listed max cap, 8gb, max mod 2gb, slots 2
+                       else {
+                               if (!$est_cap && $ref{'max-module-size'} > 0){
+                                       if ($max_cap > ( $ref{'max-module-size'} * $ref{'slots-16'})){
+                                               $est_cap = 'check';
+                                               print "H\n" if $b_debug;
+                                       }
+                               }
+                       }
+               }
+               @temp = ({
+               'capacity' => $max_cap,
+               'cap-qualifier' => $est_cap,
+               'eec' => $ref{'eec'},
+               'location' => $ref{'location'},
+               'max-module-size' => $ref{'max-module-size'},
+               'mod-qualifier' => $est_mod,
+               'modules' => $ref{'modules'},
+               'slots' => $ref{'slots-16'},
+               'use' => $ref{'use'},
+               'voltage' => $ref{'voltage'},
+               });
+               @return = (@return,@temp);
+       }
+       eval $end if $b_log;
+       return @return;
+}
+sub process_size {
+       my ($size) = @_;
+       my ($b_trim,$unit) = (0,'');
+       return 'N/A' if ( ! $size );
+       return $size if $size =~ /\D/;
+       if ( $size < 1024 ){
+               $unit='MiB';
+       }
+       elsif ( $size < 1024000 ){
+               $size = $size / 1024;
+               $unit='GiB';
+               $b_trim = 1;
+       }
+       elsif ( $size < 1024000000 ){
+               $size = $size / 1024000;
+               $unit='TiB';
+               $b_trim = 1;
+       }
+       # we only want a max 2 decimal places, and only when it's 
+       # a unit > MB
+       $size = sprintf("%.2f",$size) if $b_trim;
+       $size =~ s/\.[0]+$//;
+       $size = "$size $unit";
+       return $size;
+}
+sub calculate_size {
+       my ($data, $size) = @_;
+       if ( $data =~ /^[0-9]+\s*[GMTP]B/) {
+               if ( $data =~ /([0-9]+)\s*GB/ ) {
+                       $data = $1 * 1024;
+               }
+               elsif ( $data =~ /([0-9]+)\s*MB/ ) {
+                       $data = $1;
+               }
+               elsif ( $data =~ /([0-9]+)\s*TB/ ) {
+                       $data = $1 * 1024 * 1000;
+               }
+               elsif ( $data =~ /([0-9]+)\s*PB/ ) {
+                       $data = $1 * 1024 * 1000 * 1000;
+               }
+               if ($data =~ /^[0-9][0-9]+$/ && $data > $size ) {
+                       $size=$data;
+               }
+       }
+       else {
+               $size = 0;
+       }
+       return $size;
+}
+}
+
+## RepoData
+{
+package RepoData;
+
+# easier to keep these package global, but undef after done
+my (@dbg_files,$debugger_dir);
+my $num = 0;
+sub get {
+       eval $start if $b_log;
+       ($debugger_dir) = @_;
+       my (@data,@rows);
+       if ($bsd_type){
+               @rows = get_repos_bsd();
+       }
+       else {
+               @rows = get_repos_linux();
+       }
+       if ($debugger_dir){
+               @rows = @dbg_files;
+               undef @dbg_files;
+               undef $debugger_dir;
+       }
+       else {
+               if (!@rows){
+                       my $pm = (!$bsd_type) ? 'package manager': 'OS type';
+                       @data = (
+                       {main::key($num++,'Alert') => "No repo data detected. Does $self_name support your $pm?"},
+                       );
+                       @rows = (@data);
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub get_repos_linux {
+       eval $start if $b_log;
+       my (@content,@data,@data2,@data3,@files,$repo,@repos,@rows);
+       my ($key,$path);
+       my $apk = '/etc/apk/repositories';
+       my $apt = '/etc/apt/sources.list';
+       my $eopkg_dir = '/var/lib/eopkg/';
+       my $pacman = '/etc/pacman.conf';
+       my $pacman_g2 = '/etc/pacman-g2.conf';
+       my $pisi_dir = '/etc/pisi/';
+       my $portage_dir = '/etc/portage/repos.conf/';
+       my $slackpkg = '/etc/slackpkg/mirrors';
+       my $slackpkg_plus = '/etc/slackpkg/slackpkgplus.conf';
+       my $yum_conf = '/etc/yum.conf';
+       my $yum_repo_dir = '/etc/yum.repos.d/';
+       my $zypp_repo_dir = '/etc/zypp/repos.d/';
+       my $b_test = 0;
+       # apt - debian, buntus, also sometimes some yum/rpm repos may create 
+       # apt repos here as well
+       if (-f $apt || -d "$apt.d"){
+               my ($apt_arch,$apt_comp,$apt_suites,$apt_types,@apt_urls,@apt_working,
+               $b_apt_enabled,$file,$string);
+               my $counter = 0;
+               @files = main::globber('/etc/apt/sources.list.d/*.list');
+               push @files, $apt;
+               main::log_data('data',"apt repo files:\n" . main::joiner(\@files, "\n", 'unset') ) if $b_log;
+               foreach ( sort @files){
+                       # altlinux uses rpms in apt files!
+                       @data = repo_builder($_,'apt','^\s*(deb|rpm)') if -r $_;
+                       @rows = (@rows,@data);
+               }
+               #@files = main::globber("$ENV{'HOME'}/bin/scripts/inxi/data/repo/apt/*.sources");
+               @files = main::globber('/etc/apt/sources.list.d/*.sources');
+               main::log_data('data',"apt deb822 repo files:\n" . main::joiner(\@files, "\n", 'unset') ) if $b_log;
+               foreach $file (@files){
+                       @data2 = main::reader($file,'strip');
+                       my $count = scalar @data2;
+                       push @dbg_files, $file if $debugger_dir;
+                       #print "$file\n";
+                       @apt_urls = ();
+                       @apt_working = ();
+                       $counter = 0;
+                       $b_apt_enabled = 1;
+                       foreach my $row (@data2){
+                               $counter++;
+                               next if $row =~ /^\s*$|^\s*#/;
+                               #print "row:$row\n";
+                               if ($row =~ /^Types:\s*(.*)/){
+                                       my $type_holder= $1;
+                                       #print "ath:$type_holder\n";
+                                       if ($apt_types && @apt_working){
+                                               if ($b_apt_enabled){
+                                                       #print "1: url builder\n";
+                                                       foreach $repo (@apt_working){
+                                                               $string = $apt_types;
+                                                               $string .= ' [arch=' . $apt_arch . ']' if $apt_arch;
+                                                               $string .= ' ' . $repo;
+                                                               $string .= ' ' . $apt_suites if $apt_suites ;
+                                                               $string .= ' ' . $apt_comp if $apt_comp;
+                                                               
+                                                               #print "s1:$string\n";
+                                                               push @data3, $string;
+                                                       }
+                                               }
+                                               #print join "\n",@data3,"\n";
+                                               @apt_urls = (@apt_urls,@data3);
+                                               @data3 = ();
+                                               @apt_working = ();
+                                               $apt_arch = '';
+                                               $apt_comp = '';
+                                               $apt_suites = '';
+                                               $apt_types = '';
+                                       }
+                                       $apt_types = $type_holder;
+                                       $b_apt_enabled = 1;
+                               }
+                               if ($row =~ /^Enabled:\s*(.*)/){
+                                       my $status = $1;
+                                       $b_apt_enabled = ($status =~ /no/i) ? 0: 1;
+                               }
+                               if ($row =~ /:\//){
+                                       my $url = $row;
+                                       $url =~ s/^URIs:\s*//;
+                                       push @apt_working, $url if $url;
+                               }
+                               if ($row =~ /^Suites:\s*(.*)/){
+                                       $apt_suites = $1;
+                               }
+                               if ($row =~ /^Components:\s*(.*)/){
+                                       $apt_comp = $1;
+                               }
+                               if ($row =~ /^Architectures:\s*(.*)/){
+                                       $apt_arch = $1;
+                               }
+                               # we've hit the last line!!
+                               if ($counter == $count && @apt_working){
+                                       #print "2: url builder\n";
+                                       if ($b_apt_enabled){
+                                               foreach $repo (@apt_working){
+                                                       my $string = $apt_types;
+                                                       $string .= ' [arch=' . $apt_arch . ']' if $apt_arch;
+                                                       $string .= ' ' . $repo;
+                                                       $string .= ' ' . $apt_suites if $apt_suites ;
+                                                       $string .= ' ' . $apt_comp if $apt_comp;
+                                                       #print "s2:$string\n";
+                                                       push @data3, $string;
+                                               }
+                                       }
+                                       #print join "\n",@data3,"\n";
+                                       @apt_urls = (@apt_urls,@data3);
+                                       @data3 = ();
+                                       @apt_working = ();
+                                       $apt_arch = '';
+                                       $apt_comp = '';
+                                       $apt_suites = '';
+                                       $apt_types = '';
+                               }
+                       }
+                       if (@apt_urls){
+                               $key = repo_builder('active','apt');
+                               @apt_urls = url_cleaner(@apt_urls);
+                       }
+                       else {
+                               $key = repo_builder('missing','apt');
+                       }
+                       @data = (
+                       {main::key($num++,$key) => $file},
+                       [@apt_urls],
+                       );
+                       @rows = (@rows,@data);
+               }
+               @files = ();
+       }
+       # pacman: Arch and derived
+       if (-f $pacman || -f $pacman_g2){
+               $repo = 'pacman';
+               if (-f $pacman_g2 ){
+                       $pacman = $pacman_g2;
+                       $repo = 'pacman-g2';
+               }
+               @files = main::reader($pacman,'strip');
+               if (@files){
+                       @repos = grep {/^\s*Server/i} @files;
+                       @files = grep {/^\s*Include/i} @files;
+               }
+               if (@files){
+                       @files = map {
+                               my @working = split( /\s+=\s+/, $_); 
+                               $working[1];
+                       } @files;
+               }
+               @files = sort(@files);
+               @files = main::uniq(@files);
+               unshift @files, $pacman if @repos;
+               foreach (@files){
+                       if (-f $_){
+                               @data = repo_builder($_,$repo,'^\s*Server','\s*=\s*',1);
+                               @rows = (@rows,@data);
+                       }
+                       else {
+                               # set it so the debugger knows the file wasn't there
+                               push @dbg_files, $_ if $debugger_dir;
+                               @data = (
+                               {main::key($num++,'File listed in') => $pacman},
+                               [("$_ does not seem to exist.")],
+                               );
+                               @rows = (@rows,@data);
+                       }
+               }
+               if (!@rows){
+                       @data = (
+                       {main::key($num++,repo_builder('missing','no-files')) => $pacman },
+                       );
+                       @rows = (@rows,@data);
+               }
+       }
+       # slackware
+       if (-f $slackpkg || -f $slackpkg_plus){
+               #$slackpkg = "$ENV{HOME}/bin/scripts/inxi/data/repo/slackware/slackpkg-2.conf";
+               if (-f $slackpkg){
+                       @data = repo_builder($slackpkg,'slackpkg','^[[:space:]]*[^#]+');
+                       @rows = (@rows,@data);
+               }
+               if (-f $slackpkg_plus){
+                       push @dbg_files, $slackpkg_plus if $debugger_dir;
+                       @data =  main::reader($slackpkg_plus,'strip');
+                       my (@repoplus_list,$active_repos);
+                       foreach my $row (@data){
+                               @data2 = split /\s*=\s*/, $row;
+                               @data2 = map { $_ =~ s/^\s+|\s+$//g ; $_ } @data2;
+                               last if $data2[0] =~ /^SLACKPKGPLUS/ && $data2[1] eq 'off';
+                               # REPOPLUS=( slackpkgplus restricted alienbob ktown multilib slacky)
+                               if ($data2[0] =~ /^REPOPLUS/){
+                                       @repoplus_list = split /\s+/, $data2[1];
+                                       @repoplus_list = map {s/\(|\)//g; $_} @repoplus_list;
+                                       $active_repos = join ('|',@repoplus_list);
+                                       
+                               }
+                               # MIRRORPLUS['multilib']=http://taper.alienbase.nl/mirrors/people/alien/multilib/14.1/
+                               if ($active_repos && $data2[0] =~ /^MIRRORPLUS/){
+                                       $data2[0] =~ s/MIRRORPLUS\[\'|\'\]//g;
+                                       if ($data2[0] =~ /$active_repos/){
+                                               push @content,"$data2[0] ~ $data2[1]";
+                                       }
+                               }
+                       }
+                       if (! @content){
+                               $key = repo_builder('missing','slackpkg+');
+                       }
+                       else {
+                               @content = url_cleaner(@content);
+                               $key = repo_builder('active','slackpkg+');
+                       }
+                       @data = (
+                       {main::key($num++,$key) => $slackpkg_plus},
+                       [@content],
+                       );
+                       @data = url_cleaner(@data);
+                       @rows = (@rows,@data);
+                       @content = ();
+               }
+       }
+       # redhat/suse
+       if (-d $yum_repo_dir || -f $yum_conf || -d $zypp_repo_dir){
+               if (-d $yum_repo_dir || -f $yum_conf){
+                       @files = main::globber("$yum_repo_dir*.repo");
+                       push @files, $yum_conf if -f $yum_conf;
+                       $repo = 'yum';
+               }
+               elsif (-d $zypp_repo_dir){
+                       @files = main::globber("$zypp_repo_dir*.repo");
+                       main::log_data('data',"zypp repo files:\n" . main::joiner(\@files, "\n", 'unset')) if $b_log;
+                       $repo = 'zypp';
+               }
+               #$repo = 'yum';
+               #push @files, "$ENV{'HOME'}/bin/scripts/inxi/data/repo/yum/rpmfusion-nonfree-1.repo";
+               if (@files){
+                       foreach (sort @files){
+                               @data2 = main::reader($_);
+                               push @dbg_files, $_ if $debugger_dir;
+                               my ($enabled,$url,$title) = (undef,'','');
+                               foreach my $line (@data2){
+                                       # this is a hack, assuming that each item has these fields listed, we collect the 3
+                                       # items one by one, then when the url/enabled fields are set, we print it out and
+                                       # reset the data. Not elegant but it works. Note that if enabled was not present
+                                       # we assume it is enabled then, and print the line, reset the variables. This will
+                                       # miss the last item, so it is printed if found in END
+                                       if ($line =~ /^\[(.+)\]/){
+                                               my $temp = $1;
+                                               if ($url && $title && defined $enabled){
+                                                       if ($enabled > 0){
+                                                               push @content, "$title ~ $url";
+                                                       }
+                                                       ($enabled,$url,$title) = (undef,'','');
+                                               }
+                                               $title = $temp;
+                                       }
+                                       # Note: it looks like enabled comes before url
+                                       elsif ($line =~ /^(metalink|mirrorlist|baseurl)\s*=\s*(.*)/){
+                                               $url = $2;
+                                       }
+                                       # note: enabled = 1. enabled = 0 means disabled
+                                       elsif ($line =~ /^enabled\s*=\s*([01])/){
+                                               $enabled = $1;
+                                       }
+                                       # print out the line if all 3 values are found, otherwise if a new
+                                       # repoTitle is hit above, it will print out the line there instead
+                                       if ($url && $title && defined $enabled){
+                                               if ($enabled > 0){
+                                                       push @content, "$title ~ $url";
+                                               }
+                                               ($enabled,$url,$title) = (0,'','');
+                                       }
+                               }
+                               # print the last one if there is data for it
+                               if ($url && $title && $enabled){
+                                       push @content, "$title ~ $url";
+                               }
+                               
+                               if (! @content){
+                                       $key = repo_builder('missing',$repo);
+                               }
+                               else {
+                                       @content = url_cleaner(@content);
+                                       $key = repo_builder('active',$repo);
+                               }
+                               @data = (
+                               {main::key($num++,$key) => $_},
+                               [@content],
+                               );
+                               @rows = (@rows,@data);
+                               @content = ();
+                       }
+               }
+               # print Data::Dumper::Dumper \@rows;
+       }
+       # gentoo 
+       if (-d $portage_dir && main::check_program('emerge')){
+               @files = main::globber("$portage_dir*.conf");
+               $repo = 'portage';
+               if (@files){
+                       foreach (sort @files){
+                               @data2 = main::reader($_);
+                               push @dbg_files, $_ if $debugger_dir;
+                               my ($enabled,$url,$title) = (undef,'','');
+                               foreach my $line (@data2){
+                                       # this is a hack, assuming that each item has these fields listed, we collect the 3
+                                       # items one by one, then when the url/enabled fields are set, we print it out and
+                                       # reset the data. Not elegant but it works. Note that if enabled was not present
+                                       # we assume it is enabled then, and print the line, reset the variables. This will
+                                       # miss the last item, so it is printed if found in END
+                                       if ($line =~ /^\[(.+)\]/){
+                                               my $temp = $1;
+                                               if ($url && $title && defined $enabled){
+                                                       if ($enabled > 0){
+                                                               push @content, "$title ~ $url";
+                                                       }
+                                                       ($enabled,$url,$title) = (undef,'','');
+                                               }
+                                               $title = $temp;
+                                       }
+                                       elsif ($line =~ /^(sync-uri)\s*=\s*(.*)/){
+                                               $url = $2;
+                                       }
+                                       # note: enabled = 1. enabled = 0 means disabled
+                                       elsif ($line =~ /^auto-sync\s*=\s*([01])/){
+                                               $enabled = $1;
+                                       }
+                                       # print out the line if all 3 values are found, otherwise if a new
+                                       # repoTitle is hit above, it will print out the line there instead
+                                       if ($url && $title && defined $enabled){
+                                               if ($enabled > 0){
+                                                       push @content, "$title ~ $url";
+                                               }
+                                               ($enabled,$url,$title) = (undef,'','');
+                                       }
+                               }
+                               # print the last one if there is data for it
+                               if ($url && $title && $enabled){
+                                       push @content, "$title ~ $url";
+                               }
+                               if (! @content){
+                                       $key = repo_builder('missing','portage');
+                               }
+                               else {
+                                       @content = url_cleaner(@content);
+                                       $key = repo_builder('active','portage');
+                               }
+                               @data = (
+                               {main::key($num++,$key) => $_},
+                               [@content],
+                               );
+                               @rows = (@rows,@data);
+                               @content = ();
+                       }
+               }
+       }
+       # Alpine linux
+       if (-f $apk){
+               @data = repo_builder($apk,'apk','^\s*[^#]+');
+               @rows = (@rows,@data);
+       }
+       # Mandriva/Mageia using: urpmq
+       if ( $path = main::check_program('urpmq') ){
+               @data2 = main::grabber("$path --list-media active --list-url","\n",'strip');
+               main::writer("$debugger_dir/system-repo-data-urpmq.txt",@data2) if $debugger_dir;
+               # now we need to create the structure: repo info: repo path
+               # we do that by looping through the lines of the output and then
+               # putting it back into the <data>:<url> format print repos expects to see
+               # note this structure in the data, so store first line and make start of line
+               # then when it's an http line, add it, and create the full line collection.
+               # Contrib ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/contrib/release
+               # Contrib Updates ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/contrib/updates
+               # Non-free ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/non-free/release
+               # Non-free Updates ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2011/x86_64/media/non-free/updates
+               # Nonfree Updates (Local19) /mnt/data/mirrors/mageia/distrib/cauldron/x86_64/media/nonfree/updates
+               foreach (@data2){
+                       # need to dump leading/trailing spaces and clear out color codes for irc output
+                       $_ =~ s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g;
+                       $_ =~ s/\e\[([0-9];)?[0-9]+m//g;
+                       # urpmq output is the same each line, repo name space repo url, can be:
+                       # rsync://, ftp://, file://, http:// OR repo is locally mounted on FS in some cases
+                       if (/(.+)\s([\S]+:\/\/.+)/){
+                               # pack the repo url
+                               push @content, $1;
+                               @content = url_cleaner(@content);
+                               # get the repo
+                               $repo = $2;
+                               @data = (
+                               {main::key($num++,'urpmq repo') => $repo},
+                               [@content],
+                               );
+                               @rows = (@rows,@data);
+                               @content = ();
+                       }
+               }
+       }
+       # Pardus/Solus
+       if ( (-d $pisi_dir && ( $path = main::check_program('pisi') ) ) || 
+               (-d $eopkg_dir && ( $path = main::check_program('eopkg') ) ) ){
+               #$path = 'eopkg';
+               my $which = ($path =~ /pisi$/) ? 'pisi': 'eopkg';
+               my $cmd = ($which eq 'pisi') ? "$path list-repo": "$path lr";
+               #my $file = "$ENV{HOME}/bin/scripts/inxi/data/repo/solus/eopkg-2.txt";
+               #@data2 = main::reader($file,'strip');
+               @data2 = main::grabber("$cmd 2>/dev/null","\n",'strip');
+               main::writer("$debugger_dir/system-repo-data-$which.txt",@data2) if $debugger_dir;
+               # now we need to create the structure: repo info: repo path
+               # we do that by looping through the lines of the output and then
+               # putting it back into the <data>:<url> format print repos expects to see
+               # note this structure in the data, so store first line and make start of line
+               # then when it's an http line, add it, and create the full line collection.
+               # Pardus-2009.1 [Aktiv]
+               #       http://packages.pardus.org.tr/pardus-2009.1/pisi-index.xml.bz2
+               # Contrib [Aktiv]
+               #       http://packages.pardus.org.tr/contrib-2009/pisi-index.xml.bz2
+               # Solus [inactive]
+               #       https://packages.solus-project.com/shannon/eopkg-index.xml.xz
+               foreach (@data2){
+                       next if /^\s*$/;
+                       # need to dump leading/trailing spaces and clear out color codes for irc output
+                       $_ =~ s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g;
+                       $_ =~ s/\e\[([0-9];)?[0-9]+m//g;
+                       if (/^\/|:\/\//){
+                               push @content, $_ if $repo;
+                       }
+                       # Local [inactive] Unstable [active]
+                       elsif ( /^(.*)\s\[([\S]+)\]/){
+                               $repo = $1;
+                               $repo = ($2 =~ /^activ/i) ? $repo : '';
+                       }
+                       if ($repo && @content){
+                               @content = url_cleaner(@content);
+                               $key = repo_builder('active',$which);
+                               @data = (
+                               {main::key($num++,$key) => $repo},
+                               [@content],
+                               );
+                               @rows = (@rows,@data);
+                               $repo = '';
+                               @content = ();
+                       }
+               }
+               # last one if present
+               if ($repo && @content){
+                       @content = url_cleaner(@content);
+                       $key = repo_builder('active',$which);
+                       @data = (
+                       {main::key($num++,$key) => $repo},
+                       [@content],
+                       );
+                       @rows = (@rows,@data);
+               }
+       }
+       # print Dumper \@rows;
+       eval $end if $b_log;
+       return @rows;
+}
+sub get_repos_bsd {
+       eval $start if $b_log;
+       my (@content,@data,@data2,@data3,@files,@rows);
+       my ($key);
+       my $bsd_pkg = '/usr/local/etc/pkg/repos/';
+       my $freebsd = '/etc/freebsd-update.conf';
+       my $freebsd_pkg = '/etc/pkg/FreeBSD.conf';
+       my $netbsd = '/usr/pkg/etc/pkgin/repositories.conf';
+       my $openbsd = '/etc/pkg.conf';
+       my $openbsd2 = '/etc/installurl';
+       my $portsnap =  '/etc/portsnap.conf';
+       if ( -f $portsnap || -f $freebsd || -d $bsd_pkg){
+               if ( -f $portsnap ) {
+                       @data = repo_builder($portsnap,'portsnap','^\s*SERVERNAME','\s*=\s*',1);
+                       @rows = (@rows,@data);
+               }
+               if ( -f $freebsd ){
+                       @data = repo_builder($freebsd,'freebsd','^\s*ServerName','\s+',1);
+                       @rows = (@rows,@data);
+               }
+#              if ( -f $freebsd_pkg ){
+#                      @data = repo_builder($freebsd_pkg,'freebsd-pkg','^\s*url',':\s+',1);
+#                      @rows = (@rows,@data);
+#              }
+               if ( -d $bsd_pkg || -f $freebsd_pkg){
+                       @files = main::globber('/usr/local/etc/pkg/repos/*.conf');
+                       push @files, $freebsd_pkg if -f $freebsd_pkg;
+                       if (@files){
+                               my ($url);
+                               foreach (@files){
+                                       push @dbg_files, $_ if $debugger_dir;
+                                       # these will be result sets separated by an empty line
+                                       # first dump all lines that start with #
+                                       @content =  main::reader($_,'strip');
+                                       # then do some clean up on the lines
+                                       @content = map { $_ =~ s/{|}|,|\*//g; $_; } @content if @content;
+                                       # get all rows not starting with a # and starting with a non space character
+                                       my $url = '';
+                                       foreach my $line (@content){
+                                               if ($line !~ /^\s*$/){
+                                                       my @data2 = split /\s*:\s*/, $line;
+                                                       @data2 = map { $_ =~ s/^\s+|\s+$//g; $_; } @data2;
+                                                       if ($data2[0] eq 'url'){
+                                                               $url = "$data2[1]:$data2[2]";
+                                                               $url =~ s/"|,//g;
+                                                       }
+                                                       #print "url:$url\n" if $url;
+                                                       if ($data2[0] eq 'enabled'){
+                                                               if ($url && $data2[1] eq 'yes'){
+                                                                       push @data3, "$url"
+                                                               }
+                                                               $url = '';
+                                                       }
+                                               }
+                                       }
+                                       if (! @data3){
+                                               $key = repo_builder('missing','bsd-package');
+                                       }
+                                       else {
+                                               @data3 = url_cleaner(@data3);
+                                               $key = repo_builder('active','bsd-package');
+                                       }
+                                       @data = (
+                                       {main::key($num++,$key) => $_},
+                                       [@data3],
+                                       );
+                                       @rows = (@rows,@data);
+                                       @data3 = ();
+                               }
+                       }
+               }
+       }
+       elsif (-f $openbsd || -f $openbsd2) {
+               if (-f $openbsd){
+                       @data = repo_builder($openbsd,'openbsd','^installpath','\s*=\s*',1);
+                       @rows = (@rows,@data);
+               }
+               if (-f $openbsd2){
+                       @data = repo_builder($openbsd2,'openbsd','^(http|ftp)','',1);
+                       @rows = (@rows,@data);
+               }
+       }
+       elsif (-f $netbsd){
+               # not an empty row, and not a row starting with #
+               @data = repo_builder($netbsd,'netbsd','^\s*[^#]+$');
+               @rows = (@rows,@data);
+       }
+       # BSDs do not default always to having repo files, so show correct error 
+       # mesage in that case
+       if (!@rows){
+               if ($bsd_type eq 'freebsd'){
+                       $key = repo_builder('missing','freebsd-nf');
+               }
+               elsif ($bsd_type eq 'openbsd'){
+                       $key = repo_builder('missing','openbsd-nf');
+               }
+               elsif ($bsd_type eq 'netbsd'){
+                       $key = repo_builder('missing','netbsd-nf');
+               }
+               else {
+                       $key = repo_builder('missing','bsd-nf');
+               }
+               @data = (
+               {main::key($num++,'Message') => $key},
+               [()],
+               );
+               @rows = (@rows,@data);
+       }
+       eval $start if $b_log;
+       return @rows;
+}
+sub repo_builder {
+       eval $start if $b_log;
+       my ($file,$type,$search,$split,$count) = @_;
+       my (@content,@data,$missing,$key);
+       my %unfound = (
+       'apk' => 'No active APK repos in',
+       'apt' => 'No active apt repos in',
+       'bsd-package' => 'No enabled BSD pkg servers in',
+       'bsd-nf' => 'No BSD pkg server files found',
+       'eopkg' => 'No active eopkg repos found',
+       'pacman' => 'No active pacman repos in',
+       'pacman-g2' => 'No active pacman-g2 repos in',
+       'pisi' => 'No active pisi repos found',
+       'portage' => 'No enabled portage sources in',
+       'portsnap' => 'No ports servers in',
+       'freebsd' => 'No FreeBSD update servers in',
+       'freebsd-nf' => 'No FreeBSD update server files found',
+       'freebsd-pkg' => 'No FreeBSD default pkg server in',
+       'openbsd' => 'No OpenBSD pkg mirrors in',
+       'openbsd-nf' => 'No OpenBSD pkg mirror files found',
+       'netbsd' => 'No NetBSD pkg servers in',
+       'netbsd-nf' => 'No NetBSD pkg server files found',
+       'no-files' => 'No repo files found in',
+       'slackpkg' => 'No active slackpkg repos in',
+       'slackpkg+' => 'No active slackpkg+ repos in',
+       'yum' => 'No active yum repos in',
+       'zypp' => 'No active zypp repos in',
+       );
+       $missing = $unfound{$type};
+       return $missing if $file eq 'missing';
+       my %keys = (
+       'apk' => 'APK repo',
+       'apt' => 'Active apt repos in',
+       'bsd-package' => 'BSD enabled pkg servers in',
+       'eopkg' => 'Active eopkg repo',
+       'freebsd' => 'FreeBSD update server',
+       'freebsd-pkg' => 'FreeBSD default pkg server',
+       'pacman' => 'Active pacman repo servers in',
+       'pacman-g2' => 'Active pacman-g2 repo servers in',
+       'pisi' => 'Active pisi repo',
+       'portage' => 'Enabled portage sources in',
+       'portsnap' => 'BSD ports server',
+       'openbsd' => 'OpenBSD pkg mirror',
+       'netbsd' => 'NetBSD pkg servers',
+       'slackpkg' => 'slackpkg repos in',
+       'slackpkg+' => 'slackpkg+ repos in',
+       'yum' => 'Active yum repos in',
+       'zypp' => 'Active zypp repos in',
+       );
+       $key = $keys{$type};
+       return $key if $file eq 'active';
+       push @dbg_files, $file if $debugger_dir;
+       @content =  main::reader($file);
+       @content = grep {/$search/i && !/^\s*$/} @content if @content;
+       @content = data_cleaner(@content);
+       if ($split){
+               @content = map { 
+               my @inner = split (/$split/, $_);
+               $inner[$count];
+               } @content;
+       }
+       if (!@content){
+               $key = $missing;
+       }
+       else {
+               @content = url_cleaner(@content);
+       }
+       @data = (
+       {main::key($num++,$key) => $file},
+       [@content],
+       );
+       eval $end if $b_log;
+       return @data;
+}
+sub data_cleaner {
+       my (@content) = @_;
+       # basics: trim white space, get rid of double spaces
+       @content = map { $_ =~ s/^\s+|\s+$//g; $_ =~ s/\s\s+/ /g; $_} @content;
+       return @content;
+}
+# clean if irc
+sub url_cleaner {
+       my (@content) = @_;
+       @content = map { $_ =~ s/:\//: \//; $_} @content if $b_irc;
+       return @content;
+}
+sub file_path {
+       my ($filename,$dir) = @_;
+       my ($working);
+       $working = $filename;
+       $working =~ s/^\///;
+       $working =~ s/\//-/g;
+       $working = "$dir/file-repo-$working.txt";
+       return $working;
+}
+}
+
+## SensorData
+{
+package SensorData;
+my (@sensors_data);
+my ($b_ipmi) = (0);
+sub get {
+       eval $start if $b_log;
+       my ($key1,$program,$val1,@data,@rows,%sensors);
+       my $num = 0;
+       my $source = 'sensors';
+       # we're allowing 1 or 2 ipmi tools, first the gnu one, then the 
+       # almost certain to be present in BSDs
+       if ( $b_ipmi || 
+           ( main::globber('/dev/ipmi**') && 
+           ( ( $program = main::check_program('ipmi-sensors') ) ||
+           ( $program = main::check_program('ipmitool') ) ) ) ){
+               if ($b_ipmi || $b_root){
+                       %sensors = ipmi_data($program);
+                       @data = create_output('ipmi',%sensors);
+                       if (!@data) {
+                               $key1 = 'Message';
+                               $val1 = main::row_defaults('sensors-data-ipmi');
+                               #$val1 = main::row_defaults('dev');
+                               @data = ({main::key($num++,$key1) => $val1,});
+                       }
+                       @rows = (@rows,@data);
+                       $source = 'lm-sensors'; # trips per sensor type output
+               }
+               else {
+                       $key1 = 'Permissions';
+                       $val1 = main::row_defaults('sensors-ipmi-root');
+                       @data = ({main::key($num++,$key1) => $val1,});
+                       @rows = (@rows,@data);
+               }
+       }
+       my $ref = $alerts{'sensors'};
+       if ( $$ref{'action'} ne 'use'){
+               #print "here 1\n";
+               $key1 = $$ref{'action'};
+               $val1 = $$ref{$key1};
+               $key1 = ucfirst($key1);
+               @data = ({main::key($num++,$key1) => $val1,});
+               @rows = (@rows,@data);
+       }
+       else {
+               %sensors = lm_sensors_data();
+               @data = create_output($source,%sensors);
+               #print "here 2\n";
+               if (!@data) {
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('sensors-data-linux');
+                       @data = ({main::key($num++,$key1) => $val1,});
+               }
+               @rows = (@rows,@data);
+       }
+       undef @sensors_data;
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my ($source,%sensors) = @_;
+       # note: might revisit this, since gpu sensors data might be present
+       return if ! %sensors;
+       my (@gpu,@data,@rows,@fan_default,@fan_main);
+       my ($data_source) = ('');
+       my $fan_number = 0;
+       my $num = 0;
+       my $j = 0;
+       @gpu = gpu_data() if ( $source eq 'sensors' || $source eq 'lm-sensors' );
+       my $temp_unit  = (defined $sensors{'temp-unit'}) ? " $sensors{'temp-unit'}": '';
+       my $cpu_temp = (defined $sensors{'cpu-temp'}) ? $sensors{'cpu-temp'} . $temp_unit: 'N/A';
+       my $mobo_temp = (defined $sensors{'mobo-temp'}) ? $sensors{'mobo-temp'} . $temp_unit: 'N/A';
+       my $cpu1_key = ($sensors{'cpu2-temp'}) ? 'cpu-1': 'cpu' ;
+       $data_source = $source if ($source eq 'ipmi' || $source eq 'lm-sensors');
+       @data = ({
+       main::key($num++,'System Temperatures') => $data_source,
+       main::key($num++,$cpu1_key) => $cpu_temp,
+       });
+       @rows = (@rows,@data);
+       if ($sensors{'cpu2-temp'}){
+               $rows[$j]{main::key($num++,'cpu-2')} = $sensors{'cpu2-temp'} . $temp_unit;
+       }
+       if ($sensors{'cpu3-temp'}){
+               $rows[$j]{main::key($num++,'cpu-3')} = $sensors{'cpu3-temp'} . $temp_unit;
+       }
+       if ($sensors{'cpu4-temp'}){
+               $rows[$j]{main::key($num++,'cpu-4')} = $sensors{'cpu4-temp'} . $temp_unit;
+       }
+       $rows[$j]{main::key($num++,'mobo')} = $mobo_temp;
+       if (defined $sensors{'sodimm-temp'}){
+               my $sodimm_temp = $sensors{'sodimm-temp'} . $temp_unit;
+               $rows[$j]{main::key($num++,'sodimm')} = $sodimm_temp;
+       }
+       if (defined $sensors{'psu-temp'}){
+               my $psu_temp = $sensors{'psu-temp'} . $temp_unit;
+               $rows[$j]{main::key($num++,'psu')} = $psu_temp;
+       }
+       if (defined $sensors{'ambient-temp'}){
+               my $ambient_temp = $sensors{'ambient-temp'} . $temp_unit;
+               $rows[$j]{main::key($num++,'ambient')} = $ambient_temp;
+       }
+       if (scalar @gpu == 1){
+               my $gpu_temp = $gpu[0]{'temp'};
+               my $gpu_type = $gpu[0]{'type'};
+               my $gpu_unit = (defined $gpu[0]{'temp-unit'} && $gpu_temp ) ? " $gpu[0]{'temp-unit'}" : ' C';
+               $rows[$j]{main::key($num++,'gpu')} = $gpu_type;
+               $rows[$j]{main::key($num++,'temp')} = $gpu_temp . $gpu_unit;
+       }
+       $j = scalar @rows;
+       my $ref_main = $sensors{'fan-main'};
+       my $ref_default = $sensors{'fan-default'};
+       @fan_main = @$ref_main if @$ref_main;
+       @fan_default = @$ref_default if @$ref_default;
+       my $fan_def = ($data_source) ? $data_source : '';
+       if (!@fan_main && !@fan_default){
+               $fan_def = ($fan_def) ? "$data_source N/A" : 'N/A';
+       }
+       $rows[$j]{main::key($num++,'Fan Speeds (RPM)')} = $fan_def;
+       my $b_cpu = 0;
+       for (my $i = 0; $i < scalar @fan_main; $i++){
+               next if $i == 0;# starts at 1, not 0
+               if (defined $fan_main[$i]){
+                       if ($i == 1 || ($i == 2 && !$b_cpu )){
+                               $rows[$j]{main::key($num++,'cpu')} = $fan_main[$i];
+                               $b_cpu = 1;
+                       }
+                       elsif ($i == 2 && $b_cpu){
+                               $rows[$j]{main::key($num++,'mobo')} = $fan_main[$i];
+                       }
+                       elsif ($i == 3){
+                               $rows[$j]{main::key($num++,'psu')} = $fan_main[$i];
+                       }
+                       elsif ($i == 4){
+                               $rows[$j]{main::key($num++,'sodimm')} = $fan_main[$i];
+                       }
+                       elsif ($i > 4){
+                               $fan_number = $i - 4;
+                               $rows[$j]{main::key($num++,"case-$fan_number")} = $fan_main[$i];
+                       }
+               }
+       }
+       for (my $i = 0; $i < scalar @fan_default; $i++){
+               next if $i == 0;# starts at 1, not 0
+               if (defined $fan_default[$i]){
+                       $rows[$j]{main::key($num++,"fan-$i")} = $fan_default[$i];
+               }
+       }
+       $rows[$j]{main::key($num++,'psu')} = $sensors{'fan-psu'} if defined $sensors{'fan-psu'};
+       $rows[$j]{main::key($num++,'psu-1')} = $sensors{'fan-psu1'} if defined $sensors{'fan-psu1'};
+       $rows[$j]{main::key($num++,'psu-2')} = $sensors{'fan-psu2'} if defined $sensors{'fan-psu2'};
+       # note: so far, only nvidia-settings returns speed, and that's in percent
+       if (scalar @gpu == 1 && defined $gpu[0]{'fan-speed'}){
+               my $gpu_fan = $gpu[0]{'fan-speed'} . $gpu[0]{'speed-unit'};
+               my $gpu_type = $gpu[0]{'type'};
+               $rows[$j]{main::key($num++,'gpu')} = $gpu_type;
+               $rows[$j]{main::key($num++,'fan')} = $gpu_fan;
+       }
+       if (scalar @gpu > 1){
+               $j = scalar @rows;
+               $rows[$j]{main::key($num++,'GPU')} = '';
+               my $gpu_unit = (defined $gpu[0]{'temp-unit'} ) ? " $gpu[0]{'temp-unit'}" : ' C';
+               foreach my $ref (@gpu){
+                       my %info = %$ref;
+                       # speed unit is either '' or %
+                       my $gpu_fan = (defined $info{'fan-speed'}) ? $info{'fan-speed'} . $info{'speed-unit'}: undef ;
+                       my $gpu_type = $info{'type'};
+                       my $gpu_temp = (defined $info{'temp'} ) ? $info{'temp'} . $gpu_unit: 'N/A';
+                       $rows[$j]{main::key($num++,'device')} = $gpu_type;
+                       if (defined $info{'screen'} ){
+                               $rows[$j]{main::key($num++,'screen')} = $info{'screen'};
+                       }
+                       $rows[$j]{main::key($num++,'temp')} = $gpu_temp;
+                       if (defined $gpu_fan){
+                               $rows[$j]{main::key($num++,'fan')} = $gpu_fan;
+                       }
+               }
+       }
+       if ($extra > 0 && ($source eq 'ipmi' || 
+          ($sensors{'volts-12'} || $sensors{'volts-5'} || $sensors{'volts-3.3'} || $sensors{'volts-vbat'}))){
+               $j = scalar @rows;
+               $sensors{'volts-12'} ||= 'N/A';
+               $sensors{'volts-5'} ||= 'N/A';
+               $sensors{'volts-3.3'} ||= 'N/A';
+               $sensors{'volts-vbat'} ||= 'N/A';
+               $rows[$j]{main::key($num++,'Voltages')} = $data_source;
+               $rows[$j]{main::key($num++,'12v')} = $sensors{'volts-12'};
+               $rows[$j]{main::key($num++,'5v')} = $sensors{'volts-5'};
+               $rows[$j]{main::key($num++,'3.3v')} = $sensors{'volts-3.3'};
+               $rows[$j]{main::key($num++,'vbat')} = $sensors{'volts-vbat'};
+               if ($extra > 1 && $source eq 'ipmi' ){
+                       $sensors{'volts-dimm-p1'} ||= 'N/A';
+                       $sensors{'volts-dimm-p2'} ||= 'N/A';
+                       $rows[$j]{main::key($num++,'dimm-p1')} = $sensors{'volts-dimm-p1'} if $sensors{'volts-dimm-p1'};
+                       $rows[$j]{main::key($num++,'dimm-p2')} = $sensors{'volts-dimm-p2'} if $sensors{'volts-dimm-p2'};
+                       $rows[$j]{main::key($num++,'soc-p1')} = $sensors{'volts-soc-p1'} if $sensors{'volts-soc-p1'};
+                       $rows[$j]{main::key($num++,'soc-p2')} = $sensors{'volts-soc-p2'} if $sensors{'volts-soc-p2'};
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub ipmi_data {
+       eval $start if $b_log;
+       my ($program) = @_;
+       my ($b_cpu_0,$cmd,$file,@data,$fan_working,%sensors,@row,$sys_fan_nu,
+       $temp_working,$working_unit);
+       $program ||= 'ipmi-sensors'; # only for debugging, will always exist if reaches here
+       my ($b_ipmitool,$i_key,$i_value,$i_unit);
+       if ($program =~ /ipmi-sensors$/){
+               $cmd = $program;
+               ($b_ipmitool,$i_key,$i_value,$i_unit) = (0,1,3,4);
+       }
+       else {
+               $cmd = "$program sensors";
+               ($b_ipmitool,$i_key,$i_value,$i_unit) = (1,0,1,2);
+       }
+       @data = main::grabber("$cmd 2>/dev/null");
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-archerseven-1.txt";
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-crazy-epyc-1.txt";
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-crazy-epyc-1.txt";$program='ipmi-sensors';
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-RK016013.txt";$program='ipmi-sensors';
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-lathander.txt";
+       #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-zwerg.txt";
+       #@data = main::reader($file);
+       return if ! @data;
+       foreach (@data){
+               next if /^\s*$/;
+               # print "$_\n";
+               @row = split /\s*\|\s*/, $_;
+               next if $row[$i_value] !~ /^[0-9\.]+$/i;
+               # print "$row[$i_key] - $row[$i_value]\n";
+               if ($row[$i_key] =~ /^(System[\s_]Temp|System[\s_]?Board)$/i){
+                       $sensors{'mobo-temp'} = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($row[$i_key] =~ /^(Ambient)$/i){
+                       $sensors{'ambient-temp'} = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # Platform Control Hub (PCH), it is the X370 chip on the Crosshair VI Hero.
+               # VRM: voltage regulator module
+               # NOTE: CPU0_TEMP CPU1_TEMP is possible, unfortunately; CPU Temp Interf 
+               elsif ( !$sensors{'cpu-temp'} && $row[$i_key] =~ /^CPU([01])?([\s_]Temp)?$/i) {
+                       $b_cpu_0 = 1 if defined $1 && $1 == 0;
+                       $sensors{'cpu-temp'} = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($row[$i_key] =~ /^CPU([1-4])([\s_]Temp)?$/i) {
+                       $temp_working = $1;
+                       $temp_working++ if $b_cpu_0;
+                       $sensors{"cpu${temp_working}-temp"} = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # for temp1/2 only use temp1/2 if they are null or greater than the last ones
+               elsif ($row[$i_key] =~ /^(MB[_]?TEMP1|Temp[\s_]1)$/i) {
+                       $temp_working = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       if ( !$sensors{'temp1'} || ( defined $temp_working && $temp_working > 0 ) ) {
+                               $sensors{'temp1'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($row[$i_key] =~ /^(MB[_]?TEMP2|Temp[\s_]2)$/i) {
+                       $temp_working = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       if ( !$sensors{'temp2'} || ( defined $temp_working && $temp_working > 0 ) ) {
+                               $sensors{'temp2'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # temp3 is only used as an absolute override for systems with all 3 present
+               elsif ($row[$i_key] =~ /^(MB[_]?TEMP3|Temp[\s_]3)$/i) {
+                       $temp_working = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       if ( !$sensors{'temp3'} || ( defined $temp_working && $temp_working > 0 ) ) {
+                               $sensors{'temp3'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif (!$sensors{'sodimm-temp'} && $row[$i_key] =~ /^(DIMM-[0-9][A-Z]?)$/i){
+                       $sensors{'sodimm-temp'} = int($row[$i_value]);
+                       $working_unit = $row[$i_unit];
+                       $working_unit =~ s/degrees\s// if $b_ipmitool; 
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # note: can be cpu fan:, cpu fan speed:, etc.
+               elsif ($row[$i_key] =~ /^(CPU|Processor)[\s_]Fan/i) {
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       $sensors{'fan-main'}[1] = int($row[$i_value]);
+               }
+               # note that the counters are dynamically set for fan numbers here
+               # otherwise you could overwrite eg aux fan2 with case fan2 in theory
+               # note: cpu/mobo/ps are 1/2/3
+               elsif ($row[$i_key] =~ /^(SYS[\s_])?FAN[\s_]?([0-9A-F]+)/i) {
+                       $sys_fan_nu = hex($2);
+                       next if $row[$i_value] !~ /^[0-9\.]+$/;
+                       $fan_working = int($row[$i_value]);
+                       $sensors{'fan-default'} = () if !$sensors{'fan-default'};
+                       if ( $sys_fan_nu =~ /^([0-9]+)$/ ) {
+                               # add to array if array index does not exist OR if number is > existing number
+                               if ( defined $sensors{'fan-default'}[$sys_fan_nu] ) {
+                                       if ( $fan_working >= $sensors{'fan-default'}[$sys_fan_nu] ) {
+                                               $sensors{'fan-default'}[$sys_fan_nu] = $fan_working;
+                                       }
+                               }
+                               else {
+                                       $sensors{'fan-default'}[$sys_fan_nu] = $fan_working;
+                               }
+                       }
+               }
+               elsif ($row[$i_key] =~ /^(FAN PSU|PSU FAN)$/i) {
+                       $sensors{'fan-psu'} = int($row[$i_value]);
+               }
+               elsif ($row[$i_key] =~ /^(FAN PSU1|PSU1 FAN)$/i) {
+                       $sensors{'fan-psu-1'} = int($row[$i_value]);
+               }
+               elsif ($row[$i_key] =~ /^(FAN PSU2|PSU2 FAN)$/i) {
+                       $sensors{'fan-psu-2'} = int($row[$i_value]);
+               }
+               if ($extra > 0){
+                       if ($row[$i_key] =~ /^(MAIN\s|P[_]?)?12V$/i) {
+                               $sensors{'volts-12'} = $row[$i_value];
+                       }
+                       elsif ($row[$i_key] =~ /^(MAIN\s5V|P5V|5VCC|5V PG)$/i) {
+                               $sensors{'volts-5'} = $row[$i_value];
+                       }
+                       elsif ($row[$i_key] =~ /^(MAIN\s3.3V|P3V3|3.3VCC|3.3V PG)$/i) {
+                               $sensors{'volts-3.3'} = $row[$i_value];
+                       }
+                       elsif ($row[$i_key] =~ /^((P_)?VBAT|CMOS Battery|BATT 3.0V)$/i) {
+                               $sensors{'volts-vbat'} = $row[$i_value];
+                       }
+                       # NOTE: VDimmP1ABC VDimmP1DEF
+                       elsif (!$sensors{'volts-dimm-p1'} && $row[$i_key] =~ /^(P1_VMEM|VDimmP1|MEM RSR A PG)/i) {
+                               $sensors{'volts-dimm-p1'} = $row[$i_value];
+                       }
+                       elsif (! $sensors{'volts-dimm-p2'} && $row[$i_key] =~ /^(P2_VMEM|VDimmP2|MEM RSR B PG)/i) {
+                               $sensors{'volts-dimm-p2'} = $row[$i_value];
+                       }
+                       elsif (!$sensors{'volts-soc-p1'} && $row[$i_key] =~ /^(P1_SOC_RUN$)/i) {
+                               $sensors{'volts-soc-p1'} = $row[$i_value];
+                       }
+                       elsif (! $sensors{'volts-soc-p2'} && $row[$i_key] =~ /^(P2_SOC_RUN$)/i) {
+                               $sensors{'volts-soc-p2'} = $row[$i_value];
+                       }
+               }
+       }
+       # print Data::Dumper::Dumper \%sensors;
+       %sensors = data_processor(%sensors) if %sensors;
+       main::log_data('dump','ipmi: %sensors',\%sensors) if $b_log;
+       eval $end if $b_log;
+       # print Data::Dumper::Dumper \%sensors;
+       return %sensors;
+}
+sub lm_sensors_data {
+       eval $start if $b_log;
+       my (%sensors);
+       my ($b_valid,$sys_fan_nu)  = (0,0);
+       my ($adapter,$fan_working,$temp_working,$working_unit)  = ('','','','');
+       @sensors_data = main::grabber(main::check_program('sensors') . " 2>/dev/null");
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sensors/amdgpu-w-fan-speed-stretch-k10.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sensors/peci-tin-geggo.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sensors/sensors-w-other-biker.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sensors/sensors-asus-chassis-1.txt";
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sensors/sensors-devnull-1.txt";
+       #@sensors_data = main::reader($file);
+       #print @sensors_data;
+       @sensors_data = map {$_ =~ s/\s*:\s*\+?/:/;$_} @sensors_data;
+       foreach (@sensors_data){
+               # we get this from gpu_data()
+               if (/^(amdgpu|intel|nouveau|radeon|.*hwmon)-pci/){
+                       $b_valid = 0;
+                       $adapter = '';
+                       next;
+               }
+               if (/^(?:(?!amdgpu|intel|nouveau|radeon|.*hwmon).)*-(isa|pci|virtual)-/){
+                       $b_valid = 1;
+                       $adapter = $1;
+                       next;
+               }
+               next if !$b_valid;
+               my @working = split /:/, $_;
+               next if !$working[0] || /^Adapter/;
+               #print "$working[0]:$working[1]\n";
+               # There are some guesses here, but with more sensors samples it will get closer.
+               # note: using arrays starting at 1 for all fan arrays to make it easier overall
+               # we have to be sure we are working with the actual real string before assigning
+               # data to real variables and arrays. Extracting C/F degree unit as well to use
+               # when constructing temp items for array. 
+               # note that because of charset issues, no "°" degree sign used, but it is required 
+               # in testing regex to avoid error. It might be because I got that data from a forum post,
+               # note directly via debugger.
+               if ($_ =~ /^(AMBIENT|M\/B|MB|SIO|SYS).*:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $sensors{'mobo-temp'} = $2;
+                       $working_unit = $3;
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # issue 58 msi/asus show wrong for CPUTIN so overwrite it if PECI 0 is present
+               # http://www.spinics.net/lists/lm-sensors/msg37308.html
+               # NOTE: had: ^CPU.*\+([0-9]+): but that misses: CPUTIN and anything not with + in starter
+               # However, "CPUTIN is not a reliable measurement because it measures difference to Tjmax,
+               # which is the maximum CPU temperature reported as critical temperature by coretemp"
+               # NOTE: I've seen an inexplicable case where: CPU:52.0°C fails to match with [\s°] but 
+               # does match with: [\s°]*. I can't account for this, but that's why the * is there
+               # Tdie is a new k10temp-pci syntax for cpu die temp
+               elsif ($_ =~ /^(CPU.*|Tdie.*):([0-9\.]+)[\s°]*(C|F)/i) {
+                       $temp_working = $2;
+                       $working_unit = $3;
+                       if ( !$sensors{'cpu-temp'} || 
+                          ( defined $temp_working && $temp_working > 0 && $temp_working > $sensors{'cpu-temp'} ) ) {
+                               $sensors{'cpu-temp'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($_ =~ /^PECI\sAgent\s0.*:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $sensors{'cpu-peci-temp'} = $1;
+                       $working_unit = $2;
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($_ =~ /^(P\/S|Power).*:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $sensors{'psu-temp'} = $2;
+                       $working_unit = $3;
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($_ =~ /^SODIMM.*:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $sensors{'sodimm-temp'} = $1;
+                       $working_unit = $2;
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # for temp1/2 only use temp1/2 if they are null or greater than the last ones
+               elsif ($_ =~ /^temp1:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $temp_working = $1;
+                       $working_unit = $2;
+                       if ( !$sensors{'temp1'} || 
+                          ( defined $temp_working && $temp_working > 0 && $temp_working > $sensors{'temp1'} ) ) {
+                               $sensors{'temp1'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               elsif ($_ =~ /^temp2:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $temp_working = $1;
+                       $working_unit = $2;
+                       if ( !$sensors{'temp2'} || 
+                          ( defined $temp_working && $temp_working > 0 && $temp_working > $sensors{'temp2'} ) ) {
+                               $sensors{'temp2'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # temp3 is only used as an absolute override for systems with all 3 present
+               elsif ($_ =~ /^temp3:([0-9\.]+)[\s°]*(C|F)/i) {
+                       $temp_working = $1;
+                       $working_unit = $2;
+                       if ( !$sensors{'temp3'} || 
+                          ( defined $temp_working && $temp_working > 0 && $temp_working > $sensors{'temp3'} ) ) {
+                               $sensors{'temp3'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # final fallback if all else fails, funtoo user showed sensors putting
+               # temp on wrapped second line, not handled
+               elsif ($_ =~ /^(core0|core 0|Physical id 0)(.*):([0-9\.]+)[\s°]*(C|F)/i) {
+                       $temp_working = $3;
+                       $working_unit = $4;
+                       if ( !$sensors{'core-0-temp'} || 
+                          ( defined $temp_working && $temp_working > 0 && $temp_working > $sensors{'core-0-temp'} ) ) {
+                               $sensors{'core-0-temp'} = $temp_working;
+                       }
+                       $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit;
+               }
+               # note: can be cpu fan:, cpu fan speed:, etc.
+               elsif (!$sensors{'fan-main'}[1] && $_ =~ /^(CPU|Processor).*:([0-9]+)[\s]RPM/i) {
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       $sensors{'fan-main'}[1] = $2;
+               }
+               elsif (!$sensors{'fan-main'}[2] && $_ =~ /^(M\/B|MB|SYS).*:([0-9]+)[\s]RPM/i) {
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       $sensors{'fan-main'}[2] = $2;
+               }
+               elsif (!$sensors{'fan-main'}[3] && $_ =~ /(Power|P\/S|POWER).*:([0-9]+)[\s]RPM/i) {
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       $sensors{'fan-main'}[3] = $2;
+               }
+               elsif (!$sensors{'fan-main'}[4] && $_ =~ /(SODIMM).*:([0-9]+)[\s]RPM/i) {
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       $sensors{'fan-main'}[4] = $2;
+               }
+               # note that the counters are dynamically set for fan numbers here
+               # otherwise you could overwrite eg aux fan2 with case fan2 in theory
+               # note: cpu/mobo/ps/sodimm are 1/2/3/4
+               elsif ($_ =~ /^(AUX|CASE|CHASSIS).*:([0-9]+)[\s]RPM/i) {
+                       $temp_working = $2;
+                       $sensors{'fan-main'} = () if !$sensors{'fan-main'};
+                       for ( my $i = 5; $i < 30; $i++ ){
+                               next if defined $sensors{'fan-main'}[$i];
+                               if ( !defined $sensors{'fan-main'}[$i] ){
+                                       $sensors{'fan-main'}[$i] = $temp_working;
+                                       last;
+                               }
+                       }
+               }
+               # in rare cases syntax is like: fan1: xxx RPM
+               elsif ($_ =~ /^FAN(1)?:([0-9]+)[\s]RPM/i) {
+                       $sensors{'fan-default'} = () if !$sensors{'fan-default'};
+                       $sensors{'fan-default'}[1] = $2;
+               }
+               elsif ($_ =~ /^FAN([2-9]|1[0-9]).*:([0-9]+)[\s]RPM/i) {
+                       $fan_working = $2;
+                       $sys_fan_nu = $1;
+                       $sensors{'fan-default'} = () if !$sensors{'fan-default'};
+                       if ( $sys_fan_nu =~ /^([0-9]+)$/ ) {
+                               # add to array if array index does not exist OR if number is > existing number
+                               if ( defined $sensors{'fan-default'}[$sys_fan_nu] ) {
+                                       if ( $fan_working >= $sensors{'fan-default'}[$sys_fan_nu] ) {
+                                               $sensors{'fan-default'}[$sys_fan_nu] = $fan_working;
+                                       }
+                               }
+                               else {
+                                       $sensors{'fan-default'}[$sys_fan_nu] = $fan_working;
+                               }
+                       }
+               }
+               if ($extra > 0){
+                       if ($_ =~ /^[+]?(12 Volt|12V).*:([0-9\.]+)\sV/i) {
+                               $sensors{'volts-12'} = $2;
+                       }
+                       # note: 5VSB is a field name
+                       elsif ($_ =~ /^[+]?(5 Volt|5V):([0-9\.]+)\sV/i) {
+                               $sensors{'volts-5'} = $2;
+                       }
+                       elsif ($_ =~ /^[+]?(3\.3 Volt|3\.3V).*:([0-9\.]+)\sV/i) {
+                               $sensors{'volts-3.3'} = $2;
+                       }
+                       elsif ($_ =~ /^(Vbat).*:([0-9\.]+)\sV/i) {
+                               $sensors{'volts-vbat'} = $2;
+                       }
+               }
+       }
+       # print Data::Dumper::Dumper \%sensors;
+       %sensors = data_processor(%sensors) if %sensors;
+       main::log_data('dump','lm-sensors: %sensors',\%sensors) if $b_log;
+       # print Data::Dumper::Dumper \%sensors;
+       eval $end if $b_log;
+       return %sensors;
+}
+
+# oddly, openbsd sysctl actually has hw.sensors data!
+sub sysctl_data {
+       eval $start if $b_log;
+       my (@data,%sensors);
+       foreach (@sysctl_sensors){
+               if (/^hw.sensors\.([0-9a-z]+)\.(temp|fan|volt)([0-9])/){
+                       my $sensor = $1;
+                       my $type = $2;
+                       my $number = $3;
+                       my @working = split /:/, $_;
+               }
+               last if /^(hw.cpuspeed|hw.vendor|hw.physmem)/;
+       }
+       
+       %sensors = data_processor(%sensors) if %sensors;
+       main::log_data('dump','%sensors',\%sensors) if $b_log;
+       # print Data::Dumper::Dumper \%sensors;
+       eval $end if $b_log;
+       return %sensors;
+}
+sub set_temp_unit {
+       my ($sensors,$working) = @_;
+       my $return_unit = '';
+       
+       if ( !$sensors && $working ){
+               $return_unit = $working;
+       }
+       elsif ($sensors){
+               $return_unit = $sensors;
+       }
+       return $return_unit;
+}
+
+sub data_processor {
+       eval $start if $b_log;
+       my (%sensors) = @_;
+       my ($cpu_temp,$cpu2_temp,$cpu3_temp,$cpu4_temp,$index_count_fan_default,
+       $index_count_fan_main,$mobo_temp,$psu_temp) = (0,0,0,0,0,0,0,0);
+       my ($fan_type,$i,$j) = (0,0,0);
+       my $temp_diff = 20; # for C, handled for F after that is determined
+       my (@fan_main,@fan_default);
+       # first we need to handle the case where we have to determine which temp/fan to use for cpu and mobo:
+       # note, for rare cases of weird cool cpus, user can override in their prefs and force the assignment
+       # this is wrong for systems with > 2 tempX readings, but the logic is too complex with 3 variables
+       # so have to accept that it will be wrong in some cases, particularly for motherboard temp readings.
+       if ( $sensors{'temp1'} && $sensors{'temp2'} ){
+               if ( $sensors_cpu_nu ) {
+                       $fan_type = $sensors_cpu_nu;
+               }
+               else {
+                       # first some fringe cases with cooler cpu than mobo: assume which is cpu temp based on fan speed
+                       # but only if other fan speed is 0.
+                       if ( $sensors{'temp1'} >= $sensors{'temp2'} && 
+                            defined $fan_default[1] && defined $fan_default[2] && $fan_default[1] == 0 && $fan_default[2] > 0 ) {
+                               $fan_type = 2;
+                       }
+                       elsif ( $sensors{'temp2'} >= $sensors{'temp1'} && 
+                               defined $fan_default[1] && defined $fan_default[2] && $fan_default[2] == 0 && $fan_default[1] > 0 ) {
+                               $fan_type = 1;
+                       }
+                       # then handle the standard case if these fringe cases are false
+                       elsif ( $sensors{'temp1'} >= $sensors{'temp2'} ) {
+                               $fan_type = 1;
+                       }
+                       else {
+                               $fan_type = 2;
+                       }
+               }
+       }
+       # need a case for no temps at all reported, like with old intels
+       elsif ( !$sensors{'temp2'} && !$sensors{'cpu-temp'} ){
+               if ( !$sensors{'temp1'} && !$sensors{'mobo-temp'} ){
+                       $fan_type = 1;
+               }
+               elsif ( $sensors{'temp1'} && !$sensors{'mobo-temp'} ){
+                       $fan_type = 1;
+               }
+               elsif ( $sensors{'temp1'} && $sensors{'mobo-temp'} ){
+                       $fan_type = 1;
+               }
+       }
+       # convert the diff number for F, it needs to be bigger that is
+       if ( $sensors{'temp-unit'} && $sensors{'temp-unit'} eq "F" ) {
+               $temp_diff = $temp_diff * 1.8
+       }
+       if ( $sensors{'cpu-temp'} ) {
+               # specific hack to handle broken CPUTIN temps with PECI
+               if ( $sensors{'cpu-peci-temp'} && ( $sensors{'cpu-temp'} - $sensors{'cpu-peci-temp'} ) > $temp_diff ){
+                       $cpu_temp = $sensors{'cpu-peci-temp'};
+               }
+               # then get the real cpu temp, best guess is hottest is real, though only within narrowed diff range
+               else {
+                       $cpu_temp = $sensors{'cpu-temp'};
+               }
+       }
+       else {
+               if ($fan_type ){
+                       # there are some weird scenarios
+                       if ( $fan_type == 1 ){
+                               if ( $sensors{'temp1'} && $sensors{'temp2'} && $sensors{'temp2'} > $sensors{'temp1'} ) {
+                                       $cpu_temp = $sensors{'temp2'};
+                               }
+                               else {
+                                       $cpu_temp = $sensors{'temp1'};
+                               }
+                       }
+                       else {
+                               if ( $sensors{'temp1'} && $sensors{'temp2'} && $sensors{'temp1'} > $sensors{'temp2'} ) {
+                                       $cpu_temp = $sensors{'temp1'};
+                               }
+                               else {
+                                       $cpu_temp = $sensors{'temp2'};
+                               }
+                       }
+               }
+               else {
+                       $cpu_temp = $sensors{'temp1'}; # can be null, that is ok
+               }
+               if ( $cpu_temp ) {
+                       # using $sensors{'temp3'} is just not reliable enough, more errors caused than fixed imo
+                       #if ( $sensors{'temp3'} && $sensors{'temp3'} > $cpu_temp ) {
+                       #       $cpu_temp = $sensors{'temp3'};
+                       #}
+                       # there are some absurdly wrong $sensors{'temp1'}: acpitz-virtual-0 $sensors{'temp1'}: +13.8°C
+                       if ( $sensors{'core-0-temp'} && ($sensors{'core-0-temp'} - $cpu_temp) > $temp_diff ) {
+                               $cpu_temp = $sensors{'core-0-temp'};
+                       }
+               }
+       }
+       # if all else fails, use core0/peci temp if present and cpu is null
+       if ( !$cpu_temp ) {
+               if ( $sensors{'core-0-temp'} ) {
+                       $cpu_temp = $sensors{'core-0-temp'};
+               }
+               # note that peci temp is known to be colder than the actual system
+               # sometimes so it is the last fallback we want to use even though in theory
+               # it is more accurate, but fact suggests theory wrong.
+               elsif ( $sensors{'cpu-peci-temp'} ) {
+                       $cpu_temp = $sensors{'cpu-peci-temp'};
+               }
+       }
+       # then the real mobo temp
+       if ( $sensors{'mobo-temp'} ){
+               $mobo_temp = $sensors{'mobo-temp'};
+       }
+       elsif ( $fan_type ){
+               if ( $fan_type == 1 ) {
+                       if ( $sensors{'temp1'} && $sensors{'temp2'} && $sensors{'temp2'} > $sensors{'temp1'} ) {
+                               $mobo_temp = $sensors{'temp1'};
+                       }
+                       else {
+                               $mobo_temp = $sensors{'temp2'};
+                       }
+               }
+               else {
+                       if ( $sensors{'temp1'} && $sensors{'temp2'} && $sensors{'temp1'} > $sensors{'temp2'} ) {
+                               $mobo_temp = $sensors{'temp2'};
+                       }
+                       else {
+                               $mobo_temp = $sensors{'temp1'};
+                       }
+               }
+               ## NOTE: not safe to assume $sensors{'temp3'} is the mobo temp, sad to say
+               #if ( $sensors{'temp1'} && $sensors{'temp2'} && $sensors{'temp3'} && $sensors{'temp3'} < $mobo_temp ) {
+               #       $mobo_temp = $sensors{'temp3'};
+               #}
+       }
+       else {
+               $mobo_temp = $sensors{'temp2'};
+       }
+       @fan_main = @{$sensors{'fan-main'}} if $sensors{'fan-main'};
+       $index_count_fan_main = (@fan_main) ? scalar @fan_main : 0;
+       @fan_default = @{$sensors{'fan-default'}} if $sensors{'fan-default'};
+       $index_count_fan_default = (@fan_default) ? scalar @fan_default : 0;
+       # then set the cpu fan speed
+       if ( ! $fan_main[1] ) {
+               # note, you cannot test for $fan_default[1] or [2] != "" 
+               # because that creates an array item in gawk just by the test itself
+               if ( $fan_type == 1 && defined $fan_default[1] ) {
+                       $fan_main[1] = $fan_default[1];
+                       $fan_default[1] = undef;
+               }
+               elsif ( $fan_type == 2 && defined $fan_default[2] ) {
+                       $fan_main[1] = $fan_default[2];
+                       $fan_default[2] = undef;
+               }
+       }
+       # clear out any duplicates. Primary fan real trumps fan working always if same speed
+       for ($i = 1; $i <= $index_count_fan_main; $i++) {
+               if ( defined $fan_main[$i] && $fan_main[$i] ) {
+                       for ($j = 1; $j <= $index_count_fan_default; $j++) {
+                               if ( defined $fan_default[$j] && $fan_main[$i] == $fan_default[$j] ) {
+                                       $fan_default[$j] = undef;
+                               }
+                       }
+               }
+       }
+       # now see if you can find the fast little mobo fan, > 5000 rpm and put it as mobo
+       # note that gawk is returning true for some test cases when $fan_default[j] < 5000
+       # which has to be a gawk bug, unless there is something really weird with arrays
+       # note: 500 > $fan_default[j] < 1000 is the exact trigger, and if you manually 
+       # assign that value below, the > 5000 test works again, and a print of the value
+       # shows the proper value, so the corruption might be internal in awk. 
+       # Note: gensub is the culprit I think, assigning type string for range 501-1000 but 
+       # type integer for all others, this triggers true for >
+       for ($j = 1; $j <= $index_count_fan_default; $j++) {
+               if ( defined $fan_default[$j] && $fan_default[$j] > 5000 && !$fan_main[2] ) {
+                       $fan_main[2] = $fan_default[$j];
+                       $fan_default[$j] = '';
+                       # then add one if required for output
+                       if ( $index_count_fan_main < 2 ) {
+                               $index_count_fan_main = 2;
+                       }
+               }
+       }
+       # if they are ALL null, print error message. psFan is not used in output currently
+       if ( !$cpu_temp && !$mobo_temp && !$fan_main[1] && !$fan_main[2] && !$fan_main[1] && !@fan_default ) {
+               %sensors = ();
+       }
+       else {
+               my ($ambient_temp,$psu_fan,$psu1_fan,$psu2_fan,$psu_temp,$sodimm_temp,
+               $v_12,$v_5,$v_3_3,$v_dimm_p1,$v_dimm_p2,$v_soc_p1,$v_soc_p2,$v_vbat);
+               $psu_temp = $sensors{'psu-temp'} if $sensors{'psu-temp'};
+               # sodimm fan is fan_main[4]
+               $sodimm_temp = $sensors{'sodimm-temp'} if $sensors{'sodimm-temp'};
+               $cpu2_temp = $sensors{'cpu2-temp'} if $sensors{'cpu2-temp'};
+               $cpu3_temp = $sensors{'cpu3-temp'} if $sensors{'cpu3-temp'};
+               $cpu4_temp = $sensors{'cpu4-temp'} if $sensors{'cpu4-temp'};
+               $ambient_temp = $sensors{'ambient-temp'} if $sensors{'ambient-temp'};
+               $psu_fan = $sensors{'fan-psu'} if $sensors{'fan-psu'};
+               $psu1_fan = $sensors{'fan-psu-1'} if $sensors{'fan-psu-1'};
+               $psu2_fan = $sensors{'fan-psu-2'} if $sensors{'fan-psu-2'};
+               # so far only for ipmi, sensors data is junk for volts
+               if ($extra > 0 && 
+                   ($sensors{'volts-12'} || $sensors{'volts-5'} || $sensors{'volts-3.3'} || $sensors{'volts-vbat'}) ){
+                       $v_12 = $sensors{'volts-12'} if $sensors{'volts-12'};
+                       $v_5 = $sensors{'volts-5'} if $sensors{'volts-5'};
+                       $v_3_3 = $sensors{'volts-3.3'} if  $sensors{'volts-3.3'};
+                       $v_vbat = $sensors{'volts-vbat'} if $sensors{'volts-vbat'};
+                       $v_dimm_p1 = $sensors{'volts-dimm-p1'} if $sensors{'volts-dimm-p1'};
+                       $v_dimm_p2 = $sensors{'volts-dimm-p2'} if $sensors{'volts-dimm-p2'};
+                       $v_soc_p1 = $sensors{'volts-soc-p1'} if $sensors{'volts-soc-p1'};
+                       $v_soc_p2 = $sensors{'volts-soc-p2'} if $sensors{'volts-soc-p2'};
+               }
+               %sensors = (
+               'ambient-temp' => $ambient_temp,
+               'cpu-temp' => $cpu_temp,
+               'cpu2-temp' => $cpu2_temp,
+               'cpu3-temp' => $cpu3_temp,
+               'cpu4-temp' => $cpu4_temp,
+               'mobo-temp' => $mobo_temp,
+               'psu-temp' => $psu_temp,
+               'temp-unit' => $sensors{'temp-unit'},
+               'fan-main' => \@fan_main,
+               'fan-default' => \@fan_default,
+               'fan-psu' => $psu_fan,
+               'fan-psu1' => $psu1_fan,
+               'fan-psu2' => $psu2_fan,
+               );
+               if ($psu_temp){
+                       $sensors{'psu-temp'} = $psu_temp;
+               }
+               if ($sodimm_temp){
+                       $sensors{'sodimm-temp'} = $sodimm_temp;
+               }
+               if ($extra > 0 && ($v_12 || $v_5 || $v_3_3 || $v_vbat) ){
+                       $sensors{'volts-12'} = $v_12;
+                       $sensors{'volts-5'} = $v_5;
+                       $sensors{'volts-3.3'} = $v_3_3;
+                       $sensors{'volts-vbat'} = $v_vbat;
+                       $sensors{'volts-dimm-p1'} = $v_dimm_p1;
+                       $sensors{'volts-dimm-p2'} = $v_dimm_p2;
+                       $sensors{'volts-soc-p1'} = $v_soc_p1;
+                       $sensors{'volts-soc-p2'} = $v_soc_p2;
+               }
+       }
+       eval $end if $b_log;
+       return %sensors;
+}
+sub gpu_data {
+       eval $start if $b_log;
+       return @gpudata if $b_gpudata;
+       my ($cmd,@data,@data2,$path,@screens,$temp);
+       my ($j) = (0);
+       if ($path = main::check_program('nvidia-settings')){
+               # first get the number of screens. This only work if you are in X
+               if ($b_display) {
+                       @data = main::grabber("$path -q screens 2>/dev/null");
+                       foreach (@data){
+                               if ( /(:[0-9]\.[0-9])/ ) {
+                                       push @screens, $1;
+                               }
+                       }
+               }
+               # do a guess, this will work for most users, it's better than nothing for out of X
+               else {
+                       $screens[0] = ':0.0';
+               }
+               # now we'll get the gpu temp for each screen discovered. The print out function
+               # will handle removing screen data for single gpu systems. -t shows only data we want
+               # GPUCurrentClockFreqs: 520,600
+               # GPUCurrentFanSpeed: 50 0-100, not rpm, percent I think
+               # VideoRam: 1048576
+               # CUDACores: 16 
+               # PCIECurrentLinkWidth: 16
+               # PCIECurrentLinkSpeed: 5000
+               # RefreshRate: 60.02 Hz [oer screen]
+               # ViewPortOut=1280x1024+0+0}, DPY-1: nvidia-auto-select @1280x1024 +1280+0 {ViewPortIn=1280x1024,
+               # ViewPortOut=1280x1024+0+0}
+               # ThermalSensorReading: 50
+               # PCIID: 4318,2661 - the pci stuff doesn't appear to work
+               # PCIBus: 2
+               # PCIDevice: 0
+               # Irq: 30
+               foreach my $screen (@screens){
+                       my $screen2 = $screen;
+                       $screen2 =~ s/\.[0-9]$//;
+                       $cmd = '-q GPUCoreTemp -q VideoRam -q GPUCurrentClockFreqs -q PCIECurrentLinkWidth ';
+                       $cmd .= '-q Irq -q PCIBus -q PCIDevice -q GPUCurrentFanSpeed';
+                       $cmd = "$path -c $screen2 $cmd 2>/dev/null";
+                       @data = main::grabber($cmd);
+                       main::log_data('cmd',$cmd) if $b_log;
+                       @data = (@data,@data2);
+                       $j = scalar @gpudata;
+                       $gpudata[$j] = ({});
+                       foreach my $item (@data){
+                               if ($item =~ /^\s*Attribute\s\'([^']+)\'\s.*:\s*([\S]+)\.$/){
+                                       my $attribute = $1;
+                                       my $value = $2;
+                                       $gpudata[$j]{'type'} = 'nvidia';
+                                       $gpudata[$j]{'speed-unit'} = '%';
+                                       $gpudata[$j]{'screen'} = $screen;
+                                       if (!$gpudata[$j]{'temp'} && $attribute eq 'GPUCoreTemp'){
+                                               $gpudata[$j]{'temp'} = $value;
+                                       }
+                                       elsif (!$gpudata[$j]{'ram'} && $attribute eq 'VideoRam'){
+                                               $gpudata[$j]{'ram'} = $value;
+                                       }
+                                       elsif (!$gpudata[$j]{'clock'} && $attribute eq 'GPUCurrentClockFreqs'){
+                                               $gpudata[$j]{'clock'} = $value;
+                                       }
+                                       elsif (!$gpudata[$j]{'bus'} && $attribute eq 'PCIBus'){
+                                               $gpudata[$j]{'bus'} = $value;
+                                       }
+                                       elsif (!$gpudata[$j]{'bus-id'} && $attribute eq 'PCIDevice'){
+                                               $gpudata[$j]{'bus-id'} = $value;
+                                       }
+                                       elsif (!$gpudata[$j]{'fan-speed'} && $attribute eq 'GPUCurrentFanSpeed'){
+                                               $gpudata[$j]{'fan-speed'} = $value;
+                                       }
+                               }
+                       }
+               }
+       }
+       if ($path = main::check_program('aticonfig')){
+               # aticonfig --adapter=0 --od-gettemperature
+               @data = main::grabber("$path --adapter=all --od-gettemperature 2>/dev/null");
+               foreach (@data){
+                       if (/Sensor [^0-9]*([0-9\.]+) /){
+                               $j = scalar @gpudata;
+                               $gpudata[$j] = ({});
+                               my $value = $1;
+                               $gpudata[$j]{'type'} = 'amd';
+                               $gpudata[$j]{'temp'} = $value;
+                       }
+               }
+       }
+       if (@sensors_data){
+               my ($b_found,$holder) = (0,'');
+               foreach (@sensors_data){
+                       next if (/^Adapter:/ || /^\s*$/);
+                       if (/^(amdgpu|intel|nouveau|radeon)-pci-(.*)/){
+                               $b_found = 1;
+                               $holder = $1;
+                               $j = scalar @gpudata;
+                       }
+                       if (/^(?:(?!amdgpu|.*hwmon|intel|nouveau|radeon).)*-(pci|virtual|isa)-(.*)/){
+                               $b_found = 0;
+                               $holder = '';
+                       }
+                       if ($b_found){
+                               if (/^temp.*:([0-9]+).*(C|F)/){
+                                       $gpudata[$j]{'temp'} = $1;
+                                       $gpudata[$j]{'type'} = $holder;
+                                       $gpudata[$j]{'unit'} = $2;
+                               }
+                               if (/^fan.*:([0-9]+).*(RPM)?/){
+                                       $gpudata[$j]{'fan-speed'} = $1;
+                                       # NOTE: we test for nvidia %, everything else stays with nothing
+                                       $gpudata[$j]{'speed-unit'} = '';
+                               }
+                               main::log_data('dump','sensors output: video: @gpudata',\@gpudata);
+                       }
+               }
+       }
+       # we'll probably use this data elsewhere so make it a one time call
+       $b_gpudata = 1;
+       # print Data::Dumper::Dumper \@gpudata;
+       eval $end if $b_log;
+       return @gpudata;
+}
+}
+
+## SlotData
+{
+package SlotData;
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows,$key1,$val1);
+       my $num = 0;
+       my $ref = $alerts{'dmidecode'};
+       if ( $$ref{'action'} eq 'use' && (!$b_arm || $b_slot_tool )){
+               @rows = slot_data();
+       }
+       elsif ($b_arm && !$b_slot_tool){
+               $key1 = 'ARM';
+               $val1 = main::row_defaults('arm-pci','');
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       elsif ( $$ref{'action'} ne 'use'){
+               $key1 = $$ref{'action'};
+               $val1 = $$ref{$key1};
+               $key1 = ucfirst($key1);
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub slot_data {
+       eval $start if $b_log;
+       my (@data,@rows);
+       my $num = 0;
+       foreach (@dmi){
+               $num = 1;
+               my @ref = @$_;
+               if ($ref[0] == 9){
+                       my ($designation,$id,$length,$type,$usage) = ('','','','','');
+                       # skip first two row, we don't need that data
+                       splice @ref, 0, 2 if @ref;
+                       my $j = scalar @rows;
+                       foreach my $item (@ref){
+                               if ($item !~ /^~/){ # skip the indented rows
+                                       my @value = split /:\s+/, $item;
+                                       if ($value[0] eq 'Type'){
+                                               $type = $value[1];
+                                       }
+                                       if ($value[0] eq 'Designation'){
+                                               $designation = $value[1];
+                                       }
+                                       if ($value[0] eq 'Current Usage'){
+                                               $usage = $value[1];
+                                               
+                                       }
+                                       if ($value[0] eq 'ID'){
+                                               $id = $value[1];
+                                       }
+                                       if ($extra > 1 && $value[0] eq 'Length'){
+                                               $length = $value[1];
+                                       }
+                               }
+                       }
+                       if ($type){
+                               $id = 'N/A' if ($id eq '' );
+                               if ($type eq 'Other' && $designation){
+                                       $type = $designation;
+                               }
+                               elsif ($type && $designation) {
+                                       $type = "$type $designation";
+                               }
+                               @data = (
+                               {
+                               main::key($num++,'Slot') => $id,
+                               main::key($num++,'type') => $type,
+                               main::key($num++,'status') => $usage,
+                               },
+                               );
+                               @rows = (@rows,@data);
+                               if ($extra > 1 ){
+                                       $rows[$j]{main::key($num++,'length')} = $length;
+                               }
+                       }
+               }
+       }
+       if (!@rows){
+               my $key = 'Message';
+               @data = ({
+               main::key($num++,$key) => main::row_defaults('pci-slot-data',''),
+               },);
+               @rows = (@rows,@data);
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+}
+
+## UnmountedData
+{
+package UnmountedData;
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows,$key1,$val1);
+       my $num = 0;
+       if ($bsd_type){
+               $key1 = 'Message';
+               $val1 = main::row_defaults('unmounted-data-bsd');
+       }
+       else {
+               if (my $file = main::system_files('partitions')){
+                       @data = unmounted_data($file);
+                       if (!@data){
+                               $key1 = 'Message';
+                               $val1 = main::row_defaults('unmounted-data');
+                       }
+                       else {
+                               @rows = create_output(@data);
+                       }
+               }
+               else {
+                       $key1 = 'Message';
+                       $val1 = main::row_defaults('unmounted-file');
+               }
+       }
+       if (!@rows && $key1){
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my (@unmounted) = @_;
+       my (@data,@rows,$fs);
+       my $num = 0;
+       @unmounted = sort { $a->{'dev-base'} cmp $b->{'dev-base'} } @unmounted;
+       foreach my $ref (@unmounted){
+               my %row = %$ref;
+               $num = 1;
+               my @data2 = main::get_size($row{'size'}) if (defined $row{'size'});
+               my $size = (@data2) ? $data2[0] . ' ' . $data2[1]: 'N/A';
+               if ($row{'fs'}){
+                       $fs = lc($row{'fs'});
+               }
+               else {
+                       if (main::check_program('file')){
+                               $fs = ($b_root) ? 'N/A' : main::row_defaults('root-required');
+                       }
+                       else {
+                               $fs = 'requires file';
+                       }
+               }
+               @data = ({
+               main::key($num++,'ID') => , "/dev/$row{'dev-base'}",
+               main::key($num++,'size') => , $size,
+               main::key($num++,'fs') => , $fs,
+               main::key($num++,'label') => , $row{'label'},
+               main::key($num++,'uuid') => , $row{'uuid'},
+               });
+               @rows = (@rows,@data);
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub unmounted_data {
+       eval $start if $b_log;
+       my ($file) = @_;
+       my ($fs,$label,$size,$uuid,@data,%part,@unmounted);
+       my @mounted = ('scd[0-9]+','sr[0-9]+','cdrom[0-9]*','cdrw[0-9]*',
+       'dvd[0-9]*','dvdrw[0-9]*','fd[0-9]','ram[0-9]*');
+       my @mounts = main::reader($file,'strip');
+       my $num = 0;
+       PartitionData::set_lsblk() if !$bsd_type && !$b_lsblk;
+       # set labels, uuid, gpart
+       PartitionData::partition_data() if !$b_partitions;
+       PartitionData::set_label_uuid() if !$b_label_uuid;
+       RaidData::raid_data() if !$b_raid;
+       @mounted = get_mounted(@mounted);
+       foreach (@mounts){
+               my @working = split /\s+/, $_;
+               ($fs,$label,$uuid,$size) = ('','','','');
+               # note that size 1 means it is a logical extended partition container
+               # lvm might have dm-1 type syntax
+               # need to exclude loop type file systems, squashfs for example
+               # NOTE: nvme needs special treatment because the main device is: nvme0n1
+               # note: $working[2] != 1 is wrong, it's not related
+               if ( $working[-1] !~ /^(nvme[0-9]+n|mmcblk|mtdblk|mtdblock)[0-9]+$/ && 
+                    $working[-1] =~ /[a-z][0-9]+$|dm-[0-9]+$/ && $working[-1] !~ /loop/ && 
+                    !(grep {$working[-1] =~ /$_/} @mounted)){
+                       %part = PartitionData::check_lsblk($working[-1],0) if (@lsblk && $working[-1]);
+                       if (%part){
+                               $fs = $part{'fs'};
+                               $label = $part{'label'};
+                               $uuid = $part{'uuid'};
+                               $size = $part{'size'} if $part{'size'} && !$working[2];
+                       }
+                       $size ||= $working[2];
+                  $fs = unmounted_filesystem($working[-1]) if !$fs;
+                  $label = PartitionData::get_label("/dev/$working[-1]") if !$label;
+                       $uuid = PartitionData::get_uuid("/dev/$working[-1]") if !$uuid;
+                       @data = ({
+                       'dev-base' => $working[-1],
+                       'fs' => $fs,
+                       'label' => $label,
+                       'size' => $size,
+                       'uuid' => $uuid,
+                       });
+                       @unmounted = (@unmounted,@data);
+               }
+       }
+       # print Data::Dumper::Dumper @unmounted;
+       main::log_data('dump','@unmounted',\@unmounted) if $b_log;
+       eval $end if $b_log;
+       return @unmounted;
+}
+sub get_mounted {
+       eval $start if $b_log;
+       my (@mounted) = @_;
+       foreach my $ref (@partitions){
+               my %row = %$ref;
+               push @mounted, $row{'dev-base'} if $row{'dev-base'};
+       }
+       foreach my $ref (@raid){
+               my %row = %$ref;
+               my $ref2 = $row{'arrays'};
+               # we want to not show md0 etc in unmounted report
+               push @mounted, $row{'id'} if $row{'id'}; 
+               my @arrays = (ref $ref2 eq 'ARRAY' ) ? @$ref2 : ();
+               @arrays = grep {defined $_} @arrays;
+               foreach my $array (@arrays){
+                       my %row2 = %$array;
+                       my $ref3 = $row2{'components'};
+                       my @components = (ref $ref3 eq 'ARRAY') ? @$ref3 : ();
+                       foreach my $component (@components){
+                               my @temp = split /~/, $component;
+                               push @mounted, $temp[0];
+                       }
+               }
+       }
+       eval $end if $b_log;
+       return @mounted;
+}
+sub unmounted_filesystem {
+       eval $start if $b_log;
+       my ($item) = @_;
+       my ($data,%part);
+       my ($file,$fs,$path) = ('','','');
+       if ($path = main::check_program('file')) {
+               $file = $path;
+       }
+       # order matters in this test!
+       my @filesystems = ('ext2','ext3','ext4','ext5','ext','ntfs',
+       'fat32','fat16','FAT\s\(.*\)','vfat','fatx','tfat','swap','btrfs',
+       'ffs','hammer','hfs\+','hfs\splus','hfs\sextended\sversion\s[1-9]','hfsj',
+       'hfs','jfs','nss','reiserfs','reiser4','ufs2','ufs','xfs','zfs');
+       if ($file){
+               # this will fail if regular user and no sudo present, but that's fine, it will just return null
+               # note the hack that simply slices out the first line if > 1 items found in string
+               # also, if grub/lilo is on partition boot sector, no file system data is available
+               $data = (main::grabber("$sudo$file -s /dev/$item 2>/dev/null"))[0];
+               if ($data){
+                       foreach (@filesystems){
+                               if ($data =~ /($_)[\s,]/i){
+                                       $fs = $1;
+                                       $fs = main::trimmer($fs);
+                                       last;
+                               }
+                       }
+               }
+       }
+       main::log_data('data',"fs: $fs") if $b_log;
+       eval $end if $b_log;
+       return $fs;
+}
+}
+
+## UsbData
+{
+package UsbData;
+
+sub get {
+       eval $start if $b_log;
+       my (@data,@rows,$key1,$val1);
+       my $num = 0;
+       my $ref = $alerts{'lsusb'};
+       my $ref2 = $alerts{'usbdevs'};
+       if ( $$ref{'action'} ne 'use' && $$ref2{'action'} ne 'use'){
+               if ($os eq 'linux' ){
+                       $key1 = $$ref{'action'};
+                       $val1 = $$ref{$key1};
+               }
+               else {
+                       $key1 = $$ref2{'action'};
+                       $val1 = $$ref2{$key1};
+               }
+               $key1 = ucfirst($key1);
+               @rows = ({main::key($num++,$key1) => $val1,});
+       }
+       else {
+               @rows = usb_data();
+               if (!@rows){
+                       my $key = 'Message';
+                       @data = ({
+                       main::key($num++,$key) => main::row_defaults('usb-data',''),
+                       },);
+                       @rows = (@rows,@data);
+               }
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub usb_data {
+       eval $start if $b_log;
+       return if ! @usb;
+       my (@data,@row,@rows,$bus_id,$chip_id,$speed,$protocol,$class,$vendor,$product);
+       my $num = 0;
+       my $j = 0;
+       # note: the data has been presorted in set_lsusb_data by:
+       # bus id then device id, so we don't need to worry about the order
+       foreach my $ref (@usb){
+               my @id = @$ref;
+               $j = scalar @rows;
+               $num = 1;
+               $bus_id = "$id[0]:$id[1]";
+               $chip_id = $id[2];
+               my $b_hub = 0;
+               # it's a hub
+               if ($id[1] == 1){
+                       foreach my $line (@id){
+                               #print "$line\n";
+                               @row = split /:/, $line;
+                               next if ! defined $row[0];
+                               if ($row[0] eq 'bcdUSB' && defined $row[1]){
+                                       $speed  = ($row[1] =~ /^[0-9,\.]+$/) ? sprintf("%1.1f",$row[1]) : $row[1];
+                               }
+                               elsif ($row[0] eq '~bInterfaceProtocol' && $row[2] ){
+                                       $protocol = $row[2];
+                               }
+                       }
+                       $protocol ||= 'N/A';
+                       $speed ||= 'N/A';
+                       #print "pt0:$protocol\n";
+                       @data = ({
+                       main::key($num++,'Hub') => $bus_id,
+                       main::key($num++,'usb') => $speed,
+                       main::key($num++,'type') => $protocol,
+                       },);
+                       @rows = (@rows,@data);
+                       if ($extra > 1){
+                               $rows[$j]{main::key($num++,'chip ID')} = $chip_id;
+                       }
+               }
+               # it's a device
+               else {
+                       ($class,$product,$protocol,$vendor,$speed) = ('','','','','');
+                       foreach my $line (@id){
+                               @row = split /:/, $line;
+                               next if ! defined $row[0];
+                               if ($row[0] eq 'bcdUSB' && defined $row[1]){
+                                       $speed  = sprintf("%.1f",$row[1]);
+                               }
+                               elsif ($row[0] eq 'bDeviceClass' && defined $row[1] && $row[1] == 9){
+                                       $b_hub = 1;
+                               }
+                               elsif ($row[0] eq 'idVendor' && $row[2]){
+                                       $vendor  = main::cleaner($row[2]);
+                               }
+                               elsif ($row[0] eq 'idProduct' && $row[2]){
+                                       $product = main::cleaner($row[2]);
+                               }
+                               # we want hubs to cascade to last item
+                               elsif ($row[0] eq '~bInterfaceClass' && $row[2] && defined $row[1] && $row[1] != 9){
+                                       $class = main::cleaner($row[2]);
+                               }
+                               elsif ($row[0] eq '~bInterfaceProtocol' && defined $row[2]){
+                                       $protocol = $row[2];
+                                       $protocol =~ s/none//i if $protocol;
+                                       last if $class;
+                               }
+                       }
+                       if ( $b_hub ){
+                               if ($vendor && $product){
+                                       $protocol = "$vendor $product";
+                               }
+                               elsif (!$product && $protocol && $vendor){
+                                       $protocol = "$vendor $protocol";
+                               }
+                               $speed ||= 'N/A';
+                               $protocol ||= 'N/A';
+                               #print "pt2:$protocol\n";
+                               @data = ({
+                               main::key($num++,'Hub') => $bus_id,
+                               main::key($num++,'usb') => $speed,
+                               main::key($num++,'type') => $protocol,
+                               },);
+                               @rows = (@rows,@data);
+                       }
+                       else {
+                               if ($vendor && $product){
+                                       if ($product !~ /$vendor/){
+                                               $product = "$vendor $product";
+                                       }
+                               }
+                               elsif (!$product && !$vendor && $protocol){
+                                       $product = $protocol;
+                               }
+                               elsif (!$product){
+                                       $product = $vendor;
+                               }
+                               # bInterfaceProtocol:0 but $row[2] undefined
+                               #print "pt3:$class:$product\n";
+                               # for we want Mass Storage Device instead of Bulk-Only
+                               # we want to filter out certain protocol values that are less 
+                               # informative than the class type.
+                               if ($protocol && $class && $class ne $protocol && protocol_filter($protocol) ){
+                                       $class = $protocol;
+                               }
+                               $class ||= 'N/A';
+                               #print "pt3:$class:$product\n";
+                               $product ||= 'N/A';
+                               $speed ||= 'N/A';
+                               $rows[$j]{main::key($num++,'Device')} = $product;
+                               $rows[$j]{main::key($num++,'bus ID')} = $bus_id;
+                               if ($extra > 0){
+                                       $rows[$j]{main::key($num++,'usb')} = $speed;
+                               }
+                               $rows[$j]{main::key($num++,'type')} = $class;
+                       }
+                       if ($extra > 1){
+                               $rows[$j]{main::key($num++,'chip ID')} = $chip_id;
+                       }
+               }
+       }
+       #print Data::Dumper::Dumper \@rows;
+       eval $end if $b_log;
+       return @rows;
+}
+sub protocol_filter {
+       eval $start if $b_log;
+       my ($string) = @_;
+       $string =~ s/Bulk-Only|streaming|Bidirectional|None//i if $string;
+       eval $end if $b_log;
+       return $string;
+}
+}
+
+## add metric / imperial (us) switch
+## WeatherData
+{
+package WeatherData;
+
+sub get {
+       eval $start if $b_log;
+       my (@rows,$key1,$val1);
+       my $num = 0;
+       @rows = create_output();
+       eval $end if $b_log;
+       return @rows;
+}
+sub create_output {
+       eval $start if $b_log;
+       my $num = 0;
+       my (@data,@location,@rows,%weather,);
+       my ($conditions) = ('NA');
+       if ($show{'weather-location'}){
+               my $location_string;
+               $location_string = $show{'weather-location'};
+               $location_string =~ s/\+/ /g;
+               if ( $location_string =~ /,/){
+                       my @temp = split /,/, $location_string;
+                       my $sep = '';
+                       my $string = '';
+                       foreach (@temp){
+                               $_ = ucfirst($_);
+                               $string .= $sep . $_;
+                               $sep = ', ';
+                       }
+                       $location_string = $string;
+               }
+               $location_string = main::apply_filter($location_string);
+               @location = ($show{'weather-location'},$location_string,'');
+       }
+       else {
+               @location = get_location();
+               if (!$location[0]) {
+                       return @rows = ({
+                       main::key($num++,'Message') => main::row_defaults('weather-null','current location'),
+                       });
+               }
+       }
+       %weather = get_weather(@location);
+       if (!$weather{'weather'}) {
+               return @rows = ({
+               main::key($num++,'Message') => main::row_defaults('weather-null','weather data'),
+               });
+       }
+       $conditions = "$weather{'weather'}";
+       my $temp = unit_output($weather{'temp'},$weather{'temp-c'},'C',$weather{'temp-f'},'F');
+       @data = ({
+       main::key($num++,'Temperature') => $temp,
+       main::key($num++,'Conditions') => $conditions,
+       },);
+       @rows = (@rows,@data);
+       if ($extra > 0){
+               my $pressure = unit_output($weather{'pressure'},$weather{'pressure-mb'},'mb',$weather{'pressure-in'},'in');
+               my $wind = wind_output($weather{'wind'},$weather{'wind-direction'},$weather{'wind-mph'},$weather{'wind-ms'},
+               $weather{'wind-gust-mph'},$weather{'wind-gust-ms'});
+               $rows[0]{main::key($num++,'Wind')} = $wind;
+               $rows[0]{main::key($num++,'Humidity')} = $weather{'humidity'};
+               $rows[0]{main::key($num++,'Pressure')} = $pressure;
+       }
+       if ($extra > 1){
+               if ($weather{'heat-index'}){
+                       my $heat = unit_output($weather{'heat-index'},$weather{'heat-index-c'},'C',$weather{'heat-index-f'},'F');
+                       $rows[0]{main::key($num++,'Heat Index')} = $heat;
+               }
+               if ($weather{'windchill'}){
+                       my $chill = unit_output($weather{'windchill'},$weather{'windchill-c'},'C',$weather{'windchill-f'},'F');
+                       $rows[0]{main::key($num++,'Wind Chill')} = $chill ;
+               }
+               if ($weather{'dewpoint'}){
+                       my $dew = unit_output($weather{'dewpoint'},$weather{'dewpoint-c'},'C',$weather{'dewpoint-f'},'F');
+                       $rows[0]{main::key($num++,'Dew Point')} = $dew;
+               }
+       }
+       if ($extra > 2){
+               if (!$show{'filter'}){
+                       $rows[0]{main::key($num++,'Location')} = $location[1];
+                       $rows[0]{main::key($num++,'altitude')} = elevation_output($weather{'elevation-m'},$weather{'elevation-ft'});
+               }
+       }
+       $rows[0]{main::key($num++,'Current Time')} = $weather{'date-time'};
+       if ($extra > 2){
+               $rows[0]{main::key($num++,'Observation Time')} = $weather{'observation-time-local'};
+       }
+       eval $end if $b_log;
+       return @rows;
+}
+sub elevation_output {
+       eval $start if $b_log;
+       my ($meters,$feet) = @_;
+       my ($result,$i_unit,$m_unit) = ('','ft','m');
+       $feet = sprintf("%.0f", 3.28 * $meters) if defined $meters && !$feet;
+       $meters = sprintf("%.1f", $feet / 3.28 ) if defined $feet && !$meters;
+       $meters = sprintf("%.0f", $meters) if $meters;
+       if ( defined $meters  && $weather_unit eq 'mi' ){
+               $result = "$meters $m_unit ($feet $i_unit)";
+       }
+       elsif (defined $meters && $weather_unit eq 'im' ){
+               $result = "$feet $i_unit ($meters $m_unit)";
+       }
+       elsif (defined $meters && $weather_unit eq 'm' ){
+               $result = "$meters $m_unit";
+       }
+       elsif (defined $feet && $weather_unit eq 'i' ){
+               $result = "$feet $i_unit";
+       }
+       else {
+               $result = 'N/A';
+       }
+       eval $end if $b_log;
+       return $result;
+}
+sub unit_output {
+       eval $start if $b_log;
+       my ($primary,$metric,$m_unit,$imperial,$i_unit) = @_;
+       my $result = '';
+       if ($metric && $imperial && $weather_unit eq 'mi' ){
+               $result = "$metric $m_unit ($imperial $i_unit)";
+       }
+       elsif ($metric && $imperial && $weather_unit eq 'im' ){
+               $result = "$imperial $i_unit ($metric $m_unit)";
+       }
+       elsif ($metric && $weather_unit eq 'm' ){
+               $result = "$metric $m_unit";
+       }
+       elsif ($imperial && $weather_unit eq 'i' ){
+               $result = "$imperial $i_unit";
+       }
+       elsif ($primary){
+               $result = $primary;
+       }
+       else {
+               $result = 'N/A';
+       }
+       eval $end if $b_log;
+       return $result;
+}
+sub wind_output {
+       eval $start if $b_log;
+       my ($primary,$direction,$mph,$ms,$gust_mph,$gust_ms) = @_;
+       my ($result,$gust_kmh,$kmh,$i_unit,$m_unit,$km_unit) = ('','','','mph','m/s','km/h');
+       # get rid of possible gust values if they are the same as wind values
+       $gust_mph = undef if $gust_mph && $mph && $mph eq $gust_mph;
+       $gust_ms = undef if $gust_ms && $ms && $ms eq $gust_ms;
+       # calculate and round, order matters so that rounding only happens after math done
+       $ms = 0.44704 * $mph if $mph && !$ms;
+       $mph = $ms * 2.23694 if $ms && !$mph;
+       $kmh = sprintf("%.0f",  18 * $ms / 5) if $ms;
+       $ms = sprintf("%.1f", $ms ) if  $ms; # very low mph speeds yield 0, which is wrong
+       $mph = sprintf("%.0f", $mph) if $mph;
+       $gust_ms = 0.44704 * $gust_mph if $gust_mph && !$gust_ms;
+       $gust_kmh = 18 * $gust_ms / 5 if $gust_ms;
+       $gust_mph = $gust_ms * 2.23694 if $gust_ms && !$gust_mph;
+       $gust_mph = sprintf("%.0f", $gust_mph) if $gust_mph;
+       $gust_kmh = sprintf("%.0f", $gust_kmh) if $gust_kmh;
+       $gust_ms = sprintf("%.0f", $gust_ms ) if  $gust_ms;
+       if (!$mph && $primary){
+               $result = $primary;
+       }
+       elsif ($mph && $direction ){
+               if ( $weather_unit eq 'mi' ){
+                       $result = "from $direction at $ms $m_unit ($kmh $km_unit, $mph $i_unit)";
+               }
+               elsif ( $weather_unit eq 'im' ){
+                       $result = "from $direction at $mph $i_unit ($ms $m_unit, $kmh $km_unit)";
+               }
+               elsif ( $weather_unit eq 'm' ){
+                       $result = "from $direction at $ms $m_unit ($kmh $km_unit)";
+               }
+               elsif ( $weather_unit eq 'i' ){
+                       $result = "from $direction at $mph $i_unit";
+               }
+               if ($gust_mph){
+                       if ( $weather_unit eq 'mi' ){
+                               $result .= ". Gusting to $ms $m_unit ($kmh $km_unit, $mph $i_unit)";
+                       }
+                       elsif ( $weather_unit eq 'im' ){
+                               $result .= ". Gusting to $mph $i_unit ($ms $m_unit, $kmh $km_unit)";
+                       }
+                       elsif ( $weather_unit eq 'm' ){
+                               $result .= ". Gusting to $ms $m_unit ($kmh $km_unit)";
+                       }
+                       elsif ( $weather_unit eq 'i' ){
+                               $result .= ". Gusting to $mph $i_unit";
+                       }
+               }
+       }
+       elsif ($primary){
+               $result = $primary;
+       }
+       else {
+               $result = 'N/A';
+       }
+       eval $end if $b_log;
+       return $result;
+}
+sub get_weather {
+       eval $start if $b_log;
+       my (@location) = @_;
+       my $now = POSIX::strftime "%Y%m%d%H%M", localtime;
+       my ($date_time,$freshness,$tz,@weather_data,%weather);
+       my $loc_name = lc($location[0]);
+       $loc_name =~ s/-\/|\s|,/-/g;
+       $loc_name =~ s/--/-/g;
+       my $file_cached = "$user_data_dir/weather-$loc_name.txt";
+       if (-f $file_cached){
+               @weather_data = main::reader($file_cached);
+               $freshness = (split /\^\^/, $weather_data[0])[1];
+               #print "$now:$freshness\n";
+       }
+       if (!$freshness || $freshness < ($now - 90) ) {
+               @weather_data = (); # reset so we don't write the previous data to file!!
+               my $url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=$location[0]";
+               my $temp;
+#              {
+#                      #my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml";
+#                      # my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/feed-oslo-1.xml";
+#                      local $/;
+#                      my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml";
+#                      open my $fh, '<', $file or die "can't open $file: $!";
+#                      $temp = <$fh>;
+#              }
+               $temp = main::download_file('stdout',$url);
+               $temp =~ s/\r|\n\n/\n/g;
+               my @weather_temp = split /\n/, $temp;
+               foreach (@weather_temp){
+                       chomp $_;
+                       $_ =~ s/<\/[^>]+>//;
+                       $_ =~ s/.*icon.*|\r//g;
+                       $_ =~ s/\s\s/ /g;
+                       $_ =~ s/^\s+|\s+$//g;
+                       $_ =~ s/>/^^/;
+                       $_ =~ s/^<|NA$//g;
+                       $_ =~ s/^(current|credit|terms|image|title|link|.*_url).*//;
+                       push @weather_data, $_ if $_ !~ /^\s*$/;
+               }
+               unshift (@weather_data,("timestamp^^$now"));
+               main::writer($file_cached,\@weather_data);
+               #print "$file_cached: download/cleaned\n";
+       }
+       #print join "\n", @weather_data, "\n";
+       # NOTE: because temps can be 0, we can't do if value tests
+       foreach (@weather_data){
+               my @working = split /\s*\^\^\s*/,$_;
+               next if ! defined $working[1] || $working[1] eq '';
+               if ( $working[0] eq 'dewpoint_string' ){
+                       $weather{'dewpoint'} = $working[1];
+                       $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/;
+                       $weather{'dewpoint-c'} = $2;;
+                       $weather{'dewpoint-f'} = $1;;
+               }
+               elsif ( $working[0] eq 'dewpoint_c' ){
+                       $weather{'dewpoint-c'} = $working[1];
+               }
+               elsif ( $working[0] eq 'dewpoint_f' ){
+                       $weather{'dewpoint-f'} = $working[1];
+               }
+               # there are two elevations, we want the first one
+               elsif (!$weather{'elevation-m'} && $working[0] eq 'elevation'){
+                       # note: bug in source data uses ft for meters, not 100% of time, but usually
+                       $weather{'elevation-m'} = $working[1];
+                       $weather{'elevation-m'} =~ s/\s*(ft|m).*$//;
+               }
+               elsif ( $working[0] eq 'heat_index_string' ){
+                       $weather{'heat-index'} = $working[1];
+                       $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/;
+                       $weather{'heat-index-c'} = $2;;
+                       $weather{'heat-index-f'} = $1;
+               }
+               elsif ( $working[0] eq 'heat_index_c' ){
+                       $weather{'heat-index-c'} = $working[1];
+               }
+               elsif ( $working[0] eq 'heat_index_f' ){
+                       $weather{'heat-index-f'} = $working[1];
+               }
+               elsif ( $working[0] eq 'relative_humidity' ){
+                       $weather{'humidity'} = $working[1];
+               }
+               elsif ( $working[0] eq 'local_time' ){
+                       $weather{'local-time'} = $working[1];
+               }
+               elsif ( $working[0] eq 'local_epoch' ){
+                       $weather{'local-epoch'} = $working[1];
+               }
+               elsif ( $working[0] eq 'observation_time_rfc822' ){
+                       $weather{'observation-time-gmt'} = $working[1];
+               }
+               elsif ( $working[0] eq 'observation_epoch' ){
+                       $weather{'observation-epoch'} = $working[1];
+               }
+               elsif ( $working[0] eq 'observation_time' ){
+                       $weather{'observation-time-local'} = $working[1];
+                       $weather{'observation-time-local'} =~ s/Last Updated on //;
+               }
+               elsif ( $working[0] eq 'pressure_string' ){
+                       $weather{'pressure'} = $working[1];
+               }
+               elsif ( $working[0] eq 'pressure_mb' ){
+                       $weather{'pressure-mb'} = $working[1];
+               }
+               elsif ( $working[0] eq 'pressure_in' ){
+                       $weather{'pressure-in'} = $working[1];
+               }
+               elsif ( $working[0] eq 'temperature_string' ){
+                       $weather{'temp'} = $working[1];
+                       $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/;
+                       $weather{'temp-c'} = $2;;
+                       $weather{'temp-f'} = $1;
+#                      $weather{'temp'} =~ s/\sF/\xB0 F/; # B0
+#                      $weather{'temp'} =~ s/\sF/\x{2109}/;
+#                      $weather{'temp'} =~ s/\sC/\x{2103}/;
+               }
+               elsif ( $working[0] eq 'temp_f' ){
+                       $weather{'temp-f'} = $working[1];
+               }
+               elsif ( $working[0] eq 'temp_c' ){
+                       $weather{'temp-c'} = $working[1];
+               }
+               elsif ( $working[0] eq 'visibility' ){
+                       $weather{'visibility'} = $working[1];
+               }
+               elsif ( $working[0] eq 'visibility_km' ){
+                       $weather{'visibility-km'} = $working[1];
+               }
+               elsif ( $working[0] eq 'visibility_mi' ){
+                       $weather{'visibility-mi'} = $working[1];
+               }
+               elsif ( $working[0] eq 'weather' ){
+                       $weather{'weather'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_degrees' ){
+                       $weather{'wind-degrees'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_dir' ){
+                       $weather{'wind-direction'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_mph' ){
+                       $weather{'wind-mph'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_gust_mph' ){
+                       $weather{'wind-gust-mph'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_gust_ms' ){
+                       $weather{'wind-gust-ms'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_ms' ){
+                       $weather{'wind-ms'} = $working[1];
+               }
+               elsif ( $working[0] eq 'wind_string' ){
+                       $weather{'wind'} = $working[1];
+               }
+               elsif ( $working[0] eq 'windchill_string' ){
+                       $weather{'windchill'} = $working[1];
+                       $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/;
+                       $weather{'windchill-c'} = $2;
+                       $weather{'windchill-f'} = $1;
+               }
+               elsif ( $working[0] eq 'windchill_c' ){
+                       $weather{'windchill-c'} = $working[1];
+               }
+               elsif ( $working[0] eq 'windchill_f' ){
+                       $weather{'windchill_f'} = $working[1];
+               }
+       }
+       if ($show{'weather-location'}){
+               $weather{'observation-time-local'} =~ /^(.*)\s([\S]+)$/;
+               $tz = $2;
+               # very clever trick, just make the system think it's in the 
+               # remote timezone for this local block only
+               local $ENV{'TZ'} = $tz;
+               $date_time = POSIX::strftime "%c", localtime;
+               $weather{'date-time'} = $date_time;
+       }
+       else {
+               $date_time = POSIX::strftime "%c", localtime;
+               $tz = ( $location[2] ) ? " ($location[2])" : ''; 
+               $weather{'date-time'} = $date_time . $tz;
+       }
+       # we get the wrong time using epoch for remote -W location
+       if ( !$show{'weather-location'} && $weather{'observation-epoch'}){
+               $weather{'observation-time-local'} = POSIX::strftime "%c", localtime($weather{'observation-epoch'});
+       }
+       return %weather;
+       eval $end if $b_log;
+}
+sub get_location {
+       eval $start if $b_log;
+       my ($city,$country,$freshness,%loc,$loc_arg,$loc_string,@loc_data,$state);
+       my $now = POSIX::strftime "%Y%m%d%H%M", localtime;
+       my $file_cached = "$user_data_dir/location-main.txt";
+       if (-f $file_cached){
+               @loc_data = main::reader($file_cached);
+               $freshness = (split /\^\^/, $loc_data[0])[1];
+       }
+       if (!$freshness || $freshness < $now - 90) {
+               my $temp;
+               my $url = "http://geoip.ubuntu.com/lookup";
+#              {
+#                      local $/;
+#                      my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/location-1.xml";
+#                      open my $fh, '<', $file or die "can't open $file: $!";
+#                      $temp = <$fh>;
+#              }
+               $temp  = main::download_file('stdout',$url);
+               @loc_data = split /\n/, $temp;
+               @loc_data = map {
+               s/<\?.*<Response>//;
+               s/<\/[^>]+>/\n/g;
+               s/>/^^/g;
+               s/<//g;
+               $_;
+               } @loc_data;
+               @loc_data = split /\n/, $loc_data[0];
+               unshift (@loc_data,("timestamp^^$now"));
+               main::writer($file_cached,\@loc_data);
+               #print "$file_cached: download/cleaned\n";
+       }
+       foreach (@loc_data){
+               my @working = split /\s*\^\^\s*/,$_;
+               #print "$working[0]:$working[1]\n";
+               if ($working[0] eq 'CountryCode3' ) {
+                       $loc{'country3'} = $working[1];
+               }
+               elsif ($working[0] eq 'CountryCode' ) {
+                       $loc{'country'} = $working[1];
+               }
+               elsif ($working[0] eq 'CountryName' ) {
+                       $loc{'country2'} = $working[1];
+               }
+               elsif ($working[0] eq 'RegionCode' ) {
+                       $loc{'region-id'} = $working[1];
+               }
+               elsif ($working[0] eq 'RegionName' ) {
+                       $loc{'region'} = $working[1];
+               }
+               elsif ($working[0] eq 'City' ) {
+                       $loc{'city'} = $working[1];
+               }
+               elsif ($working[0] eq 'ZipPostalCode' ) {
+                       $loc{'zip'} = $working[1];
+               }
+               elsif ($working[0] eq 'Latitude' ) {
+                       $loc{'lat'} = $working[1];
+               }
+               elsif ($working[0] eq 'Longitude' ) {
+                       $loc{'long'} = $working[1];
+               }
+               elsif ($working[0] eq 'TimeZone' ) {
+                       $loc{'tz'} = $working[1];
+               }
+       }
+       #print Data::Dumper::Dumper \%loc;
+       # assign location, cascade from most accurate
+       # latitude,longitude first
+       if ($loc{'lat'} && $loc{'long'}){
+               $loc_arg = "$loc{'lat'},$loc{'long'}";
+       }
+       # city,state next
+       elsif ($loc{'city'} && $loc{'region-id'}){
+               $loc_arg = "$loc{'city'},$loc{'region-id'}";
+       }
+       # postal code last, that can be a very large region
+       elsif ($loc{'zip'}){
+               $loc_arg = $loc{'zip'};
+       }
+       $country = ($loc{'country3'}) ? $loc{'country3'} : $loc{'country'};
+       $city = ($loc{'city'}) ? $loc{'city'} : 'City N/A';
+       $state = ($loc{'region-id'}) ? $loc{'region-id'} : 'Region N/A';
+       $loc_string = main::apply_filter("$city, $state, $country");
+       my @location = ($loc_arg,$loc_string,$loc{'tz'});
+       #print ($loc_arg,"\n", join "\n", @loc_data, "\n",scalar @loc_data, "\n");
+       eval $end if $b_log;
+       return @location;
+}
+}
+
+#### -------------------------------------------------------------------
+#### UTILITIES FOR DATA LINES
+#### -------------------------------------------------------------------
+
+sub get_compiler_version {
+       eval $start if $b_log;
+       my (@compiler);
+       if (my $file = system_files('version') ) {
+               @compiler = get_compiler_version_linux($file);
+       }
+       else {
+               @compiler = get_compiler_version_bsd();
+       }
+       eval $end if $b_log;
+       return @compiler;
+}
+
+sub get_compiler_version_bsd {
+       eval $start if $b_log;
+       my (@compiler,@working);
+       if ($alerts{'sysctl'}{'action'} eq 'use'){
+               # for dragonfly, we will use free mem, not used because free is 0
+               my @working;
+               foreach (@sysctl){
+                       # freebsd seems to use bytes here
+                       # Not every line will have a : separator though the processor should make 
+                       # most have it. This appears to be 10.x late feature add, I don't see it
+                       # on earlier BSDs
+                       if (/^kern.compiler_version/){
+                               @working = split /:\s*/, $_;
+                               $working[1] =~ /.*(gcc|clang)\sversion\s([\S]+)\s.*/;
+                               @compiler = ($1,$2);
+                               last;
+                       }
+               }
+       }
+       else {
+               @compiler = ('N/A','');
+       }
+       log_data('dump','@compiler',\@compiler) if $b_log;
+       eval $end if $b_log;
+       return @compiler;
+}
+
+sub get_compiler_version_linux {
+       eval $start if $b_log;
+       my ($file) = @_;
+       my (@compiler,$type);
+       my @data = reader($file);
+       my $result = $data[0] if @data;
+       if ($result){
+               $result =~ /(gcc|clang).*version\s([\S]+)/;
+               # $result = $result =~ /\*(gcc|clang)\*eval\*/;
+               if ($1){
+                       $type = $2;
+                       $type ||= 'N/A'; # we don't really know what linux clang looks like!
+                       @compiler = ($1,$type);
+               }
+       }
+       log_data('dump','@compiler',\@compiler) if $b_log;
+       
+       eval $end if $b_log;
+       return @compiler;
+}
+
+## Get DesktopEnvironment
+## returns array:
+# 0 - desktop name
+# 1 - version
+# 2 - toolkit
+# 3 - toolkit version
+# 4 - info extra desktop data
+# 5 - wm
+# 6 - wm version
+{
+package DesktopEnvironment;
+my ($b_xprop,$desktop_session,$kde_session_version,$xdg_desktop,@desktop,@data,@xprop);
+sub get {
+       # NOTE $XDG_CURRENT_DESKTOP envvar is not reliable, but it shows certain desktops better.
+       # most desktops are not using it as of 2014-01-13 (KDE, UNITY, LXDE. Not Gnome)
+       $desktop_session = ( $ENV{'DESKTOP_SESSION'} ) ? lc($ENV{'DESKTOP_SESSION'}) : '';
+       $xdg_desktop = ( $ENV{'XDG_CURRENT_DESKTOP'} ) ? lc($ENV{'XDG_CURRENT_DESKTOP'}) : '';
+       $kde_session_version = ($ENV{'KDE_SESSION_VERSION'}) ? $ENV{'KDE_SESSION_VERSION'} : '';
+       get_kde_data();
+       if (!@desktop){
+               get_env_de_data();
+       }
+       if (!@desktop){
+               get_env_xprop_de_data();
+       }
+       if (!@desktop && $b_xprop ){
+               get_xprop_de_data();
+       }
+       if (!@desktop){
+               get_ps_de_data();
+       }
+       if ($extra > 2 && @desktop){
+               set_info_data();
+       }
+       if ($b_display && !$b_force_display && $extra > 1){
+               get_wm();
+       }
+       main::log_data('dump','@desktop', \@desktop) if $b_log;
+       # ($b_xprop,$kde_session_version,$xdg_desktop,@data,@xprop) = undef;
+       return @desktop;
+}
+sub get_kde_data {
+       eval $start if $b_log;
+       my ($program,@version_data,@version_data2);
+       my $kde_full_session = ($ENV{'KDE_FULL_SESSION'}) ? $ENV{'KDE_FULL_SESSION'} : '';
+       return 1 if ($xdg_desktop ne 'kde' && !$kde_session_version && $kde_full_session ne 'true' );
+       # works on 4, assume 5 will id the same, why not, no need to update in future
+       # KDE_SESSION_VERSION is the integer version of the desktop
+       # NOTE: as of plasma 5, the tool: about-distro MAY be available, that will show
+       # actual desktop data, so once that's in debian/ubuntu, if it gets in, add that test
+       if ($xdg_desktop eq 'kde' || $kde_session_version ){
+               if ($kde_session_version && $kde_session_version <= 4){
+                       @data = main::program_values("kded$kde_session_version");
+                       if (@data){
+                               $desktop[0] = $data[3];
+                               $desktop[1] = main::program_version("kded$kde_session_version",$data[0],$data[1],$data[2],$data[5],$data[6]);
+                               # kded exists, so we can now get the qt data string as well
+                               if ($desktop[1] && ($program = main::check_program("kded$kde_session_version")) ){
+                                       @version_data = main::grabber("$program --version 2>/dev/null");
+                               }
+                       }
+                       $desktop[0] = 'KDE' if !$desktop[0];
+               }
+               else {
+                       # NOTE: this command string is almost certain to change, and break, with next 
+                       # major plasma desktop, ie, 6. 
+                       # qdbus org.kde.plasmashell /MainApplication org.qtproject.Qt.QCoreApplication.applicationVersion
+                       # Qt: 5.4.2
+                       # KDE Frameworks: 5.11.0
+                       # kf5-config: 1.0
+                       # for QT, and Frameworks if we use it
+                       if (!@version_data && ($program = main::check_program("kf$kde_session_version-config") )){
+                               @version_data = main::grabber("$program --version 2>/dev/null");
+                       }
+                       if (!@version_data && ($program = main::check_program("kded$kde_session_version"))){
+                               @version_data = main::grabber("$program --version 2>/dev/null");
+                       }
+                       if ($program = main::check_program("plasmashell")){
+                               @version_data2 = main::grabber("$program --version 2>/dev/null");
+                               $desktop[1] = main::awk(\@version_data2,'^plasmashell',-1,'\s+');
+                       }
+                       $desktop[0] = 'KDE Plasma';
+               }
+               if (!$desktop[1]){
+                       $desktop[1] = ($kde_session_version) ? $kde_session_version: main::row_defaults('unknown-desktop-version');
+               }
+               # print Data::Dumper::Dumper \@version_data;
+               if ($extra > 1){
+                       if (@version_data){
+                               $desktop[3] = main::awk(\@version_data,'^Qt:', 2,'\s+');
+                       }
+                       # qmake can have variants, qt4-qmake, qt5-qmake, also qt5-default but not tested
+                       if (!$desktop[3] && ($program = main::check_program("qmake"))){
+                               # note: this program has issues, it may appear to be in /usr/bin, but it 
+                               # often fails to execute, so the below will have null output, but use as a 
+                               # fall back test anyway.
+                               @version_data = main::grabber("$program --version 2>/dev/null");
+                               $desktop[3] = main::awk(\@version_data,'^Using Qt version',4) if @version_data;
+                       }
+                       $desktop[2] = 'Qt';
+               }
+       }
+       # KDE_FULL_SESSION property is only available since KDE 3.5.5.
+       elsif ($kde_full_session eq 'true'){
+               @version_data = main::grabber("kded --version 2>/dev/null");
+               $desktop[0] = 'KDE';
+               $desktop[1] = main::awk(\@version_data,'^KDE:',2,'\s+') if @version_data;
+               if (!$desktop[1]){
+                       $desktop[1] = '3.5';
+               }
+               if ($extra > 1 && @version_data){
+                       $desktop[2] = 'Qt';
+                       $desktop[3] = main::awk(\@version_data,'^Qt:',2,'\s+') if @version_data;
+               }
+       }
+       eval $end if $b_log;
+}
+sub get_env_de_data {
+       eval $start if $b_log;
+       my ($program,@version_data);
+       main::set_ps_gui() if ! $b_ps_gui;
+       if ($desktop_session eq 'trinity' || $xdg_desktop eq 'trinity' || (grep {/^tde/} @ps_gui) ){
+               $desktop[0] = 'Trinity';
+               if ($program = main::check_program('kdesktop')){
+                       @version_data = main::grabber("$program --version 2>/dev/null");
+                       $desktop[1] = main::awk(\@version_data,'^TDE:',2,'\s+') if @version_data;
+               }
+               if ($extra > 1 && @version_data){
+                       $desktop[2] = 'Qt';
+                       $desktop[3] = main::awk(\@version_data,'^Qt:',2,'\s+') if @version_data;
+               }
+       }
+       elsif ($xdg_desktop eq 'unity'){
+               @data = main::program_values('unity');
+               $desktop[0] = $data[3];
+               $desktop[0] ||= 'Unity';
+               $desktop[1] = main::program_version('cinnamon',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               #set_gtk_data() if $extra > 1;
+       }
+       elsif ( $xdg_desktop =~ /budgie/ ){
+               @data = main::program_values('budgie');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('budgie-desktop',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       # debian package: lxde-core. 
+       # NOTE: some distros fail to set XDG data for root
+       elsif ( $xdg_desktop =~ /^(lxde|razor|lxqt)$/ || (grep {/^(razor-session|lxsession|lxqt-session)$/} @ps_gui)){
+               # note: openbox-lxde --version may be present, but returns openbox data
+               if ($xdg_desktop eq 'lxde' || (grep {/^lxsession$/} @ps_gui )){
+                       @data = main::program_values('lxde');
+                       $desktop[0] = $data[3];
+                       $desktop[1] = main::program_version('lxpanel',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+               # NOTE: lxqt-about opens a gui dialog
+               elsif ($xdg_desktop eq 'razor' || $xdg_desktop eq 'lxqt' || (grep {/^(razor-desktop|lxqt-session)$/} @ps_gui)) {
+                       if (grep {/^lxqt-session$/} @ps_gui){
+                               @data = main::program_values('lxqt');
+                               $desktop[0] = $data[3];
+                               # BAD: lxqt-about opens dialogue, sigh
+                               $desktop[1] = main::program_version('lxqt-panel',$data[0],$data[1],$data[2],$data[5],$data[6]);
+                       }
+                       elsif (grep {/^razor-session$/} @ps_gui){
+                               $desktop[0] = 'Razor-Qt';
+                       }
+                       else {
+                               $desktop[0] = 'LX-Qt-Variant';
+                       }
+                       set_qt_data() if $extra > 1;
+               }
+       }
+       # note, X-Cinnamon value strikes me as highly likely to change, so just 
+       # search for the last part
+       elsif ( $xdg_desktop =~ /cinnamon/ ){
+               @data = main::program_values('cinnamon');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('cinnamon',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               #set_gtk_data() if $extra > 1;
+       }
+       elsif ($xdg_desktop eq 'pantheon' || $desktop_session eq 'pantheon'){
+               @data = main::program_values('pantheon');
+               $desktop[0] = $data[3];
+               #$desktop[1] = main::program_version('pantheon',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               #set_gtk_data() if $extra > 1;
+       }
+       eval $end if $b_log;
+}
+sub get_env_xprop_de_data {
+       eval $start if $b_log;
+       my ($program,$value,@version_data);
+       # NOTE: Always add to set_prop the search term if you add an item!!
+       set_xprop();
+       # note that cinnamon split from gnome, and and can now be id'ed via xprop,
+       # but it will still trigger the next gnome true case, so this needs to go 
+       # before gnome test eventually this needs to be better organized so all the 
+       # xprop tests are in the same section, but this is good enough for now.
+       # NOTE: was checking for 'muffinr' but that's not part of cinnom
+       if ( (main::check_program('muffin') || main::check_program('cinnamon-session') ) && 
+            ($b_xprop && main::awk(\@xprop,'_muffin') )){
+               @data = main::program_values('cinnamon');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('cinnamon',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               #set_gtk_data() if $extra > 1;
+               $desktop[0] ||= 'Cinnamon';
+       }
+       elsif ($xdg_desktop eq 'mate' || ( $b_xprop && main::awk(\@xprop,'_marco') )){
+               # NOTE: mate-about reported wrong version, 1.18.0 when actual was 1.18.2
+               if ($program = main::check_program('mate-session') ) {
+                       $value = 'mate-session';
+               }
+               if ($value){
+                       @data = main::program_values($value);
+                       $desktop[0] = $data[3];
+                       $desktop[1] = main::program_version($program,$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+               #set_gtk_data() if $extra > 1;
+               $desktop[0] ||= 'MATE';
+       }
+       # note, GNOME_DESKTOP_SESSION_ID is deprecated so we'll see how that works out
+       # https://bugzilla.gnome.org/show_bug.cgi?id=542880.
+       # NOTE: manjaro is leaving XDG data null, which forces the manual check for gnome, sigh...
+       elsif ($xdg_desktop eq 'gnome' || $ENV{'GNOME_DESKTOP_SESSION_ID'} || 
+             (main::check_program('gnome-shell') && $b_xprop && main::awk(\@xprop,'^_gnome') ) ){
+               if ($program = main::check_program('gnome-about') ) {
+                       @data = main::program_values('gnome-about');
+                       $desktop[1] = main::program_version('gnome-about',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+               elsif ($program = main::check_program('gnome-shell') ) {
+                       @data = main::program_values('gnome-shell');
+                       $desktop[1] = main::program_version('gnome-shell',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+               # set_gtk_data() if $extra > 1;
+               $desktop[0] = ( $data[3] ) ? $data[3] : 'Gnome';
+       }
+       eval $end if $b_log;
+}
+sub get_xprop_de_data {
+       eval $start if $b_log;
+       my ($program,@version_data,$version);
+       #print join "\n", @xprop, "\n";
+       # String: "This is xfdesktop version 4.2.12"
+       # alternate: xfce4-about --version > xfce4-about 4.10.0 (Xfce 4.10)
+       # note: some distros/wm (e.g. bunsen) set xdg to xfce to solve some other 
+       # issues so don't test for that. $xdg_desktop eq 'xfce'
+       # the sequence here matters, some desktops like icewm, razor, let you set different 
+       # wm, so we want to get the main controlling desktop first, then fall back to the wm
+       # detections. get_wm() will handle alternate wm detections.
+       if ((main::check_program('xfdesktop')) && main::awk(\@xprop,'^(xfdesktop|xfce)' )){
+               # this is a very expensive test that doesn't usually result in a find
+               # talk to xfce to see what id they will be using for xfce 5
+#              if (main::awk(\@xprop, 'xfce4')){
+#                      $version = '4';
+#              }
+               if (main::awk(\@xprop, 'xfce5')){
+                       $version = '5';
+               }
+               else {
+                       $version = '4';
+               }
+               @data = main::program_values('xfdesktop');
+               $desktop[0] = $data[3];
+               # xfdesktop --version out of x fails to get display, so no data
+               @version_data = main::grabber('xfdesktop --version 2>/dev/null');
+               # out of x, this error goes to stderr, so it's an empty result
+               $desktop[1] = main::awk(\@version_data,$data[0],$data[1],'\s+');
+               #$desktop[1] = main::program_version('xfdesktop',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               if ( !$desktop[1] ){
+                       @data = main::program_values("xfce${version}-panel");
+                       # print Data::Dumper::Dumper \@data;
+                       # this returns an error message to stdout in x, which breaks the version
+                       # xfce4-panel --version out of x fails to get display, so no data
+                       $desktop[1] = main::program_version("xfce${version}-panel",$data[0],$data[1],$data[2],$data[5],$data[6]);
+                       # out of x this kicks out an error: xfce4-panel: Cannot open display
+                       $desktop[1] = '' if $desktop[1] !~ /[0-9]\./; 
+               }
+               $desktop[0] ||= 'Xfce';
+               $desktop[1] ||= ''; # xfce isn't going to be 4 forever
+               if ($extra > 1){
+                       @data = main::program_values('xfdesktop-toolkit');
+                       #$desktop[3] = main::program_version('xfdesktop',$data[0],$data[1],$data[2],$data[5],$data[6]);
+                       $desktop[3] = main::awk(\@version_data,$data[0],$data[1],'\s+');
+                       $desktop[2] = $data[3];
+               }
+       }
+       elsif (main::check_program('enlightenment') && main::awk(\@xprop,'enlightenment' )){
+               $desktop[0] = 'Enlightenment';
+               # no -v or --version but version is in xprop -root
+               # ENLIGHTENMENT_VERSION(STRING) = "Enlightenment 0.16.999.49898"
+               $desktop[1] = main::awk(\@xprop,'enlightenment_version',2,'\s+=\s+' );
+               $desktop[1] = (split /"/, $desktop[1])[1] if $desktop[1];
+               $desktop[1] = (split /\s+/, $desktop[1])[1] if $desktop[1];
+       }
+       # must come right after xfce 
+       elsif (main::check_program('icewm') && main::awk(\@xprop,'icewm' )){
+               @data = main::program_values('icewm');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('icewm',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       # debian package: i3-wm
+       elsif (main::check_program('i3') && main::awk(\@xprop,'^i3_' )){
+               @data = main::program_values('i3');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('i3',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       elsif (main::check_program('mwm') && main::awk(\@xprop,'^_motif' )){
+               @data = main::program_values('mwm');
+               $desktop[0] = $data[3];
+               # $desktop[1] = main::program_version('mwm',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       # debian package name: wmaker
+       elsif (main::check_program('WindowMaker') && main::awk(\@xprop,'^_?windowmaker' )){
+               @data = main::program_values('wmaker');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('wmaker',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       elsif (main::check_program('wm2') && main::awk(\@xprop,'^_wm2' )){
+               @data = main::program_values('wm2');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('wm2',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       elsif (main::check_program('herbstluftwm') && main::awk(\@xprop,'herbstluftwm' )){
+               @data = main::program_values('herbstluftwm');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('herbstluftwm',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       elsif ( (main::check_program('blackbox') || main::check_program('fluxbox')) && main::awk(\@xprop,'blackbox_pid' )){
+               if (@ps_gui && (grep {/^fluxbox$/} @ps_gui )){
+                       @data = main::program_values('fluxbox');
+                       $desktop[0] = $data[3];
+                       $desktop[1] = main::program_version('fluxbox',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+               else {
+                       @data = main::program_values('blackbox');
+                       $desktop[0] = $data[3];
+                       $desktop[1] = main::program_version('blackbox',$data[0],$data[1],$data[2],$data[5],$data[6]);
+               }
+       }
+       elsif (main::check_program('openbox') && main::awk(\@xprop,'openbox_pid' )){
+               @data = main::program_values('openbox');
+               $desktop[0] = $data[3];
+               $desktop[1] = main::program_version('openbox',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       elsif (main::check_program('amiwm') && main::awk(\@xprop,'amiwm' )){
+               @data = main::program_values('amiwm');
+               $desktop[0] = $data[3];
+               #$desktop[1] = main::program_version('openbox',$data[0],$data[1],$data[2],$data[5],$data[6]);
+       }
+       # need to check starts line because it's so short
+       eval $end if $b_log;
+}
+sub get_ps_de_data {
+       eval $start if $b_log;
+       my ($program,@version_data);
+       main::set_ps_gui() if !$b_ps_gui;
+       if (@ps_gui){
+               # 1 check program; 2 search; 3 values; 4 version; 5 -optional: print value
+               my @desktops =(
+               ['fluxbox','fluxbox','fluxbox','fluxbox'],
+               ['fvwm-crystal','fvwm-crystal','fvwm-crystal','fvwm'],
+               ['fvwm2','fvwm2','fvwm2','fvwm2'],
+               ['fvwm','fvwm','fvwm','fvwm'],
+               ['pekwm','pekwm','pekwm','pekwm'],
+               ['awesome','awesome','awesome','awesome'],
+               ['blackbox','blackbox','blackbox','blackbox'],
+               ['openbox','openbox','openbox','openbox'],
+               # not in debian apt
+               ['scrotwm','scrotwm','scrotwm','scrotwm'],
+               ['spectrwm','spectrwm','spectrwm','spectrwm'],
+               ['twm','twm','twm','twm'],
+               # note: built from source, but I assume it will show: /usr/bin/dwm
+               ['dwm','dwm','dwm','dwm'],
+               # not in debian apt, current is wmii, version 3
+               ['wmii2','wmii2','wmii2','wmii2'],
+               ['wmii','wmii','wmii','wmii'],
+               ['9wm','9wm','9wm','9wm'],
+               ['amiwm','amiwm','amiwm','amiwm'],
+               ['flwm','flwm','flwm','flwm'],
+               ['jwm','jwm','jwm','jwm'],
+               ['mwm','mwm','mwm','mwm'],
+               ['notion','notion','notion','notion'],
+               ['ratpoison','ratpoison','ratpoison','ratpoison'],
+               ['sawfish','sawfish','sawfish','sawfish'],
+               ['matchbox-window-manager','matchbox-window-manager',
+                 'matchbox-window-manager','matchbox-window-manager'],
+               ['afterstep','afterstep','afterstep','afterstep'],
+               ['WindowMaker','WindowMaker','wmaker','wmaker'],
+               ['windowlab','windowlab','windowlab','windowlab'],
+               ['xmonad','xmonad','xmonad','xmonad'],
+               );
+               foreach my $ref (@desktops){
+                       my @item = @$ref;
+                       # no need to use check program with short list of ps_gui
+                       # if ( main::check_program($item[0]) && (grep {/^$item[1]$/} @ps_gui)){
+                       if (grep {/^$item[1]$/} @ps_gui){
+                               @data = main::program_values($item[2]);
+                               $desktop[0] = $data[3];
+                               if ($data[1] && $data[2]){
+                                       $desktop[1] = main::program_version($item[3],$data[0],$data[1],$data[2],$data[5],$data[6]);
+                               }
+                               last;
+                       }
+               }
+       }
+       eval $end if $b_log;
+}
+
+sub set_qt_data {
+       eval $start if $b_log;
+       my ($program,@data,@version_data);
+       my $kde_version = $kde_session_version;
+       $program = '';
+       if (!$kde_version){
+               if ($program = main::check_program("kded6") ){$kde_version = 6;}
+               elsif ($program = main::check_program("kded5") ){$kde_version = 5;}
+               elsif ($program = main::check_program("kded4") ){$kde_version = 4;}
+               elsif ($program = main::check_program("kded") ){$kde_version = '';}
+       }
+       # alternate: qt4-default, qt4-qmake or qt5-default, qt5-qmake
+       if (!$desktop[3] && ($program = main::check_program("qmake"))){
+               @version_data = main::grabber("$program --version 2>/dev/null");
+               $desktop[2] = 'Qt';
+               $desktop[3] = main::awk(\@version_data,'^Using Qt version',4) if @version_data;
+       }
+       if (!$desktop[3] && ($program = main::check_program("qtdiag") )){
+               @data = main::program_values('qtdiag');
+               $desktop[3] = main::program_version($program,$data[0],$data[1],$data[2],$data[5],$data[6]);
+               $desktop[2] = $data[3];
+       }
+       if (!$desktop[3] && ($program = main::check_program("kf$kde_version-config") )){
+               @version_data = main::grabber("$program --version 2>/dev/null");
+               $desktop[2] = 'Qt';
+               $desktop[3] = main::awk(\@version_data,'^Qt:',2) if @version_data;
+       }
+       # note: qt 5 does not show qt version in kded5, sigh
+       if (!$desktop[3] && ($program = main::check_program("kded$kde_version"))){
+               @version_data = main::grabber("$program --version 2>/dev/null");
+               $desktop[2] = 'Qt';
+               $desktop[3] = main::awk(\@version_data,'^Qt:',2) if @version_data;
+       }
+       eval $end if $b_log;
+}
+
+sub get_wm {
+       eval $start if $b_log;
+       if (!$b_wmctrl) {
+               get_wm_main();
+       }
+       if ( (!$desktop[5] || $b_wmctrl) && (my $program = main::check_program('wmctrl'))){
+               get_wm_wmctrl($program);
+       }
+       eval $end if $b_log;
+}
+sub get_wm_main {
+       eval $start if $b_log;
+       my ($wms,$working);
+       # xprop is set only if not kde/gnome/cinnamon/mate/budgie/lx..
+       if ($b_xprop){
+               #KWIN_RUNNING
+               $wms = 'blackbox|compiz|kwin_wayland|kwin_x11|kwin|marco|muffin|';
+               $wms .= 'openbox|herbstluftwm|twin|wm2|windowmaker|i3';
+               foreach (@xprop){
+                       if (/\b($wms)\b/){
+                               $working = $1;
+                               $working = 'wmaker' if $working eq 'windowmaker';
+                               last;
+                       }
+               }
+       }
+       if (!$desktop[5]){
+               main::set_ps_gui() if ! $b_ps_gui;
+               # order matters, see above logic
+               $wms = '9wm|afterstep|amiwm|awesome|budgie-wm|compiz|fluxbox|blackbox|dwm|';
+               $wms .= 'flwm|fvwm-crystal|fvwm2|fvwm|gala|gnome-shell|i3|jwm|';
+               $wms .= 'twin|kwin_wayland|kwin_x11|kwin|matchbox-window-manager|marco|';
+               $wms .= 'muffin|mutter|metacity|mwm|notion|openbox|ratpoison|sawfish|scrotwm|spectrwm|';
+               $wms .= 'twm|windowlab|WindowMaker|wm2|wmii2|wmii|xfwm4|xfwm5|xmonad';
+               foreach (@ps_gui){
+                       if (/^($wms)$/){
+                               $working = $1;
+                               last;
+                       }
+               }
+       }
+       get_wm_version('manual',$working) if $working;
+       $desktop[5] = $working if !$desktop[5] && $working;
+       eval $end if $b_log;
+}
+sub get_wm_wmctrl {
+       eval $start if $b_log;
+       my ($program) = @_;
+       my $cmd = "$program -m 2>/dev/null";
+       my @data = main::grabber($cmd,'','strip');
+       main::log_data('dump','@data',\@data) if $b_log;
+       $desktop[5] = main::awk(\@data,'^Name',2,'\s*:\s*');
+       $desktop[5] = '' if $desktop[5] && $desktop[5] eq 'N/A';
+       if ($desktop[5]){
+               # variants: gnome shell; 
+               # IceWM 1.3.8 (Linux 3.2.0-4-amd64/i686) ; Metacity (Marco) ; Xfwm4
+               $desktop[5] =~ s/\d+\.\d\S+|[\[\(].*\d+\.\d.*[\)\]]//g;
+               $desktop[5] = main::trimmer($desktop[5]);
+               # change Metacity (Marco) to marco
+               if ($desktop[5] =~ /marco/i) {$desktop[5] = 'marco'}
+               elsif (lc($desktop[5]) eq 'gnome shell') {$desktop[5] = 'gnome-shell'}
+               elsif ($desktop_session eq 'trinity' && lc($desktop[5]) eq 'kwin') {$desktop[5] = 'Twin'}
+               get_wm_version('wmctrl',$desktop[5]);
+       }
+       eval $end if $b_log;
+}
+sub get_wm_version {
+       eval $start if $b_log;
+       my ($type,$wm) = @_;
+       # we don't want the gnome-shell version, and the others have no --version
+       # we also don't want to run --version again on stuff we already have tested
+       return if ! $wm || $wm =~ /^(budgie-wm|gnome-shell)$/ || ($desktop[0] && lc($desktop[0]) eq lc($wm) );
+       my $temp = (split /\s+/, $wm)[0];
+       if ($temp){
+               $temp = (split /\s+/, $temp)[0];
+               $temp = lc($temp);
+               $temp = 'wmaker' if $temp eq 'windowmaker';
+               my @data = main::program_values($temp);
+               return if !@data;
+               # print Data::Dumper::Dumper \@data;
+               $desktop[5] = $data[3] if $type eq 'manual';
+               # note: if values returns 0 for 1 and 2, it doesn't support versioning
+               if ($extra > 2 && $data[1] && $data[2]){
+                       my $version = main::program_version($temp,$data[0],$data[1],$data[2],$data[5],$data[6]);
+                       $desktop[6] = $version if $version;
+               }
+       }
+       eval $end if $b_log;
+}
+
+sub set_gtk_data {
+       eval $start if $b_log;
+       my ($version,$program,@data);
+       # this is a hack, and has to be changed with every toolkit version change, and 
+       # only dev systems      # have this installed, but it's a cross distro command try it.
+       if ($program = main::check_program('pkg-config')){
+               @data = main::grabber("$program --modversion gtk+-4.0 2>/dev/null");
+               $version = main::awk(\@data,'\S');
+               # note: opensuse gets null output here, we need the command to get version and output sample
+               if ( !$version ){
+                       @data = main::grabber("$program --modversion gtk+-3.0 2>/dev/null");
+                       $version = main::awk(\@data,'\S');
+               }
+               if ( !$version ){
+                       @data = main::grabber("$program --modversion gtk+-2.0 2>/dev/null");
+                       $version = main::awk(\@data,'\S');
+               }
+       }
+       # now let's go to more specific version tests, this will never cover everything and that's fine.
+       if (!$version){
+               # we'll try some known package managers next. dpkg will handle a lot of distros 
+               # this is the most likely order as of: 2014-01-13. Not going to try to support all 
+               # package managers too much work, just the very biggest ones.
+               if ($program = main::check_program('dpkg')){
+                       @data = main::grabber("$program -s libgtk-3-0 2>/dev/null");
+                       $version = main::awk(\@data,'^\s*Version',2,'\s+');
+                       # just guessing on gkt 4 package name
+                       if (!$version){
+                               @data = main::grabber("$program -s libgtk-4-0 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s+');
+                       }
+                       if (!$version){
+                               @data = main::grabber("$program -s libgtk2.0-0 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s+');
+                       }
+               }
+               elsif ($program = main::check_program('pacman')){
+                       @data = main::grabber("$program -Qi gtk3 2>/dev/null");
+                       $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       # just guessing on gkt 4 package name
+                       if (!$version){
+                               @data = main::grabber("$program -Qi gtk4 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       }
+                       if (!$version){
+                               @data = main::grabber("$program -Qi gtk2 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       }
+               }
+               elsif ($program = main::check_program('rpm')){
+                       @data = main::grabber("$program -qi libgtk-3-0 2>/dev/null");
+                       $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       # just guessing on gkt 4 package name
+                       if (!$version){
+                               @data = main::grabber("$program -qi libgtk-4-0 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       }
+                       if (!$version){
+                               @data = main::grabber("$program -qi libgtk-3-0 2>/dev/null");
+                               $version = main::awk(\@data,'^\s*Version',2,'\s*:\s*');
+                       }
+               }
+       }
+       $desktop[2] = 'Gtk';
+       eval $end if $b_log;
+}
+sub set_info_data {
+       eval $start if $b_log;
+       my (@data,@info,$item);
+       my $pattern = 'gnome-panel|kicker|lxpanel|lxqt-panel|matchbox-panel|';
+       $pattern .= 'mate-panel|plasma-desktop|plasma-netbook|razor-panel|razorqt-panel|';
+       $pattern .= 'wingpanel|xfce4-panel|xfce5-panel';
+       if (@data = grep {/^($pattern)$/} @ps_gui ) {
+               # only one entry per type, can be multiple
+               foreach $item (@data){
+                       if (! grep {$item =~ /$_/} @info){
+                               $item = main::trimmer($item);
+                               $item =~ s/.*\///;
+                               push @info, (split /\s+/, $item)[0];
+                       }
+               }
+       }
+       $desktop[4] = join (',', @info) if @info;
+       eval $end if $b_log;
+}
+
+sub set_xprop {
+       eval $start if $b_log;
+       if (my $program = main::check_program('xprop')){
+               @xprop = main::grabber("xprop -root $display_opt 2>/dev/null");
+               if (@xprop){
+                       # add wm / de as required, but only add what is really tested for above
+                       # XFDESKTOP_IMAGE_FILE; XFCE_DESKTOP
+                       my $pattern = '^amiwm|blackbox_pid|compiz|enlightenment|^_gnome|herbstluftwm|';
+                       $pattern .= '^kwin_|^i3_|icewm|_marco|^_motif|_muffin|openbox_pid|';
+                       $pattern .= '^_?windowmaker|^_wm2|^(xfdesktop|xfce)';
+                       # let's only do these searches once
+                       @xprop = grep {/^\S/ && /($pattern)/i} @xprop;
+                       $_ = lc for @xprop;
+                       $b_xprop = 1 if scalar @xprop > 0;
+               }
+       }
+       # print "@xprop\n";
+       eval $end if $b_log;
+}
+
+}
+
+sub get_display_manager {
+       eval $start if $b_log;
+       my (@data,@found,$temp,$working,$b_run,$b_vrun,$b_vrunrc);
+       # ldm - LTSP display manager. Note that sddm does not appear to have a .pid 
+       # extension in Arch note: to avoid positives with directories, test for -f 
+       # explicitly, not -e
+       my @dms = qw(entranced.pid gdm.pid gdm3.pid kdm.pid ldm.pid 
+       lightdm.pid lxdm.pid mdm.pid nodm.pid pcdm.pid  sddm.pid slim.lock 
+       tint2.pid wdm.pid xdm.pid xenodm.pid);
+       # this is the only one I know of so far that has --version
+       # lightdm outputs to stderr, so it has to be redirected
+       my @dms_version = qw(lightdm);
+       $b_run = 1 if -d "/run";
+       # in most linux, /var/run is a sym link to /run, so no need to check it twice
+       if ( -d "/var/run" ){
+               my $rdlink = readlink('/var/run');
+               $b_vrun = 1 if !$rdlink || ($rdlink && $rdlink ne '/run');
+               $b_vrunrc = 1 if -d "/var/run/rc.d";
+       }
+       foreach my $id (@dms){
+               # note: $working will create a dir name out of the dm $id, then 
+               # test if pid is in that note: sddm, in an effort to be unique and special, 
+               # do not use a pid/lock file, but rather a random string inside a directory 
+               # called /run/sddm/ so assuming the existence of the pid inside a directory named
+               # from the dm. Hopefully this change will not have negative results.
+               $working = $id;
+               $working =~ s/\.\S+$//;
+               # note: there were issues with duplicated dm's in inxi, checking @found corrects it
+               if ( ( ( $b_run && ( -f "/run/$id" || -d "/run/$working" ) ) || 
+                  ( $b_vrun && ( -f "/var/run/$id" || -d "/var/run/$working" ) ) || 
+                  ( $b_vrunrc && ( -f "/var/run/rc.d/$working" || -d "/var/run/rc.d/$id" ) ) ) && 
+                  ! grep {/$working/} @found ){
+                       if ($extra > 2 && awk( \@dms_version, $working) && (my $path = main::check_program($working)) ){
+                               @data = main::grabber("$path --version 2>&1");
+                               $temp = awk(\@data,'\S',2,'\s+');
+                               $working .= ' ' . $temp if $temp;
+                       }
+                       push @found, $working;
+               }
+       }
+       if (!@found && grep {/\/usr.*\/x/ && !/\/xprt/} @ps_cmd){
+               if (awk (\@ps_cmd, 'startx') ){
+                       $found[0] = 'startx';
+               }
+               elsif (awk (\@ps_cmd, 'xinit') ){
+                       $found[0] = 'xinit';
+               }
+       }
+       # might add this in, but the rate of new dm's makes it more likely it's an 
+       # unknown dm, so we'll keep output to N/A
+       log_data('dump','display manager: @found',\@found) if $b_log;
+       eval $end if $b_log;
+       return join ', ', @found if @found;
+}
+
+## Get DistroData
+{
+package DistroData;
+my (@distro_data,@osr);
+sub get {
+       eval $start if $b_log;
+       if ($bsd_type){
+               get_bsd_os();
+       }
+       else {
+               get_linux_distro();
+       }
+       eval $end if $b_log;
+       return @distro_data;
+}
+
+sub get_bsd_os {
+       eval $start if $b_log;
+       my ($distro) = ('');
+       if ($bsd_type eq 'darwin'){
+               my $file = '/System/Library/CoreServices/SystemVersion.plist';
+               if (-f $file){
+                       my @data = main::reader($file);
+                       @data = grep {/(ProductName|ProductVersion)/} @data if @data;
+                       @data = grep {/<string>/} @data if @data;
+                       @data = map {s/<[\/]?string>//g; } @data if @data;
+                       $distro = join (' ', @data);
+               }
+       }
+       else {
+               $distro = "$uname[0] $uname[2]";
+       }
+       @distro_data = ($distro,'');
+       eval $end if $b_log;
+}
+
+sub get_linux_distro {
+       eval $start if $b_log;
+       my ($distro,$distro_id,$distro_file,$system_base) = ('','','','');
+       my ($b_issue,$b_osr,$b_use_issue,@working);
+       # order matters!
+       my @derived = qw(antix-version aptosid-version kanotix-version knoppix-version 
+       pclinuxos-release mandrake-release manjaro-release mx-version pardus-release 
+       porteus-version sabayon-release siduction-version sidux-version slitaz-release 
+       solusos-release turbolinux-release zenwalk-version);
+       my $derived_s = join "|", @derived;
+       my @primary = qw(altlinux-release arch-release gentoo-release redhat-release slackware-version 
+       SuSE-release);
+       my $primary_s = join "|", @primary;
+       my $exclude_s = 'debian_version|devuan_version|ubuntu_version';
+       # note, pclinuxos has all these mandrake/mandriva files, careful!
+       my $lsb_good_s = 'mandrake-release|mandriva-release|mandrakelinux-release|manjaro-release';
+       my $os_release_good_s = 'altlinux-release|arch-release|pclinuxos-release|rpi-issue|SuSE-release';
+       # note: always exceptions, so wild card after release/version: 
+       # /etc/lsb-release-crunchbang
+       # wait to handle since crunchbang file is one of the few in the world that 
+       # uses this method
+       my @distro_files = main::globber('/etc/*[-_]{[rR]elease,[vV]ersion,issue}*');
+       my $lsb_release = '/etc/lsb-release';
+       my $b_lsb = 1 if -f $lsb_release;
+       my ($etc_issue,$issue,$lc_issue) = ('','/etc/issue','');
+       $b_issue = 1 if -f $issue;
+       # note: OpenSuse Tumbleweed 2018-05 has made /etc/issue created by sym link to /run/issue
+       # and then made that resulting file 700 permissions, which is obviously a mistake
+       $etc_issue = (main::reader($issue))[0] if -r $issue;
+       $etc_issue = main::clean_characters($etc_issue);
+       my $os_release = '/etc/os-release';
+       @osr = main::reader($os_release) if -r $os_release;
+       # debian issue can end with weird escapes like \n \l 
+       # antergos: Antergos Linux \r (\l)
+       if ($etc_issue){
+               $lc_issue = lc($etc_issue) if $etc_issue;
+               if ($lc_issue =~ /(antergos|grml|linux lite)/){
+                       $distro_id = $1;
+                       $b_use_issue = 1;
+               }
+               elsif ($lc_issue =~ /(raspbian|peppermint)/){
+                       $distro_id = $1;
+                       $distro_file = $os_release if @osr;
+               }
+       }
+       # Note that antergos changed this around        # 2018-05, and now lists 
+       # antergos in os-release, sigh... We want these distros to use os-release 
+       # if it contains their names. Last check below
+       if ( @osr && (grep {/manjaro|antergos|chakra|pclinuxos/i} @osr ) ){
+               $distro_file = $os_release;
+               #$system_base = 'Arch Linux';
+       }
+       $distro_id = 'armbian' if grep {/armbian/} @distro_files;
+       main::log_data('dump','@distro_files',\@distro_files) if $b_log;
+       main::log_data('data',"distro_file-1: $distro_file") if $b_log;
+       if (!$distro_file){
+               if (scalar @distro_files == 1){
+                       $distro_file = $distro_files[0];
+               }
+               elsif (scalar @distro_files > 1) {
+                       # special case, to force manjaro/antergos which also have arch-release
+                       # manjaro should use lsb, which has the full info, arch uses os release
+                       # antergos should use /etc/issue. We've already checked os-release above
+                       if ($distro_id eq 'antergos' || (grep {/antergos|chakra|manjaro/} @distro_files )){
+                               @distro_files = grep {!/arch-release/} @distro_files;
+                               #$system_base = 'Arch Linux';
+                       }
+                       my $distro_files_s = join "|", @distro_files;
+                       @working = (@derived,@primary);
+                       foreach my $file (@working){
+                               if ( "/etc/$file" =~ /($distro_files_s)$/){
+                                       # Now lets see if the distro file is in the known-good working-lsb-list
+                                       # if so, use lsb-release, if not, then just use the found file
+                                       # this is for only those distro's with self named release/version files
+                                       # because Mint does not use such, it must be done as below 
+                                       if (@osr && $file =~ /($os_release_good_s)$/){
+                                               $distro_file = $os_release;
+                                       }
+                                       elsif ($b_lsb && $file =~ /$lsb_good_s/){
+                                               $distro_file = $lsb_release;
+                                       }
+                                       else {
+                                               $distro_file = "/etc/$file";
+                                       }
+                                       last;
+                               }
+                       }
+               }
+       }
+       main::log_data('data',"distro_file-2: $distro_file") if $b_log;
+       # first test for the legacy antiX distro id file
+       if ( -f '/etc/antiX'){
+               @working = main::reader('/etc/antiX');
+               $distro = main::awk(\@working,'antix.*\.iso') if @working;
+               $distro = main::clean_characters($distro) if $distro;
+       }
+       # this handles case where only one release/version file was found, and it's lsb-release. 
+       # This would never apply for ubuntu or debian, which will filter down to the following 
+       # conditions. In general if there's a specific distro release file available, that's to 
+       # be preferred, but this is a good backup.
+       elsif ($distro_file && $b_lsb && ($distro_file =~ /\/etc\/($lsb_good_s)$/ || $distro_file eq $lsb_release) ){
+               $distro = get_lsb_release();
+       }
+       elsif ($distro_file && $distro_file eq $os_release){
+               $distro = get_os_release();
+               $b_osr = 1;
+       }
+       # if distro id file was found and it's not in the exluded primary distro file list, read it
+       elsif ( $distro_file && -s $distro_file && $distro_file !~ /\/etc\/($exclude_s)$/){
+               # new opensuse uses os-release, but older ones may have a similar syntax, so just use 
+               # the first line
+               if ($distro_file eq '/etc/SuSE-release'){
+                       # leaving off extra data since all new suse have it, in os-release, this file has 
+                       # line breaks, like os-release  but in case we  want it, it's: 
+                       # CODENAME = Mantis  | VERSION = 12.2 
+                       # for now, just take first occurrence, which should be the first line, which does 
+                       # not use a variable type format
+                       @working = main::reader($distro_file);
+                       $distro = main::awk(\@working,'suse');
+               }
+               else {
+                       $distro = (main::reader($distro_file))[0];
+               }
+               $distro = main::clean_characters($distro) if $distro;
+       }
+       # otherwise try  the default debian/ubuntu /etc/issue file
+       elsif ($b_issue){
+               if ( !$distro_id && $etc_issue && $lc_issue =~ /(mint|lmde)/ ){
+                       $distro_id = $1;
+                       $b_use_issue = 1;
+               }
+               # os-release/lsb gives more manageable and accurate output than issue, 
+               # but mint should use issue for now. Antergos uses arch os-release, but issue shows them
+               if (!$b_use_issue && @osr){
+                       $distro = get_os_release();
+                       $b_osr = 1;
+               }
+               elsif (!$b_use_issue && $b_lsb){
+                       $distro = get_lsb_release();
+               }
+               elsif ($etc_issue) {
+                       $distro =  $etc_issue;
+                       # this handles an arch bug where /etc/arch-release is empty and /etc/issue 
+                       # is corrupted only older arch installs that have not been updated should 
+                       # have this fallback required, new ones use os-release
+                       if ( $distro =~ /arch linux/i){
+                               $distro = 'Arch Linux';
+                       }
+               }
+       }
+       # a final check. If a long value, before assigning the debugger output, if os-release
+       # exists then let's use that if it wasn't tried already. Maybe that will be better.
+       # not handling the corrupt data, maybe later if needed. 10 + distro: (8) + string
+       if ($distro && length($distro) > 60 ){
+               if (!$b_osr && @osr){
+                       $distro = get_os_release();
+                       $b_osr = 1;
+               }
+       }
+       # test for /etc/lsb-release as a backup in case of failure, in cases 
+       # where > one version/release file were found but the above resulted 
+       # in null distro value. 
+       if (!$distro){
+               if (!$b_osr && @osr){
+                       $distro = get_os_release();
+                       $b_osr = 1;
+               }
+               elsif ($b_lsb){
+                       $distro = get_lsb_release();
+               }
+       }
+       # now some final null tries
+       if (!$distro ){
+               # if the file was null but present, which can happen in some cases, then use 
+               # the file name itself to set the distro value. Why say unknown if we have 
+               # a pretty good idea, after all?
+               if ($distro_file){
+                       $distro_file =~ s/\/etc\/|[-_]|release|version//g;
+                       $distro = $distro_file;
+               }
+       }
+       if ($extra > 0){
+               my $base_default = 'antix-version|mx-version'; # osr has base ids
+               my $base_issue = 'bunsen'; # base only found in issue
+               my $base_manual = 'kali'; # synthesize, no direct data available
+               my $base_osr = 'aptosid|grml|siduction'; # osr base, distro id in list of distro files
+               my $base_osr_issue = 'grml|linux lite'; # osr base, distro id in issue
+               my $base_upstream_lsb = '/etc/upstream-release/lsb-release';
+               my $base_upstream_osr = '/etc/upstream-release/os-release';
+               # first: try, some distros have upstream-release, elementary, new mint
+               # and anyone else who uses this method for fallback ID
+               if ( -r $base_upstream_osr){
+                       my @osr_working = main::reader($base_upstream_osr);
+                       if ( @osr_working){
+                               my (@osr_temp);
+                               @osr_temp = @osr;
+                               @osr = @osr_working;
+                               $system_base = get_os_release();
+                               @osr = @osr_temp if !$system_base;
+                               (@osr_temp,@osr_working) = (undef,undef);
+                       }
+               }
+               elsif ( -r $base_upstream_lsb){
+                       $system_base = get_lsb_release($base_upstream_lsb);
+               }
+               if (!$system_base && @osr){
+                       my ($base_type) = ('');
+                       if ($etc_issue && (grep {/($base_issue)/i} @osr)){
+                               $system_base = $etc_issue;
+                       }
+                       # more tests added here for other ubuntu derived distros
+                       elsif ( @distro_files && (grep {/($base_default)/} @distro_files) ){
+                               $base_type = 'default';
+                       }
+                       elsif ($distro_id && $distro_id =~ /(mint)/){
+                               $base_type = 'ubuntu';
+                       }
+                       elsif ( ( ($distro_id && $distro_id =~ /($base_osr_issue)/ ) || 
+                             (@distro_files && (grep {/($base_osr)/} @distro_files)) ) && 
+                             !(grep {/($base_osr)/i} @osr)){
+                               $system_base = get_os_release();
+                       }
+                       if (!$system_base && $base_type){
+                               $system_base = get_os_release($base_type);
+                       }
+               }
+               if (!$system_base && $lc_issue && $lc_issue =~ /($base_manual)/){
+                       my $id = $1;
+                       my %manual = (
+                       'kali' => 'Debian testing',
+                       );
+                       $system_base = $manual{$id};
+               }
+       }
+       $distro =~ s/Debian/Armbian/ if ($distro && $distro_id eq 'armbian');
+       ## finally, if all else has failed, give up
+       $distro ||= 'unknown';
+       @distro_data = ($distro,$system_base);
+       eval $end if $b_log;
+}
+
+sub get_lsb_release {
+       eval $start if $b_log;
+       my ($lsb_file) = @_;
+       $lsb_file ||= '/etc/lsb-release';
+       my ($distro,$id,$release,$codename,$description) = ('','','','','');
+       my @content = main::reader($lsb_file);
+       main::log_data('dump','@content',\@content) if $b_log;
+       @content = map {s/,|\*|\\||\"|[:\47]|^\s+|\s+$|n\/a//ig; $_} @content if @content;
+       foreach (@content){
+               next if /^\s*$/;
+               my @working = split /\s*=\s*/, $_;
+               next if !$working[0];
+               if ($working[0] eq 'DISTRIB_ID' && $working[1]){
+                       if ($working[1] =~ /^Manjaro/i){
+                               $id = 'Manjaro Linux';
+                       }
+                       # in the old days, arch used lsb_release
+#                      elsif ($working[1] =~ /^Arch$/i){
+#                              $id = 'Arch Linux';
+#                      }
+                       else {
+                               $id = $working[1];
+                       }
+               }
+               elsif ($working[0] eq 'DISTRIB_RELEASE' && $working[1]){
+                       $release = $working[1];
+               }
+               elsif ($working[0] eq 'DISTRIB_CODENAME' && $working[1]){
+                       $codename = $working[1];
+               }
+               # sometimes some distros cannot do their lsb-release files correctly, 
+               # so here is one last chance to get it right.
+               elsif ($working[0] eq 'DISTRIB_DESCRIPTION' && $working[1]){
+                       $description = $working[1];
+               }
+       }
+       if (!$id && !$release && !$codename && $description){
+               $distro = $description;
+       }
+       else {
+               $distro = "$id $release $codename";
+               $distro =~ s/^\s+|\s\s+|\s+$//g; # get rid of double and trailing spaces 
+       }
+       eval $end if $b_log;
+       return $distro;
+}
+sub get_os_release {
+       eval $start if $b_log;
+       my ($base_type) = @_;
+       my ($base_id,$base_name,$base_version,$distro,$distro_name,$pretty_name,
+       $lc_name,$name,$version_name,$version_id) = ('','','','','','','','','','');
+       my @content = @osr;
+       main::log_data('dump','@content',\@content) if $b_log;
+       @content = map {s/\\||\"|[:\47]|^\s+|\s+$|n\/a//ig; $_} @content if @content;
+       foreach (@content){
+               next if /^\s*$/;
+               my @working = split /\s*=\s*/, $_;
+               next if !$working[0];
+               if ($working[0] eq 'PRETTY_NAME' && $working[1]){
+                       $pretty_name = $working[1];
+               }
+               elsif ($working[0] eq 'NAME' && $working[1]){
+                       $name = $working[1];
+                       $lc_name = lc($name);
+               }
+               elsif ($working[0] eq 'VERSION' && $working[1]){
+                       $version_name = $working[1];
+                       $version_name =~ s/,//g;
+               }
+               elsif ($working[0] eq 'VERSION_ID' && $working[1]){
+                       $version_id = $working[1];
+               }
+               # for mint system base
+               if ($base_type ){
+                       if ($working[0] eq 'ID_LIKE' && $working[1]){
+                               if ($base_type eq 'ubuntu'){
+                                       $working[1] =~ s/ubuntu\sdebian/ubuntu/;
+                                       $working[1] = 'ubuntu' if $working[1] eq 'debian';
+                               }
+                               $base_name = ucfirst($working[1]);
+                       }
+                       elsif ($base_type eq 'ubuntu' && $working[0] eq 'UBUNTU_CODENAME' && $working[1]){
+                               $base_version = ucfirst($working[1]);
+                       }
+               }
+       }
+       # NOTE: tumbleweed has pretty name but pretty name does not have version id
+       # arco shows only the release name, like kirk, in pretty name. Too many distros 
+       # are doing pretty name wrong, and just putting in the NAME value there
+       if (!$base_type){
+               if ($name && $version_name){
+                       $distro = $name;
+                       $distro = 'Arco Linux' if $lc_name =~ /^arco/;
+                       if ($version_id && $version_name !~ /$version_id/){
+                               $distro .= ' ' . $version_id;
+                       }
+                       $distro .= " $version_name";
+               }
+               elsif ($pretty_name && ($pretty_name !~ /tumbleweed/i && $lc_name ne 'arcolinux') ){
+                       $distro = $pretty_name;
+               }
+               elsif ($name){
+                       $distro = $name;
+                       if ($version_id){
+                               $distro .= ' ' . $version_id;
+                       }
+               }
+       }
+       # note: mint has varying formats here, some have ubuntu as name, 17 and earlier
+       else {
+               # mint 17 used ubuntu os-release,  so won't have $base_version
+               if ($base_name && $base_version){
+                       $base_id = ubuntu_id($base_version) if $base_type eq 'ubuntu' && $base_version;
+                       $base_id = '' if $base_id && "$base_name$base_version" =~ /$base_id/;
+                       $base_id .= ' ' if $base_id;
+                       $distro = "$base_name $base_id$base_version";
+               }
+               elsif ($base_type eq 'default' && ($pretty_name || ($name && $version_name) ) ){
+                       $distro = ($name && $version_name) ? "$name $version_name" : $pretty_name;
+               }
+               # LMDE has only limited data in os-release, no _LIKE values
+               elsif ( $base_type eq 'ubuntu' && $lc_name =~ /^(debian|ubuntu)/ && ($pretty_name || ($name && $version_name))){
+                       $distro = ($name && $version_name) ? "$name $version_name": $pretty_name;
+               }
+       }
+       eval $end if $b_log;
+       return $distro;
+}
+# note, these are only for matching derived names, no need to go
+# all the way back here, update as new names are known. This is because 
+# Mint is using UBUNTU_CODENAME without ID data.
+sub ubuntu_id {
+       eval $start if $b_log;
+       my ($codename) = @_;
+       $codename = lc($codename);
+       my ($id) = ('');
+       my %codenames = (
+       'cosmic' => '18.10',
+       'bionic' => '18.04 LTS',
+       'artful' => '17.10',
+       'zesty' => '17.04',
+       'yakkety' => '16.10',
+       'xenial' => '16.04 LTS',
+       'wily' => '15.10',
+       'vivid' => '15.04',
+       'utopic' => '14.10',
+       'trusty' => '14.04 LTS ',
+       'saucy' => '13.10',
+       'raring' => '13.04',
+       'quantal' => '12.10',
+       'precise' => '12.04 LTS ',
+       );
+       $id = $codenames{$codename} if defined $codenames{$codename};
+       eval $end if $b_log;
+       return $id;
+}
+}
+sub get_gcc_data {
+       eval $start if $b_log;
+       my ($gcc,@data,@gccs,@temp);
+       # NOTE: We can't use program_version because we don't yet know where
+       # the version number is
+       if (my $program = check_program('gcc') ){
+               @data = grabber("$program --version 2>/dev/null");
+               $gcc = awk(\@data,'^gcc');
+       }
+       if ($gcc){
+               # strip out: gcc (Debian 6.3.0-18) 6.3.0 20170516
+               # gcc (GCC) 4.2.2 20070831 prerelease [FreeBSD]
+               $gcc =~ s/\([^\)]*\)//g;
+               $gcc = get_piece($gcc,2);
+       }
+       if ($extra > 1){
+               # glob /usr/bin for gccs, strip out all non numeric values
+               @temp = globber('/usr/bin/gcc-*');
+               foreach (@temp){
+                       if (/\/gcc-([0-9.]+)$/){
+                               push @gccs, $1;
+                       }
+               }
+       }
+       unshift @gccs, $gcc;
+       log_data('dump','@gccs',\@gccs) if $b_log;
+       eval $end if $b_log;
+       return @gccs;
+}
+# rasberry pi only
+sub get_gpu_ram_arm {
+       eval $start if $b_log;
+       my ($gpu_ram) = (0);
+       if (my $program = check_program('vcgencmd')){
+               # gpu=128M
+               # "VCHI initialization failed" - you need to add video group to your user
+               my $working = (grabber("$program get_mem gpu 2>/dev/null"))[0];
+               $working = (split /\s*=\s*/, $working)[1] if $working;
+               $gpu_ram = translate_size($working) if $working;
+       }
+       log_data('data',"gpu ram: $gpu_ram") if $b_log;
+       eval $end if $b_log;
+       return $gpu_ram;
+}
+# standard systems
+sub get_gpu_ram {
+       eval $start if $b_log;
+       my ($gpu_ram) = (0);
+       eval $end if $b_log;
+       return $gpu_ram;
+}
+
+sub get_hostname {
+       eval $start if $b_log;
+       my $hostname = '';
+       if ( $ENV{'HOSTNAME'} ){
+               $hostname = $ENV{'HOSTNAME'};
+       }
+       elsif ( !$bsd_type && -f "/proc/sys/kernel/hostname" ){
+               $hostname = (reader('/proc/sys/kernel/hostname'))[0];
+       }
+       # puppy removed this from core modules, sigh
+       # this is faster than subshell of hostname
+       elsif (check_module('Sys::Hostname')){
+               import Sys::Hostname;
+               $hostname = Sys::Hostname::hostname();
+       }
+       elsif (my $program = check_program('hostname')) {
+               $hostname = (grabber("$program 2>/dev/null"))[0];
+       }
+       $hostname ||= 'N/A';
+       eval $end if $b_log;
+       return $hostname;
+}
+
+sub get_init_data {
+       eval $start if $b_log;
+       my $runlevel = get_runlevel_data();
+       my $default = ($extra > 1) ? get_runlevel_default() : '';
+       my ($init,$init_version,$rc,$rc_version,$program) = ('','','','','');
+       my $comm = ( -e '/proc/1/comm' ) ? (reader('/proc/1/comm'))[0] : '';
+       my (@data);
+       # this test is pretty solid, if pid 1 is owned by systemd, it is systemd
+       # otherwise that is 'init', which covers the rest of the init systems.
+       # more data may be needed for other init systems.
+       if ( $comm ){
+               if ( $comm =~ /systemd/ ){
+                       $init = 'systemd';
+                       if ( $program = check_program('systemd')){
+                               $init_version = program_version($program,'^systemd','2','--version');
+                       }
+                       if (!$init_version && ($program = check_program('systemctl') ) ){
+                               $init_version = program_version($program,'^systemd','2','--version');
+                       }
+               }
+               # epoch version == Epoch Init System 1.0.1 "Sage"
+               elsif ($comm =~ /epoch/){
+                       $init = 'Epoch';
+                       $init_version = program_version('epoch', '^Epoch', '4','version');
+               }
+               # missing data: note, runit can install as a dependency without being the 
+               # init system: http://smarden.org/runit/sv.8.html
+               # NOTE: the proc test won't work on bsds, so if runit is used on bsds we 
+               # will need more data
+               elsif ($comm =~ /runit/){
+                       $init = 'runit';
+               }
+       }
+       if (!$init){
+               # output: /sbin/init --version:  init (upstart 1.1)
+               # init (upstart 0.6.3)
+               if ($init_version = program_version('init', 'upstart', '3','--version')){
+                       $init = 'Upstart';
+               }
+               elsif (check_program('launchctl')){
+                       $init = 'launchd';
+               }
+               elsif ( -f '/etc/inittab' ){
+                       $init = 'SysVinit';
+                       if (check_program('strings')){
+                               @data = grabber('strings /sbin/init');
+                               $init_version = awk(\@data,'version\s+[0-9]');
+                               $init_version = get_piece($init_version,2) if $init_version;
+                       }
+               }
+               elsif ( -f '/etc/ttys' ){
+                       $init = 'init (BSD)';
+               }
+       }
+       if ( grep { /openrc/ } globber('/run/*openrc*') ){
+               $rc = 'OpenRC';
+               # /sbin/openrc --version == openrc (OpenRC) 0.13
+               if ($program = check_program('openrc')){
+                       $rc_version = program_version($program, '^openrc', '3','--version');
+               }
+               # /sbin/rc --version == rc (OpenRC) 0.11.8 (Gentoo Linux)
+               elsif ($program = check_program('rc')){
+                       $rc_version = program_version($program, '^rc', '3','--version');
+               }
+               if ( -e '/run/openrc/softlevel' ){
+                       $runlevel = (reader('/run/openrc/softlevel'))[0];
+               }
+               elsif ( -e '/var/run/openrc/softlevel'){
+                       $runlevel = (reader('/var/run/openrc/softlevel'))[0];
+               }
+               elsif ( $program = check_program('rc-status')){
+                       $runlevel = (grabber("$program -r 2>/dev/null"))[0];
+               }
+       }
+       my %init = (
+       'init-type' => $init,
+       'init-version' => $init_version,
+       'rc-type' => $rc,
+       'rc-version' => $rc_version,
+       'runlevel' => $runlevel,
+       'default' => $default,
+       );
+       eval $end if $b_log;
+       return %init;
+}
+
+sub get_kernel_data {
+       eval $start if $b_log;
+       my ($kernel,$ksplice) = ('','');
+       # Linux; yawn; 4.9.0-3.1-liquorix-686-pae; #1 ZEN SMP PREEMPT liquorix 4.9-4 (2017-01-14); i686
+       # FreeBSD; siwi.pair.com; 8.2-STABLE; FreeBSD 8.2-STABLE #0: Tue May 31 14:36:14 EDT 2016     erik5@iddhi.pair.com:/usr/obj/usr/src/sys/82PAIRx-AMD64; amd64
+       if (@uname){
+               $kernel = $uname[2];
+               if (check_program('uptrack-uname') && $kernel){
+                       $ksplice = qx(uptrack-uname -rm);
+                       $ksplice = trimmer($ksplice);
+                       $kernel = ($ksplice) ? $ksplice . ' (ksplice)' : $kernel;
+               }
+               $kernel .= ' ' . $uname[-1];
+               $kernel = ($bsd_type) ? $uname[0] . ' ' . $kernel : $kernel;
+       }
+       $kernel ||= 'N/A';
+       log_data('data',"kernel: $kernel ksplice: $ksplice") if $b_log;
+       eval $end if $b_log;
+       return $kernel;
+}
+
+sub get_kernel_bits {
+       eval $start if $b_log;
+       my $bits = '';
+       if (@uname){
+               $bits = $uname[-1];
+               $bits = ($bits =~ /64/ ) ? 64 : 32;
+       }
+       $bits ||= 'N/A';
+       eval $end if $b_log;
+       return $bits;
+}
+
+sub get_memory_data {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my ($memory);
+       if (my $file = system_files('meminfo') ) {
+               $memory = get_memory_data_linux($type,$file);
+       }
+       else {
+               $memory = get_memory_data_bsd($type);
+       }
+       eval $end if $b_log;
+       return $memory;
+}
+
+sub get_memory_data_linux {
+       eval $start if $b_log;
+       my ($type,$file) = @_;
+       my ($gpu,$memory,$not_used,$total) = (0,'',0,0);
+       my @data = reader($file);
+       foreach (@data){
+               if ($_ =~ /^MemTotal:/){
+                       $total = get_piece($_,2);
+               }
+               elsif ($_ =~ /^(MemFree|Buffers|Cached):/){
+                       $not_used +=  get_piece($_,2);
+               }
+       }
+       $gpu = get_gpu_ram_arm() if $b_arm;
+       #$gpu = translate_size('128M');
+       $total += $gpu;
+       my $used = $total - ($not_used);
+       my $percent = ($used && $total) ? sprintf("%.1f", ($used/$total)*100) : '';
+       if ($type eq 'string'){
+               $percent = " ($percent%)" if $percent;
+               $memory = sprintf("%.1f/%.1f MiB", $used/1024, $total/1024) . $percent;
+       }
+       else {
+               $memory = "$total:$used:$percent:$gpu";
+       }
+       log_data('data',"memory: $memory") if $b_log;
+       eval $end if $b_log;
+       return $memory;
+}
+
+# openbsd/linux
+# procs    memory       page                    disks    traps          cpu
+# r b w    avm     fre  flt  re  pi  po  fr  sr wd0 wd1  int   sys   cs us sy id
+# 0 0 0  55256 1484092  171   0   0   0   0   0   2   0   12   460   39  3  1 96
+# freebsd:
+# procs      memory      page                    disks     faults         cpu
+# r b w     avm    fre   flt  re  pi  po    fr  sr ad0 ad1   in   sy   cs us sy id
+# 0 0 0  21880M  6444M   924  32  11   0   822 827   0   0  853  832  463  8  3 88
+# with -H
+# 2 0 0 14925812  936448    36  13  10   0    84  35   0   0   84   30   42 11  3 86
+# dragonfly
+#  procs      memory      page                    disks     faults      cpu
+#  r b w     avm    fre  flt  re  pi  po  fr  sr ad0 ad1   in   sy  cs us sy id
+#  0 0 0       0  84060 30273993 2845 12742 1164 407498171 320960902   0   0 ....
+sub get_memory_data_bsd {
+       eval $start if $b_log;
+       my ($type) = @_;
+       my $memory = '';
+       my ($avail,$total,$free_mem,$real_mem) = (0,0,0,0);
+       my (@data,$message);
+       my $arg = ($bsd_type ne 'openbsd') ? '-H' : '';
+       if (my $program = check_program('vmstat')){
+               # see above, it's the last line. -H makes it hopefully all in kB so no need 
+               # for K/M/G tests
+               my $row = (grabber("vmstat $arg 2>/dev/null",'\n','strip'))[-1];
+               if ( $row ){
+                       @data = split /\s+/, $row;
+                       # dragonfly can have 0 avg, but they may fix that so make test dynamic
+                       if ($data[3] != 0){
+                               $avail = ($bsd_type ne 'openbsd') ? sprintf ('%.1f',$data[3]/1024) : $data[3];
+                       }
+                       elsif ($data[4] != 0){
+                               $free_mem = sprintf ('%.1f',$data[4]);
+                       }
+               }
+       }
+       ## code to get total goes here:
+       my $ref = $alerts{'sysctl'};
+       if ($$ref{'action'} eq 'use'){
+               # for dragonfly, we will use free mem, not used because free is 0
+               my @working;
+               foreach (@sysctl){
+                       # freebsd seems to use bytes here
+                       if (!$real_mem && /^hw.physmem:/){
+                               @working = split /:\s*/,$_;
+                               #if ($working[1]){
+                                       $working[1] =~ s/^[^0-9]+|[^0-9]+$//g;
+                                       $real_mem = sprintf("%.1f", $working[1]/1024);
+                               #}
+                               last if $free_mem;
+                       }
+                       # But, it uses K here. Openbsd/Dragonfly do not seem to have this item
+                       # this can be either: Free Memory OR Free Memory Pages
+                       elsif (/^Free Memory:/){
+                               @working = split /:\s*/,$_;
+                               $working[1] =~ s/[^0-9]+//g;
+                               $free_mem = sprintf("%.1f", $working[1]);
+                               last if $real_mem;
+                       }
+               }
+       }
+       else {
+               $message = "sysctl $$ref{'action'}"
+       }
+       # not using, but leave in place for a bit in case we want it
+       # my $type = ($free_mem) ? ' free':'' ;
+       # hack: temp fix for openbsd/darwin: in case no free mem was detected but we have physmem
+       if (($avail || $free_mem) && !$real_mem){
+               my $error = ($message) ? $message: 'total N/A';
+               my $used = (!$free_mem) ? $avail : $real_mem - $free_mem;
+               if ($type eq 'string'){
+                       $used = sprintf("%.1f",$used/1024);
+                       $memory = "$used/($error) MB";
+               }
+               else {
+                       $memory = "$error:$used:";
+               }
+       }
+       # use openbsd/dragonfly avail mem data if available
+       elsif (($avail || $free_mem) && $real_mem) {
+               my $used = (!$free_mem) ? $avail : $real_mem - $free_mem;
+               my $percent = ($used && $real_mem) ? sprintf("%.1f", ($used/$real_mem)*100) : '';
+               if ($type eq 'string'){
+                       $used = sprintf("%.1f",$used/1024);
+                       $real_mem = sprintf("%.1f",$real_mem/1024);
+                       $percent = " ($percent)" if $percent;
+                       $memory = "$used/$real_mem MB" . $percent;
+               }
+               else {
+                       $memory = "$real_mem:$used:$percent:0";
+               }
+       }
+       eval $end if $b_log;
+       return $memory;
+}
+
+sub get_module_version {
+       eval $start if $b_log;
+       my ($module) = @_;
+       return if ! $module;
+       my ($version);
+       my $path = "/sys/module/$module/version";
+       if (-f $path){
+               $version = (reader($path))[0];
+       }
+       elsif (-f "/sys/module/$module/uevent"){
+               $version = 'kernel';
+       }
+       #print "version:$version\n";
+       if (!$version) {
+               if (my $path = check_program('modinfo')){
+                       my @data = grabber("$path $module 2>/dev/null");
+                       $version = awk(\@data,'^version',2,':\s+') if @data;
+               }
+       }
+       $version ||= '';
+       eval $end if $b_log;
+       return $version;
+}
+
+# args: 1 - pci device string; 2 - pci cleaned subsystem string
+sub get_pci_vendor {
+       eval $start if $b_log;
+       my ($device, $subsystem) = @_;
+       return if !$subsystem;
+       my ($vendor,$sep) = ('','');
+       my @data = split /\s+/, $subsystem;
+       foreach (@data){
+               if ($device !~ !/\b$_\b/){
+                       $vendor .= $sep . $_;
+                       $sep = ' ';
+               }
+               else {
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return $vendor;
+}
+
+# # check? /var/run/nologin for bsds?
+sub get_runlevel_data {
+       eval $start if $b_log;
+       my $runlevel = '';
+       if ( my $program = check_program('runlevel')){
+               $runlevel = (grabber("$program 2>/dev/null"))[0];
+               $runlevel =~ s/[^\d]//g if $runlevel;
+               #print_line($runlevel . ";;");
+       }
+       eval $end if $b_log;
+       return $runlevel;
+}
+
+# note: it appears that at least as of 2014-01-13, /etc/inittab is going 
+# to be used for default runlevel in upstart/sysvinit. systemd default is 
+# not always set so check to see if it's linked.
+sub get_runlevel_default {
+       eval $start if $b_log;
+       my @data;
+       my $default = '';
+       my $b_systemd = 0;
+       my $inittab = '/etc/inittab';
+       my $systemd = '/etc/systemd/system/default.target';
+       my $upstart = '/etc/init/rc-sysinit.conf';
+       # note: systemd systems do not necessarily have this link created
+       if ( -e $systemd){
+               $default = readlink($systemd);
+               $default =~ s/.*\/// if $default; 
+               $b_systemd = 1;
+       }
+       # http://askubuntu.com/questions/86483/how-can-i-see-or-change-default-run-level
+       # note that technically default can be changed at boot but for inxi purposes 
+       # that does not matter, we just want to know the system default
+       elsif ( -e $upstart){
+               # env DEFAULT_RUNLEVEL=2
+               @data = reader($upstart);
+               $default = awk(\@data,'^env\s+DEFAULT_RUNLEVEL',2,'=');
+       }
+       # handle weird cases where null but inittab exists
+       if (!$default && -e $inittab ){
+               @data = reader($inittab);
+               $default = awk(\@data,'^id.*initdefault',2,':');
+       }
+       eval $end if $b_log;
+       return $default;
+}
+
+sub get_self_version {
+       eval $start if $b_log;
+       my $patch = $self_patch;
+       if ( $patch ne '' ){
+               # for cases where it was for example: 00-b1 clean to -b1
+               $patch =~ s/^[0]+-?//;
+               $patch = "-$patch" if $patch;
+       }
+       eval $end if $b_log;
+       return $self_version . $patch;
+}
+
+sub get_shell_data {
+       eval $start if $b_log;
+       my ($ppid) = @_;
+       my $cmd = "ps -p $ppid -o comm= 2>/dev/null";
+       my $shell = qx($cmd);
+       log_data('cmd',$cmd) if $b_log;
+       chomp($shell);
+       if ($shell){
+               #print "shell pre: $shell\n";
+               # when run in debugger subshell, would return sh as shell,
+               # and parent as perl, that is, pinxi itself, which is actually right.
+               # trim leading /.../ off just in case. ps -p should return the name, not path 
+               # but at least one user dataset suggests otherwise so just do it for all.
+               $shell =~ s/^.*\///; 
+               my $working = $ENV{'SHELL'};
+               $working =~ s/^.*\///; 
+               # NOTE: su -c "inxi -F" results in shell being su
+               if (($shell eq 'sh' || $shell eq 'sudo' || $shell eq 'su' ) && $shell ne $working){
+                       $client{'su-start'} = $shell if ($shell eq 'sudo' || $shell eq 'su');
+                       $shell = $working;
+               }
+               #print "shell post: $shell\n";
+               # sh because -v/--version doesn't work on it
+               if ( $shell ne 'sh' ) {
+                       @app = main::program_values(lc($shell));
+                       if ($app[0]){
+                               $client{'version'} = main::program_version($shell,$app[0],$app[1],$app[2],$app[5],$app[6]);
+                       }
+                       # guess that it's two and --version
+                       else {
+                               # we're just guessing at the search phrase and position
+                               if ($shell){
+                                       $client{'version'} = main::program_version($shell,$shell,2,'');
+                               }
+                               else {
+                                       $client{'version'} = row_defaults('unknown-shell');
+                               }
+                       }
+                       $client{'version'} =~ s/(\(.*|-release|-version)//;
+               }
+               $client{'name'} = lc($shell);
+               $client{'name-print'} = $shell;
+       }
+       else {
+               $client{'name'} = 'shell';
+               $client{'name-print'} = 'Unknown Shell';
+       }
+       $client{'su-start'} = 'sudo' if (!$client{'su-start'} && $ENV{'SUDO_USER'});
+       eval $end if $b_log;
+}
+
+sub get_shell_source {
+       eval $start if $b_log;
+       my (@data);
+       my ($msg,$self_parent,$shell_parent) = ('','','');
+       my $ppid = getppid();
+       $self_parent = get_start_parent($ppid);
+       if ($b_log){
+               $msg = ($ppid) ? "self parent: $self_parent ppid: $ppid": "self parent: undefined";
+               log_data('data',$msg);
+       }
+       #print "self parent: $self_parent ppid: $ppid\n";
+       if ($self_parent){
+               $shell_parent = get_shell_parent($self_parent);
+               $client{'su-start'} = $shell_parent if ($shell_parent eq 'su' && !$client{'su-start'});
+               #print "shell parent 1: $shell_parent\n";
+               if ($b_log){
+                       $msg = ($shell_parent) ? "shell parent 1: $shell_parent": "shell parent 1: undefined";
+                       log_data('data',$msg);
+               }
+               # in case sudo starts inxi, parent is shell (or perl inxi if run by debugger)
+               # so: perl (2) started pinxi with sudo (3) in sh (4) in terminal
+               for my $i (2..4){
+                       if ( $shell_parent && 
+                    $shell_parent =~ /^(bash|csh|dash|ksh|lksh|loksh|mksh|pdksh|perl|sh|su|sudo|tcsh|zsh)$/ ){
+                               # no idea why have to do script_parent action twice in su case, but you do.
+                               $self_parent = get_start_parent($self_parent);
+                               $shell_parent = get_shell_parent($self_parent);
+                               #print "shell parent 2: $shell_parent\n";
+                               if ($b_log){
+                                       $msg = ($shell_parent) ? "shell parent $i: $shell_parent": "shell parent $i: undefined";
+                                       log_data('data',$msg);
+                               }
+                       }
+                       else {
+                               last;
+                       }
+               }
+               # to work around a ps -p or gnome-terminal bug, which returns 
+               # gnome-terminal- trim - off end 
+               $shell_parent =~ s/-$// if $shell_parent;
+       }
+       if ($b_log){
+               $self_parent ||= '';
+               $shell_parent ||= '';
+               log_data('data',"parents: self: $self_parent shell: $shell_parent");
+       }
+       eval $end if $b_log;
+       return $shell_parent;
+}
+
+# utilities for get_shell_source 
+# arg: 1 - parent id
+sub get_start_parent {
+       eval $start if $b_log;
+       my ($parent) = @_;
+       # ps -j -fp : bsds ps do not have -f for PPID, so we can't get the ppid
+       my $cmd = "ps -j -fp $parent";
+       log_data('cmd',$cmd) if $b_log;
+       my @data = grabber($cmd);
+       #shift @data if @data;
+       my $self_parent = awk(\@data,"$parent",3,'\s+');
+       eval $end if $b_log;
+       return $self_parent;
+}
+
+# arg: 1 - parent id
+sub get_shell_parent {
+       eval $start if $b_log;
+       my ($parent) = @_;
+       my $cmd = "ps -j -p $parent";
+       log_data('cmd',$cmd) if $b_log;
+       my @data = grabber($cmd,'strip');
+       #shift @data if @data;
+       my $shell_parent = awk(\@data, "$parent",-1,'\s+');
+       eval $end if $b_log;
+       return $shell_parent;
+}
+
+# this will test against default IP like: (:0) vs full IP to determine 
+# ssh status. Surprisingly easy test? Cross platform
+sub get_ssh_status {
+       eval $start if $b_log;
+       my ($b_ssh,$ssh);
+       # fred   pts/10       2018-03-24 16:20 (:0.0)
+       # fred-remote pts/1        2018-03-27 17:13 (43.43.43.43)
+       if (my $program = check_program('who')){
+               $ssh = (grabber("$program am i 2>/dev/null"))[0];
+               # crude IP validation
+               if ($ssh && $ssh =~ /\(([:0-9a-f]{8,}|[1-9][\.0-9]{6,})\)$/){
+                       $b_ssh = 1;
+               }
+       }
+       eval $end if $b_log;
+       return $b_ssh;
+}
+
+sub get_tty_console_irc {
+       eval $start if $b_log;
+       my ($type) = @_;
+       return $tty_session if defined $tty_session;
+       if ( $type eq 'vtrn' && defined $ENV{'XDG_VTNR'} ){
+               $tty_session = $ENV{'XDG_VTNR'};
+       }
+       else {
+               my $ppid = getppid();
+               $tty_session = awk(\@ps_aux,".*$ppid.*$client{'name'}",7,'\s+');
+               $tty_session =~ s/^[^[0-9]+// if $tty_session;
+       }
+       $tty_session = '' if ! defined $tty_session;
+       log_data('data',"conole-irc-tty:$tty_session") if $b_log;
+       eval $end if $b_log;
+       return $tty_session;
+}
+
+sub get_tty_number {
+       eval $start if $b_log;
+       my ($tty);
+       if ( defined $ENV{'XDG_VTNR'} ){
+               $tty = $ENV{'XDG_VTNR'};
+       }
+       else {
+               $tty = POSIX::ttyname(1);
+               #variants: /dev/pts/1 /dev/tty1 /dev/ttyp2 /dev/ttyra [hex number a]
+               $tty =~ s/.*\/[^0-9]*//g if defined $tty;
+       }
+       $tty = '' if ! defined $tty;
+       log_data('data',"tty:$tty") if $b_log;
+       eval $end if $b_log;
+       return $tty;
+}
+
+# 2:58PM  up 437 days,  8:18, 3 users, load averages: 2.03, 1.72, 1.77
+# 04:29:08 up  3:18,  3 users,  load average: 0,00, 0,00, 0,00
+# 10:23PM  up 5 days, 16:17, 1 user, load averages: 0.85, 0.90, 1.00
+# 05:36:47 up 1 day,  3:28,  4 users,  load average: 1,88, 0,98, 0,62
+# 05:36:47 up 1 day,  3 min,  4 users,  load average: 1,88, 0,98, 0,62
+sub get_uptime {
+       eval $start if $b_log;
+       my ($days,$hours,$minutes,$uptime) = ('','','','');
+       if (check_program('uptime')){
+               $uptime = qx(uptime);
+               $uptime = trimmer($uptime);
+               #$uptime = '05:36:47 up 3 min,  4 users,  load average: 1,88, 0,98, 0,62';
+               if ($uptime && 
+                $uptime =~ /[\S]+\s+up\s+(([0-9]+)\s+day[s]?,\s+)?(([0-9]{1,2}):([0-9]{1,2})|([0-9]+)\smin[s]?),\s+[0-9]+\s+user/){
+                       $days = $2 . 'd' if $2;
+                       $days .= ' ' if ($days && ($4 || $6));
+                       if ($4 && $5){
+                               $hours = $4 . 'h ';
+                               $minutes = $5 . 'm';
+                       }
+                       elsif ($6){
+                               $minutes = $6 . 'm';
+                               
+                       }
+                       $uptime = $days . $hours . $minutes;
+               }
+       }
+       $uptime ||= 'N/A';
+       eval $end if $b_log;
+       return $uptime;
+}
+
+# NOTE: annoyingly, /sys does NOT actually use the id, it uses 
+# the count of physical devices, starting at 0 for hub, on the bus.
+# args: $1 - $bus number; $2 - vendor:chip id
+sub get_usb_drivers {
+       eval $start if $b_log;
+       my ($bus,$id) = @_;
+       return if !$bus || !$id;# these will be > 0
+       my ($chip,$driver,$file,$path,$vendor,$working,$working2,@drivers,@temp);
+       @temp = split /:/, $id;
+       $vendor = $temp[0];
+       $chip = $temp[1];
+       # some have it one level deeper than others
+       my @globs = ("/sys/bus/usb/devices/usb$bus/$bus-*/","/sys/bus/usb/devices/usb$bus/$bus-*/$bus-*/");
+       foreach (@globs){
+               $path = get_usb_path($vendor,$chip,$_);
+               last if $path;
+       }
+       if ($path){
+               if ( -e "${path}driver"){
+                       if ($working = Cwd::abs_path("${path}driver")){
+                               $working =~ s/^.*\///;
+                               if ($working && $working ne 'usb' && $working ne 'usbhid'){
+                                       push @drivers, $working;
+                               }
+                       }
+               }
+               # test 2
+               @temp = main::globber("$path$bus-*/");
+               #print "@temp\n";
+               foreach (@temp){
+                       #print "p2:". $_ . "driver\n";
+                       $file = $_ . 'driver';
+                       #print "f:$file\n";
+                       if (-e $file){
+                               #print "here\n";
+                               #print (Cwd::abs_path($file), "\n");
+                               if ($working = Cwd::abs_path($file)){
+                                       #print "w:$working\n";
+                                       $working =~ s/^.*\///;
+                                       if ($working && $working ne 'usb' && $working ne 'usbhid' && ! grep {/$working/} @drivers){
+                                               push @drivers, $working;
+                                       }
+                               }
+                       }
+               }
+               #print join "\n", @drivers, "\n";
+               $driver = join ',', @drivers if @drivers;
+       }
+       @temp = ($driver,$path);
+       eval $end if $b_log;
+       return @temp;
+}
+
+sub get_usb_path {
+       eval $start if $b_log;
+       my ($vendor,$chip,$glob) = @_;
+       my ($path,$working,$working2);
+       #print "$vendor,$chip,$glob\n";
+       my @temp = main::globber($glob);
+       #print join "\n", @temp, "\n";
+       # first we need to get the device path, since it's not predictable
+       foreach (@temp){
+               #print "$_\n";
+               $working = $_ . 'idVendor';
+               $working2 = $_ . 'idProduct';
+               if (-e $working && (main::reader($working))[0] eq $vendor && 
+                   -e $working2 && (main::reader($working2))[0] eq $chip){
+                       $path = $_;
+                       #print "$_\n";
+                       last;
+               }
+       }
+       eval $end if $b_log;
+       return $path
+}
+
+
+#### -------------------------------------------------------------------
+#### SET DATA VALUES
+#### -------------------------------------------------------------------
+
+sub set_dmesg_boot_data {
+       eval $start if $b_log;
+       my ($file,@temp);
+       my ($counter) = (0);
+       $b_dmesg_boot_check = 1;
+       if (!$b_fake_dboot){
+               $file = system_files('dmesg-boot');
+       }
+       else {
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmesg-boot/bsd-disks-diabolus.txt";
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmesg-boot/freebsd-disks-solestar.txt";
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmesg-boot/freebsd-enceladus-1.txt";
+               ## matches: toshiba: openbsd-5.6-sysctl-2.txt
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmesg-boot/openbsd-5.6-dmesg.boot-1.txt";
+               ## matches: compaq: openbsd-5.6-sysctl-1.txt"
+               $file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmesg-boot/openbsd-dmesg.boot-1.txt";
+       }
+       if ($file){
+               return if ! -r $file;
+               @dmesg_boot = reader($file);
+               # some dmesg repeats, so we need to dump the second and > iterations
+               # replace all indented items with ~ so we can id them easily while
+               # processing note that if user, may get error of read permissions
+               # for some weird reason, real mem and avail mem are use a '=' separator, 
+               # who knows why, the others are ':'
+               foreach (@dmesg_boot){
+                       $counter++ if /^(OpenBSD|DragonFly|FreeBSD is a registered trademark)/;
+                       last if $counter > 1;
+                       $_ =~ s/\s*=\s*|:\s*/:/;
+                       $_ =~ s/\"//g;
+                       $_ =~ s/^\s+/~/;
+                       $_ =~ s/\s\s/ /g;
+                       $_ =~ s/^(\S+)\sat\s/$1:at /; # ada0 at ahcich0
+                       push @temp, $_;
+                       if (/^bios[0-9]:(at|vendor)/){
+                               push @sysctl_machine, $_;
+                       }
+               }
+               @dmesg_boot = @temp;
+               # FreeBSD: 'da*' is a USB device 'ada*' is a SATA device 'mmcsd*' is an SD card
+               if ($b_dm_boot_disk && @dmesg_boot){
+                       @dm_boot_disk = grep {/^(ad|ada|da|mmcblk|mmcsd|nvme[0-9]+n|sd|wd)[0-9]+(:|\sat\s)/} @dmesg_boot;
+                       log_data('dump','@dm_boot_disk',\@dm_boot_disk) if $b_log;
+                       print Dumper \@dm_boot_disk if $test[9];
+               }
+               if ($b_dm_boot_optical && @dmesg_boot){
+                       @dm_boot_optical = grep {/^(cd)[0-9]+(\([^)]+\))?(:|\sat\s)/} @dmesg_boot;
+                       log_data('dump','@dm_boot_optical',\@dm_boot_optical) if $b_log;
+                       print Dumper \@dm_boot_optical if $test[9];
+               }
+       }
+       log_data('dump','@dmesg_boot',\@dmesg_boot) if $b_log;
+       #print Dumper \@dmesg_boot if $test[9];
+       eval $end if $b_log;
+}
+
+# note, all actual tests have already been run in check_tools so if we
+# got here, we're good. 
+sub set_dmi_data {
+       eval $start if $b_log;
+       if ($alerts{'dmidecode'}{'action'} eq 'use' ){
+               set_dmidecode_data();
+       }
+       eval $end if $b_log;
+}
+
+sub set_dmidecode_data {
+       eval $start if $b_log;
+       my ($content,@data,@working,$type,$handle);
+       #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/dmidecode/pci-freebsd-8.2-2";
+       #open my $fh, '<', $file or die "can't open $file: $!";
+       #chomp(@data = <$fh>);
+       my $path = check_program('dmidecode'); 
+       $content = qx($path 2>/dev/null) if $path;
+       @data = split /\n/, $content;
+       # we don't need the opener lines of dmidecode output
+       # but we do want to preserve the indentation. Empty lines
+       # won't matter, they will be skipped, so no need to handle them.
+       # some dmidecodes do not use empty line separators
+       splice @data, 0, 5 if @data;
+       my $j = 0;
+       my $b_skip = 1;
+       foreach (@data){
+               if (!/^Hand/){
+                       next if $b_skip;
+                       if (/^[^\s]/){
+                               $_ = lc($_);
+                               $_ =~ s/\s(information)//;
+                               push @working, $_;
+                       }
+                       elsif (/^\t/){
+                               $_ =~ s/^\t\t/~/;
+                               $_ =~ s/^\t|\s+$//g;
+                               push @working, $_;
+                       }
+               }
+               elsif (/^Handle\s(0x[0-9A-Fa-f]+).*DMI\stype\s([0-9]+),.*/){
+                       $j = scalar @dmi;
+                       $handle = hex($1);
+                       $type = $2;
+                       $b_slot_tool = 1 if $type && $type == 9;
+                       $b_skip = ( $type > 126 )? 1 : 0;
+                       next if $b_skip;
+                       # we don't need 32, system boot, or 127, end of table
+                       if (@working){
+                               if ($working[0] != 32 && $working[0] < 127){
+                                       $dmi[$j] = (
+                                       [@working],
+                                       );
+                               }
+                       }
+                       @working = ($type,$handle);
+               }
+       }
+       if (@working && $working[0] != 32 && $working[0] != 127){
+               $j = scalar @dmi;
+               $dmi[$j] = (
+               [@working],
+               );
+       }
+       # last by not least, sort it by dmi type, now we don't have to worry
+       # about random dmi type ordering in the data, which happens. Also sort 
+       # by handle, as secondary sort.
+       @dmi = sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @dmi;
+       main::log_data('dump','@dmi',\@dmi) if $b_log;
+       print Dumper \@dmi if $test[2];
+       eval $end if $b_log;
+}
+
+sub set_ip_data {
+       eval $start if $b_log;
+       if ($alerts{'ip'}{'action'} eq 'use' ){
+               set_ip_addr();
+       }
+       elsif ($alerts{'ifconfig'}{'action'} eq 'use'){
+               set_ifconfig();
+       }
+       eval $end if $b_log;
+}
+
+sub set_ip_addr {
+       eval $start if $b_log;
+       my $program = check_program('ip');
+       my @data = grabber("$program addr 2>/dev/null",'\n','strip') if $program;
+       #my @data = reader("$ENV{'HOME'}/bin/scripts/inxi/data/if/scope-ipaddr-1.txt",'strip') or die $!;
+       my ($b_skip,$broadcast,$if,$ip,@ips,$scope,$if_id,$type,@temp,@temp2);
+       foreach (@data){
+               if (/^[0-9]/){
+                       #print "$_\n";
+                       if (@ips){
+                       #print "$if\n";
+                               @temp = ($if,[@ips]);
+                               @ifs = (@ifs,@temp);
+                               @ips = ();
+                       }
+                       @temp = split /:\s+/,$_;
+                       $if = $temp[1];
+                       if ($if eq 'lo'){
+                               $b_skip = 1;
+                               $if = '';
+                               next;
+                       }
+                       $b_skip = 0;
+                       @temp = ();
+               }
+               elsif (!$b_skip && /^inet/){
+                       #print "$_\n";
+                       @temp = split /\s+/, $_;
+                       ($broadcast,$ip,$scope,$if_id,$type) = ('','','','','');
+                       $ip = $temp[1];
+                       $type = ($temp[0] eq 'inet') ? 4 : 6 ;
+                       if ($temp[2] eq 'brd'){
+                               $broadcast = $temp[3];
+                       }
+                       if (/scope\s([^\s]+)(\s(.+))?/){
+                               $scope = $1;
+                               $if_id = $3;
+                       }
+                       @temp = ($type,$ip,$broadcast,$scope,$if_id);
+                       @ips = (@ips,[@temp]);
+                       #print Dumper \@ips;
+               }
+       }
+       #print Dumper \@ips if $test[4];
+       if (@ips){
+               @temp = ($if,[@ips]);
+               @ifs = (@ifs,@temp);
+       }
+       main::log_data('dump','@ifs',\@ifs) if $b_log;
+       print Dumper \@ifs if $test[3];
+       eval $end if $b_log;
+}
+
+sub set_ifconfig {
+       eval $start if $b_log;
+       my $program = check_program('ifconfig'); # not in user path, sbin
+       my @data = grabber("$program 2>/dev/null",'\n','') if $program;
+       #my @data = reader("$ENV{'HOME'}/bin/scripts/inxi/data/if/vps-ifconfig-1.txt",'') or die $!;
+       my ($b_skip,$broadcast,$if,@ips_bsd,$ip,@ips,$scope,$if_id,$type,@temp,@temp2);
+       my ($state,$speed,$duplex,$mac);
+       foreach (@data){
+               if (/^[\S]/i){
+                       #print "$_\n";
+                       if (@ips){
+                       #print "here\n";
+                               @temp = ($if,[@ips]);
+                               @ifs = (@ifs,@temp);
+                               @ips = ();
+                       }
+                       if ($mac){
+                               @temp = ($if,[($state,$speed,$duplex,$mac)]);
+                               @ifs_bsd = (@ifs_bsd,@temp);
+                               ($state,$speed,$duplex,$mac,$if_id) = ('','','','','');
+                       }
+                       $if = (split /\s+/,$_)[0];
+                       $if =~ s/:$//; # em0: flags=8843
+                       $if_id = $if;
+                       $if = (split /:/, $if)[0] if $if;
+                       if ($if =~ /^lo/){
+                               $b_skip = 1;
+                               $if = '';
+                               $if_id = '';
+                               next;
+                       }
+                       $b_skip = 0;
+               }
+               # lladdr openbsd
+               elsif (!$b_skip && $bsd_type && /^\s+(ether|media|status|lladdr)/){
+                       $_ =~ s/^\s+//;
+                       # media: Ethernet 100baseTX <full-duplex> freebsd 7.3 
+                       # media: Ethernet autoselect (1000baseT <full-duplex>) Freebsd 8.2
+                       # 
+                       if (/^media/){
+                               # openbsd: media: Ethernet autoselect (1000baseT full-duplex)
+                               if ($bsd_type && $bsd_type eq 'openbsd'){
+                                       $_ =~ /\s\([\S]+\s([\S]+)\)/;
+                                       $duplex = $1;
+                               }
+                               else {
+                                       $_ =~ /<([^>]+)>/;
+                                       $duplex = $1;
+                               }
+                               $_ =~ /\s\(([1-9][\S]+\s)/;
+                               $speed = $1;
+                               $speed =~ s/\s+$// if $speed;
+                       }
+                       elsif (!$mac && /^ether|lladdr/){
+                               $mac = (split /\s+/, $_)[1];
+                       }
+                       elsif (/^status/){
+                               $state = (split /\s+/, $_)[1];
+                       }
+               }
+               elsif (!$b_skip && /^\s+inet/){
+                       #print "$_\n";
+                       $_ =~ s/^\s+//;
+                       $_ =~ s/addr:\s/addr:/;
+                       @temp = split /\s+/, $_;
+                       ($broadcast,$ip,$scope,$type) = ('','','','');
+                       $ip = $temp[1];
+                       # fe80::225:90ff:fe13:77ce%em0
+#                      $ip =~ s/^addr:|%([\S]+)//;
+                       if ($1 && $1 ne $if_id){
+                               $if_id = $1;
+                       }
+                       $type = ($temp[0] eq 'inet') ? 4 : 6 ;
+                       if (/(Bcast:|broadcast\s)([\S]+)/){
+                               $broadcast = $2;
+                       }
+                       if (/(scopeid\s[^<]+<|Scope:|scopeid\s)([^>]+)[>]?/){
+                               $scope = $2;
+                       }
+                       $scope = 'link' if $ip =~ /^fe80/;
+                       @temp = ($type,$ip,$broadcast,$scope,$if_id);
+                       @ips = (@ips,[@temp]);
+                       #print Dumper \@ips;
+               }
+       }
+       if (@ips){
+               @temp = ($if,[@ips]);
+               @ifs = (@ifs,@temp);
+       }
+       if ($mac){
+               @temp = ($if,[($state,$speed,$duplex,$mac)]);
+               @ifs_bsd = (@ifs_bsd,@temp);
+               ($state,$speed,$duplex,$mac) = ('','','','');
+       }
+       print Dumper \@ifs if $test[3];
+       print Dumper \@ifs_bsd if $test[3];
+       main::log_data('dump','@ifs',\@ifs) if $b_log;
+       main::log_data('dump','@ifs_bsd',\@ifs_bsd) if $b_log;
+       eval $end if $b_log;
+}
+
+sub set_pci_data {
+       eval $start if $b_log;
+       if ( $b_pci ){
+               if (!$bsd_type){
+                       if ($alerts{'lspci'}{'action'} eq 'use' ){
+                               set_lspci_data();
+                       }
+                       # ! -d '/proc/bus/pci'
+                       # this is sketchy, a sbc won't have pci, but a non sbc arm may have it, so 
+                       # build up both and see what happens
+                       if ($b_arm || $b_mips){
+                               set_soc_data();
+                       }
+               }
+               else {
+                       #if (1 == 1){
+                       if ($alerts{'pciconf'}{'action'} eq 'use'){
+                               set_pciconf_data();
+                       }
+               }
+       }
+       eval $end if $b_log;
+}
+
+# 0 type
+# 1 type_id
+# 2 bus_id
+# 3 sub_id
+# 4 device
+# 5 vendor_id
+# 6 chip_id
+# 7 rev
+# 8 port
+# 9 driver
+# 10 modules
+# 11 driver_nu [bsd, like: em0 - driver em; nu 0. Used to match IF in -n
+# 12 subsystem/vendor
+# 13 subsystem vendor:chip id
+sub set_lspci_data {
+       eval $start if $b_log;
+       my ($busid,$busid_nu,$content,$port,$driver,$modules,$device,$vendor_id,$chip_id,$rev,
+       $subsystem,$subsystem_id,$type,$type_id,@pcis,@temp,@working);
+       # my @pcis = grabber('lspci -nnv','\n','strip');
+       my $path = check_program('lspci');
+       $content = qx($path -nnv 2>/dev/null) if $path;
+       @pcis = split /\n/, $content if $content;
+       #my $file = "$ENV{HOME}/bin/scripts/inxi/data/lspci/racermach-1-knnv.txt";
+       #my $file = "$ENV{HOME}/bin/scripts/inxi/data/lspci/rk016013-knnv.txt";
+       #@pcis = reader($file);
+       #print scalar @pcis;
+       @pcis = map {$_ =~ s/^\s+//; $_} @pcis if @pcis;
+       $b_pci_tool = 1 if @pcis && scalar @pcis > 10;
+       foreach (@pcis){
+               #print "$_\n";
+               if ($device){
+                       if ($_ =~ /^\s*$/) {
+                               @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id,
+                               $rev,$port,$driver,$modules,$subsystem,$subsystem_id);
+                               @pci = (@pci,[@temp]);
+                               $device = '';
+                               #print "$busid $device_id r:$rev p: $port\n$type\n$device\n";
+                       }
+                       elsif ($_ =~ /^Subsystem.*\[([a-f0-9]{4}:[a-f0-9]{4})\]/){
+                               $subsystem_id = $1;
+                               $subsystem = (split /^Subsystem:\s*/,$_)[1];
+                               $subsystem =~ s/(\s?\[[^\]]+\])+$//g;
+                               $subsystem = cleaner($subsystem);
+                               $subsystem = pci_cleaner($subsystem,'pci');
+                               $subsystem = pci_cleaner_subsystem($subsystem);
+                               #print "ss:$subsystem\n";
+                       }
+                       elsif ($_ =~ /^I\/O\sports/){
+                               $port = (split /\s+/,$_)[3];
+                               #print "p:$port\n";
+                       }
+                       elsif ($_ =~ /^Kernel\sdriver\sin\suse/){
+                               $driver = (split /:\s*/,$_)[1];
+                       }
+                       elsif ($_ =~ /^Kernel\smodules/i){
+                               $modules = (split /:\s*/,$_)[1];
+                       }
+               }
+               # note: arm servers can have more complicated patterns
+               # 0002:01:02.0 Ethernet controller [0200]: Cavium, Inc. THUNDERX Network Interface Controller virtual function [177d:a034] (rev 08)
+               elsif ($_ =~ /^(([0-9a-f]{2,4}:)?[0-9a-f]{2}:[0-9a-f]{2})[.:]([0-9a-f]+)\s(.*)\s\[([0-9a-f]{4}):([0-9a-f]{4})\](\s\(rev\s([^\)]+)\))?/){
+                       $busid = $1;
+                       $busid_nu = hex($3);
+                       @working = split /:\s+/, $4;
+                       $device = $working[1];
+                       $type = $working[0];
+                       $vendor_id = $5;
+                       $chip_id = $6;
+                       $rev = ($8)? $8 : '';
+                       $device = cleaner($device);
+                       $working[0] =~ /\[([^\]]+)\]$/;
+                       $type_id = $1;
+                       $b_hardware_raid = 1 if $type_id eq '0104';
+                       $type = lc($type);
+                       $type = pci_cleaner($type,'pci');
+                       $type =~ s/\s+$//;
+                       $port = '';
+                       $driver = '';
+                       $modules = '';
+                       $subsystem = '';
+                       $subsystem_id = '';
+               }
+       }
+       if ($device && $busid){
+               @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id,
+               $rev,$port,$driver,$modules,$subsystem,$subsystem_id);
+               @pci = (@pci,[@temp]);
+               $device = '';
+       }
+       print Dumper \@pci if $test[4];
+       main::log_data('dump','@pci',\@pci) if $b_log;
+       eval $end if $b_log;
+}
+
+# em0@pci0:6:0:0:      class=0x020000 card=0x10d315d9 chip=0x10d38086 rev=0x00 hdr=0x00
+#     vendor     = 'Intel Corporation'
+#     device     = 'Intel 82574L Gigabit Ethernet Controller (82574L)'
+#     class      = network
+#     subclass   = ethernet
+sub set_pciconf_data {
+       eval $start if $b_log;
+       my ($busid,$busid_nu,$content,$port,$driver,$driver_nu,$modules,$device,$vendor,
+       $vendor_id,$chip_id,$rev,$type,$type_id,@data,@temp,@working);
+#      my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/pciconf/pci-freebsd-8.2-2";
+#      open my $fh, '<', $file or die "can't open $file: $!";
+#      chomp(@data = <$fh>);
+       my $path = check_program('pciconf');
+       $content = qx($path -lv 2>/dev/null) if $path;
+       @data = split /\n/, $content if $content;
+       $b_pci_tool = 1 if @data && scalar @data > 10;
+       foreach (@data){
+               if ($_ =~ /^[^@]+\@pci/){
+                       push @working, '';
+               }
+               $_ =~ s/^\s+//;
+               push @working, $_;
+       }
+       foreach (@working){
+               if ($driver){
+                       if ($_ =~ /^\s*$/) {
+                               $vendor = cleaner($vendor);
+                               $device = cleaner($device);
+                               if ($vendor && $device){
+                                       if ($vendor !~ /$device/i){
+                                               $device = "$vendor $device";
+                                       }
+                               }
+                               elsif (!$device){
+                                       $device = $vendor;
+                               }
+                               @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id,
+                               $rev,$port,$driver,$modules,$driver_nu);
+                               @pci = (@pci,[@temp]);
+                               $driver = '';
+                               #print "$busid $device_id r:$rev p: $port\n$type\n$device\n";
+                       }
+                       elsif ($_ =~ /^vendor/){
+                               $vendor = (split /\s+=\s+/,$_)[1];
+                               #print "p:$port\n";
+                       }
+                       elsif ($_ =~ /^device/){
+                               $device = (split /\s+=\s+/,$_)[1];
+                       }
+                       elsif ($_ =~ /^class/i){
+                               $type = (split /\s+=\s+/,$_)[1];
+                       }
+               }
+               elsif (/^([^@]+)\@pci([0-9]{1,3}:[0-9]{1,3}:[0-9]{1,3}):([0-9]{1,3}).*class=([^\s]+)\scard=([^\s]+)\schip=([^\s]+)\srev=([^\s]+)/){
+                       $driver = $1;
+                       $busid = $2;
+                       $busid_nu = $3;
+                       $type_id = $4;
+                       #$vendor_id = $5;
+                       $vendor_id = substr($6,6,4);
+                       $chip_id = substr($6,2,4);
+                       $rev = $7;
+                       $vendor = '';
+                       $device = '';
+                       $type = '';
+                       $driver =~ /(^[a-z]+)([0-9]+$)/;
+                       $driver = $1;
+                       $driver_nu = $2;
+               }
+       }
+       if ($driver && $busid){
+               $vendor = cleaner($vendor);
+               $device = cleaner($device);
+               $device = ( $vendor && $device !~ /$vendor/) ? "$vendor $device" : $device;
+               @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id,$rev,$port,$driver,$modules,$driver_nu);
+               @pci = (@pci,[@temp]);
+               $device = '';
+       }
+       print Dumper \@pci if $test[4];
+       main::log_data('dump','@pci',\@pci) if $b_log;
+       eval $end if $b_log;
+}
+
+## 1
+# /soc/1c30000.ethernet/uevent:["DRIVER=dwmac-sun8i", "OF_NAME=ethernet", 
+# "OF_FULLNAME=/soc/ethernet@1c30000", "OF_COMPATIBLE_0=allwinner,sun8i-h3-emac", 
+# "OF_COMPATIBLE_N=1", "OF_ALIAS_0=ethernet0", # "MODALIAS=of:NethernetT<NULL>Callwinner,sun8i-h3-emac"]
+## 2
+# /soc:audio/uevent:["DRIVER=bcm2835_audio", "OF_NAME=audio", "OF_FULLNAME=/soc/audio", 
+# "OF_COMPATIBLE_0=brcm,bcm2835-audio", "OF_COMPATIBLE_N=1", "MODALIAS=of:NaudioT<NULL>Cbrcm,bcm2835-audio"]
+## 3
+# /soc:fb/uevent:["DRIVER=bcm2708_fb", "OF_NAME=fb", "OF_FULLNAME=/soc/fb", 
+# "OF_COMPATIBLE_0=brcm,bcm2708-fb", "OF_COMPATIBLE_N=1", "MODALIAS=of:NfbT<NULL>Cbrcm,bcm2708-fb"]
+## 4
+# /soc/1c40000.gpu/uevent:["OF_NAME=gpu", "OF_FULLNAME=/soc/gpu@1c40000", 
+# "OF_COMPATIBLE_0=allwinner,sun8i-h3-mali", "OF_COMPATIBLE_1=allwinner,sun7i-a20-mali", 
+# "OF_COMPATIBLE_2=arm,mali-400", "OF_COMPATIBLE_N=3", 
+# "MODALIAS=of:NgpuT<NULL>Callwinner,sun8i-h3-maliCallwinner,sun7i-a20-maliCarm,mali-400"]
+## 5
+# /sys/devices/platform/soc/soc:internal-regs/d0018180.gpio/uevent
+## 6
+# /sys/devices/soc.0/1180000001800.mdio/8001180000001800:05/uevent
+#  ["DRIVER=AR8035", "OF_NAME=ethernet-phy"
+## 7
+# /sys/devices/soc.0/1c30000.eth/uevent
+## 8
+# /sys/devices/wlan.26/uevent [from pine64]
+sub set_soc_data {
+       eval $start if $b_log;
+       my ($content,@files,@temp2,@temp3,@working);
+       if (-d '/sys/devices/platform/'){
+               @files = globber('/sys/devices/platform/soc*/*/uevent');
+               @temp2 = globber('/sys/devices/platform/soc*/*/*/uevent');
+               @files = (@files,@temp2) if @temp2;
+       }
+       if (globber('/sys/devices/soc*')){
+               @temp2 = globber('/sys/devices/soc*/*/uevent');
+               @files = (@files,@temp2) if @temp2;
+               @temp2 = globber('/sys/devices/soc*/*/*/uevent');
+               @files = (@files,@temp2) if @temp2;
+       }
+       @temp2 = globber('/sys/devices/*/uevent'); # see case 8
+       @files = (@files,@temp2) if @temp2;
+       @temp2 = undef;
+       # not sure why, but even as root/sudo, /subsystem/uevent is unreadable with -r test true
+       @files = grep {!/subsystem/} @files if @files;
+       foreach my $file (@files){
+               next if -z $file;
+               my ($busid,$busid_nu,$chip_id,$device,$driver,$modules,$port,$rev,
+               $temp,$type,$type_id,$vendor_id,@working);
+               $chip_id = $file;
+               # variants: /soc/20100000.ethernet /soc/soc:audio /soc:/ /soc@0/
+               # mips: /sys/devices/soc.0/1180000001800.mdio/8001180000001800:07/
+               $chip_id =~ /\/sys\/devices\/(platform\/)?(soc[^\/]*\/)?([^\/]+\/)?([^\/]+\/)?([^\/]+)[\.:]([^\/]+)\/uevent$/;
+               $chip_id = $5;
+               $temp = $6;
+               @working = reader($file, 'strip') if -r $file;
+               foreach my $data (@working){
+                       @temp2 = split /=/, $data;
+                       if ($temp2[0] eq 'DRIVER'){
+                               $driver = $temp2[1];
+                               $driver =~ s/-/_/g if $driver; # kernel uses _, not - in module names
+                       }
+                       elsif ($temp2[0] eq 'OF_NAME'){
+                               $type = $temp2[1];
+                       }
+                       elsif ($temp2[0] eq 'OF_COMPATIBLE_0'){
+                               @temp3 = split /,/, $temp2[1];
+                               $device = $temp3[-1];
+                               $vendor_id = $temp3[0];
+                       }
+               }
+               # it's worthless, we can't use it
+               next if ! defined $type;
+               $driver = '' if ! defined $driver;
+               $busid = (defined $temp && $temp =~ /^[0-9]+$/) ? $temp: 0;
+               $busid_nu =  0;
+               $type_id =  '';
+               $port = '';
+               $rev = '';
+               $modules = '';
+               # note: use these for main Card match for -AGN
+               $b_soc_audio = 1 if $type =~ /^(audio|daudio|hdmi|multimedia)$/;
+               $b_soc_gfx = 1 if $type =~ /^(vga|disp|display|3d|fb|gpu|hdmi)$/;
+               $b_soc_net = 1 if $type =~ /^(eth|ethernet|ethernet-phy|network|wifi|wlan)$/;
+               @temp3 = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id,$rev,$port,$driver,$modules);
+               @pci = (@pci,[@temp3]);
+       }
+       print Dumper \@pci if $test[4];
+       main::log_data('dump','@pci',\@pci) if $b_log;
+       eval $end if $b_log;
+}
+sub set_ps_aux {
+       eval $start if $b_log;
+       @ps_aux = split "\n",qx(ps aux);;
+       shift @ps_aux; # get rid of header row
+       $_=lc for @ps_aux; # this is a super fast way to set to lower
+       # note: regular perl /.../inxi but sudo /.../inxi is added for sudo start
+       # for pinxi, we want to see the useage data for cpu/ram
+       @ps_aux = grep {!/\/$self_name\b/} @ps_aux if $self_name eq 'inxi';
+       # this is for testing for the presence of the command
+       @ps_cmd = grep {!/^\[/} map {
+               my @split = split /\s+/, $_;
+               # slice out 10th to last elements of ps aux rows
+               my $final = $#split;
+               # some stuff has a lot of data, chrome for example
+               $final = ($final > 12) ? 12 : $final;
+               @split = @split[10 .. $final ];
+               join " ", @split;
+       } @ps_aux;
+       #@ps_cmd = grep {!/^\[/} @ps_cmd;
+       # never, because ps loaded before option handler
+       print Dumper \@ps_cmd if $test[5];
+       eval $end if $b_log;
+}
+sub set_ps_gui {
+       eval $start if $b_log;
+       $b_ps_gui = 1;
+       my ($working,@match,@temp);
+       # desktops
+       if ($show{'system'}){
+               @temp=qw(razor-desktop razor-session lxsession lxqt-session tdelauncher tdeinit_phase1);
+               @match = (@match,@temp);
+               @temp=qw(afterstep awesome blackbox 3dwm dwm fluxbox flwm 
+               fvwm-crystal fvwm2 fvwm i3 jwm matchbox-panel openbox sawfish 
+               scrotwm spectrwm twm WindowMaker wm2 wmii2 wmii);
+               @match = (@match,@temp);
+       }
+       # wm:
+       if ($show{'system'} && $extra > 1){
+               @temp=qw(9wm 3dwm afterstep amiwm awesome blackbox budgie-wm compiz 
+               dwm fluxbox flwm fvwm-crystal fvwm2 fvwm gala gnome-shell i3 jwm 
+               twin kwin_wayland kwin_x11 kwin marco matchbox-window-manager metacity 
+               metisse mir muffin mutter mwm notion openbox ratpoison sawfish 
+               scrotwm spectrwm twm windowlab WindowMaker wm2 wmii2 wmii xfwm4 
+               xfwm5 xmonad);
+               @match = (@match,@temp);
+       }
+       # info: 
+       if ($show{'system'} && $extra > 2){
+               @temp=qw(budgie-panel gnome-panel kicker lxpanel lxqt-panel 
+               matchbox-panel mate-panel plasma-desktop plasma-netbook razor-panel 
+               razorqt-panel wingpanel xfce4-panel xfce5-panel);
+               @match = (@match,@temp);
+       }
+       # compositors (for wayland these are also the server, note 
+       if ($show{'graphic'} && $extra > 1){
+               @temp=qw(budgie-wm compiz compton dwc dcompmgr enlightenment 
+               grefson ireplace kmscon kwin_wayland kwin_x11 metisse mir moblin 
+               rustland sway swc unagi wayhouse westford weston xcompmgr);
+               @match = (@match,@temp);
+       }
+       @match = uniq(@match);
+       my $matches = join '|', @match;
+       foreach (@ps_cmd){
+               if (/^[\S]*\b($matches)(\s|$)/){
+                       $working = $1;
+                       push @ps_gui, $working; # deal with duplicates with uniq
+               }
+       }
+       @ps_gui = uniq(@ps_gui) if @ps_gui;
+       print Dumper \@ps_gui if $test[5];
+       log_data('dump','@ps_gui',\@ps_gui) if $b_log;
+       eval $end if $b_log;
+}
+
+sub set_sysctl_data {
+       eval $start if $b_log;
+       return if $alerts{'sysctl'}{'action'} ne 'use';
+       my (@temp);
+       # darwin sysctl has BOTH = and : separators, and repeats data. Why? 
+       if (!$b_fake_sysctl){
+               my $program = check_program('sysctl');
+               @temp = grabber("$program -a 2>/dev/null");
+       }
+       else {
+               #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sysctl/obsd_6.1_sysctl_soekris6501_root.txt";
+               #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sysctl/obsd_6.1sysctl_lenovot500_user.txt";
+               ## matches: compaq: openbsd-dmesg.boot-1.txt
+               my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sysctl/openbsd-5.6-sysctl-1.txt"; 
+               ## matches: toshiba: openbsd-5.6-dmesg.boot-1.txt
+               #my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/sysctl/openbsd-5.6-sysctl-2.txt"; 
+               @temp = reader($file);
+       }
+       foreach (@temp){
+               $_ =~ s/\s*=\s*|:\s+/:/;
+               $_ =~ s/\"//g;
+               push @sysctl, $_;
+               # we're building these here so we can use these arrays to test 
+               # in each feature if we will try to build the feature for bsds
+               if (/^hw\.sensors/ && !/^hw\.sensors\.acpi(bat|cmb)/ && !/^hw.sensors.softraid/){
+                       push @sysctl_sensors, $_;
+               }
+               elsif (/^hw\.(vendor|product|version|serialno|uuid)/){
+                       push @sysctl_machine, $_;
+               }
+               elsif (/^hw\.sensors\.acpi(bat|cmb)/){
+                       push @sysctl_battery, $_;
+               }
+       }
+       print Dumper \@sysctl if $test[7];
+       # this thing can get really long.
+       if ($b_log){
+               #main::log_data('dump','@sysctl',\@sysctl);
+       }
+       eval $end if $b_log;
+}
+
+# http://www.usb.org/developers/defined_class
+sub set_usb_data {
+       eval $start if $b_log;
+       if ($alerts{'lsusb'}{'action'} eq 'use' ){
+               #$usb_level = 2;
+               # NOTE: we can't get reliable usb network device with short
+               if ($usb_level == 2){
+                       set_lsusb_data_long();
+               }
+               else {
+                       set_lsusb_data_short();
+               }
+       }
+       elsif ( $alerts{'usbdevs'}{'action'} eq 'use'){
+               set_usbdevs_data();
+       }
+       eval $end if $b_log;
+}
+
+sub set_lsusb_data_short {
+       eval $start if $b_log;
+       my ($content,@data);
+       my $b_live = 1;
+       if ($b_live){
+               my $path = check_program('lsusb');
+               $content = qx($path 2>/dev/null) if $path;
+               @data = split /\n/, $content if $content;
+       }
+       else {
+               open my $fh, '<', "$ENV{'HOME'}/bin/scripts/inxi/data/lsusb/mdmarmer-lsusb.txt" or die $!;
+               chomp(@data = <$fh>);
+       }
+       foreach (@data){
+               next if /^\s*$|^Couldn't/; # expensive second call: || /UNAVAIL/
+               my @working = split /\s+/, $_;
+               $working[3] =~ s/:$//;
+               my $id = int($working[3]);
+               if ($id > 1){
+                       my $bus = int($working[1]);
+                       my $chip = $working[5];
+                       my @temp = @working[6..$#working];
+                       my $name = join ' ', @temp;
+                       if ($name !~ /hub/i){
+                               @usb = (@usb,[$bus,$id,$chip,$name]);
+                       }
+               }
+       }
+       print Dumper \@usb if $test[6];
+       main::log_data('dump','@usb: short',\@usb) if $b_log;
+       eval $end if $b_log;
+}
+
+sub set_lsusb_data_long {
+       eval $start if $b_log;
+       my ($content,@data,@working,$bus_id,$device_id,$id,$b_skip);
+       my $j = 0;
+       my $b_live = 1;
+       if ($b_live){
+               my $path = check_program('lsusb');
+               $content = qx($path -v 2>/dev/null) if $path;
+               @data = split /\n/, $content if $content;
+       }
+       else {
+               my $file;
+               #$file = "$ENV{'HOME'}/bin/scripts/inxi/data/lsusb/mdmarmer-lsusb-v.txt";
+               $file = "$ENV{'HOME'}/bin/scripts/inxi/data/lsusb/lsusb-v-dz64.txt";
+               open my $fh, '<', $file or die $!;
+               chomp(@data = <$fh>);
+       }
+       foreach (@data){
+               # we won't need all the lsusb data, so set it to skip
+               # after the last item we might want
+               # Couldn't open device, some information will be missing
+               next if /^\s*$|^Couldn't/; # expensive second call: || /UNAVAIL/
+               if (!$b_skip && $bus_id && /^\s\s/){
+                       #if ($_ =~ /\bDescriptor\b:/){
+                       if ($_ =~ /^\s+([\S]+)\sDescriptor:/){
+                               #$_ =~ /^\s+([\S]+)\sDescriptor:/;
+                               $_ = "Descriptor_$1";
+                       }
+                       else {
+                               $_ =~ s/^\s\s|[\s]+$//g;
+                               $_ =~ s/^[\s]+/~/g;
+                               #$_ =~ s/[\s]+$//g;
+                               $_ =~ s/\sType/_Type/g;
+                               $_ =~ s/^([\S]+)[\s]+(.*)//;
+                               my $one = ($1) ? $1: '';
+                               my $two = ($2) ? $2: '';
+                               $_ = "$one:$two";
+                               $b_skip = 1 if $one eq '~bInterfaceProtocol';
+                               #$_ = cleaner($_);
+                               if (/([\S]+):([0-9]+|0x[0-9a-f]+)\s(.*)/){
+                                       $_ = "$1:$2:$3";
+                                       #$b_skip = 1 if $1 eq '~bInterfaceProtocol';
+                               }
+                               #print "$1\n";
+                       }
+                       push @working, $_;
+               }
+               elsif (/^Bus\s([0-9]+)\sDevice\s([0-9]+):\sID\s(([0-9a-f]{4}):([0-9a-f]{4})).*/){
+               #elsif (/^Bus\s/){
+                       #if (/^Bus\s([0-9]+)\sDevice\s([0-9]+):\sID\s(([0-9a-f]{4}):([0-9a-f]{4})).*/){
+                               $j = scalar @usb;
+                               $bus_id = int($1);
+                               $device_id = int($2);
+                               $id = $3;
+                               $b_skip = 0;
+                               # we don't need 32, system boot, or 127, end of table
+                               if (@working){
+                                       if ($working[0] != 32 && $working[0] != 127){
+                                               $usb[$j] = (
+                                               [@working],
+                                               );
+                                       }
+                               }
+                               @working = ($bus_id,$device_id,$id);
+                       #}
+               }
+       }
+       if (@working){
+               $j = scalar @usb;
+               $usb[$j] = (
+               [@working],
+               );
+       }
+       # last by not least, sort it by dmi type, now we don't have to worry
+       # about random dmi type ordering in the data, which happens
+       @usb = sort { $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1] } @usb;
+       print Dumper \@usb if $test[6];
+       main::log_data('dump','@usb: long',\@usb) if $b_log;
+       eval $end if $b_log;
+}
+
+# Controller /dev/usb2:
+# addr 1: full speed, self powered, config 1, UHCI root hub(0x0000), Intel(0x8086), rev 1.00
+#  port 1 addr 2: full speed, power 98 mA, config 1, USB Receiver(0xc52b), Logitech(0x046d), rev 12.01
+#  port 2 powered
+sub set_usbdevs_data {
+       eval $start if $b_log;
+       my (@data,@working,$class,$bus_id,$addr_id,$id,$speed,$protocol);
+       my $j = 0;
+       my $ports = 0;
+       if (!$b_fake_usbdevs){
+               my $program = check_program('usbdevs');
+               my $content = qx($program -v 2>/dev/null);
+               @data = split /\n/, $content;
+       }
+       else {
+               open my $fh, '<', "$ENV{'HOME'}/bin/scripts/inxi/data/lsusb/bsd-usbdevs-v-1.txt" or die $!;
+               chomp(@data = <$fh>);
+       }
+       foreach (@data){
+               if (/^Controller\s\/dev\/usb([0-9]+)/){
+                       $j = scalar @usb;
+                       $ports = 0;
+                       $bus_id = $1;
+                       @working = ();
+               }
+               elsif (/^addr\s([0-9]+):\s([^,]+),[^,]+,[^,]+,\s?([^,]+)\(0x([0-9a-f]{4})\),\s?([^,]+)\s?\(0x([0-9a-f]{4})\)/){
+                       $j = scalar @usb;
+                       $addr_id = $1;
+                       $speed = "bcdUSB:$2";
+                       $id = "$4:$6";
+                       $protocol="~bInterfaceProtocol:0:$5 $3";
+                       #print "p1:$protocol\n";
+                       $class='bDeviceClass:9:Hub';
+                       @working = ($bus_id,$addr_id,$id,$speed,$class,$protocol);
+                       if (@working){
+                               $usb[$j] = (
+                               [@working],
+                               );
+                       }
+                       @working = ();
+               }
+               elsif (/^\s+port\s([0-9]+)\saddr\s([0-9]+):\s([^,]+),[^,]+,[^,]+,\s?([^,]+)\(0x([0-9a-f]{4})\),\s?([^,]+)\s?\(0x([0-9a-f]{4})\)/){
+                       $j = scalar @usb;
+                       $addr_id = "$2";
+                       $speed = "bcdUSB:$3";
+                       $id = "$5:$7";
+                       $protocol="~bInterfaceProtocol:0:$6 $4";
+                       #print "p2:$protocol\n";
+                       $ports++;
+                       @working = ($bus_id,$addr_id,$id,$speed,$protocol);
+                       if (@working){
+                               $usb[$j] = (
+                               [@working],
+                               );
+                       }
+                       @working = ();
+               }
+               elsif (/^\s+port\s([0-9]+)\spowered/){
+                       $ports++;
+               }
+       }
+       if (@working){
+               $j = scalar @usb;
+               $usb[$j] = (
+               [@working],
+               );
+       }
+       main::log_data('dump','@usb: usbdevs',\@usb) if $b_log;
+       print Dumper \@usb if $test[6];
+       eval $end if $b_log;
+}
+
+########################################################################
+#### GENERATE LINES
+########################################################################
+
+#### -------------------------------------------------------------------
+#### LINE CONTROLLERS
+#### -------------------------------------------------------------------
+
+sub assign_data {
+       my (%row) = @_;
+       return if ! %row;
+       if ($output_type eq 'screen'){
+               print_data(%row);
+       }
+       else {
+               %rows = (%rows,%row);
+       }
+}
+
+sub generate_lines {
+       eval $start if $b_log;
+       my (%row,$b_pci_check,$b_dmi_check);
+       set_ps_aux() if ! @ps_aux;
+       set_sysctl_data() if $b_sysctl;
+       # note: ps aux loads before logging starts, so create debugger data here
+       if ($b_log){
+               # I don't think we need to see this, it's long, but leave in case we do
+               #main::log_data('dump','@ps_aux',\@ps_aux);
+               main::log_data('dump','@ps_cmd',\@ps_cmd);
+       }
+       if ( $show{'short'} ){
+               set_dmesg_boot_data() if ($bsd_type && !$b_dmesg_boot_check);
+               %row = generate_short_data();
+               assign_data(%row);
+       }
+       else {
+               if ( $show{'system'} ){
+                       %row = generate_system_data();
+                       assign_data(%row);
+               }
+               if ( $show{'machine'} ){
+                       if ($b_dmi && !$b_dmi_check ){
+                               set_dmi_data() ; 
+                               $b_dmi_check = 1;
+                       }
+                       set_dmesg_boot_data() if ($bsd_type && !$b_dmesg_boot_check);
+                       %row = line_handler('Machine','machine');
+                       assign_data(%row);
+               }
+               if ( $show{'battery'} ){
+                       set_dmi_data() if $b_dmi && !$b_dmi_check; 
+                       $b_dmi_check = 1;
+                       %row = line_handler('Battery','battery');
+                       if (%row || $show{'battery-forced'}){
+                               assign_data(%row);
+                       }
+               }
+               if ( $show{'ram'} ){
+                       set_dmi_data() if $b_dmi && !$b_dmi_check; 
+                       $b_dmi_check = 1;
+                       %row = line_handler('Memory','ram');
+                       assign_data(%row);
+               }
+               if ( $show{'slot'} ){
+                       set_dmi_data() if $b_dmi && !$b_dmi_check; 
+                       $b_dmi_check = 1;
+                       %row = line_handler('PCI Slots','slot');
+                       assign_data(%row);
+               }
+               if ( $show{'cpu'} || $show{'cpu-basic'} ){
+                       set_dmesg_boot_data() if ($bsd_type && !$b_dmesg_boot_check);
+                       my $arg = ($show{'cpu-basic'}) ? 'basic' : 'full' ;
+                       %row = line_handler('CPU','cpu',$arg);
+                       assign_data(%row);
+               }
+               if ( $show{'graphic'} ){
+                       set_pci_data() if !$b_pci_check; 
+                       $b_pci_check = 1;
+                       %row = line_handler('Graphics','graphic');
+                       assign_data(%row);
+               }
+               if ( $show{'audio'} ){
+                       set_pci_data() if !$b_pci_check; 
+                       $b_pci_check = 1;
+                       %row = line_handler('Audio','audio');
+                       assign_data(%row);
+               }
+               if ( $show{'network'} ){
+                       set_usb_data() if !$b_usb_check;
+                       set_pci_data() if !$b_pci_check; 
+                       set_ip_data() if ($show{'ip'} || ($bsd_type && $show{'network-advanced'}));
+                       $b_pci_check = 1;
+                       $b_usb_check = 1;
+                       %row = line_handler('Network','network');
+                       assign_data(%row);
+               }
+               if ( $show{'disk'} || $show{'disk-basic'} || $show{'disk-total'} || $show{'optical'} ){
+                       set_dmesg_boot_data() if ($bsd_type && !$b_dmesg_boot_check);
+                       %row = line_handler('Drives','disk');
+                       assign_data(%row);
+               }
+               if ( $show{'raid'} ){
+                       set_pci_data() if !$b_pci_check; 
+                       %row = line_handler('RAID','raid');
+                       assign_data(%row);
+               }
+               if ( $show{'partition'} || $show{'partition-full'}){
+                       %row = line_handler('Partition','partition');
+                       assign_data(%row);
+               }
+               if ( $show{'unmounted'} ){
+                       %row = line_handler('Unmounted','unmounted');
+                       assign_data(%row);
+               }
+               if ( $show{'usb'} ){
+                       set_usb_data() if !$b_usb_check;
+                       %row = line_handler('USB','usb');
+                       assign_data(%row);
+                       $b_usb_check = 1;
+               }
+               if ( $show{'sensor'} ){
+                       %row = line_handler('Sensors','sensor');
+                       assign_data(%row);
+               }
+               if ( $show{'repo'} ){
+                       %row = line_handler('Repos','repo');
+                       assign_data(%row);
+               }
+               if ( $show{'process'} ){
+                       %row = line_handler('Processes','process');
+                       assign_data(%row);
+               }
+               if ( $show{'weather'} ){
+                       %row = line_handler('Weather','weather');
+                       assign_data(%row);
+               }
+               if ( $show{'info'} ){
+                       %row = generate_info_data();
+                       assign_data(%row);
+               }
+       }
+       if ( $output_type ne 'screen' ){
+               output_handler(%rows);
+       }
+       eval $end if $b_log;
+}
+
+sub line_handler {
+       eval $start if $b_log;
+       my ($key,$sub,$arg) = @_;
+       my %subs = (
+       'audio' => \&AudioData::get,
+       'battery' => \&BatteryData::get,
+       'cpu' => \&CpuData::get,
+       'disk' => \&DiskData::get,
+       'graphic' => \&GraphicData::get,
+       'machine' => \&MachineData::get,
+       'network' => \&NetworkData::get,
+       'partition' => \&PartitionData::get,
+       'raid' => \&RaidData::get,
+       'ram' => \&RamData::get,
+       'repo' => \&RepoData::get,
+       'process' => \&ProcessData::get,
+       'sensor' => \&SensorData::get,
+       'slot' => \&SlotData::get,
+       'unmounted' => \&UnmountedData::get,
+       'usb' => \&UsbData::get,
+       'weather' => \&WeatherData::get,
+       );
+       my (%data);
+       my $data_name = main::key($prefix++,$key);
+       my @rows = $subs{$sub}->($arg);
+       if (@rows){
+               %data = ($data_name => \@rows,);
+       }
+       eval $end if $b_log;
+       return %data;
+}
+
+#### -------------------------------------------------------------------
+#### SHORT, DEBUG
+#### -------------------------------------------------------------------
+
+sub generate_short_data {
+       eval $start if $b_log;
+       my $num = 0;
+       my $kernel_os = ($bsd_type) ? 'OS' : 'Kernel';
+       my $client = $client{'name-print'};
+       my $client_shell = ($b_irc) ? 'Client' : 'Shell';
+       if ($client{'version'}){
+               $client .= ' ' . $client{'version'};
+       }
+       my ($cpu_string,$speed,$speed_key,$type) = ('','','speed','');
+       my $memory = get_memory_data('string');
+       my @cpu = CpuData::get('short');
+       if (scalar @cpu > 1){
+               $type = ($cpu[2]) ? " (-$cpu[2]-)" : '';
+               ($speed,$speed_key) = ('','');
+               if ($cpu[6]){
+                       $speed_key = "$cpu[3]/$cpu[5]";
+                       $cpu[4] =~ s/ MHz//;
+                       $speed = "$cpu[4]/$cpu[6]";
+               }
+               else {
+                       $speed_key = $cpu[3];
+                       $speed = $cpu[4];
+               }
+               $cpu[1] ||= row_defaults('cpu-model-null');
+               $cpu_string = $cpu[0] . ' ' . $cpu[1] . $type;
+       }
+       elsif ($bsd_type) {
+               if ($alerts{'sysctl'}{'action'}){
+                       if ($alerts{'sysctl'}{'action'} ne 'use'){
+                               $cpu_string = "sysctl $alerts{'sysctl'}{'action'}";
+                               $speed = "sysctl $alerts{'sysctl'}{'action'}";
+                       }
+                       else {
+                               $cpu_string = 'bsd support coming';
+                               $speed = 'bsd support coming';
+                       }
+               }
+       }
+       my @disk = DiskData::get('short');
+       # print Dumper \@disk;
+       my $disk_string = 'N/A';
+       my ($size,$used,$size_type,$used_type) = ('','','','');
+       my (@temp,$size_holder,$used_holder);
+       if (@disk){
+               $size = $disk[0]{'size'};
+               if ($disk[0]{'size'} && $disk[0]{'size'} =~ /^[0-9\.]+$/){
+                       $size_holder = $disk[0]{'size'};
+                       @temp = get_size($size);
+                       $size = $temp[0];
+                       $size_type = " $temp[1]";
+               }
+               $used = $disk[0]{'used'};
+               if (defined $disk[0]{'used'} && $disk[0]{'used'} =~ /^[0-9\.]+$/){
+                       $used_holder = $disk[0]{'used'};
+                       @temp = get_size($used);
+                       $used = $temp[0];
+                       $used_type = " $temp[1]";
+               }
+               # in some fringe cases size can be 0 so only assign 'N/A' if no percents etc
+               if ($size_holder && $used_holder){
+                       my $percent = ' (' . sprintf("%.1f", $used_holder/$size_holder*100) . '% used)';
+                       $disk_string = "$size$size_type$percent";
+               }
+               else {
+                       $size ||= row_defaults('disk-size-0');
+                       $disk_string = "$used$used_type/$size$size_type";
+               }
+       }
+       #print join '; ', @cpu, " sleep: $cpu_sleep\n";
+       $memory ||= 'N/A';
+       my @data = ({
+               main::key($num++,'CPU') => $cpu_string,
+               main::key($num++,$speed_key) => $speed,
+               main::key($num++,$kernel_os) => &get_kernel_data(),
+               main::key($num++,'Up') => &get_uptime(),
+               main::key($num++,'Mem') => $memory,
+               main::key($num++,'Storage') => $disk_string,
+               # could make -1 for ps aux itself, -2 for ps aux and self
+               main::key($num++,'Procs') => scalar @ps_aux,
+               main::key($num++,$client_shell) => $client,
+               main::key($num++,$self_name) => &get_self_version(),
+       },);
+       my %row = (
+       main::key($prefix,'SHORT') => [(@data),],
+       );
+       eval $end if $b_log;
+       return %row;
+}
+
+#### -------------------------------------------------------------------
+#### CONSTRUCTED LINES
+#### -------------------------------------------------------------------
+
+sub generate_info_data {
+       eval $start if $b_log;
+       my $num = 0;
+       my $gcc_alt = '';
+       my $running_in = '';
+       my $data_name = main::key($prefix++,'Info');
+       my ($b_gcc,%row,$gcc,$index,$ref);
+       my ($gpu_ram,$parent,$percent,$total,$used) = (0,'','','','');
+       my $client_shell = ($b_irc) ? 'Client' : 'Shell';
+       my $client = $client{'name-print'};
+       my @gccs = get_gcc_data();
+       if (@gccs){
+               $gcc = shift @gccs;
+               if ($extra > 1 && @gccs){
+                       $gcc_alt = join '/', @gccs;
+               }
+               $b_gcc = 1;
+       }
+       $gcc ||= 'N/A';
+       if (!$b_irc && $extra > 1 ){
+               # bsds don't support -f option to get PPID
+               if (($b_display && !$b_force_display) && !$bsd_type){
+                       $parent = get_shell_source();
+               }
+               else {
+                       $parent = get_tty_number();
+                       $parent = "tty $parent" if $parent ne '';
+               }
+               if ($parent eq 'login'){
+                       $client{'su-start'} = $parent if !$client{'su-start'};
+                       $parent = undef;
+               }
+               # can be tty 0 so test for defined
+               $running_in = $parent if defined $parent;
+               if ($extra > 2 && $running_in && get_ssh_status() ){
+                       $running_in .= ' (SSH)';
+               }
+       }
+       my $memory = get_memory_data('splits');
+       if ($memory){
+               my @temp = split /:/, $memory;
+               my @temp2 = get_size($temp[0]);
+               $gpu_ram = $temp[3] if $temp[3];
+               $total = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0];
+               @temp2 = get_size($temp[1]);
+               $used = ($temp2[1]) ? $temp2[0] . ' ' . $temp2[1] : $temp2[0];
+               $used .= " ($temp[2]%)" if $temp[2];
+               if ($gpu_ram){
+                       @temp2 = get_size($gpu_ram);
+                       $gpu_ram = $temp2[0] . ' ' . $temp2[1] if $temp2[1];
+               }
+       }
+       $memory ||= 'N/A';
+       my %data = (
+       $data_name => [{
+               main::key($num++,'Processes') => scalar @ps_aux, 
+               main::key($num++,'Uptime') => &get_uptime(),
+               main::key($num++,'Memory') => $total,
+               },],
+       );
+       $index = scalar(@{ $data{$data_name} } ) - 1;
+       $data{$data_name}[$index]{main::key($num++,'used')} = $used;
+       if ($gpu_ram){
+               $data{$data_name}[$index]{main::key($num++,'gpu')} = $gpu_ram;
+       }
+       if ( (!$b_display || $b_force_display) || $extra > 0 ){
+               my %init = get_init_data();
+               my $init_type = ($init{'init-type'}) ? $init{'init-type'}: 'N/A';
+               $data{$data_name}[$index]{main::key($num++,'Init')} = $init_type;
+               if ($extra > 1 ){
+                       my $init_version = ($init{'init-version'}) ? $init{'init-version'}: 'N/A';
+                       $data{$data_name}[$index]{main::key($num++,'v')} = $init_version;
+               }
+               if ($init{'rc-type'}){
+                       $data{$data_name}[$index]{main::key($num++,'rc')} = $init{'rc-type'};
+                       if ($init{'rc-version'}){
+                               $data{$data_name}[$index]{main::key($num++,'v')} = $init{'rc-version'};
+                       }
+               }
+               if ($init{'runlevel'}){
+                       $data{$data_name}[$index]{main::key($num++,'runlevel')} = $init{'runlevel'};
+               }
+               if ($extra > 1 ){
+                       if ($init{'default'}){
+                               my $default = ($init{'init-type'} eq 'systemd' && $init{'default'} =~ /[^0-9]$/ ) ? 'target' : 'default';
+                               $data{$data_name}[$index]{main::key($num++,$default)} = $init{'default'};
+                       }
+               }
+       }
+       if ($extra > 0 ){
+               my $b_clang;
+               my $clang_version = '';
+               if (my $path = check_program('clang')){
+                       $clang_version = program_version($path,'clang',3,'--version');
+                       $clang_version ||= 'N/A';
+                       $b_clang = 1;
+               }
+               my $compiler = ($b_gcc || $b_clang) ? '': 'N/A';
+               $data{$data_name}[$index]{main::key($num++,'Compilers')} = $compiler;
+               if ($b_gcc){
+                       $data{$data_name}[$index]{main::key($num++,'gcc')} = $gcc;
+                       if ( $extra > 1 && $gcc_alt){
+                               $data{$data_name}[$index]{main::key($num++,'alt')} = $gcc_alt;
+                       }
+               }
+               if ($b_clang){
+                       $data{$data_name}[$index]{main::key($num++,'clang')} = $clang_version;
+               }
+       }
+       if ($extra > 2 && $client{'su-start'}){
+               $client .= " ($client{'su-start'})";
+       }
+       $data{$data_name}[$index]{main::key($num++,$client_shell)} =  $client;
+       if ($extra > 0 && $client{'version'}){
+               $data{$data_name}[$index]{main::key($num++,'v')} = $client{'version'};
+       }
+       if ( $running_in ){
+               $data{$data_name}[$index]{main::key($num++,'running in')} = $running_in;
+       }
+       $data{$data_name}[$index]{main::key($num++,$self_name)} = &get_self_version();
+       
+       eval $end if $b_log;
+       return %data;
+}
+
+sub generate_system_data {
+       eval $start if $b_log;
+       my $num = 0;
+       my (%row,$ref,$index,$val1);
+       my $data_name = main::key($prefix++,'System');
+       my ($desktop,$desktop_info,$desktop_key,$toolkit,$wm) = ('','','Desktop','','');
+       my (@desktop_data,$desktop_version);
+       
+       my %data = (
+       $data_name => [{}],
+       );
+       $index = scalar(@{ $data{$data_name} } ) - 1;
+       if ($show{'host'}){
+               $data{$data_name}[$index]{main::key($num++,'Host')} = &get_hostname();
+       }
+       $data{$data_name}[$index]{main::key($num++,'Kernel')} = &get_kernel_data();
+       $data{$data_name}[$index]{main::key($num++,'bits')} = &get_kernel_bits;
+       if ($extra > 0){
+               my @compiler = get_compiler_version(); # get compiler data
+               if (scalar @compiler != 2){
+                       @compiler = ('N/A', '');
+               }
+               $data{$data_name}[$index]{main::key($num++,'compiler')} = $compiler[0];
+               # if no compiler, obviously no version, so don't waste space showing.
+               if ($compiler[0] ne 'N/A'){
+                       $compiler[1] ||= 'N/A';
+                       $data{$data_name}[$index]{main::key($num++,'v')} = $compiler[1];
+               }
+       }
+       # note: tty can have the value of 0 but the two tools 
+       # return '' if undefined, so we test for explicit ''
+       if ($b_display){
+               my @desktop_data = DesktopEnvironment::get();
+               $desktop = $desktop_data[0] if $desktop_data[0];
+               $desktop_version = $desktop_data[1] if $desktop_data[1];
+               $desktop .= ' ' . $desktop_version if $desktop_version;
+               if ($extra > 0 && $desktop_data[3]){
+                       #$desktop .= ' (' . $desktop_data[2];
+                       #$desktop .= ( $desktop_data[3] ) ? ' ' . $desktop_data[3] . ')' : ')';
+                       $toolkit = "$desktop_data[2] $desktop_data[3]";
+               }
+               if ($extra > 2 && $desktop_data[4]){
+                       $desktop_info = $desktop_data[4];
+               }
+               # don't print the desktop if it's a wm and the same
+               if ($extra > 1 && $desktop_data[5] && 
+                   (!$desktop_data[0] || $desktop_data[5] =~ /^(gnome[\s\-_]shell|budgie-wm)$/i || 
+                   index(lc($desktop_data[5]),lc($desktop_data[0])) == -1 )){
+                       $wm = $desktop_data[5];
+                       $wm .= ' ' . $desktop_data[6] if $extra > 2 && $desktop_data[6];
+               }
+       }
+       if (!$b_display || ( !$desktop && $b_root)) {
+               my $tty = get_tty_number();
+               if (!$desktop){
+                       $desktop_info = '';
+               }
+               # it is defined, as ''
+               if ( $tty eq '' && $client{'console-irc'}){
+                       $tty = get_tty_console_irc('vtnr');
+               }
+               $desktop = "tty $tty" if $tty ne '';
+               $desktop_key = 'Console';
+       }
+       $desktop ||= 'N/A';
+       $data{$data_name}[$index]{main::key($num++,$desktop_key)} = $desktop;
+       if ($toolkit){
+               $data{$data_name}[$index]{main::key($num++,'tk')} = $toolkit;
+       }
+       if ($extra > 2){
+               if ($desktop_info){
+                       $data{$data_name}[$index]{main::key($num++,'info')} = $desktop_info;
+               }
+       }
+       if ($extra > 1){
+               $data{$data_name}[$index]{main::key($num++,'wm')} = $wm if $wm;
+               my $dms = get_display_manager();
+               $dms ||= 'N/A';
+               $data{$data_name}[$index]{main::key($num++,'dm')} = $dms;
+       }
+       #if ($extra > 2 && $desktop_key ne 'Console'){
+       #       my $tty = get_tty_number();
+       #       $data{$data_name}[$index]{main::key($num++,'vc')} = $tty if $tty ne '';
+       #}
+       my $distro_key = ($bsd_type) ? 'OS': 'Distro';
+       my @distro_data = DistroData::get();
+       my $distro = $distro_data[0];
+       $distro ||= 'N/A';
+       $data{$data_name}[$index]{main::key($num++,$distro_key)} = $distro;
+       if ($extra > 0 && $distro_data[1]){
+               $data{$data_name}[$index]{main::key($num++,'base')} = $distro_data[1];
+       }
+       eval $end if $b_log;
+       return %data;
+}
+
+#######################################################################
+#### LAUNCH
 ########################################################################
 
-main $@ ## From the End comes the Beginning
+main(); ## From the End comes the Beginning
 
 ## note: this EOF is needed for smxi handling, this is what triggers the full download ok
 ###**EOF**###