GRAPHICAL_DESKTOP 4 WindowMaker icewm 3 Epplets WMRack blackbox enlightenment ethemes icepref wmakerconf xlockmore xfce 2 wmnet 2 GNOME 1 lesstif-mwm xbanner GNOME 5 gnome-core 4 gnome-tiles gnome-users-guide gdm gnome-applets gnome-user-docs gtk-themes sawfish-themes gnome-network gnome-utils xscreensaver mdk-eazel-engine-capplet gnome-control-center gnome-control-center-plus 4 CHARSET"iso-8859-1" || CHARSET"iso-8859-15" nautilus gnome-vfs-extras 5 !CHARSET"iso-8859-1" !CHARSET"iso-8859-15" gmc 3 gnome-pilot 2 bug-buddy eog g-print 1 sawfish-themer KDE 5 kdebase kdebase-nsplugins 5 kde-i18n-ca kde-i18n-cs kde-i18n-da kde-i18n-de kde-i18n-el kde-i18n-es kde-i18n-et kde-i18n-fi kde-i18n-fr kde-i18n-he kde-i18n-hu kde-i18n-is kde-i18n-it kde-i18n-ja kde-i18n-ko kde-i18n-nl kde-i18n-no kde-i18n-no_NY kde-i18n-pl kde-i18n-pt kde-i18n-pt_BR kde-i18n-ro kde-i18n-ru kde-i18n-sk kde-i18n-sr kde-i18n-sv kde-i18n-tr kde-i18n-uk kde-i18n-zh_CN.GB2312 kde-i18n-zh_TW.Big5 4 kdeaddutils kdeadmin kdegraphics kdemultimedia kdenetwork krozat kdepim kdetoys kdeutils quanta 2 kdesdk kdoc VIDEO 4 xine-ui xine-oss xine-xv powerdvd RealPlayer RealPlayer-rpnp 3 nist xanim xmms-smpeg xmovie 2 mtv mtv-fullscreen-extension 1 bcast mpeg2_movie TV 4 zapping xawtv gatos 4 KDE kwintv OFFICE 4 staroffice-en staroffice-de staroffice-es staroffice-fr staroffice-it acroread acroread-nppdf 4 cbb gaddr ical xpdf moneydance 3 wv 4 LOCALES"zh" pyDict 2 units lyx phpgroupware GNOME 4 abiword gnome-pim gnome-pim-conduits gnumeric Guppi mrproject 3 dia gnome-pilot-conduits 2 ggv gnucash KDE 5 koffice 5 koffice-i18n-az koffice-i18n-hu koffice-i18n-ro koffice-i18n-cs koffice-i18n-it koffice-i18n-ru koffice-i18n-da koffice-i18n-ja koffice-i18n-sk koffice-i18n-de koffice-i18n-lt koffice-i18n-sl koffice-i18n-en_GB koffice-i18n-nl koffice-i18n-sv koffice-i18n-eo koffice-i18n-no koffice-i18n-ta koffice-i18n-es koffice-i18n-no_NY koffice-i18n-tr koffice-i18n-et koffice-i18n-pl koffice-i18n-fr koffice-i18n-pt 2 klyx SOUND 5 sox 4 aumix 4 KDE xmms-arts AUDIO 4 eMusic grip xmms xmms-skins 4 GNOME gnome-audio gnome-media xmms-gnome 4 KDE kdelibs-sound 4 3D xmms-mesa 3 cdp cdparanoia cdrecord-cdda2wav mikmod mp3info mpg123 playmidi playmidi-X11 space_sounds timidity-instruments xmms-esd xmms-kjofol-skins xmms-mikmod xmms-more-vis-plugins 3 GNOME gnome-audio-extra 4 HW"Rio 500" rio500 grio500 1 viavoicemenu_de viavoicemenu_fr viavoicemenu_us DEVELOPMENT 4 ViaVoice_sdk ACCESSIBILITY 2 gtkeyboard PUBLISHING 5 enscript ghostscript-module-X nlpr 4 docbook-dtd31-sgml ghostscript-utils gv psutils texinfo 3 latex2html jadetex tetex-afm tetex-dvips tetex-latex tetex-xdvi tetex-latex-arab tetex-latex-heb 2 BOOKS tetex-doc 1 docbook-style-dsssl openjade sgml-tools ghostscript-module-SVGALIB cttex 1 BOOKS docbook-style-dsssl-doc 5 CUPS cups-drivers xpp 5 CUPS || LPR foomatic 1 CUPS hpoj SHELLS 3 zsh 3 BOOKS bash-doc zsh-doc 2 eshell pdksh tcsh 1 bash1 SCIENCES 5 3D openuniverse 4 gnuplot gtkgraph 3 xlispstat HDF 2 mathplot octave 1 xoscope EDITORS 5 vim-enhanced 4 emacs-X11 3 GXedit vim-X11 2 nedit cooledit emacs-tramp joe emacs-leim jed-xjed xemacs xemacs-extras xemacs-info yudit 1 auctex emacs-el jed xemacs-el emacs-nox xwpe xwpe-X11 GNOME 4 gedit TEXT_TOOLS 5 words 4 groff patch recode rgrep 4 ispell-af ispell-br ispell-ca ispell-cs ispell-da ispell-de ispell-el ispell-en ispell-eo ispell-es ispell-fi ispell-fr ispell-ga ispell-gd ispell-gv ispell-id ispell-it ispell-nl ispell-no ispell-pl ispell-pt ispell-pt_BR ispell-ro ispell-ru ispell-sk ispell-sl ispell-sv 3 ed 2 groff-gxditview lout BOOKS 3 gawk-doc 1 lout-doc COMMUNICATIONS 4 efax 3 dip jpilot pilot-link 2 hylafax-server hylafax-client 2 lrzsz mgetty mgetty-contrib mgetty-sendfax mgetty-viewfax mgetty-voice minicom 1 jpilot-Mail jpilot-syncmal statserial tkbabel TERMINALS 5 open xterm 4 screen kon2 3 Eterm vlock 1 aterm x3270 4 KDE ktelnet 4 GNOME gnome-telnet 4 LOCALES"ja" kterm NETWORKING_CHAT 3 talk ytalk NETWORKING_CHAT_SERVER 4 jabber 3 talk-server NETWORKING_FILE_TRANSFER 5 lftp mirrordir rsync 4 gftp rdist 3 fmirror ncftp tftp 3 GNOME dpsftp 2 uucp NETWORKING_FILE_TRANSFER_SERVER 4 proftpd 2 wu-ftpd anonftp NETWORKING_INSTANT_MESSAGING 4 licq 3 everybuddy licq-autoreply licq-console licq-forwarder licq-rms licq-update-hosts GNOME 4 gnomeicu gabber gaim 3 gtk+licq NETWORKING_IRC 4 KDE kvirc 3 BitchX mozilla-irc 3 GNOME xchat 2 irssi 1 eggdrop NETWORKING_FIREWALLING_SERVER 5 iproute2 routed iptables ipchains 3 prelude 1 portsentry NETWORKING_MAIL 5 mailx metamail 4 GNOME evolution 4 faces mozilla-mail 3 fetchmail urlview sylpheed 3 !GNOME !KDE xmailbox 2 FaxMail comsat elm faces-xface 1 exmh mutt fetchmail-daemon fetchmailconf pine NETWORKING_MAIL_SERVER 5 postfix 4 sympa NETWORKING_NEWS 5 GNOME pan 3 slrn tin trn 1 slrn-pull xrn NETWORKING_NEWS_SERVER 4 inn 3 inews NETWORKING_OTHER 3 finger fwhois ucd-snmp-utils 2 htdig macutils queso NETWORKING_OTHER_SERVER 3 dhcp gated timed xntp3 3 intimed mcserv nscd imap pidentd rdate bootparamd cleanfeed ucd-snmp leafnode cvsweb finger-server mars-nwe XFree86-Xvfb XFree86-Xnest 2 heartbeat diald NIS 5 ypbind NIS_SERVER 5 ypserv NETWORKING_REMOTE_ACCESS 5 openssh-clients telnet telnet-client-krb5 4 traceroute rfbdrake 3 vnc 2 rsh vnc-SVGALIB vnc-java 2 BOOKS vnc-doc 1 sftp NETWORKING_REMOTE_ACCESS_SERVER 5 openssh-server 4 rfbdrake 2 vnc-server 1 telnet-server-krb5 rsh-server NETWORKING_DNS 4 bind-utils NETWORKING_DNS_SERVER 5 bind 4 caching-nameserver 1 nslint NETWORKING_FILE 4 samba-client nfs-utils-clients 2 arkeia-gui arkeia-client 2 GNOME gnomba gtm 1 am-utils autofs NETWORKING_FILE_SERVER 5 nfs-utils 4 samba 3 BOOKS samba-doc 2 arkeia-server-shareware samba-winbind 3 squid squidGuard NETWORKING_WWW 5 lynx 4 !LOCALES"eu" netscape-communicator netscape-plugins 4 wget links mozilla 4 GNOME screem galeon 3 plugger opera bluefish 4 !LOCALES"eu" netscape-castellano netscape-catalan netscape-chinese_gb2312 netscape-chinese_big5 netscape-euskara netscape-francais netscape-german netscape-japanese netscape-polish netscape-russian netscape-walon NETWORKING_WWW_SERVER 5 apache apache-mod_perl mod_ssl 4 apache-suexec 4 BOOKS apache-manual 3 php-dba_gdbm_db2 php-gd php-imap php-ldap php-manual php-mysql php-oracle php-pgsql php-readline php mod_php mod_ssl-sxnet auth_ldap HTML-Embperl PHP-nuke 1 Zope Zope-core Zope-pcgi Zope-zserver GRAPHICS 4 blender 4 ImageMagick giftrans gimp gphoto gphoto2 qiv xli ac3d 3 gimp-data-extras gqview sketch xloadimage xpcd xwpick 2 gif2png gimp-perl xfig xpcd-gimp gd-utils cameleo VariCAD VariCAD_de VariCAD_it VariCAD_es 1 libgr-progs libungif-progs qcad transfig zgv xpaint xmorph 1 3D Mesa-demos GNOME 4 gnome-iconedit 3 ee ARCHIVING 5 dump ncompress sharutils unzip mt-st 4 gtkzip 3 unarj unstuff 2 zip taper 1 lha GNOME 4 gnozip 3 gtktalog BURNER 5 mkisofs cdrecord 4 GNOME gnome-toaster gcombust 4 X xcdroast 2 X eroaster DEVELOPMENT 3 cdrecord-devel SCANNER 5 sane-frontends 4 gimp 4 xsane PHOTO 4 gphoto gphoto2 3 gnomemeeting openmcu DATABASES 5 postgresql 3 MySQL-client MySQL-shared DATABASES_SERVER 4 postgresql-server 3 MySQL 3 postgresql-perl postgresql-python 2 postgresql-jdbc postgresql-tk postgresql-odbc postgresql-tcl postgresql-test DEVELOPMENT 4 perl-Mysql 3 postgresql-devel 1 MySQL-bench MySQL-devel GAMES 4 sin-demo 4 clanbomber freeciv mures methane lbreakout2 toppler frozen-bubble 3 shogo-demo 3 cxhextris xboard xgammon xkobo xpat2 xpuzzles xsoldier xfishtank nethack_falconseye rocksndiamonds tuxpuck teg powermanga 2 Maelstrom pingus ltris penguin-command pysol xtrojka bunnies xpilot trophy 1 7colors xrally fortune-mod nil GNOME 4 gnome-chess gnome-games 2 3D gtulpas 1 gtkgo DEVELOPMENT 2 gnomes-games-devel KDE 4 kdegames DEVELOPMENT 2 kdegames-devel 3D 4 csmash armagetron !HW"Riva.*128" chromium tuxracer 3 !HW"Riva.*128" !HW"Rage X[CL]" !HW"Rage Mobility (?:P\/M|L) " !HW"3D Rage (?:LT|Pro)" # XF3 3D bzflag 3 FlightGear DEVELOPMENT 5 autoconf automake m4 make 3 autoconf2.5 1 automake1.5 1 tmake pmake pmake-customs 5 libtool binutils ltrace gdb gcc 2 gperf cdecl cproto indent ElectricFence 2 gcc3.0 1 egcs glibc-profile xxgdb 4 gcc-c++ libstdc++-devel 2 gcc3.0-c++ libstdc++3.0-devel 2 libsigc++-examples 1 egcs-c++ 3 bison flex 2 byacc 2 doxygen C++2LaTeX 3 jikes kaffe 2 gcc3.0-java 1 gcc-java 1 gcc3.0-g77 5 cvs rcs 2 diffstat tkcvs 3 swig 4 python veepee tkinter 3 python-devel python-imaging pygtk-glarea pygtk pygtk-libglade rpm-python 4 perl perl-Term-Readline-Gnu perl-libnet perl-libwww-perl perl-devel 3 perl-DBI perl-Digest-MD5 perl-IO-stringy perl-PDL perl-Parse-RecDescent perl-GTK-GLArea perl-Tk perlftlib eperl perl-URI perl-DB_File perl-Data-ShowTable perl-HTML-Parser perl-HTML-SimpleParse perl-MIME-Base64 perl-MIME-tools perl-MP3-Info perl-MailTools perl-Msgcat 4 rpm-build 3 rpmlint rpm-devel 3 kernel-source 2 gcc-g77 nasm dev86 1 egcs-g77 egcs-objc gcc-objc gcc-chill 2 teyjus gprolog ocaml umb-scheme mawk guile clisp SmallEiffel p2c ghc ghc-prof happy haskell-GTK haskell-GTK-devel hugs98 mercury swi-prolog ruby-extensions ruby-gtk ruby 4 gettext-devel BOOKS 2 pam-doc python-docs slang-doc kernel-doc nasm-doc qt2-doc ruby-doc lkmpg KDE 4 kdevelop 3 kdebase-devel kdegraphics-devel kdelibs-devel kdelibs-sound-devel kdemultimedia-devel kdenetwork-devel 1 kdesupport-devel kdeaddutils-devel koffice-devel GNOME 4 perl-GTK-Glade perl-GTK-Gnome 3 memprof glade gnome-guile pygnome gnome-core-devel gnome-guile-devel gnome-objc-devel gnome-pim-devel gob 2 pygnome-applet pygnome-capplet pygnome-libglade rep-gtk-gnome gnomemm-devel gnome-pilot-devel glademm 1 gnucash-devel rep-gtk-libglade DEVELOPMENT_OTHER 2 ImageMagick-devel Mesa-common-devel Xaw3d-devel detect-devel gd-devel p2c-devel gmp-devel guile-devel libgr-devel libjpeg-devel libpng-devel libtermcap-devel libtiff-devel libungif-devel libxml-devel libxml2-devel linuxconf-devel slang-devel sox-devel zlib-devel XFree86-devel dev86-devel gdk-pixbuf-devel gimp-devel gpm-devel kudzu-devel libghttp-devel libgtop-devel libmikmod-devel librep-devel libunicode-devel popt-devel pwdb-devel t1lib-devel ucd-snmp-devel mpeg_lib-devel WindowMaker-devel aalib-devel adns-devel alsa-lib-devel audiofile-devel cups-devel e2fsprogs-devel egcs-objc-devel g-wrap-devel gkrellm-devel ibtk libPropList-devel licq-devel openldap-devel parted-devel pciutils-devel pilot-link-devel svgalib-devel recode-devel aspell-devel cracklib-devel faces-devel fnlib-devel freetype-devel gtk+mdk-devel gtkglarea-devel XFree86-static-libs imap-devel php-devel pth-devel sane-devel swig-devel xdelta-devel isapnptools-devel pango-devel readline-devel MONITORING 5 procinfo psacct 5 PCMCIA apmd 4 lsof swatch tcpdump traceroute 3 iplog arpwatch lslk nmap nmap-frontend procps-X11 xcpustate xosview xsysinfo xtraceroute 2 cfengine gkrellm gps rusers mon 1 logcheck bonnie GNOME 4 gtop FILE_TOOLS 5 file gnupg mkxauth mtools slocate 4 symlinks 3 FileRunner dosfstools draksync mc tree 2 git gentoo sfm xwc 1 xdelta CONFIG 4 userdrake linuxconf linuxconf-lang-fr linuxconf-lang-de 4 GNOME gnome-linuxconf 3 webmin Bastille-Curses-module 3 ipvsadm quota timetool usernet 2 gfcc control-panel adjtimex isicom xinput timeconfig Bastille-Tk-module samba-swat 1 ipxutils BOOKS 4 mandrake_doc-de mandrake_doc-en mandrake_doc-es mandrake_doc-fr mandrake_doc-it X 5 XFree86 rxvt XFree86-75dpi-fonts 5 icewm-light 4 gurpmi rpmdrake drakconf fonts-ttf-decoratives harddrake fonts-ttf-west_european mandrake-mime menudrake drakfloppy drakfont mandrake_desk mdkonline drakfirsttime mdk-eazel-engine bootsplash 4 LOCALES"ja" || LOCALES"ko" || LOCALES"zh" rxvt-CJK 3 XFree86-100dpi-fonts draksync drakprofile 3 USB usbview 2 LOCALES"ja" || LOCALES"ko" || LOCALES"zh" jmcce 2 tksysv imlib-cfgeditor Aurora-Monitor-NewStyle-Categorizing-WsLib Aurora-Monitor-NewStyle-WsLib Aurora-Monitor-Traditional-Gtk+ Aurora-Monitor-Traditional-WsLib 1 DrakeLogo drakprofile Mesa xtoolwait X11R6-contrib GNOME 3 gnome-print gnorpm 5 xcin Chinput xa+cv xvnkb FreeWnn ami kinput2-wnn4 5 GNOME ami-gnome 2 app-defaults-be app-defaults-cs app-defaults-ga app-defaults-ru app-defaults-th app-defaults-uk 5 CHARSET"iso-8859-13" fonts-type1-baltic LOCALES"ru" || LOCALES"uk" XFree86-cyrillic-fonts LOCALES"he" fonts-hebrew-elmar LOCALES"hy" fonts-ttf-armenian LOCALES"ja" fonts-ttf-japanese LOCALES"ko" fonts-ttf-korean LOCALES"th" fonts-ttf-thai LOCALES"zh_CN" || LOCALES"zh" fonts-ttf-gb2312 LOCALES"zh_TW" || LOCALES"zh" taipeifonts fonts-ttf-big5 DOCS 5 man info man-pages SYSTEM 5 at MAKEDEV anacron eject kudzu ldetect devfsd locales iputils urpmi cpio msec 5 HIGH_SECURITY libsafe kernel-secure 5 BIGMEM kernel-enterprise 5 SMP kernel-smp 5 PCMCIA pcmcia-cs 5 USB hotplug 4 ftp ftp-client-krb5 gpm hexedit strace sudo grub procmail 3 bc 2 sndconfig kernel-utils sane acpid adns audiofile freeswan awesfx fbset vlan-utils 1 fdutils genromfs mailcap pinfo BOOKS 5 man-pages-cs man-pages-de man-pages-es man-pages-fr man-pages-hu man-pages-id man-pages-ja man-pages-ko man-pages-pl man-pages-ru man-pages-zh 4 grub-doc lilo-doc howto-html-de howto-html-el howto-html-en howto-html-es howto-html-fr howto-html-hr howto-html-hu howto-html-id howto-html-it howto-html-ja howto-html-ko howto-html-pl howto-html-ro howto-html-sl howto-html-sv howto-html-zh 4 NIS yp-tools 3 PCMCIA irda-utils 3 USB usbutils 4 LOCALES"ar" || LOCALES"fa" || LOCALES"he" || LOCALES"yi" acon 3 LOCALES"zh" zh-autoconvert EMULATORS 3 wine 2 BasiliskII-jit 1 BasiliskII 2 xdosemu 1 xmame xmess INSTALL 5 pptp-adsl dhcpcd dhcpxd dhcp-client isdn4net isdn-light isdn4k-utils ibod rp-pppoe pump wireless-tools speedtouch 5 XFree86-SVGA XFree86-server hotplug dev ypbind speedtouch_mgmt 4 XFree86-S3 XFree86-S3V XFree86-VGA16 XFree86-Mach64 XFree86-FBDev 3 XFree86-3DLabs XFree86-Sun XFree86-SunMono XFree86-Sun24 1 XFree86-8514 XFree86-AGX XFree86-I128 XFree86-Mach32 XFree86-Mach8 XFree86-Mono XFree86-P9000 XFree86-W32 XFree86 XFree86-glide-module Device3Dfx Glide_V3-DRI Glide_V5 Mesa ppp nfs-utils-clients kernel kernel-smp kernel-enterprise kernel22 kernel22-smp kernel22-secure autologin ntp cups-drivers samba ncpfs hpoj ucd-snmp libptal0 reiserfsprogs jfsprogs xfsprogs ipchains printer-utils rlpr samba-client xpp pdq printer-testpages autologin ntp ipchains lvm cups LPRng lpr foomatic printer-utils printer-testpages gimpprint xpp nc qtcups kups sndconfig ImageMagick ghostscript printer-filters mpage a2ps ppdfilt libppd1 gpr groff libgimpprint1 numlock sane-backends sane-frontends xsane xsane-gimp id='n288' href='#n288'>288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
package scanner;
# scanner.pm $Id$
# Yves Duret <yduret at mandriva.com>
# Till Kamppeter <till at mandriva.com>
# Copyright (C) 2001-2008 Mandriva
#
# 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 2, 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# pbs/TODO:
# - scsi mis-configuration (should work better now)
# - with 2 scanners same manufacturer -> will overwrite previous conf -> only 1 conf !! (should work now)
# - lp: see printerdrake
# - install: prefix --> done (partially)

