#! /bin/bash

# Johannes Meixner <jsmeix@suse.de>, 2004, 2005, 2006, 2007, 2008, 2010
#
# Copyright (c) 2010 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# 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, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com

#set -x

export PATH="/sbin:/usr/sbin:/usr/bin:/bin"
export LC_ALL="POSIX"
export LANG="POSIX"
umask 022

MY_NAME=${0##*/}
OUTPUT_FORMAT="$1"
[ -z "$OUTPUT_FORMAT" ] && OUTPUT_FORMAT="ASCII"
[ "$OUTPUT_FORMAT" != "ASCII" -a "$OUTPUT_FORMAT" != "YCP" ] && { echo -en "\nUsage:\n$MY_NAME {ASCII|YCP}\n" 1>&2 ; exit 1 ; }

# Input:

# Create temporary file names:
TMP_DATA=$(mktemp -u /tmp/$MY_NAME.XXXXXX)
TMP_DATA_RAW=$(mktemp -u /tmp/$MY_NAME.XXXXXX)

# Get the raw data for usual scanners via "sane-find-scanner":
MAXIMUM_WAIT="10"
if [ -x /usr/bin/sane-find-scanner ]
then sane-find-scanner -q >$TMP_DATA_RAW &
     sanefindscannerPID=$!
     for i in $( seq $MAXIMUM_WAIT )
     do ps $sanefindscannerPID &>/dev/null || break
        sleep 1
     done
     if ps $sanefindscannerPID &>/dev/null
     then kill -9 $sanefindscannerPID &>/dev/null
     fi
else echo "Cannot execute /usr/bin/sane-find-scanner" 1>&2
     exit 2
fi
# Only USB and SCSI scanners are taken into account:
egrep '^found SCSI |^found USB ' <$TMP_DATA_RAW | sort -u | sed -e 's/^found //' | tr \" \' >$TMP_DATA

# Append the raw data for HP all-in-one devices via "hp-probe".
# The scanner unit in HP all-in-one USB devices works even without a CUPS queue
# (at least the scanner in my LaserJet 1220 works without a "hp:/usb/..." queue)
# so that for HP all-in-one USB devices "hp-probe -busb -escan" is used.
# The scanner unit in HP all-in-one network devices works only with a CUPS queue
# (the hpaio backend derives its SANE device from the "hp:/net/..." DeviceURI)
# so that for HP all-in-one network devices "hp-probe -bcups -escan" is used.
# It is no fatal error if /usr/bin/hp-probe is not executable
# (e.g. when the hplip RPM is not installed because no HP device is used):
MAXIMUM_WAIT="10"
if [ -x /usr/bin/hp-probe ]
then hp-probe -busb -escan | tr -s '[:blank:]' | grep 'hp:/usb/' >$TMP_DATA_RAW &
     hpprobePID=$!
     for i in $( seq $MAXIMUM_WAIT )
     do ps $hpprobePID &>/dev/null || break
        sleep 1
     done
     if ps $hpprobePID &>/dev/null
     then kill -9 $hpprobePID &>/dev/null
     fi
     hp-probe -bcups -escan | tr -s '[:blank:]' | grep 'hp:/net/' >>$TMP_DATA_RAW &
     hpprobePID=$!
     for i in $( seq $MAXIMUM_WAIT )
     do ps $hpprobePID &>/dev/null || break
        sleep 1
     done
     if ps $hpprobePID &>/dev/null
     then kill -9 $hpprobePID &>/dev/null
     fi
     # Remove leading and trailing spaces:
     sed -i -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' $TMP_DATA_RAW
     # Convert entries with 'hp:/usb/...' DeviceURI to the 'USB scanner' syntax of "sane-find-scanner":
     sed -i -e 's|^hp:/usb/[^ ]* \(.*\)|USB scanner (vendor=0x03f0 [HP], product=0x0000 [\1])|' $TMP_DATA_RAW
     # Convert entries with 'hp:/net/...' DeviceURI to a new 'NETWORK scanner' syntax:
     sed -i -e 's|^hp:/net/[^ ]* \(.*\)|NETWORK scanner (vendor [HP], product [\1])|' $TMP_DATA_RAW
     # Append the HP all-in-one device entries:
     sort -u $TMP_DATA_RAW >>$TMP_DATA
fi

# Output:

# Output header:
if [ "$OUTPUT_FORMAT" = "YCP" ]
then echo "[" 
else echo "CONNECTION|DEVICE|MANUFACTURER|USB_VENDOR_ID|MODEL|USB_PRODUCT_ID|DESCRIPTION"
fi

# Output scanner entries:
exec <$TMP_DATA
while read DESCRIPTION
do echo $DESCRIPTION | grep -q '^USB ' && CONNECTION="USB"
   echo $DESCRIPTION | grep -q '^SCSI ' && CONNECTION="SCSI"
   echo $DESCRIPTION | grep -q '^NETWORK ' && CONNECTION="NETWORK"
   DEVICE=$( echo $DESCRIPTION | sed -n -e 's/.* at \(.*\)/\1/p' )
   MANUFACTURER=""
   MODEL=""
   USB_VENDOR_ID=""
   USB_PRODUCT_ID=""
   if [ "$CONNECTION" = "USB" ]
   then MODEL=$( echo $DESCRIPTION | tr '[]' '||' | cut -d '|' -s -f 4 | tr '_' ' ' )
        [ -n "$MODEL" ] && MANUFACTURER=$( echo $DESCRIPTION | tr '[]' '||' | cut -d '|' -s -f 2 )
        USB_VENDOR_ID=$( echo $DESCRIPTION | sed -n -e 's/^.*vendor=\(0x[0-9A-Fa-f][0-9A-Fa-f]*\).*$/\1/p' | tr '[:upper:]' '[:lower:]' )
        USB_PRODUCT_ID=$( echo $DESCRIPTION | sed -n -e 's/^.*product=\(0x[0-9A-Fa-f][0-9A-Fa-f]*\).*$/\1/p' | tr '[:upper:]' '[:lower:]' )
   fi
   # Skip "SCSI via USB" scanners:
   # There is a small number of USB scanners which are not talked to via libusb.
   # They talk an unusual protocol which is a derivative of usb storage.
   # User space should treat them as SCSI scanners.
   # See the Novell/Suse Bugzilla bug
   # https://bugzilla.novell.com/show_bug.cgi?id=382744
   [ "0x04ce:0x0300" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x0094" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x0099" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x009a" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x00a0" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x00a3" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x80a3" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x80ac" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   [ "0x05da:0x00b6" = "$USB_VENDOR_ID:$USB_PRODUCT_ID" ] && continue
   if [ "$CONNECTION" = "SCSI" ]
   then # For SCSI: Simply use the first word as manufacturer name.
        # This is a keep-it-simple attempt because a few SCSI scanners
        # may report a two-word manufacturer name on the SCSI bus
        # (e.g. something like "Across Technologies" or "Linotype Hell").
        # Note that " was changed to ' when TMP_DATA was created above.
        MANUFACTURER=$( echo $DESCRIPTION | cut -d \' -s -f 2 | cut -d ' ' -s -f1 )
   fi
   if [ "$CONNECTION" = "NETWORK" ]
   then # Currently this connection happens only for HP all-in-one devices
        # and then the syntax of an entry is like for a USB scanner:
        MODEL=$( echo $DESCRIPTION | tr '[]' '||' | cut -d '|' -s -f 4 | tr '_' ' ' )
        [ -n "$MODEL" ] && MANUFACTURER=$( echo $DESCRIPTION | tr '[]' '||' | cut -d '|' -s -f 2 )
   fi
   # For HP set the name which is used in the SANE description files:
   if [ "$MANUFACTURER" = "HP" -o "$MANUFACTURER" = "hp" ]
   then MANUFACTURER="Hewlett-Packard"
   fi
   if [ "$OUTPUT_FORMAT" = "YCP" ]
   then echo -e "  \$[ \"connection\":\"$CONNECTION\",\n     \"device\":\"$DEVICE\",\n     \"manufacturer\":\"$MANUFACTURER\",\n     \"usb_vendor_id\":\"$USB_VENDOR_ID\",\n     \"model\":\"$MODEL\",\n     \"usb_product_id\":\"$USB_PRODUCT_ID\",\n     \"description\":\"$DESCRIPTION\"\n  ],"
   else echo "$CONNECTION|$DEVICE|$MANUFACTURER|$USB_VENDOR_ID|$MODEL|$USB_PRODUCT_ID|$DESCRIPTION"
   fi
done

# Output a footer for YCP
if [ "$OUTPUT_FORMAT" = "YCP" ]
then echo -e "  \$[]\n]"
fi

# Remove the temporary files
rm $TMP_DATA $TMP_DATA_RAW
exit 0