use common;
use detect_devices;
use log;
use handle_configs;

my $sanedir = "$::prefix/etc/sane.d";
my $scannerDBdir = "$::prefix$ENV{SHARE_PATH}/ldetect-lst";
our $scannerDB = readScannerDB("$scannerDBdir/ScannerDB");

sub confScanner {
    my ($model, $port, $vendor, $product, $firmware) = @_;
    $port ||= "$::prefix/dev/scanner";
    my $a = $scannerDB->{$model}{server};
    #print "file:[$a]\t[$model]\t[$port]\n| ", (join "\n| ", @{$scannerDB->{$model}{lines}}),"\n";
    my @driverconf = cat_("$sanedir/$a.conf");
    my @configlines = @{$scannerDB->{$model}{lines}};
    foreach my $line (@configlines) {
	$line =~ s/\$DEVICE/$port/g if $port;
	next if $line =~ /\$DEVICE/;
	$line =~ s/\$VENDOR/$vendor/g if $vendor;
	next if $line =~ /\$VENDOR/;
	$line =~ s/\$PRODUCT/$product/g if $product;
	next if $line =~ /\$PRODUCT/;
	$line =~ s/\$FIRMWARE/$firmware/g if $firmware;
	next if $line =~ /\$FIRMWARE/;
	my $linetype;
	if ($line =~ /^(\S*)LINE\s+(.*?)$/) {
	    $linetype = $1;
	    $line = $2;
	}
	next if !$line;
	if (!$linetype ||
	    ($linetype eq "USB" && ($port =~ /usb/i || $vendor)) ||
	    ($linetype eq "PARPORT" && !$vendor &&
	     $port =~ /(parport|pt_drv|parallel)/i) ||
	    ($linetype eq "SCSI" && !$vendor &&
	     $port =~ m!(/sg|scsi|/scanner)!i)) {
	    handle_configs::set_directive(\@driverconf, $line, 1);
	} elsif ($linetype eq "FIRMWARE" && $firmware) {
	    handle_configs::set_directive(\@driverconf, $line, 0);
	}
    }
    output("$sanedir/$a.conf", @driverconf);
    add2dll($a);
}

sub add2dll {
    return if member($_[0], chomp_(cat_("$sanedir/dll.conf")));
    my @dllconf = cat_("$sanedir/dll.conf");
    handle_configs::add_directive(\@dllconf, $_[0]);
    output("$sanedir/dll.conf", @dllconf);
}

sub setfirmware {
    my ($backend, $firmwareline) = @_;
    my @driverconf = cat_("$sanedir/$backend.conf");
    handle_configs::set_directive(\@driverconf, $firmwareline, 0);
    output("$sanedir/$backend.conf", @driverconf);
}

sub installfirmware {
    # Install the firmware file in /usr/share/sane/firmware
    my ($firmware, $backend) = @_;
    return "" if !$firmware;
    # Install firmware
    run_program::rooted($::prefix, "mkdir", "-p",
			"/usr/share/sane/firmware") || do {
			    $in->ask_warn(N("Error"),
					  N("Could not create directory /usr/share/sane/firmware!"));
			    return "";
			};
    # Link /usr/share/sane/firmware to /usr/share/sane/<backend name> as
    # some backends ignore the supplied absolute path to the firmware file
    # and always search their own directory
    if ($backend) {
	run_program::rooted($::prefix, "ln", "-sf",
			    "/usr/share/sane/firmware",
			    "/usr/share/sane/$backend") || do {
				$in->ask_warn(N("Error"),
					      N("Could not create link /usr/share/sane/%s!", $backend));
				return "";
			    };
    }
    run_program::rooted($::prefix, "cp", "-f", "$firmware",
			"/usr/share/sane/firmware") || do {
			    $in->ask_warn(N("Error"),
					  N("Could not copy firmware file %s to /usr/share/sane/firmware!", $firmware));
			    return "";
			};
    $firmware =~ s!^(.*)(/[^/]+)$!/usr/share/sane/firmware$2!;
    run_program::rooted($::prefix, "chmod", "644",
			$firmware) || do {
			    $in->ask_warn(N("Error"),
					  N("Could not set permissions of firmware file %s!", $firmware));
			    return "";
			};
    return $firmware;
}

sub configured {
    my ($in) = @_;
    my @res;
    my $parportscannerfound = 0;
    # Run "scanimage -L", to find the scanners which are already working
    local *LIST;
    open LIST, "LC_ALL=C scanimage -L |";
    while (my $line = <LIST>) {
	if ($line =~ /^\s*device\s*`([^`']+)'\s+is\s+a\s+(\S.*)$/) {
	    # Extract port and description
	    my $port = $1;
	    my $description = $2;
	    # Remove duplicate scanners appearing through saned and the
	    # "net" backend
	    next if $port =~ /^net:(localhost|127.0.0.1):/;
	    # Is the scanner hooked to a parallel or serial port?
	    if ($port =~ /(parport|pt_drv|parallel|ttys)/i) {
		$parportscannerfound = 1;
	    }
	    # Determine which SANE backend the scanner in question uses
	    $port =~ /^([^:]+):/;
	    my $backend = $1;
	    # Does the scanner need a firmware file
	    my $firmwareline = firmwareline($backend);
	    # Store collected data
	    push @res, { 
		port => $port, 
		val => { 
		    DESCRIPTION => $description,
		    ($backend ? (BACKEND => $backend) : ()),
		    ($firmwareline ? 
		     (FIRMWARELINE => $firmwareline) : ()),
		}
	    };
	}
    }
    close LIST;
    # We have a parallel port scanner, make it working for non-root users
    nonroot_access_for_parport($parportscannerfound, $in);
    return @res;
}

sub nonroot_access_for_parport {

    # This function configures a non-root access for parallel port
    # scanners by running saned as root, exporting the scanner to
    # localhost and letting the user's frontend use the "net" backend
    # to access the scanner through the loopback network device.

    # See also
    # http://www.linuxprinting.org/download/digitalimage/Scanning-as-Normal-User-on-Wierd-Scanner-Mini-HOWTO.txt

    # Desired state of this facility: 1: Enable, 0: Disable
    my ($enable, $in) = @_;
    # Is saned running?
    my $sanedrunning = services::starts_on_boot("saned");
    # Is the "net" SANE backend active
    my $netbackendactive = grep { /^\s*net\s*$/ }
      cat_("/etc/sane.d/dll.conf");
    # Set this to 1 to tell the caller that the list of locally available
    # scanners has changed (Here if the SANE client configuration has
    # changed)
    my $changed = 0;
    my $importschanged = 0;
    if ($enable) {
	# Enable non-root access
	
	# Install/start saned
	if (!$sanedrunning) {
	    # Make sure saned and xinetd is installed and 
	    # running
	    if (!files_exist('/usr/sbin/xinetd',
			     '/usr/sbin/saned')) {
		if (!$in->do_pkgs->install('xinetd', 'saned')) {
		    $in->ask_warn(N("Scannerdrake"),
				  N("Could not install the packages needed to share your scanner(s).") . " " .
				  N("Your scanner(s) will not be available for non-root users."));
		}
		return 0;
	    }
	}

	# Modify /etc/xinetd.d/saned to let saned run as root
	my @sanedxinetdconf = cat_("/etc/xinetd.d/saned");
	s/(user\s*=\s*).*$/$1root/ foreach @sanedxinetdconf;
	s/(group\s*=\s*).*$/$1root/ foreach @sanedxinetdconf;
	output("/etc/xinetd.d/saned", @sanedxinetdconf);

	# Read list of hosts to where to export the local scanners
	my @exports = cat_("/etc/sane.d/saned.conf");
	# Read list of hosts from where to import scanners
	my @imports = cat_("/etc/sane.d/net.conf");
	# Add "localhost" to the machines which saned exports
	handle_configs::set_directive(\@exports, "localhost")
	    if !member("localhost\n", @exports);
	# Add "localhost" to the machines which "net" imports
	handle_configs::set_directive(\@imports, "localhost")
	    if !member("localhost\n", @imports);
	# Write /etc/sane.d/saned.conf
	output("/etc/sane.d/saned.conf", @exports);
	# Write /etc/sane.d/net.conf
	output("/etc/sane.d/net.conf", @imports);

	# Make sure that the "net" backend is active
	scanner::add2dll("net");
	
	# (Re)start saned and make sure that it gets started on
	# every boot
	services::start_service_on_boot("saned");
	services::start_service_on_boot("xinetd");
	services::restart("xinetd");

    } else {
	# Disable non-root access

	if (-r "/etc/xinetd.d/saned") {
	    # Modify /etc/xinetd.d/saned to let saned run as saned
	    my @sanedxinetdconf = cat_("/etc/xinetd.d/saned");
	    s/(user\s*=\s*).*$/$1saned/ foreach @sanedxinetdconf;
	    s/(group\s*=\s*).*$/$1saned/ foreach @sanedxinetdconf;
	    output("/etc/xinetd.d/saned", @sanedxinetdconf);
	    # Restart xinetd
	    services::restart("xinetd") if $sanedrunning;
	}
    }

    return 1;
}

sub detect {
    my @configured = @_;
    my @res;
    # Run "sane-find-scanner", this also detects USB scanners which only
    # work with libusb.

    my @devices = detect_devices::probeall();

    local *DETECT;
    open DETECT, "LC_ALL=C sane-find-scanner -q |";
    while (my $line = <DETECT>) {
	my ($vendorid, $productid, $make, $model, $description, $port, $driver);
	if ($line =~ /^\s*found\s+USB\s+scanner/i) {
	    # Found an USB scanner
	    if ($line =~ /vendor=(0x[0-9a-f]+)[^0-9a-f\[]+[^\[]*\[([^\[\]]+)\].*prod(|uct)=(0x[0-9a-f]+)[^0-9a-f\[]+[^\[]*\[([^\[\]]+)\]/) {
		# Scanner connected via libusb
		$vendorid = $1;
		$make = $2;
		$productid = $4;
		$model = $5;
		$description = "$make|$model";
	    } elsif ($line =~ /vendor=(0x[0-9a-f]+)[^0-9a-f]+.*prod(|uct)=(0x[0-9a-f]+)[^0-9a-f]+/) {
		# Scanner connected via scanner.o kernel module
		$vendorid = $1;
		$productid = $3;
	    }
	    if ($vendorid && $productid) {
		my ($vendor) = ($vendorid =~ /0x([0-9a-f]+)/);
		my ($id) = ($productid =~ /0x([0-9a-f]+)/);
		my ($device) = grep { sprintf("%04x", $_->{vendor}) eq $vendor && sprintf("%04x", $_->{id}) eq $id } @devices;

		if ($device) {
		    $driver = $device->{driver};
		} else {
		    #warn "Failed to lookup $vendorid and $productid!\n";
		}
                 
		# We have vendor and product ID, look up the scanner in
		# the usbtable
		foreach my $entry (common::catMaybeCompressed("$scannerDBdir/usbtable")) {
		    if ($entry =~ 
			/^\s*$vendorid\s+$productid\s+.*\"([^\"]+)\"\s*$/) {
			$description = $1;
			$description =~ s/Seiko\s+Epson/Epson/i;
			if ($description =~ /^([^\|]+)\|(.*)$/) {
			    $make = $1;
			    $model = $2;
			}
			last;
		    }
		}
	    }
	} elsif ($line =~ /^\s*found\s+SCSI/i) {
	    # SCSI scanner
	    if ($line =~ /\"([^\"\s]+)\s+([^\"]+?)\s+([^\"\s]+)\"/) {
		$make = $1;
		$model = $2;
		$description = "$make|$model";
	    }
	} else {
	    # Comment line in output of "sane-find-scanner"
	    next;
	}
	# The Alcatel Speed Touch internet scanner is not supported by
	# SANE
	next if $description =~ /Alcatel.*Speed.*Touch|Camera|ISDN|ADSL/i;
	# Extract port
	$port = $1 if $line =~ /\s+(\S+)\s*$/;
	# Check for duplicate (scanner.o/libusb)
	if ($port =~ /^libusb/) {
	    my $duplicate = 0;
	    foreach (@res) {
		if ($_->{val}{vendor} eq $vendorid &&
		    $_->{val}{id} eq $productid &&
		    $_->{port} =~ /dev.*usb.*scanner/ &&
		    !defined($_->{port2})) {
		    # Duplicate entry found, merge the entries
		    $_->{port2} = $port;
		    $_->{val}{MANUFACTURER} ||= $make;
		    $_->{val}{MODEL} ||= $model;
		    $_->{val}{DESCRIPTION} ||= $description;
		    $duplicate = 1;
		    last;
		}
	    }
	    next if $duplicate;
	}
	# Store collected data
	push @res, { 
	    port => $port, 
	    val => { 
		CLASS => 'SCANNER',
		MODEL => $model,
		MANUFACTURER => $make,
		DESCRIPTION => $description,
		id => $productid,
		vendor => $vendorid,
		driver => $driver,
		drakx_device => $device,
	    } 
	};
    }
    close DETECT;
    if (@configured) {
	# Remove scanners which are already working
	foreach my $d (@res) {
	    my $searchport1 =
		handle_configs::searchstr(resolve_symlinks($d->{port}));
	    my $searchport2 =
		handle_configs::searchstr(resolve_symlinks($d->{port2}));
	    foreach my $c (@configured) {
		my $currentport = resolve_symlinks($c->{port});
		if ($currentport =~ /$searchport1$/ ||
		    $searchport2 && $currentport =~ /$searchport2$/) {
		    $d->{configured} = 1;
		    last;
		}
	    }
	}
	@res = grep { ! $_->{configured} } @res;
    }
    # blacklist device that have a driver b/c of buggy sane-find-scanner:
    return grep { member($_->{val}{driver}, qw(scanner unknown)) } @res;
}

sub resolve_symlinks {

    # Check if a given file (either the pure filename or in a SANE device
    # string as "<prefix>:<file>") is a symlink, if so expand the link.
    # If the new file name is a link, expand again, until finding the
    # physical file.
    my ($file) = @_;
    my $prefix = "";
    if ($file =~ m!^([^/]*)(/.*)$!) {
	$prefix = $1;
	$file = $2;
    } else {
	return $file;
    }
    while (1) {
	my $ls = `ls -l $file 2> /dev/null`;
	if ($ls =~ m!\s($file)\s*\->\s*(\S+)\s*$!) {
	    my $target = $2;
	    if ($target !~ m!^/! && $file =~ m!^(.*)/[^/]+$!) {
		$target = "$1/$target";
	    }
	    $file = $target;
	} else {
	    last;
	}
    }
    return $prefix . $file;
}

sub get_usb_ids_for_port {
    my ($port) = @_;
    local *DETECT;
    if ($port =~ /^\s*libusb:(\d+):(\d+)\s*$/) {
	# Use "lsusb" to find the USB IDs
	open DETECT, "LC_ALL=C lsusb -s $1:$2 |";
	while (my $line = <DETECT>) {
	    if ($line =~ /ID\s+([0-9a-f]+):(0x[0-9a-f]+)($|\s+)/) {
		# Scanner connected via scanner.o kernel module
		return "0x$1", "0x$2";
		last;
	    }
	}
    } else {
	# Run "sane-find-scanner" on the port
	open DETECT, "LC_ALL=C sane-find-scanner -q $port |";
	while (my $line = <DETECT>) {
	    if ($line =~ /^\s*found\s+USB\s+scanner/i) {
		if ($line =~ /vendor=(0x[0-9a-f]+)[^0-9a-f]+.*prod(|uct)=(0x[0-9a-f]+)[^0-9a-f]+/) {
		    # Scanner connected via scanner.o kernel module
		    return $1, $3;
		}
	    }
	}
    }
}

sub readconfiglinetemplates {
    # Read templates for configuration file lines
    my %configlines;
    my $backend;
    foreach my $line (cat_("$scannerDBdir/scannerconfigs")) {
	chomp $line;
	if ($line =~ /^\s*SERVER\s+(\S+)\s*$/) {
	    $backend = $1;
	} elsif ($backend) {
	    push @{$configlines{$backend}}, $line;
	}
    }
    return \%configlines;
}

sub firmwareline {
    # Determine whether the given SANE backend supports a firmware file
    # and return the line needed in the config file
    my ($backend) = @_;
    # Read templates for configuration file lines
    my %configlines = %{readconfiglinetemplates()};
    # Does the backend support a line for the firmware?
    my @firmwarelines = (grep { s/^FIRMWARELINE // } @{$configlines{$backend}});
    return join("\n", @firmwarelines);
}

sub readScannerDB {
    my ($file) = @_;
    my ($card, %cards);

    my $F = common::openFileMaybeCompressed($file);

    my ($lineno, $cmd, $val) = 0;
    my $fs = {
        LINE => sub { push @{$card->{lines}}, "LINE $val" },
        SCSILINE => sub { push @{$card->{lines}}, "SCSILINE $val" },
        USBLINE => sub { push @{$card->{lines}}, "USBLINE $val" },
        PARPORTLINE => sub { push @{$card->{lines}}, "PARPORTLINE $val" },
        FIRMWARELINE => sub { push @{$card->{lines}}, "FIRMWARELINE $val" },
	NAME => sub {
	    #$cards{$card->{type}} = $card if ($card and !$card->{flags}{unsupported});
	    $cards{$card->{type}} = $card if $card;
	    $val =~ s/Seiko\s+Epson/Epson/i;
	    $card = { type => $val };
	},
	SEE => sub {
	    $val =~ s/Seiko\s+Epson/Epson/i;
	    my $c = $cards{$val} or die "Error in database, invalid reference $val at line $lineno";

	    push @{$card->{lines}}, @{$c->{lines} || []};
	    add2hash($card->{flags}, $c->{flags});
	    add2hash($card, $c);
	},
	ASK => sub { $card->{ask} = $val },
	SERVER => sub { $card->{server} = $val },
	DRIVER => sub { $card->{driver} = $val },
	KERNEL => sub { push(@{$card->{kernel}}, $val) },
	SCSIKERNEL => sub { push(@{$card->{scsikernel}}, $val) },
	USBKERNEL => sub { push(@{$card->{usbkernel}}, $val) },
	PARPORTKERNEL => sub { push(@{$card->{parportkernel}}, $val) },
	UNSUPPORTED => sub { $card->{flags}{unsupported} = 1 },
	MANUAL => sub { $card->{flags}{manual} = 1 },
	MANUALREQUIRED => sub { $card->{flags}{manual} = 2 },
	COMMENT => sub {},
    };

    local $_;
    while (<$F>) { $lineno++;
	s/\s+$//;
	/^#/ and next;
	/^$/ and next;
	/^END/ and do { $cards{$card->{type}} = $card if $card; last };
	($cmd, $val) = /(\S+)\s*(.*)/ or next; #log::l("bad line $lineno ($_)"), next;
	my $f = $fs->{$cmd};