Index: unk/LICENSE.txt
===================================================================
--- /trunk/LICENSE.txt	(revision 413)
+++ 	(revision )
@@ -1,339 +1,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-                            NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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 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, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
Index: unk/Makefile
===================================================================
--- /trunk/Makefile	(revision 413)
+++ 	(revision )
@@ -1,64 +1,0 @@
-DESTDIR=
-
-BINDIR=/usr/sbin
-MANDIR=/usr/share/man/man1
-SHAREDIR=/usr/share/klask
-LIBDIR=/usr/lib/klask
-CRONDIR=/etc/cron.d
-ETCDIR=/etc/klask
-COMPDIR=/etc/bash_completion.d
-
-.PHONY: all install update sync upload stat help pkg
-
-all:
-	pod2man klask | gzip > klask.1.gz
-	pod2html klask > klask.html
-
-install: update
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(CRONDIR)
-	install    -m 0644 -o root -g root klask.cron $(DESTDIR)/$(CRONDIR)/klask
-
-update:
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(BINDIR)
-	install    -m 0755 -o root -g root klask $(DESTDIR)/$(BINDIR)
-
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(LIBDIR)
-	install    -m 0755 -o root -g root push-web $(DESTDIR)/$(LIBDIR)
-	install    -m 0755 -o root -g root klask-wrapper $(DESTDIR)/$(LIBDIR)
-
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(MANDIR)
-	install    -m 0644 -o root -g root klask.1.gz $(DESTDIR)/$(MANDIR)
-
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(SHAREDIR)
-	install    -m 0644 -o root -g root sorttable-klask.js $(DESTDIR)/$(SHAREDIR)
-	install    -m 0644 -o root -g root style-klask.css $(DESTDIR)/$(SHAREDIR)
-
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(ETCDIR)
-	install    -m 0644 -o root -g root klask-sample.conf $(DESTDIR)/$(ETCDIR)
-	install    -m 0644 -o root -g root push-web-sample.conf $(DESTDIR)/$(ETCDIR)
-
-	@install -d -m 0755 -o root -g root $(DESTDIR)/$(COMPDIR)
-	install    -m 0644 -o root -g root klask.bash_completion $(DESTDIR)/$(COMPDIR)/klask
-
-sync:
-	svn update
-
-upload:
-	cadaver --rcfile=cadaverrc
-
-pkg: all
-	./make-package-debian
-
-stat:
-	svn log|egrep '^r[[:digit:]]'|egrep -v '^r1[[:space:]]'|awk '{print $$3}'|sort|uniq -c                 |gnuplot -p -e 'set style fill solid 1.00 border 0; set style histogram; set style data histogram; set xtics rotate by 0; set style line 7 linetype 0 linecolor rgb "#222222"; set grid ytics linestyle 7; set xlabel "User contributor" font "bold"; set ylabel "Number of commit" font "bold"; plot "/dev/stdin" using 1:xticlabels(2) title "commit" linecolor rgb "#666666"'
-	(echo '0 2015'; svn log|egrep '^r[[:digit:]]'|awk '{print $$5}'|cut -f 1 -d '-'|sort|uniq -c)|sort -k 2|gnuplot -p -e 'set style fill solid 1.00 border 0; set style histogram; set style data histogram; set xtics rotate by 0; set style line 7 linetype 0 linecolor rgb "#222222"; set grid ytics linestyle 7; set xlabel "Year"             font "bold"; set ylabel "Number of commit" font "bold"; plot "/dev/stdin" using 1:xticlabels(2) title "commit" linecolor rgb "#666666"'
-
-help:
-	@echo "Possibles targets:"
-	@echo " * all     : make manual"
-	@echo " * install : complete install"
-	@echo " * update  : update install (do not update cron file)"
-	@echo " * sync    : sync with official repository"
-	@echo " * upload  : upload on public dav forge space"
-	@echo " * pkg     : build Debian package"
-	@echo " * stat    : svn stat with gnuplot graph"
Index: unk/cadaverrc
===================================================================
--- /trunk/cadaverrc	(revision 413)
+++ 	(revision )
@@ -1,10 +1,0 @@
-open https://servforge.legi.grenoble-inp.fr/pub/klask/
-
-mput *.html
-mput LICENSE.txt
-
-mkdir download
-cd download
-mput klask_*_all.deb
-
-quit
Index: unk/klask
===================================================================
--- /trunk/klask	(revision 413)
+++ 	(revision )
@@ -1,3918 +1,0 @@
-#!/usr/bin/perl -w
-#
-# Copyright (C) 2005-2020 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
-# License GNU GPL version 2 or later and Perl equivalent
-#
-# $Id$
-#
-# perltidy -i=3 -ci=3 -pt=2 -icb -cti=3 -sbt=2 -bt=2 -bbt=2 -nsfs -bar -otr -olc -nolq -l=132 -vmll -msc=1 -iscl -nbbc -nola -st klask > klask.tmp
-#
-# apt-get install snmp libnet-cidr-lite-perl libnet-netmask-perl libnet-snmp-perl libnetaddr-ip-perl libyaml-perl
-#     libcrypt-des-perl libcrypt-hcesha-perl libdigest-hmac-perl libtext-table-perl
-#     arping net-tools fping bind9-host arpwatch
-
-use strict;
-use warnings;
-use version; our $VERSION = version->declare('0.7.14');
-
-use Readonly;
-use FileHandle;
-use Net::SNMP;
-#use YAML;
-use YAML::Syck;
-use Net::Netmask;
-use Net::CIDR::Lite;
-use NetAddr::IP;
-use Getopt::Long qw(GetOptions);
-use Socket;
-use List::Util 'shuffle';
-use Digest::SHA qw(sha512_base64);
-use Text::Table; # libtext-table-perl http://blogs.perl.org/users/steven_haryanto/2014/07/benchmarking-several-ascii-table-generator-modules.html
-
-################################################################
-# general initialization
-################################################################
-
-my $KLASK_VAR      = '/var/lib/klask';
-my $KLASK_CFG_FILE = '/etc/klask/klask.conf';
-my $KLASK_DB_FILE  = "$KLASK_VAR/klaskdb";
-my $KLASK_SW_FILE  = "$KLASK_VAR/switchdb";
-
-test_running_environnement();
-
-my $KLASK_CFG = YAML::Syck::LoadFile("$KLASK_CFG_FILE");
-
-my %DEFAULT     = %{$KLASK_CFG->{'default'}};
-my @SWITCH_LIST = @{$KLASK_CFG->{'switch'}};
-
-my %SWITCH_LEVEL = ();
-my %SWITCH_DB    = ();
-LEVEL_OF_EACH_SWITCH:
-for my $sw (@SWITCH_LIST) {
-   $SWITCH_LEVEL{$sw->{'hostname'}} = $sw->{'level'} || $DEFAULT{'switch_level'} || 2;
-   $SWITCH_DB{$sw->{'hostname'}} = $sw;
-
-   # SNMP parameter initialisation
-   my %session = (-hostname => $sw->{'hostname'});
-   $session{-version} = $sw->{'version'}  || 1;
-   $session{-port}    = $sw->{'snmpport'} || $DEFAULT{'snmpport'} || 161;
-   if (exists $sw->{'version'} and $sw->{'version'} eq '3') {
-      $session{-username} = $sw->{'username'} || 'snmpadmin';
-      }
-   else {
-      $session{-community} = $sw->{'community'} || $DEFAULT{'community'} || 'public';
-      }
-   $sw->{'snmp_param_session'} = \%session;
-   }
-@SWITCH_LIST = reverse sort {$SWITCH_LEVEL{$a->{'hostname'}} <=> $SWITCH_LEVEL{$b->{'hostname'}}} @{$KLASK_CFG->{'switch'}};
-
-#my %SWITCH_PORT_COUNT = ();
-
-my %CMD_DB = (
-   'help'                 => \&cmd_help,
-   'version'              => \&cmd_version,
-   'exportdb'             => \&cmd_exportdb,
-   'updatedb'             => \&cmd_updatedb,
-   'searchdb'             => \&cmd_searchdb,
-   'removedb'             => \&cmd_removedb,
-   'insertdb'             => \&cmd_insertdb,
-   'cleandb'              => \&cmd_cleandb,
-   'search'               => \&cmd_search,
-   'enable'               => \&cmd_enable,
-   'disable'              => \&cmd_disable,
-   'status'               => \&cmd_status,
-   'updatesw'             => \&cmd_updatesw,
-   'exportsw'             => \&cmd_exportsw,
-   'iplocation'           => \&cmd_ip_location,
-   'ip-free'              => \&cmd_ip_free,
-   'search-mac-on-switch' => \&cmd_search_mac_on_switch,
-   'bad-vlan-id'          => \&cmd_bad_vlan_id,
-   'poe-enable'           => \&cmd_poe_enable,
-   'poe-disable'          => \&cmd_poe_disable,
-   'poe-status'           => \&cmd_poe_status,
-   'port-setvlan'         => \&cmd_port_setvlan,
-   'port-getvlan'         => \&cmd_port_getvlan,
-   'vlan-setname'         => \&cmd_vlan_setname,
-   'vlan-getname'         => \&cmd_vlan_getname,
-   'vlan-list'            => \&cmd_vlan_list,
-   'host-setlocation'     => \&cmd_host_setlocation,
-   'rebootsw'             => \&cmd_rebootsw,
-   );
-
-#Readonly my %INTERNAL_PORT_MAP => (
-#   0 => 'A',
-#   1 => 'B',
-#   2 => 'C',
-#   3 => 'D',
-#   4 => 'E',
-#   5 => 'F',
-#   6 => 'G',
-#   7 => 'H',
-#   );
-#Readonly my %INTERNAL_PORT_MAP_REV => reverse %INTERNAL_PORT_MAP;
-
-Readonly my %SWITCH_KIND => (
-   # HP
-   J3298A           => { type => 2, model => 'HP212M',         match => 'HPJ3298A HP ProCurve Switch 212M',    revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J3299A           => { type => 1, model => 'HP224M',         match => 'HP J3299A ProCurve Switch 224M',      revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4120A           => { type => 1, model => 'HP1600M',        match => 'HP J4120A ProCurve Switch 1600M',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9029A           => { type => 1, model => 'HP1800-8G',      match => 'PROCURVE J9029A',                     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9449A           => { type => 1, model => 'HP1810-8G',      match => 'HP ProCurve 1810G - 8 GE',            revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4093A           => { type => 1, model => 'HP2424M',        match => 'HP J4093A ProCurve Switch 2424M',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9279A           => { type => 1, model => 'HP2510G-24',     match => 'ProCurve J9279A Switch 2510G-24',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9280A           => { type => 1, model => 'HP2510G-48',     match => 'ProCurve J9280A Switch 2510G-48',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4813A           => { type => 1, model => 'HP2524',         match => 'HP J4813A ProCurve Switch 2524',      revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9625A           => { type => 2, model => 'HP2620-24P',     match => 'HP J9625A E2620-24-PoEP Switch',      revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4900A           => { type => 1, model => 'HP2626A',        match => 'HP J4900A ProCurve Switch 2626',      revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4900B           => { type => 1, model => 'HP2626B',        match => 'J4900B.+?Switch 2626',                revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4899B           => { type => 1, model => 'HP2650',         match => 'ProCurve J4899B Switch 2650',         revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9021A           => { type => 1, model => 'HP2810-24G',     match => 'ProCurve J9021A Switch 2810-24G',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9022A           => { type => 1, model => 'HP2810-48G',     match => 'ProCurve J9022A Switch 2810-48G',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4903A           => { type => 1, model => 'HP2824',         match => 'J4903A.+?Switch 2824,',               revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J9145A           => { type => 2, model => 'HP2910-24G',     match => 'ProCurve J9145A 2910al-24G Switch',   revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J8692A           => { type => 1, model => 'HP3500-24G',     match => 'J8692A Switch E?3500yl-24G',          revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4121A           => { type => 2, model => 'HP4000M',        match => 'HP J4121A ProCurve Switch 4000M',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   J4110A           => { type => 1, model => 'HP8000M',        match => 'HP J4110A ProCurve Switch 8000M',     revision => qr{, revision (\w[\d\.]+?), ROM},               },
-   JE074A           => { type => 2, model => 'HP5120-24G',     match => 'HP Comware.+?A5120-24G EI Switch',    revision => qr{Comware .*? Version (\d[\d\.]+?) Release},   },
-   JE069A           => { type => 2, model => 'HP5120-48G',     match => 'HP Comware.+?A5120-48G EI Switch',    revision => qr{Comware .*? Version (\d[\d\.]+?) Release},   },
-   JD377A           => { type => 2, model => 'HP5500-24G',     match => 'HP Comware.+?A5500-24G EI Switch',    revision => qr{Comware .*? Version (\d[\d\.]+?) Release},   },
-   JD374A           => { type => 2, model => 'HP5500-24F',     match => 'HP Comware.+?A5500-24G-SFP EI ',      revision => qr{Comware .*? Version (\d[\d\.]+?) Release},   },
-   JH149A           => { type => 2, model => 'HP5510-24F',     match => 'HPE Comware.+?HPE 5510 .+? JH149A',   revision => qr{Comware .*? Version (\d[\d\.]+?), Release},  },
-   # BayStack
-   BS350T           => { type => 1, model => 'BS350T',         match => 'BayStack 350T HW'                     },
-   # Microsens
-   'MS440209PM-48G6'=> { type => 2, model => 'MS440209PM-48G6+',  match => 'MICROSENS G6 Switch'               },
-   # Nexans
-   N3483G           => { type => 2, model => 'NA3483-6G',      match => 'GigaSwitch V3 TP SFP-I 48.+ ES3',     revision => qr{(?:/SECURITY/|-OFFICE-)V(\d[\d\.]+\w?)\)},   },
-   N3483P           => { type => 2, model => 'NA3483-6P',      match => 'GigaSwitch V3 TP.PSE.+ 48/54V ES3',   revision => qr{(?:/SECURITY/|-OFFICE-)V(\d[\d\.]+\w?)\)},   },
-   N5541G           => { type => 2, model => 'NA5541-6G',      match => 'GigaSwitch V5 TP SFP-VI 54VDC',       revision => qr{-OFFICE-V(\d[\d\.]+\w+?)\)},                 },
-   N5542G           => { type => 2, model => 'NA5542-7G',      match => 'GigaSwitch V5 TP.PSE.+ SFP-2VI 54VDC',revision => qr{-OFFICE-V(\d[\d\.]+\w+?)\)},                 },
-   # DELL
-   PC7024           => { type => 2, model => 'DPC7024',        match => 'PowerConnect 7024,.+?VxWorks',        revision => qr{PowerConnect .*?, (\d[\d\.]+?), VxWorks},    },
-   N2048            => { type => 2, model => 'DN2048',         match => 'Dell Networking N2048,',              revision => qr{Dell Networking .*?, (\d[\d\.]+?), Linux},   },
-   N4032F           => { type => 2, model => 'DN4032F',        match => 'Dell Networking N4032F,',             revision => qr{Dell Networking .*?, (\d[\d\.]+?), Linux},   },
-   N4064F           => { type => 2, model => 'DN4064F',        match => 'Dell Networking N4064F,',             revision => qr{Dell Networking .*?, (\d[\d\.]+?), Linux},   },
-   # 3COM
-   'H3C5500'        => { type => 1, model => 'H3C5500',        match => 'H3C S5500-SI Series'                  },
-   '3C17203'        => { type => 1, model => '3C17203',        match => '3Com SuperStack 3 24-Port'            },
-   '3C17204'        => { type => 1, model => '3C17204',        match => '3Com SuperStack 3 48-Port'            },
-   '3CR17562-91'    => { type => 1, model => '3CR17562-91',    match => '3Com Switch 4500 50-Port'             },
-   '3CR17255-91'    => { type => 1, model => '3CR17255-91',    match => '3Com Switch 5500G-EI 48-Port'         },
-   '3CR17251-91'    => { type => 1, model => '3CR17251-91',    match => '3Com Switch 5500G-EI 48-Port'         },
-   '3CR17571-91'    => { type => 1, model => '3CR17571-91',    match => '3Com Switch 4500 PWR 26-Port'         },
-   '3CRWX220095A'   => { type => 1, model => '3CRWX220095A',   match => '3Com Wireless LAN Controller'         },
-   '3CR17254-91'    => { type => 1, model => '3CR17254-91',    match => '3Com Switch 5500G-EI 24-Port'         },
-   '3CRS48G-24S-91' => { type => 1, model => '3CRS48G-24S-91', match => '3Com Switch 4800G 24-Port'            },
-   '3CRS48G-48S-91' => { type => 1, model => '3CRS48G-48S-91', match => '3Com Switch 4800G 48-Port'            },
-   '3C17708'        => { type => 1, model => '3C17708',        match => '3Com Switch 4050'                     },
-   '3C17709'        => { type => 1, model => '3C17709',        match => '3Com Switch 4060'                     },
-   '3C17707'        => { type => 1, model => '3C17707',        match => '3Com Switch 4070'                     },
-   '3CR17258-91'    => { type => 1, model => '3CR17258-91',    match => '3Com Switch 5500G-EI 24-Port SFP'     },
-   '3CR17181-91'    => { type => 1, model => '3CR17181-91',    match => '3Com Switch 5500-EI 28-Port FX'       },
-   '3CR17252-91'    => { type => 1, model => '3CR17252-91',    match => '3Com Switch 5500G-EI PWR 24-Port'     },
-   '3CR17253-91'    => { type => 1, model => '3CR17253-91',    match => '3Com Switch 5500G-EI PWR 48-Port'     },
-   '3CR17250-91'    => { type => 1, model => '3CR17250-91',    match => '3Com Switch 5500G-EI 24-Port'         },
-   '3CR17561-91'    => { type => 1, model => '3CR17561-91',    match => '3Com Switch 4500 26-Port'             },
-   '3CR17572-91'    => { type => 1, model => '3CR17572-91',    match => '3Com Switch 4500 PWR 50-Port'         },
-   '3C17702-US'     => { type => 1, model => '3C17702-US',     match => '3Com Switch 4900 SX'                  },
-   '3C17700'        => { type => 1, model => '3C17700',        match => '3Com Switch 4900'                     },
-   );
-
-Readonly my %OID_NUMBER => (
-   sysDescription  => '1.3.6.1.2.1.1.1.0',
-   sysName         => '1.3.6.1.2.1.1.5.0',
-   sysContact      => '1.3.6.1.2.1.1.4.0',
-   sysLocation     => '1.3.6.1.2.1.1.6.0',
-   searchPort1     => '1.3.6.1.2.1.17.4.3.1.2',       # BRIDGE-MIB (802.1D).
-   searchPort2     => '1.3.6.1.2.1.17.7.1.2.2.1.2',   # Q-BRIDGE-MIB (802.1Q) add 0 if unknown vlan id
-   vlanPortDefault => '1.3.6.1.2.1.17.7.1.4.5.1.1',   # dot1qPvid
-   vlanStatus      => '1.3.6.1.2.1.17.7.1.4.3.1.5',   # integer 4 Create, 6 Destroy
-   vlanName        => '1.3.6.1.2.1.17.7.1.4.3.1.1',   # string
-   HPicfReset      => '1.3.6.1.4.1.11.2.14.11.1.4.1', # HP reboot switch
-   ifIndex         => '1.3.6.1.2.1.17.1.4.1.2',       # dot1dBasePortIfIndex - Interface index redirection
-   ifName          => '1.3.6.1.2.1.31.1.1.1.1',       # Interface name (give port number)
-   portUpDown      => '1.3.6.1.2.1.2.2.1.7',          # 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)  = 2 (down)
-   poeState        => '1.3.6.1.2.1.105.1.1.1.3.1',    # 1.3.6.1.2.1.105.1.1.1.3.1.NoPort = 1 (poe up)  = 2 (poe down) - Cisco and Zyxel
-   NApoeState      => '1.3.6.1.4.1.266.20.3.1.1.21',  # .NoPort = 2 (poe off)  = 8 (poe atHighPower) - Nexans
-   ifAggregator => '1.2.840.10006.300.43.1.2.1.1.12', # dot3adAggPortSelectedAggID - 0 not part of an  Aggregator - Ciso Dell HP Comware -  See https://stackoverflow.com/questions/14960157/how-to-map-portchannel-to-interfaces-via-snmp https://gist.github.com/bldewolf/6314435
-   );
-
-Readonly my %PORT_UPDOWN => (
-   1 => 'enable',
-   2 => 'disable',
-   );
-
-Readonly my $RE_MAC_ADDRESS  => qr{ [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} : [0-9,A-Z]{2} }xms;
-Readonly my $RE_IPv4_ADDRESS => qr{ [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} \. [0-9]{1,3} }xms;
-
-Readonly my $RE_FLOAT_HOSTNAME => $DEFAULT{'float-regex'} || qr{ ^float }xms;
-
-Readonly my $SEP_AGGREGATOR_PORT => ','; # : is already use to join switch and port
-Readonly my $SEP_SWITCH_PORT     => ':';
-
-
-################################################################
-# main program
-################################################################
-
-my $cmd = shift @ARGV || 'help';
-if (defined $CMD_DB{$cmd}) {
-   $CMD_DB{$cmd}->(@ARGV);
-   }
-else {
-   print {*STDERR} "klask: command $cmd not found\n\n";
-   $CMD_DB{'help'}->();
-   exit 1;
-   }
-
-exit;
-
-################################################################
-# subroutine
-################################################################
-
-#---------------------------------------------------------------
-sub test_running_environnement {
-   die "Configuration file $KLASK_CFG_FILE does not exists. Klask need it !\n" if not -e "$KLASK_CFG_FILE";
-   die "Var folder $KLASK_VAR does not exists. Klask need it !\n"              if not -d "$KLASK_VAR";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub test_switchdb_environnement {
-   die "Switch database $KLASK_SW_FILE does not exists. Launch updatesw before this command !\n" if not -e "$KLASK_SW_FILE";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub test_maindb_environnement {
-   die "Main database $KLASK_DB_FILE does not exists. Launch updatedb before this command !\n" if not -e "$KLASK_DB_FILE";
-   return;
-   }
-
-#---------------------------------------------------------------
-# fast ping dont l'objectif est de remplir la table arp de la machine
-sub fast_ping {
-   # Launch this command without waiting...
-   system "fping -q -c 1 @_ > /dev/null 2>&1 &";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub shell_command {
-   my $cmd = shift;
-
-   my $fh     = new FileHandle;
-   my $result = '';
-   open $fh, q{-|}, "LANG=C $cmd" or die "Can't exec $cmd\n";
-   $result .= <$fh>;
-   close $fh;
-   chomp $result;
-   return $result;
-   }
-
-#---------------------------------------------------------------
-# donne l'@ ip, dns, arp en fonction du dns OU de l'ip
-sub resolve_ip_arp_host {
-   my $param_ip_or_host = shift;
-   my $interface        = shift || q{*};
-   my $type             = shift || q{fast};
-   my $already          = shift || q{yes};
-
-   my %ret = (
-      hostname_fq  => 'unknow',
-      ipv4_address => '0.0.0.0',
-      mac_address  => 'unknow',
-      );
-
-   # perl -MSocket -E 'say inet_ntoa(scalar gethostbyname("tech7meylan.hmg.inpg.fr"))'
-   my $packed_ip = scalar gethostbyname($param_ip_or_host);
-   return %ret if not defined $packed_ip;
-   $ret{'ipv4_address'} = inet_ntoa($packed_ip);
-   #if ($ret{'ipv4_address'} !~ m/$RE_IPv4_ADDRESS/) {
-   #   print "Error: for computer $param_ip_or_host on interface $interface, IPv4 $ret{'ipv4_address'} is not valide\n";
-   #   return %ret;
-   #   }
-
-   # perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("194.254.66.240"), AF_INET)'
-   my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) if $already eq 'yes';
-   $ret{'hostname_fq'} = $hostname_fq if defined $hostname_fq;
-
-   my $cmd = q{grep -He '\b}
-      . $ret{'ipv4_address'} . q{\b' }
-      . "/var/lib/arpwatch/$interface.dat"
-      . '| sed -e \'s|^/var/lib/arpwatch/\(.*\)\.dat:|\1 |;\' | sort -rn -k 4,4 | head -1';
-   my $cmd_arpwatch = shell_command $cmd;
-   my ($interface2, $arp, $ip, $timestamp, $host) = split m/ \s+ /xms, $cmd_arpwatch;
-
-   $ret{'interface'}    = $interface2 || $interface;
-   $ret{'mac_address'}  = $arp       if $arp;
-   $ret{'timestamp'}    = $timestamp if $timestamp;
-
-   my $nowtimestamp = time;
-
-   if ($type eq 'fast' and (not defined $timestamp or $timestamp < ($nowtimestamp - 45 * 60))) { # 45 min
-      $ret{'mac_address'} = 'unknow';
-      return %ret;
-      }
-
-   # ARP result
-   #
-   # LANG=C arp -a 194.254.66.62 -i eth331
-   # gw66-62.legi.grenoble-inp.fr (194.254.66.62) at 00:08:7c:bb:0f:c0 [ether] on eth331
-   #
-   # LANG=C ip neigh show to 194.254.66.62 dev eth331
-   # 194.254.66.62 lladdr 00:08:7c:bb:0f:c0 REACHABLE
-   # LANG=C ip neigh show to 194.254.66.62
-   # 194.254.66.62 dev eth331 lladdr 00:08:7c:bb:0f:c0 REACHABLE
-   #   my $cmd_arp  = shell_command "arp -a $param_ip_or_host -i $ret{'interface'}";
-   #   if ( $cmd_arp =~ m{ (\S*) \s \( ( $RE_IPv4_ADDRESS ) \) \s at \s ( $RE_MAC_ADDRESS ) }xms ) {
-   #      ( $ret{'hostname_fq'}, $ret{'ipv4_address'}, $ret{'mac_address'} )  = ($1, $2, $3);
-   #      }
-   if ($ret{'mac_address'} eq 'unknow') {
-      # Last chance to have the mac_address
-      if ($ret{'interface'} eq '*') {
-         my $cmd_arp = shell_command "ip neigh show to $ret{'ipv4_address'}";
-         if ($cmd_arp =~ m{ ^$RE_IPv4_ADDRESS \s dev \s ([\w\d\.\:]+) \s lladdr \s ( $RE_MAC_ADDRESS ) \s }xms) {
-            ($ret{'interface'}, $ret{'mac_address'}) = ($1, $2);
-            }
-         }
-      else {
-         my $cmd_arp = shell_command "ip neigh show to $ret{'ipv4_address'} dev $ret{'interface'}";
-         if ($cmd_arp =~ m{ ^$RE_IPv4_ADDRESS \s lladdr \s ( $RE_MAC_ADDRESS ) \s }xms) {
-            $ret{'mac_address'} = $1;
-            }
-         }
-      }
-
-   # Normalize MAC Address
-   if ($ret{'mac_address'} ne 'unknow') {
-      my @paquets = ();
-      for (split m/ : /xms, $ret{'mac_address'}) {
-         my @chars = split m//xms, uc "00$_";
-         push @paquets, "$chars[-2]$chars[-1]";
-         }
-      $ret{'mac_address'} = join q{:}, @paquets;
-      }
-
-   return %ret;
-   }
-
-#---------------------------------------------------------------
-# Find Surname of a switch
-sub get_switch_model {
-   my $sw_snmp_description = shift || 'unknow';
-   $sw_snmp_description =~ s/[\n\r]/ /g;
-
-   for my $sw_kind (keys %SWITCH_KIND) {
-      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{'match'}/ms; # option xms break search, why ?
-
-      return $SWITCH_KIND{$sw_kind}->{'model'};
-      }
-
-   return $sw_snmp_description;
-   }
-
-#---------------------------------------------------------------
-# Find Revision Firmware of a switch
-sub get_switch_revision {
-   my $sw_snmp_description = shift || 'unknow';
-   $sw_snmp_description =~ s/[\n\r]/ /g;
-
-   for my $sw_kind (keys %SWITCH_KIND) {
-      next if not $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{'match'}/ms; # option xms break search, why ?
-      last if not exists $SWITCH_KIND{$sw_kind}->{'revision'};
-
-      my ($revision) = $sw_snmp_description =~ m/$SWITCH_KIND{$sw_kind}->{'revision'}/xms;
-      return $revision || 'unknow';
-      }
-
-   return 'unknow';
-   }
-
-#---------------------------------------------------------------
-# Get switch name and switch model
-sub init_switch_names {
-   my ($verbose, $verb_description, $check_hostname, $check_location) = @_;
-
-   printf "%-26s                %-25s %s\n", 'Switch', 'Description', 'Type(Revision)' if $verbose;
-   print "----------------------------------------------------------------------------------\n" if $verbose;
-
-   INIT_EACH_SWITCH:
-   for my $sw (my @CLONE = @SWITCH_LIST) { # Make a local clone because some element can be deleted
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [
-            $OID_NUMBER{'sysDescription'},
-            $OID_NUMBER{'sysName'},
-            $OID_NUMBER{'sysContact'},
-            $OID_NUMBER{'sysLocation'},
-            ]
-         );
-      if (!defined $result) {
-         printf {*STDERR} "ERROR: %s.\n", $session->error();
-         $session->close();
-         # Remove bad switch
-         @SWITCH_LIST = grep {$_->{'hostname'} ne $sw->{'hostname'}} @SWITCH_LIST;
-         delete $SWITCH_LEVEL{$sw->{'hostname'}} if exists $SWITCH_LEVEL{$sw->{'hostname'}};
-         delete $SWITCH_DB{$sw->{'hostname'}}    if exists $SWITCH_DB{$sw->{'hostname'}};
-         next INIT_EACH_SWITCH;
-         }
-
-      $sw->{'description'} = $result->{$OID_NUMBER{'sysName'}} || $sw->{'hostname'};
-      $sw->{'model'}       = get_switch_model($result->{$OID_NUMBER{'sysDescription'}});
-      $sw->{'revision'}    = get_switch_revision($result->{$OID_NUMBER{'sysDescription'}});
-      printf "%-26s 0--------->>>> %-25s %s\n", $sw->{'hostname'}, $sw->{'description'}, $sw->{'model'}.'('.$sw->{'revision'}.')' if $verbose;
-
-      if ($verb_description) {
-         my $desc = $result->{$OID_NUMBER{'sysDescription'}};
-         $desc =~ s/[\n\r]/ /g;
-         print " +> $sw->{'hostname'} - description: $desc\n";
-         }
-      if ($check_hostname) {
-         my ($hostname) = split /\./, $sw->{'hostname'}, 2;
-         print " +> $hostname - error internal hostname: $sw->{'hostname'}\n" if $result->{$OID_NUMBER{'sysName'}} ne $hostname;
-         }
-      if ($check_location) {
-         my $location = $result->{$OID_NUMBER{'sysLocation'}};
-         $location =~ s/^"(.+)"$/$1/;
-         print " +> $sw->{'hostname'} - error location: '$location' -> '$sw->{'location'}'\n" if $location ne $sw->{'location'};
-         }
-
-      $session->close;
-      }
-
-   print "\n" if $verbose;
-   return;
-   }
-
-#---------------------------------------------------------------
-# convert hexa (only 2 digits) to decimal
-sub digit_hex2dec {
-   #00:0F:1F:43:E4:2B
-   my $car = '00' . uc shift;
-
-   return '00' if $car eq '00UNKNOW';
-   my %table = (
-      '0'=>'0',  '1'=>'1',  '2'=>'2',  '3'=>'3',  '4'=>'4',
-      '5'=>'5',  '6'=>'6',  '7'=>'7',  '8'=>'8',  '9'=>'9',
-      'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15',
-      );
-   my @chars = split m//xms, $car;
-   return $table{$chars[-2]} * 16 + $table{$chars[-1]};
-   }
-
-#---------------------------------------------------------------
-
-sub normalize_mac_address {
-   my $mac_address = shift;
-
-   # D07E-28D1-7AB8 or D07E.28D1.7AB8 or d07e28-d17ab8
-   if ($mac_address =~ m{^ (?: [0-9A-Fa-f]{4} [-\.]){2} [0-9A-Fa-f]{4} $}xms
-      or $mac_address =~ m{^ [0-9A-Fa-f]{6} - [0-9A-Fa-f]{6} $}xms
-      ) {
-      $mac_address =~ s/[-\.]//g;
-      return join q{:}, unpack('(A2)*', uc($mac_address));
-      }
-
-   return join q{:}, map {substr(uc("00$_"), -2)} split m/ [:-] /xms, $mac_address;
-   }
-
-#---------------------------------------------------------------
-# convert MAC hex address to decimal
-sub mac_address_hex2dec {
-   #00:0F:1F:43:E4:2B
-   my $mac_address = shift;
-
-   my @paquets = split m/ : /xms, $mac_address;
-   my $return = q{};
-   for (@paquets) {
-      $return .= q{.} . digit_hex2dec($_);
-      }
-   return $return;
-   }
-
-#---------------------------------------------------------------
-sub format_aggregator4html {
-   my $port_hr = shift;
-   $port_hr =~ s/($SEP_AGGREGATOR_PORT)/: /; # First occurence
-   $port_hr =~ s/($SEP_AGGREGATOR_PORT)/ /g; # Other occurence
-   return $port_hr;
-   }
-
-#---------------------------------------------------------------
-sub format_aggregator4dot {
-   my $port_hr = shift;
-   $port_hr =~ s/($SEP_AGGREGATOR_PORT)/ - /; # First occurence
-   $port_hr =~ s/($SEP_AGGREGATOR_PORT)/ /g;  # Other occurence
-   return $port_hr;
-   }
-
-#---------------------------------------------------------------
-# port can be switch index or human readable
-sub format_switchport4sort {
-   my ($switch_name, $port) = @_;
-
-   # numeric port
-   return sprintf '%s %09i', $switch_name, $port if $port =~ m{^\d+$};
-
-   # Gi3/0/14 type port
-   if ($port =~ m{^(\w+?)(\d+)/(\d+)/(\d+)$}) {
-      return sprintf '%s %03i%03i%03i', $switch_name, $2, $3, $4;
-      }
-
-   # Gi3/14 type port
-   if ($port =~ m{^(\w+?)(\d+)/(\d+)$}) {
-      return sprintf '%s %06i%03i', $switch_name, $2, $3;
-      }
-
-   # Po14 or A3 type port
-   if ($port =~ m{^(\w+?)(\d+)(.*)$}) {
-      return sprintf '%s %s%04i%s', $switch_name, $1, $2, $3;
-      }
-
-   # otherwise
-   return sprintf '%s %s', $switch_name, $port;
-   }
-
-#---------------------------------------------------------------
-# fqdn_html_breakable change it's parameter, use it like chomp perl function
-
-sub fqdn_html_breakable {
-   my $ref_fqdn = \($_[0]);
-
-   # Add HTML <wbr> before every . to permit line break
-   $$ref_fqdn =~ s{\.}{.<wbr />}g;
-   }
-
-#---------------------------------------------------------------
-# return the port and the switch where the computer is connected
-sub find_switch_port {
-   my $mac_address     = shift;
-   my $switch_proposal = shift || q{};
-   my $vlan_id         = shift || 0;
-
-   my %ret;
-   $ret{'switch_description'} = 'unknow';
-   $ret{'switch_port_id'}     = '0';
-
-   return %ret if $mac_address eq 'unknow';
-
-   my @switch_search = @SWITCH_LIST;
-   if ($switch_proposal ne q{}) {
-      for my $sw (@SWITCH_LIST) {
-         next if $sw->{'hostname'} ne $switch_proposal;
-         unshift @switch_search, $sw;
-         last;
-         }
-      }
-
-   my $oid_search_port1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
-   my $oid_search_port2 = $OID_NUMBER{'searchPort2'} . '.' . $vlan_id . mac_address_hex2dec($mac_address);
-
-   LOOP_ON_SWITCH:
-   for my $sw (@switch_search) {
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search_port1]
-         );
-      if (not defined $result) {
-         $result = $session->get_request(
-            -varbindlist => [$oid_search_port2]
-            );
-         $result->{$oid_search_port1} = $result->{$oid_search_port2} if defined $result;
-         }
-
-      if (not(defined $result and $result->{$oid_search_port1} ne 'noSuchInstance')) {
-         $session->close;
-         next LOOP_ON_SWITCH;
-         }
-
-      my $swport_id = $result->{$oid_search_port1};
-      my $swport_hr = snmp_get_switchport_id2hr($session, $swport_id);
-
-      $session->close;
-
-      # IMPORTANT !!
-      # ceci empeche la detection sur certains port ...
-      # en effet les switch sont relies entre eux par un cable reseau et du coup
-      # tous les arp de toutes les machines sont presentes sur ces ports (ceux choisis ici sont les miens)
-      # cette partie est a ameliore, voir a configurer dans l'entete
-      # 21->24 45->48
-      SWITCH_PORT_IGNORE:
-      for my $portignore (@{$sw->{'portignore'}}) {
-         next LOOP_ON_SWITCH if $swport_hr eq $portignore;
-         my ($swport_hr_limited) = split /$SEP_AGGREGATOR_PORT/, $swport_hr; # Beginning of the swith port (Aggregator)
-         next LOOP_ON_SWITCH if $swport_hr_limited eq $portignore;
-         }
-
-      $ret{'switch_hostname'}    = $sw->{'hostname'};
-      $ret{'switch_description'} = $sw->{'description'};
-      $ret{'switch_port_id'}     = $swport_id;
-      $ret{'switch_port_hr'}     = $swport_hr; # human readable
-
-      last LOOP_ON_SWITCH;
-      }
-   return %ret;
-   }
-
-#---------------------------------------------------------------
-# search all the port on all the switches where the computer is detected
-sub find_all_switch_port {
-   my $mac_address = shift;
-   my $vlan_id     = shift || 0;
-
-   my $ret = {};
-
-   return $ret if $mac_address eq 'unknow';
-
-   my $oid_search_port1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
-   my $oid_search_port2 = $OID_NUMBER{'searchPort2'} . '.' . $vlan_id . mac_address_hex2dec($mac_address);
-   LOOP_ON_ALL_SWITCH:
-   for my $sw (@SWITCH_LIST) {
-      my ($session, $error) = Net::SNMP->session( %{$sw->{'snmp_param_session'}} );
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search_port1]
-         );
-      if (not defined $result) {
-         $result = $session->get_request(
-            -varbindlist => [$oid_search_port2]
-            );
-         $result->{$oid_search_port1} = $result->{$oid_search_port2} if defined $result;
-         }
-
-      if (defined $result and $result->{$oid_search_port1} ne 'noSuchInstance') {
-         my $swport_id = $result->{$oid_search_port1};
-         my $swport_hr = snmp_get_switchport_id2hr($session, $swport_id);
-
-         SWITCH_PORT_IGNORE:
-         for my $portignore (@{$sw->{'portignore'}}) {
-            if ($swport_hr eq $portignore) {
-               $session->close;
-               next LOOP_ON_ALL_SWITCH;
-               }
-            }
-
-         $ret->{$sw->{'hostname'}}                = {};
-         $ret->{$sw->{'hostname'}}{'hostname'}    = $sw->{'hostname'};
-         $ret->{$sw->{'hostname'}}{'description'} = $sw->{'description'};
-         $ret->{$sw->{'hostname'}}{'port_id'}     = $swport_id;
-         $ret->{$sw->{'hostname'}}{'port_hr'}     = $swport_hr;
-         }
-
-      $session->close;
-      }
-   return $ret;
-   }
-
-#---------------------------------------------------------------
-sub get_list_network {
-
-   return keys %{$KLASK_CFG->{'network'}};
-   }
-
-#---------------------------------------------------------------
-sub get_current_interface {
-   my $vlan_name = shift;
-
-   return $KLASK_CFG->{'network'}{$vlan_name}{'interface'};
-   }
-
-#---------------------------------------------------------------
-sub get_current_vlan_id {
-   my $vlan_name = shift;
-
-   return 0 if not exists $KLASK_CFG->{'network'}{$vlan_name};
-   return $KLASK_CFG->{'network'}{$vlan_name}{'vlan-id'};
-   }
-
-#---------------------------------------------------------------
-sub get_current_scan_mode {
-   my $vlan_name = shift;
-
-   return $KLASK_CFG->{'network'}{$vlan_name}{'scan-mode'} || $DEFAULT{'scan-mode'} || 'active';
-   }
-
-#---------------------------------------------------------------
-sub get_current_vlan_name_for_interface {
-   my $interface = shift;
-
-   for my $vlan_name (keys %{$KLASK_CFG->{'network'}}) {
-      next if $KLASK_CFG->{'network'}{$vlan_name}{'interface'} ne $interface;
-      return $vlan_name;
-      }
-   }
-
-#---------------------------------------------------------------
-# liste l'ensemble des adresses ip d'un réseau
-sub get_list_ip {
-   my @vlan_name = @_;
-
-   my $cidrlist = Net::CIDR::Lite->new;
-
-   for my $net (@vlan_name) {
-      my @line = @{$KLASK_CFG->{'network'}{$net}{'ip-subnet'}};
-      for my $cmd (@line) {
-         for my $method (keys %{$cmd}) {
-            $cidrlist->add_any($cmd->{$method}) if $method eq 'add';
-            }
-         }
-      }
-
-   my @res = ();
-
-   for my $cidr ($cidrlist->list()) {
-      my $net = new NetAddr::IP $cidr;
-      for my $ip (@{$net}) {
-         $ip =~ s{ /32 }{}xms;
-         push @res, $ip;
-         }
-      }
-
-   return @res;
-   }
-
-#---------------------------------------------------------------
-# liste l'ensemble des routeurs du réseau
-sub get_list_main_router {
-   my @vlan_name = @_;
-
-   my @res = ();
-
-   for my $net (@vlan_name) {
-      push @res, $KLASK_CFG->{'network'}{$net}{'main-router'};
-      }
-
-   return @res;
-   }
-
-#---------------------------------------------------------------
-sub normalize_port_human_readable {
-   my $sw_port_hr = shift;
-
-   # Manufacturer abbreviation
-   $sw_port_hr =~ s/^Bridge-Aggregation/Br/i;
-   $sw_port_hr =~ s/^Port-Channel/Po/i;
-   $sw_port_hr =~ s/^Forty-?GigabitEthernet/Fo/i;
-   $sw_port_hr =~ s/^Ten-?GigabitEthernet/Te/i;
-   $sw_port_hr =~ s/^GigabitEthernet/Gi/i;
-   $sw_port_hr =~ s/^FastEthernet/Fa/i;
-
-   # Customer abbreviation
-   $sw_port_hr =~ s/^Ten/Te/i;
-   $sw_port_hr =~ s/^Giga/Gi/i;
-
-   return ucfirst $sw_port_hr;
-   }
-
-#---------------------------------------------------------------
-sub snmp_get_rwsession {
-   my ($sw) = @_;
-
-   my %session = %{$sw->{'snmp_param_session'}};
-   $session{-community} = $sw->{'community-rw'} || $DEFAULT{'community-rw'} || 'private';
-   return %session;
-   }
-
-#---------------------------------------------------------------
-sub snmp_get_switchport_id2hr {
-   my ($snmp_session, $swport_id) = @_;
-
-   # On H3C, port id (port_id) and port index (port_ix) are not the same
-   # Double SNMP request to get the name
-   # First get the index, second get the name (port_hr)
-
-   my $oid_search_ix = $OID_NUMBER{'ifIndex'} . '.' . $swport_id;
-   my $result_ix = $snmp_session->get_request(
-      -varbindlist => [$oid_search_ix]
-      );
-
-   my $swport_ix = $swport_id;
-   $swport_ix = $result_ix->{$oid_search_ix} if defined $result_ix;
-
-   return snmp_get_switchport_ix2hr($snmp_session, $swport_ix);
-   }
-
-#---------------------------------------------------------------
-sub snmp_get_switchport_ix2hr {
-   my ($snmp_session, $swport_ix) = @_;
-
-   my $oid_search_hr = $OID_NUMBER{'ifName'} .'.'. $swport_ix;
-   my $result_hr = $snmp_session->get_request(
-      -varbindlist => [$oid_search_hr]
-      );
-   my $swport_hr = $swport_ix;
-   $swport_hr = normalize_port_human_readable($result_hr->{$oid_search_hr}) if defined $result_hr;
-
-   # Aggregator port
-   if ($swport_hr =~ m/^(Trk|Br|Po)/) {
-      my $oid_search_index = $OID_NUMBER{'ifAggregator'}; # base OID
-      my @args = (-varbindlist => [$oid_search_index]);
-      LOOP_ON_OID_PORT:
-      while (defined $snmp_session->get_next_request(@args)) {
-         my ($oid_current) = $snmp_session->var_bind_names;
-         last LOOP_ON_OID_PORT if not Net::SNMP::oid_base_match($oid_search_index, $oid_current);
-
-         # IEEE8023-LAG-MIB::dot3adAggPortSelectedAggID.28 = INTEGER: 337
-         # IEEE8023-LAG-MIB::dot3adAggPortAttachedAggID.28 = INTEGER: 337
-         my $port_aggregator_index = $snmp_session->var_bind_list->{$oid_current};
-         my ($current_port_ix) = reverse split /\./, $oid_current; # last number
-
-         # prepare next loop item
-         @args = (-varbindlist => [$oid_current]);
-
-         next LOOP_ON_OID_PORT if $port_aggregator_index == 0;
-         next LOOP_ON_OID_PORT if not $port_aggregator_index == $swport_ix;
-
-         my $current_port_name = snmp_get_switchport_ix2hr($snmp_session, $current_port_ix);
-         $swport_hr .= "$SEP_AGGREGATOR_PORT$current_port_name";
-         }
-      }
-   return $swport_hr;
-   }
-
-#---------------------------------------------------------------
-# Reverse search port number
-sub snmp_get_switchport_hr2id {
-   my ($snmp_session, $swport_hr, $verbose) = @_;
-
-   # Split for Aggregator port
-   # Keep only the Aggregator part
-   ($swport_hr) = split /$SEP_AGGREGATOR_PORT/, $swport_hr;
-
-   my $swport_id = $swport_hr;
-   # direct return if already numeric (next loop is expensive) / old or simple switch
-   return $swport_id if $swport_id =~ m/^\d+$/;
-
-   my $oid_search_ix = $OID_NUMBER{'ifIndex'}; # base OID
-   my @args = (-varbindlist => [$oid_search_ix]);
-   LOOP_ON_OID_PORT:
-   while (defined $snmp_session->get_next_request(@args)) {
-      my ($oid_current) = $snmp_session->var_bind_names;
-      last LOOP_ON_OID_PORT if not Net::SNMP::oid_base_match($oid_search_ix, $oid_current);
-
-      my $port_ifIndex = $snmp_session->var_bind_list->{$oid_current};
-      my ($port_ix) = reverse split /\./, $oid_current; # last number
-      printf "PORT1: %s => %s\n", $oid_current, $port_ifIndex if $verbose;
-
-      # prepare next loop item
-      @args = (-varbindlist => [$oid_current]);
-
-      my $oid_search_ifName = $OID_NUMBER{'ifName'} . '.' . $port_ifIndex;
-      my $result = $snmp_session->get_request(-varbindlist => [$oid_search_ifName]);
-      next LOOP_ON_OID_PORT if not defined $result;
-
-      my $current_port_hr = normalize_port_human_readable($result->{$oid_search_ifName});
-      printf "PORT2: $oid_search_ifName => $current_port_hr\n" if $verbose;
-      if ($current_port_hr eq $swport_hr) {
-         print "PORT3: $current_port_hr <-> $port_ix\n" if $verbose;
-
-         # return port number ifIndex need by OID portUpDown
-         $swport_id = $port_ifIndex; # other possible value could be $port_ix
-         last LOOP_ON_OID_PORT;
-         }
-      }
-   return $swport_id;
-   }
-
-#---------------------------------------------------------------
-# Get the list of all the VLAN define on a switch
-sub snmp_get_vlan_list {
-   my ($snmp_session, $verbose) = @_;
-
-   my %vlandb = (); # Hash vlan number => vlan name
-
-   my $oid_search_index = $OID_NUMBER{'vlanName'}; # base OID
-   my @args = (-varbindlist => [$oid_search_index]);
-   LOOP_ON_VLAN:
-   while (defined $snmp_session->get_next_request(@args)) {
-      my ($oid_current) = $snmp_session->var_bind_names;
-      last LOOP_ON_VLAN if not Net::SNMP::oid_base_match($oid_search_index, $oid_current);
-
-      my $vlan_name = $snmp_session->var_bind_list->{$oid_current};
-      my ($vlan_id) = reverse split /\./, $oid_current; # last number
-      printf "VLAN: %s => %s\n", $oid_current, $vlan_name if $verbose;
-
-      $vlandb{$vlan_id} = $vlan_name;
-
-      # prepare next loop item
-      @args = (-varbindlist => [$oid_current]);
-      }
-   return %vlandb;
-   }
-
-#---------------------------------------------------------------
-# Load computer database
-sub computerdb_load {
-   my $computerdb = YAML::Syck::LoadFile("$KLASK_DB_FILE");
-
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (keys %{$computerdb}) {
-
-      # Rename switch_port -> switch_port_id (2017/09/15)
-      if (not exists $computerdb->{$ip}{'switch_port_id' and exists $computerdb->{$ip}{'switch_port'}}) {
-         $computerdb->{$ip}{'switch_port_id'} = $computerdb->{$ip}{'switch_port'} if defined $computerdb->{$ip}{'switch_port'};
-         $computerdb->{$ip}{'switch_port_id'} = 0 if $computerdb->{$ip}{'switch_port_id'} !~ m/^\d+$/; # force numeric
-         }
-      delete $computerdb->{$ip}{'switch_port'} if exists $computerdb->{$ip}{'switch_port'};
-
-      next LOOP_ON_IP_ADDRESS if exists $computerdb->{$ip}{'switch_port_hr'} and defined $computerdb->{$ip}{'switch_port_hr'};
-
-      $computerdb->{$ip}{'switch_port_hr'} = $computerdb->{$ip}{'switch_port_id'};
-      }
-
-   return $computerdb;
-   }
-
-#---------------------------------------------------------------
-# Save computer database
-sub computerdb_save {
-   my ($computerdb) = @_;
-
-   my $dirdb = $KLASK_DB_FILE;
-      $dirdb =~ s{ / [^/]* $}{}xms;
-   mkdir "$dirdb", 0755 unless -d "$dirdb";
-   YAML::Syck::DumpFile("$KLASK_DB_FILE", $computerdb);
-
-   return $computerdb;
-   }
-
-#---------------------------------------------------------------
-sub get_switchdb_checksum {
-   my %switch_db = @_; # same as global %SWITCH_DB
-
-   my $checksum_data = '';
-   for my $sw_name (sort keys %switch_db) { # sort to always have the same order
-      $checksum_data .= join ':',
-         $switch_db{$sw_name}->{'description'},
-         $switch_db{$sw_name}->{'model'},
-         $switch_db{$sw_name}->{'revision'},
-         $switch_db{$sw_name}->{'hostname'},
-         "\n";
-      }
-
-   return sha512_base64($checksum_data);
-   }
-
-#---------------------------------------------------------------
-sub update_switchdb {
-   my %args = (
-      verbose => 0,
-      @_);
-
-   init_switch_names('yes'); # nomme les switchs
-   print "\n";
-
-   my %where = ();
-   my %db_switch_output_port = ();
-   my %db_switch_ip_hostnamefq = ();
-
-   DETECT_ALL_ROUTER:
-   for my $one_router (get_list_main_router(get_list_network())) {
-      print "Info: router loop $one_router\n" if $args{'verbose'};
-      my %resol_arp = resolve_ip_arp_host($one_router, q{*}, q{low}); # resolution arp
-
-      next DETECT_ALL_ROUTER if $resol_arp{'mac_address'} eq 'unknow';
-      print "VERBOSE_1: Router detected $resol_arp{'ipv4_address'} - $resol_arp{'mac_address'}\n" if $args{'verbose'};
-
-      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{'interface'});
-      my $vlan_id   = get_current_vlan_id($vlan_name);
-      $where{$resol_arp{'ipv4_address'}} = find_all_switch_port($resol_arp{'mac_address'}, $vlan_id); # retrouve les emplacements des routeurs
-      }
-
-   ALL_ROUTER_IP_ADDRESS:
-   for my $ip_router (Net::Netmask::sort_by_ip_address(keys %where)) { # '194.254.66.254')) {
-
-      next ALL_ROUTER_IP_ADDRESS if not exists $where{$ip_router}; # /a priori/ idiot car ne sers à rien...
-
-      ALL_SWITCH_CONNECTED:
-      for my $switch_detected (keys %{$where{$ip_router}}) {
-
-         my $switch = $where{$ip_router}->{$switch_detected};
-
-         next ALL_SWITCH_CONNECTED if $switch->{'port_id'} eq '0';
-
-         $db_switch_output_port{$switch->{'hostname'}} = $switch->{'port_hr'};
-         print "VERBOSE_2: output port $switch->{'hostname'} : $switch->{'port_hr'}\n" if $args{'verbose'};
-         }
-      }
-
-   my %db_switch_link_with = ();
-
-   my @list_all_switch  = ();
-   my @list_switch_ipv4 = ();
-   for my $sw (@SWITCH_LIST) {
-      push @list_all_switch, $sw->{'hostname'};
-      }
-
-   my $timestamp = time;
-
-   ALL_SWITCH:
-   for my $one_switch (@list_all_switch) {
-      my %resol_arp = resolve_ip_arp_host($one_switch, q{*}, q{low}); # arp resolution
-      if (exists $SWITCH_DB{$one_switch}{'fake-ip'}) {
-         my $fake_ip = $SWITCH_DB{$one_switch}{'fake-ip'};
-         fast_ping($fake_ip);
-         print "WARNING: fake ip on switch $one_switch -> $fake_ip / $resol_arp{'ipv4_address'}\n" if $args{'verbose'};
-         my %resol_arp_alt = resolve_ip_arp_host($fake_ip, q{*}, q{low}); # arp resolution
-         if ($resol_arp_alt{'mac_address'} ne 'unknow') {
-            $resol_arp{'mac_address'} = $resol_arp_alt{'mac_address'};
-            $resol_arp{'interface'}   = $resol_arp_alt{'interface'};
-            $resol_arp{'ipv4_address'} .= '*';
-            # Force a MAC trace on switch
-            system "arping -c 1 -w 1 -rR -i $resol_arp_alt{'interface'} $fake_ip > /dev/null 2>&1";
-            }
-         }
-      print "Info: switch loop $one_switch\n" if $args{'verbose'};
-      next ALL_SWITCH if $resol_arp{'mac_address'} eq 'unknow';
-
-      push @list_switch_ipv4, $resol_arp{'ipv4_address'};
-
-      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{'interface'});
-      my $vlan_id   = get_current_vlan_id($vlan_name);
-      $where{$resol_arp{'ipv4_address'}} = find_all_switch_port($resol_arp{'mac_address'}, $vlan_id); # find port on all switch
-
-      if ($args{'verbose'}) {
-         print "VERBOSE_3: $one_switch $resol_arp{'ipv4_address'} $resol_arp{'mac_address'}\n";
-         print "VERBOSE_3: $one_switch --- ",
-            join(' + ', keys %{$where{$resol_arp{'ipv4_address'}}}),
-            "\n";
-         }
-
-      $db_switch_ip_hostnamefq{$resol_arp{'ipv4_address'}} = $resol_arp{'hostname_fq'};
-      print "VERBOSE_4: db_switch_ip_hostnamefq $resol_arp{'ipv4_address'} -> $resol_arp{'hostname_fq'}\n" if $args{'verbose'};
-
-      $SWITCH_DB{$one_switch}->{'ipv4_address'} = $resol_arp{'ipv4_address'};
-      $SWITCH_DB{$one_switch}->{'mac_address'}  = $resol_arp{'mac_address'};
-      $SWITCH_DB{$one_switch}->{'timestamp'}    = $timestamp;
-      $SWITCH_DB{$one_switch}->{'network'}      = $vlan_name;
-      }
-
-   ALL_SWITCH_IP_ADDRESS:
-   for my $ip (@list_switch_ipv4) {
-      #   for my $ip (Net::Netmask::sort_by_ip_address(@list_switch_ipv4)) {
-
-      print "VERBOSE_5: loop on $db_switch_ip_hostnamefq{$ip}\n" if $args{'verbose'};
-
-      next ALL_SWITCH_IP_ADDRESS if not exists $where{$ip};
-      #      next ALL_SWITCH_IP_ADDRESS if not exists $SWITCH_PORT_COUNT{ $db_switch_ip_hostnamefq{$ip} };
-
-      DETECTED_SWITCH:
-      for my $switch_detected (keys %{$where{$ip}}) {
-
-         my $switch = $where{$ip}->{$switch_detected};
-         print "VERBOSE_6: $db_switch_ip_hostnamefq{$ip} -> $switch->{'hostname'} : $switch->{'port_hr'}\n" if $args{'verbose'};
-
-         next if $switch->{'port_id'}  eq '0';
-         next if $switch->{'port_hr'}  eq $db_switch_output_port{$switch->{'hostname'}};
-         next if $switch->{'hostname'} eq $db_switch_ip_hostnamefq{$ip}; # $computerdb->{$ip}{'hostname'};
-
-         $db_switch_link_with{$db_switch_ip_hostnamefq{$ip}} ||= {};
-         $db_switch_link_with{$db_switch_ip_hostnamefq{$ip}}->{$switch->{'hostname'}} = $switch->{'port_hr'};
-         print "VERBOSE_7: +++++\n" if $args{'verbose'};
-         }
-
-      }
-
-   my %db_switch_connected_on_port          = ();
-   my $maybe_more_than_one_switch_connected = 'yes';
-   my $cloop                                = 0;
-
-   while ($maybe_more_than_one_switch_connected eq 'yes' and $cloop < 100) {
-      $cloop++;
-      print "VERBOSE_9: cloop reduction step: $cloop\n" if $args{'verbose'};
-      for my $sw (keys %db_switch_link_with) {
-         for my $connect (keys %{$db_switch_link_with{$sw}}) {
-
-            my $port_hr = $db_switch_link_with{$sw}->{$connect};
-
-            $db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"} ||= {};
-            $db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"}->{$sw}++; # Just to define the key
-            }
-         }
-
-      $maybe_more_than_one_switch_connected = 'no';
-
-      SWITCH_AND_PORT:
-      for my $swport (keys %db_switch_connected_on_port) {
-
-         next if keys %{$db_switch_connected_on_port{$swport}} == 1;
-
-         $maybe_more_than_one_switch_connected = 'yes';
-
-         my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-         my @sw_on_same_port = keys %{$db_switch_connected_on_port{$swport}};
-         print "VERBOSE_10: $swport -- " . $#sw_on_same_port . " -- @sw_on_same_port\n" if $args{'verbose'};
-
-         CONNECTED:
-         for my $sw_connected (@sw_on_same_port) {
-
-            next CONNECTED if not keys %{$db_switch_link_with{$sw_connected}} == 1;
-
-            $db_switch_connected_on_port{$swport} = {$sw_connected => 1};
-
-            for my $other_sw (@sw_on_same_port) {
-               next if $other_sw eq $sw_connected;
-
-               delete $db_switch_link_with{$other_sw}->{$sw_connect};
-               }
-
-            # We can not do better for this switch for this loop
-            next SWITCH_AND_PORT;
-            }
-         }
-      }
-
-   my %db_switch_parent = ();
-
-   for my $sw (keys %db_switch_link_with) {
-      for my $connect (keys %{$db_switch_link_with{$sw}}) {
-
-         my $port_hr = $db_switch_link_with{$sw}->{$connect};
-
-         $db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"} ||= {};
-         $db_switch_connected_on_port{"$connect$SEP_SWITCH_PORT$port_hr"}->{$sw} = $port_hr;
-
-         $db_switch_parent{$sw} = {switch => $connect, port_hr => $port_hr};
-         }
-      }
-
-   print "Switch output port and parent port connection\n";
-   print "---------------------------------------------\n";
-   for my $sw (sort keys %db_switch_output_port) {
-      if (exists $db_switch_parent{$sw}) {
-         printf "%-28s  %2s  +-->  %2s  %-25s\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{'port_hr'}, $db_switch_parent{$sw}->{'switch'};
-         }
-      else {
-         printf "%-28s  %2s  +-->  router\n", $sw, $db_switch_output_port{$sw};
-         }
-      }
-   print "\n";
-
-   print "Switch parent and children port inter-connection\n";
-   print "------------------------------------------------\n";
-   for my $swport (sort keys %db_switch_connected_on_port) {
-      my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
-         if (exists $db_switch_output_port{$sw}) {
-            printf "%-28s  %2s  <--+  %2s  %-25s\n", $sw_connect, $port_connect, $db_switch_output_port{$sw}, $sw;
-            }
-         else {
-            printf "%-28s  %2s  <--+      %-25s\n", $sw_connect, $port_connect, $sw;
-            }
-         }
-      }
-
-   my $switch_connection = {
-      output_port       => \%db_switch_output_port,
-      parent            => \%db_switch_parent,
-      connected_on_port => \%db_switch_connected_on_port,
-      link_with         => \%db_switch_link_with,
-      switch_db         => \%SWITCH_DB,
-      timestamp         => $timestamp,
-      checksum          => get_switchdb_checksum(%SWITCH_DB),
-      };
-
-   YAML::Syck::DumpFile("$KLASK_SW_FILE", $switch_connection);
-   return;
-   }
-
-################################################################
-# command
-################################################################
-
-#---------------------------------------------------------------
-sub cmd_help {
-
-   print <<'END';
-klask - port and search manager for switches, map management
-
- klask version
- klask help
-
- klask updatedb [--verbose|-v] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l] [--no-rebuildsw|-R]
- klask exportdb [--format|-f txt|html]
- klask removedb ipv4_addr* computer*
- klask insertdb --ip ipv4_addr --mac mac_addr --network vlan_name
- klask cleandb  [--verbose|-v] [--day number_of_day] [--repair-dns]
-
- klask updatesw [--verbose|-v]
- klask exportsw [--format|-f txt|dot] [--modulo|-m XX] [--shift|-s YY] [--way all|desc|child|parent] [--no-header|-H]
-
- klask searchdb [--kind|-k host|mac] computer [mac-address]
- klask search   computer
- klask search-mac-on-switch [--verbose|-v] [--vlan|-i vlan-id] switch mac_addr
-
- klask ip-free [--verbose|-v] [--day|-d days-to-death] [--format|-f txt|html] [vlan_name]
-
- klask bad-vlan-id [--day|-d days_before_alert] [--format|-f txt|html]
-
- klask enable  [--verbose|-v] switch port
- klask disable [--verbose|-v] switch port
- klask status  [--verbose|-v] switch port
-
- klask poe-enable  [--verbose|-v] switch port
- klask poe-disable [--verbose|-v] switch port
- klask poe-status  [--verbose|-v] switch port
-
- klask vlan-getname switch vlan-id
- klask vlan-list switch
-END
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_version {
-
-   print <<'END';
-klask - port and search manager for switches, map management
-Copyright (C) 2005-2020 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
-License GNU GPL version 2 or later and Perl equivalent
-END
-
-   print "Version $VERSION\n\n";
-   print ' $Id$'."\n";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_search {
-   my @computer = @_;
-
-   init_switch_names(); # nomme les switchs
-   fast_ping(@computer);
-
-   LOOP_ON_COMPUTER:
-   for my $clientname (@computer) {
-      my %resol_arp = resolve_ip_arp_host($clientname);                          # resolution arp
-      my $vlan_name = get_current_vlan_name_for_interface($resol_arp{'interface'});
-      my $vlan_id   = get_current_vlan_id($vlan_name);
-      my %where     = find_switch_port($resol_arp{'mac_address'}, '', $vlan_id); # retrouve l'emplacement
-
-      next LOOP_ON_COMPUTER
-         if $where{'switch_description'} eq 'unknow'
-         or $resol_arp{'hostname_fq'}    eq 'unknow'
-         or $resol_arp{'mac_address'}    eq 'unknow';
-
-      printf '%-22s %2s %-30s %-15s %18s',
-         $where{'switch_hostname'},
-         $where{'switch_port_hr'},
-         $resol_arp{'hostname_fq'},
-         $resol_arp{'ipv4_address'},
-         $resol_arp{'mac_address'} . "\n";
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_searchdb {
-   local @ARGV = @_;
-
-   my $kind;
-
-   GetOptions(
-      'kind=s'   => \$kind,
-      );
-
-   my %possible_search = (
-      host => \&cmd_searchdb_host,
-      mac  => \&cmd_searchdb_mac,
-      );
-
-   $kind = 'host' if not defined $possible_search{$kind};
-
-   $possible_search{$kind}->(@ARGV);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_searchdb_host {
-   my @computer = @_;
-
-   fast_ping(@computer);
-   my $computerdb = computerdb_load();
-
-   LOOP_ON_COMPUTER:
-   for my $clientname (@computer) {
-      my %resol_arp = resolve_ip_arp_host($clientname); # resolution arp
-      my $ip        = $resol_arp{'ipv4_address'};
-
-      next LOOP_ON_COMPUTER unless exists $computerdb->{$ip};
-
-      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$ip}{'timestamp'};
-      $year += 1900;
-      $mon++;
-      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-      printf "%-22s %2s %-30s %-15s %-18s %s\n",
-         $computerdb->{$ip}{'switch_hostname'},
-         $computerdb->{$ip}{'switch_port_hr'},
-         $computerdb->{$ip}{'hostname_fq'},
-         $ip,
-         $computerdb->{$ip}{'mac_address'},
-         $date;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_searchdb_mac {
-   my @mac = map {normalize_mac_address($_)} @_;
-
-   my $computerdb = computerdb_load();
-
-   LOOP_ON_MAC:
-   for my $mac (@mac) {
-      LOOP_ON_COMPUTER:
-      for my $ip (keys %{$computerdb}) {
-         next LOOP_ON_COMPUTER if $mac ne $computerdb->{$ip}{'mac_address'};
-
-         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$ip}{'timestamp'};
-         $year += 1900;
-         $mon++;
-         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-         printf "%-22s %2s %-30s %-15s %-18s %s\n",
-            $computerdb->{$ip}{'switch_hostname'},
-            $computerdb->{$ip}{'switch_port_hr'},
-            $computerdb->{$ip}{'hostname_fq'},
-            $ip,
-            $computerdb->{$ip}{'mac_address'},
-            $date;
-         #next LOOP_ON_MAC;
-         }
-
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_updatedb {
-   local @ARGV = @_;
-
-   my ($verbose, $verb_description, $check_hostname, $check_location, $no_rebuildsw);
-
-   GetOptions(
-      'verbose|v'          => \$verbose,
-      'verb-description|d' => \$verb_description,
-      'chk-hostname|h'     => \$check_hostname,
-      'chk-location|l'     => \$check_location,
-      'no-rebuildsw|R'     => \$no_rebuildsw,
-      );
-
-   my @network = @ARGV;
-   @network = get_list_network() if not @network;
-
-   test_switchdb_environnement();
-
-   my $computerdb = {};
-   $computerdb = computerdb_load() if -e "$KLASK_DB_FILE";
-   my $timestamp = time;
-
-   my %computer_not_detected = ();
-   my $timestamp_last_week = $timestamp - (3600 * 24 * 7);
-
-   my $number_of_computer = get_list_ip(@network); # + 1;
-   my $size_of_database   = keys %{$computerdb};
-   $size_of_database = 1 if $size_of_database == 0;
-   my $i                 = 0;
-   my $detected_computer = 0;
-
-   init_switch_names('yes', $verb_description, $check_hostname, $check_location); # nomme les switchs
-
-   {
-      my $switch_checksum = get_switchdb_checksum(%SWITCH_DB);
-      # Remplis le champs portignore des ports d'inter-connection pour chaque switch
-      my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
-      if ($switch_checksum ne $switch_connection->{'checksum'}) { # Verify checksum
-         if ($no_rebuildsw) {
-            print "WARNING: switch database is outdate, please rebuild if with updatesw command\n";
-            }
-         else {
-            print "WARNING: switch database is going to be rebuilt\n";
-            update_switchdb(verbose => $verbose)
-            }
-         }
-
-      my %db_switch_output_port       = %{$switch_connection->{'output_port'}};
-      my %db_switch_connected_on_port = %{$switch_connection->{'connected_on_port'}};
-      my %db_switch_chained_port = ();
-      for my $swport (keys %db_switch_connected_on_port) {
-         my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-         $db_switch_chained_port{$sw_connect} .= "$port_connect:";
-         }
-      for my $sw (@SWITCH_LIST) {
-         push @{$sw->{'portignore'}}, $db_switch_output_port{$sw->{'hostname'}} if exists $db_switch_output_port{$sw->{'hostname'}};
-         if (exists $db_switch_chained_port{$sw->{'hostname'}}) {
-            chop $db_switch_chained_port{$sw->{'hostname'}};
-            push @{$sw->{'portignore'}}, split m/ : /xms, $db_switch_chained_port{$sw->{'hostname'}};
-            }
-         #print "$sw->{'hostname'} ++ @{$sw->{'portignore'}}\n";
-         }
-      }
-
-   my %router_mac_ip = ();
-   DETECT_ALL_ROUTER:
-   #   for my $one_router ('194.254.66.254') {
-   for my $one_router (get_list_main_router(@network)) {
-      my %resol_arp = resolve_ip_arp_host($one_router);
-      $router_mac_ip{$resol_arp{'mac_address'}} = $resol_arp{'ipv4_address'};
-      }
-
-   ALL_NETWORK:
-   for my $current_net (@network) {
-
-      my @computer          = get_list_ip($current_net);
-      my $current_interface = get_current_interface($current_net);
-
-      fast_ping(@computer) if get_current_scan_mode($current_net) eq 'active';
-
-      LOOP_ON_COMPUTER:
-      for my $one_computer (@computer) {
-         $i++;
-
-         my $total_percent = int(($i * 100) / $number_of_computer);
-
-         my $localtime = time - $timestamp;
-         my ($sec, $min) = localtime $localtime;
-
-         my $time_elapse = 0;
-         $time_elapse = $localtime * (100 - $total_percent) / $total_percent if $total_percent != 0;
-         my ($sec_elapse, $min_elapse) = localtime $time_elapse;
-
-         printf "\rComputer scanned: %4i/%i (%2i%%)", $i, $number_of_computer, $total_percent;
-         printf ', detected: %4i/%i (%2i%%)', $detected_computer, $size_of_database, int(($detected_computer * 100) / $size_of_database);
-         printf ' [Time: %02i:%02i / %02i:%02i]', int($localtime / 60), $localtime % 60, int($time_elapse / 60), $time_elapse % 60;
-         printf ' %-8s %-14s', $current_interface, $one_computer;
-
-         my $already_exist = exists $computerdb->{$one_computer} ? 'yes' : 'no';
-         my %resol_arp = resolve_ip_arp_host($one_computer, $current_interface, 'fast', $already_exist);
-
-         # do not search on router connection (why ?)
-         if (exists $router_mac_ip{$resol_arp{'mac_address'}}) {
-            $computer_not_detected{$one_computer} = $current_net;
-            next LOOP_ON_COMPUTER;
-            }
-
-         # do not search on switch inter-connection
-         if (exists $SWITCH_LEVEL{$resol_arp{'hostname_fq'}}) {
-            $computer_not_detected{$one_computer} = $current_net;
-            next LOOP_ON_COMPUTER;
-            }
-
-         my $switch_proposal = q{};
-         if (exists $computerdb->{$resol_arp{'ipv4_address'}} and exists $computerdb->{$resol_arp{'ipv4_address'}}{'switch_hostname'}) {
-            $switch_proposal = $computerdb->{$resol_arp{'ipv4_address'}}{'switch_hostname'};
-            }
-
-         # do not have a mac address
-         if ($resol_arp{'mac_address'} eq 'unknow'
-               or (exists $resol_arp{'timestamps'} and $resol_arp{'timestamps'} < ($timestamp - 3 * 3600))
-               ) {
-            $computer_not_detected{$one_computer} = $current_net;
-            next LOOP_ON_COMPUTER;
-            }
-
-         my $vlan_name = get_current_vlan_name_for_interface($resol_arp{'interface'});
-         my $vlan_id   = get_current_vlan_id($vlan_name);
-         my %where     = find_switch_port($resol_arp{'mac_address'}, $switch_proposal, $vlan_id);
-
-         #192.168.24.156:
-         #  arp: 00:0B:DB:D5:F6:65
-         #  hostname: pcroyon.hmg.priv
-         #  port: 5
-         #  switch: sw-batH-legi:hp2524
-         #  timestamp: 1164355525
-
-         # do not have a mac address
-         #         if ($resol_arp{'mac_address'} eq 'unknow') {
-         #            $computer_not_detected{$one_computer} = $current_interface;
-         #            next LOOP_ON_COMPUTER;
-         #            }
-
-         # detected on a switch
-         if ($where{'switch_description'} ne 'unknow') {
-            $detected_computer++;
-            $computerdb->{$resol_arp{'ipv4_address'}} = {
-               hostname_fq        => $resol_arp{'hostname_fq'},
-               mac_address        => $resol_arp{'mac_address'},
-               switch_hostname    => $where{'switch_hostname'},
-               switch_description => $where{'switch_description'},
-               switch_port_id     => $where{'switch_port_id'},
-               switch_port_hr     => $where{'switch_port_hr'},
-               timestamp          => $timestamp,
-               network            => $current_net,
-               };
-            next LOOP_ON_COMPUTER;
-            }
-
-         # new in the database but where it is ?
-         if (not exists $computerdb->{$resol_arp{'ipv4_address'}}) {
-            $detected_computer++;
-            $computerdb->{$resol_arp{'ipv4_address'}} = {
-               hostname_fq        => $resol_arp{'hostname_fq'},
-               mac_address        => $resol_arp{'mac_address'},
-               switch_hostname    => $where{'switch_hostname'},
-               switch_description => $where{'switch_description'},
-               switch_port_id     => $where{'switch_port_id'},
-               switch_port_hr     => $where{'switch_port_hr'},
-               timestamp          => $resol_arp{'timestamp'},
-               network            => $current_net,
-               };
-            }
-
-         # mise a jour du nom de la machine si modification dans le dns
-         $computerdb->{$resol_arp{'ipv4_address'}}{'hostname_fq'} = $resol_arp{'hostname_fq'};
-
-         # mise à jour de la date de détection si détection plus récente par arpwatch
-         $computerdb->{$resol_arp{'ipv4_address'}}{'timestamp'}   = $resol_arp{'timestamp'}
-            if exists $resol_arp{'timestamp'} and $computerdb->{$resol_arp{'ipv4_address'}}{'timestamp'} < $resol_arp{'timestamp'};
-
-         # relance un arping sur la machine si celle-ci n'a pas été détectée depuis plus d'une semaine
-         $computer_not_detected{$resol_arp{'ipv4_address'}} = $current_net
-            if $computerdb->{$resol_arp{'ipv4_address'}}{'timestamp'} < $timestamp_last_week;
-
-         }
-      }
-
-   # final end of line at the end of the loop
-   printf "\n";
-
-   computerdb_save($computerdb);
-
-   for my $one_computer (keys %computer_not_detected) {
-      my $current_net       = $computer_not_detected{$one_computer};
-      my $current_interface = get_current_interface($current_net);
-      system "arping -c 1 -w 1 -rR -i $current_interface $one_computer > /dev/null 2>&1"
-         if get_current_scan_mode($current_net) eq 'active';
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_removedb {
-   my @computer = @_;
-
-   test_maindb_environnement();
-
-   my $computerdb = computerdb_load();
-
-   LOOP_ON_COMPUTER:
-   for my $one_computer (@computer) {
-
-      if ($one_computer =~ m/^ $RE_IPv4_ADDRESS $/xms
-            and exists $computerdb->{$one_computer}) {
-         delete $computerdb->{$one_computer};
-         next;
-         }
-
-      my %resol_arp = resolve_ip_arp_host($one_computer);
-
-      delete $computerdb->{$resol_arp{'ipv4_address'}} if exists $computerdb->{$resol_arp{'ipv4_address'}};
-      }
-
-   computerdb_save($computerdb);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_insertdb {
-   local @ARGV = @_;
-
-   my ($ip, $mac, $net);
-
-   GetOptions(
-      'ip|i=s'      => \$ip,
-      'mac|m=s'     => \$mac,
-      'network|n=s' => \$net,
-      );
-
-   if (not $ip or not $mac or not $net) {
-      print {*STDERR} "Error: mandatory parameter --ip\n"      if not $ip;
-      print {*STDERR} "Error: mandatory parameter --mac\n"     if not $mac;
-      print {*STDERR} "Error: mandatory parameter --network\n" if not $net;
-      print {*STDERR} "\n";
-      $CMD_DB{'help'}->();
-      exit 1;
-      }
-
-   test_maindb_environnement();
-
-   $mac = normalize_mac_address($mac);
-
-   if ($ip !~ m/^ $RE_IPv4_ADDRESS $/xms or $mac !~ m/^ $RE_MAC_ADDRESS $/xms or not exists $KLASK_CFG->{'network'}{$net}) {
-      print {*STDERR} "Error: parameter not valid --ip $ip\n"       if $ip !~ m/^ $RE_IPv4_ADDRESS $/xms;
-      print {*STDERR} "Error: parameter not valid --mac $mac\n"     if $mac !~ m/^ $RE_MAC_ADDRESS $/xms;
-      print {*STDERR} "Error: parameter not valid --network $net\n" if not exists $KLASK_CFG->{'network'}{$net};
-      print {*STDERR} "\n";
-      $CMD_DB{'help'}->();
-      exit 1;
-      }
-
-   my %resol_arp = resolve_ip_arp_host($ip); # Get hostname_fq ipv4_address
-
-   if ($resol_arp{'hostname_fq'} eq 'unknow') {
-      print {*STDERR} "Error: DNS name not defined for IPv4 $ip (mandatory)\n";
-      exit 1;
-      }
-
-   if ($resol_arp{'ipv4_address'} ne $ip) {
-      print {*STDERR} "Error: IPv4 DNS resolution strange between $ip and $resol_arp{'ipv4_address'}\n";
-      exit 1;
-      }
-
-   my $computerdb = computerdb_load();
-   my $timestamp  = time;
-
-   LOOP_ON_COMPUTER:
-   for my $current_ip (keys %{$computerdb}) {
-      if ($current_ip eq $ip) {
-         print {*STDERR} "Error: IPv4 $ip already exists in the computer database\n";
-         exit 1;
-         }
-
-      if ($computerdb->{$current_ip}{'mac_address'} eq $mac) {
-         $timestamp = $computerdb->{$current_ip}{'timestamp'} if $computerdb->{$current_ip}{'timestamp'} < $timestamp;
-         }
-      }
-
-   my $days_before_alert = $DEFAULT{'days-before-alert'} || 15;
-   $timestamp -= $days_before_alert * 24 * 3600 + 1; # Just decrement one second to be oldier
-
-   $computerdb->{$ip} = {
-      hostname_fq        => $resol_arp{'hostname_fq'},
-      mac_address        => $mac,
-      switch_hostname    => 'Unknow',
-      switch_description => 'Unknow',
-      switch_port_id     => 0,
-      switch_port_hr     => 0,
-      timestamp          => $timestamp,
-      network            => $net,
-      };
-
-   computerdb_save($computerdb);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_cleandb {
-   local @ARGV = @_;
-
-   my $days_to_clean = 15;
-   my $repairdns;
-   my $verbose;
-   my $database_has_changed;
-
-   GetOptions(
-      'day|d=i'      => \$days_to_clean,
-      'verbose|v'    => \$verbose,
-      'repair-dns|r' => \$repairdns,
-      );
-
-   my @vlan_name = get_list_network();
-
-   my $computerdb = computerdb_load();
-   my $timestamp  = time;
-
-   my $timestamp_barrier = 3600 * 24 * $days_to_clean;
-   my $timestamp_3month  = 3600 * 24 * 90;
-
-   my %mactimedb = ();
-   ALL_VLAN:
-   for my $vlan (shuffle @vlan_name) {
-
-      my @ip_list = shuffle get_list_ip($vlan);
-
-      LOOP_ON_IP_ADDRESS:
-      for my $ip (@ip_list) {
-
-         next LOOP_ON_IP_ADDRESS
-            if not exists $computerdb->{$ip};
-
-         #&& $computerdb->{$ip}{'timestamp'} > $timestamp_barrier;
-         my $ip_timestamp   = $computerdb->{$ip}{'timestamp'};
-         my $ip_mac         = $computerdb->{$ip}{'mac_address'};
-         my $ip_hostname_fq = $computerdb->{$ip}{'hostname_fq'};
-
-         $mactimedb{$ip_mac} ||= {
-            ip          => $ip,
-            timestamp   => $ip_timestamp,
-            vlan        => $vlan,
-            hostname_fq => $ip_hostname_fq,
-            };
-
-         if (
-            ( $mactimedb{$ip_mac}->{'timestamp'} - $ip_timestamp > $timestamp_barrier
-               or (
-                  $mactimedb{$ip_mac}->{'timestamp'} > $ip_timestamp
-                  and $timestamp - $mactimedb{$ip_mac}->{'timestamp'} > $timestamp_3month
-                  )
-            )
-            and (
-               not $mactimedb{$ip_mac}->{'hostname_fq'} =~ m/$RE_FLOAT_HOSTNAME/
-               or $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
-               )) {
-            print "remove ip $ip\n" if $verbose;
-            delete $computerdb->{$ip};
-            $database_has_changed++;
-            }
-
-         elsif (
-            ( $ip_timestamp - $mactimedb{$ip_mac}->{'timestamp'} > $timestamp_barrier
-               or (
-                  $ip_timestamp > $mactimedb{$ip_mac}->{'timestamp'}
-                  and $timestamp - $ip_timestamp > $timestamp_3month
-                  )
-            )
-            and (
-               not $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/
-               or $mactimedb{$ip_mac}->{'hostname_fq'} =~ m/$RE_FLOAT_HOSTNAME/
-               )) {
-            print "remove ip ".$mactimedb{$ip_mac}->{'ip'}."\n" if $verbose;
-            delete $computerdb->{$mactimedb{$ip_mac}->{'ip'}};
-            $database_has_changed++;
-            }
-
-         if ($ip_timestamp > $mactimedb{$ip_mac}->{'timestamp'}) {
-            $mactimedb{$ip_mac} = {
-               ip          => $ip,
-               timestamp   => $ip_timestamp,
-               vlan        => $vlan,
-               hostname_fq => $ip_hostname_fq,
-               };
-            }
-         }
-      }
-
-   if ($repairdns) { # Search and update unkown computer in reverse DNS
-      LOOP_ON_IP_ADDRESS:
-      for my $ip (keys %{$computerdb}) {
-         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} ne 'unknow';
-
-         my $packed_ip = scalar gethostbyname($ip);
-         next LOOP_ON_IP_ADDRESS if not defined $packed_ip;
-
-         my $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET);
-         next LOOP_ON_IP_ADDRESS if not defined $hostname_fq;
-
-         $computerdb->{$ip}{'hostname_fq'} = $hostname_fq;
-         $database_has_changed++;
-         }
-      }
-
-   computerdb_save($computerdb) if $database_has_changed;
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportdb {
-   local @ARGV = @_;
-
-   my $format = 'txt';
-
-   GetOptions(
-      'format|f=s'  => \$format,
-      );
-
-   my %possible_format = (
-      txt  => \&cmd_exportdb_txt,
-      html => \&cmd_exportdb_html,
-      );
-
-   $format = 'txt' if not defined $possible_format{$format};
-
-   $possible_format{$format}->(@ARGV);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportdb_txt {
-   test_maindb_environnement();
-
-   my $computerdb = computerdb_load();
-
-   my $tb_computer = Text::Table->new(
-      {align => 'left',   align_title => 'left',   title => 'Switch'},
-      {align => 'right',  align_title => 'right',  title => 'Port'},
-      {align => 'center', align_title => 'center', title => 'Link'},
-      {align => 'left',   align_title => 'left',   title => 'Hostname-FQ'},
-      {align => 'left',   align_title => 'left',   title => 'IPv4-Address'},
-      {align => 'left',   align_title => 'left',   title => 'MAC-Address'},
-      {align => 'left',   align_title => 'left',   title => 'Date'},
-      {align => 'left',   align_title => 'left',   title => 'VLAN'},
-      );
-
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
-
-      # to be improve in the future
-      next LOOP_ON_IP_ADDRESS
-         if $computerdb->{$ip}{'hostname_fq'} eq ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}); # switch on himself !
-
-      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$ip}{'timestamp'};
-      $year += 1900;
-      $mon++;
-      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-      my $vlan = '';
-      $vlan = $computerdb->{$ip}{'network'} . '(' . get_current_vlan_id($computerdb->{$ip}{'network'}).')' if $computerdb->{$ip}{'network'};
-
-      my $arrow = '<---';
-      $arrow = '<===' if $computerdb->{$ip}{'switch_port_hr'} =~ m/^(Trk|Br|Po)/;
-
-      $tb_computer->add(
-         $computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'},
-         $computerdb->{$ip}{'switch_port_hr'},
-         $arrow,
-         $computerdb->{$ip}{'hostname_fq'},
-         $ip,
-         $computerdb->{$ip}{'mac_address'},
-         $date,
-         $vlan,
-         );
-      }
-
-   print $tb_computer->title();
-   print $tb_computer->rule('-');
-   print $tb_computer->body();
-
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportdb_html {
-   test_maindb_environnement();
-
-   my $computerdb = computerdb_load();
-
-   #<link rel="stylesheet" type="text/css" href="style-klask.css" />
-   #<script src="sorttable-klask.js"></script>
-
-   print <<'END_HTML';
-<table class="sortable">
- <caption>Klask Host Database</caption>
- <thead>
-  <tr>
-   <th scope="col" class="klask-header-left">Switch</th>
-   <th scope="col" class="sorttable_nosort">Port</th>
-   <th scope="col" class="sorttable_nosort">Link</th>
-   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
-   <th scope="col" class="hklask-ipv4">IPv4-Address</th>
-   <th scope="col" class="sorttable_alpha">MAC-Address</th>
-   <th scope="col" class="sorttable_alpha">VLAN</th>
-   <th scope="col" class="klask-header-right">Date</th>
-  </tr>
- </thead>
- <tbody>
-END_HTML
-
-   my %mac_count = ();
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (keys %{$computerdb}) {
-
-      # to be improve in the future
-      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} eq ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}); # switch on himself !
-
-      $mac_count{$computerdb->{$ip}{'mac_address'}}++;
-      }
-
-   my $typerow = 'even';
-
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
-
-      # to be improve in the future
-      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} eq ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}); # switch on himself !
-
-      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$ip}{'timestamp'};
-      $year += 1900;
-      $mon++;
-      my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-      #      $odd_or_even++;
-      #      my $typerow = $odd_or_even % 2 ? 'odd' : 'even';
-      $typerow = $typerow eq 'even' ? 'odd' : 'even';
-
-      #my $arrow ='&#8592;';
-      #   $arrow ='&#8656;' if $computerdb->{$ip}{'switch_port_hr'} =~ m/^(Trk|Br|Po)/;
-      my $arrow = '&#10229;';
-      $arrow = '&#10232;' if $computerdb->{$ip}{'switch_port_hr'} =~ m/^(Trk|Br|Po)/;
-
-      my $switch_hostname = $computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'} || 'unkown';
-      chomp $switch_hostname;
-      my $switch_hostname_sort = format_switchport4sort($switch_hostname, $computerdb->{$ip}{'switch_port_hr'});
-
-      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
-
-      my $mac_sort = sprintf '%04i-%s', 9999 - $mac_count{$computerdb->{$ip}{'mac_address'}}, $computerdb->{$ip}{'mac_address'};
-
-      $computerdb->{$ip}{'hostname_fq'} = 'unknow' if $computerdb->{$ip}{'hostname_fq'} =~ m/^ \d+ \. \d+ \. \d+ \. \d+ $/xms;
-      my ($host_short) = split m/ \. /xms, $computerdb->{$ip}{'hostname_fq'};
-
-      my $vlan = '';
-      $vlan = $computerdb->{$ip}{'network'} . ' (' . get_current_vlan_id($computerdb->{$ip}{'network'}) . ')'
-         if $computerdb->{$ip}{'network'};
-
-      my $parent_port_hr = format_aggregator4html($computerdb->{$ip}{'switch_port_hr'});
-
-      fqdn_html_breakable($switch_hostname);
-      fqdn_html_breakable(my $hostname_fq_html = $computerdb->{$ip}{'hostname_fq'});
-
-      print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$switch_hostname_sort">$switch_hostname</td>
-   <td class="bklask-port">$parent_port_hr</td>
-   <td class="bklask-arrow">$arrow</td>
-   <td sorttable_customkey="$host_short">$hostname_fq_html</td>
-   <td sorttable_customkey="$ip_sort">$ip</td>
-   <td sorttable_customkey="$mac_sort">$computerdb->{$ip}{'mac_address'}</td>
-   <td>$vlan</td>
-   <td>$date</td>
-  </tr>
-END_HTML
-      }
-
-   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
-
-   my %db_switch_output_port       = %{$switch_connection->{'output_port'}};
-   my %db_switch_parent            = %{$switch_connection->{'parent'}};
-   my %db_switch_connected_on_port = %{$switch_connection->{'connected_on_port'}};
-   my %db_switch                   = %{$switch_connection->{'switch_db'}};
-
-   # Output switch connection
-   LOOP_ON_OUTPUT_SWITCH:
-   for my $sw (sort keys %db_switch_output_port) {
-
-      my $switch_hostname_sort = format_switchport4sort($sw, $db_switch_output_port{$sw});
-
-      $typerow = $typerow eq 'even' ? 'odd' : 'even';
-
-      #my $arrow ='&#8702;';
-      #   $arrow ='&#8680;' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
-      my $arrow = '&#10236;';
-      $arrow = '&#10238;' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
-
-      if (exists $db_switch_parent{$sw}) {
-         # Link to uplink switch
-         next LOOP_ON_OUTPUT_SWITCH;
-
-         # Do not print anymore
-         my $mac_address  = $db_switch{$db_switch_parent{$sw}->{'switch'}}->{'mac_address'};
-         my $ipv4_address = $db_switch{$db_switch_parent{$sw}->{'switch'}}->{'ipv4_address'};
-         my $timestamp    = $db_switch{$db_switch_parent{$sw}->{'switch'}}->{'timestamp'};
-
-         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;
-         $year += 1900;
-         $mon++;
-         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-         my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ [\.\*] /xms, $ipv4_address; # \* for fake-ip
-
-         my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
-
-         my $host_short = format_switchport4sort(split(m/ \. /xms, $db_switch_parent{$sw}->{'switch'}, 1), $db_switch_parent{$sw}->{'port_hr'});
-
-         my $vlan = $db_switch{$db_switch_parent{$sw}->{'switch'}}->{'network'};
-         $vlan .= ' (' . get_current_vlan_id($db_switch{$db_switch_parent{$sw}->{'switch'}}->{'network'}) . ')' if $db_switch{$db_switch_parent{$sw}->{'switch'}}->{'network'};
-
-         my $parent_port_hr = format_aggregator4html($db_switch_output_port{$sw});
-         my $child_port_hr  = format_aggregator4html($db_switch_parent{$sw}->{'port_hr'});
-
-         fqdn_html_breakable($sw);
-         fqdn_html_breakable(my $sw_child_html = $db_switch_parent{$sw}->{'switch'});
-
-         print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$switch_hostname_sort">$sw</td>
-   <td class="bklask-port">$parent_port_hr</td>
-   <td class="bklask-arrow">$arrow $child_port_hr</td>
-   <td sorttable_customkey="$host_short">$sw_child_html</td>
-   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
-   <td sorttable_customkey="$mac_sort">$mac_address</td>
-   <td>$vlan</td>
-   <td>$date</td>
-  </tr>
-END_HTML
-         }
-      else {
-         # Router
-         my $parent_port_hr = format_aggregator4html($db_switch_output_port{$sw});
-
-         my $host_short = format_switchport4sort($sw, $db_switch_output_port{$sw});
-
-         my $mac_address  = $db_switch{$sw}->{'mac_address'};
-         my $ipv4_address = $db_switch{$sw}->{'ipv4_address'};
-         my $timestamp    = $db_switch{$sw}->{'timestamp'};
-
-         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;
-         $year += 1900;
-         $mon++;
-         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-         my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ [\.\*] /xms, $ipv4_address; # \* for fake-ip
-
-         my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
-
-         my $vlan = $db_switch{$sw}->{'network'};
-         $vlan .= ' (' . get_current_vlan_id($db_switch{$sw}->{'network'}) . ')' if $db_switch{$sw}->{'network'};
-
-         my $arrow = '&#10235;';
-         $arrow = '&#10237;' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
-
-         fqdn_html_breakable($sw);
-
-         print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="router">router</td>
-   <td class="bklask-port"></td>
-   <td class="bklask-arrow">$arrow $parent_port_hr</td>
-   <td sorttable_customkey="$host_short">$sw</td>
-   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
-   <td sorttable_customkey="$mac_sort">$mac_address</td>
-   <td>$vlan</td>
-   <td>$date</td>
-  </tr>
-END_HTML
-         next LOOP_ON_OUTPUT_SWITCH;
-         }
-      }
-
-   # Child switch connection : parent <- child
-   for my $swport (sort keys %db_switch_connected_on_port) {
-      my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
-
-         my $switch_hostname_sort = format_switchport4sort($sw_connect, $port_connect);
-
-         my $mac_address  = $db_switch{$sw}->{'mac_address'};
-         my $ipv4_address = $db_switch{$sw}->{'ipv4_address'};
-         my $timestamp    = $db_switch{$sw}->{'timestamp'};
-
-         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;
-         $year += 1900;
-         $mon++;
-         my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-
-         my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ [\.\*] /xms, $ipv4_address; # \* for fake-ip
-
-         my $mac_sort = sprintf '%04i-%s', 9999, $mac_address;
-
-         $typerow = $typerow eq 'even' ? 'odd' : 'even';
-
-         #my $arrow ='&#8701;';
-         #   $arrow ='&#8678;' if $port_connect =~ m/^(Trk|Br|Po)/;
-         my $arrow = '&#10235;';
-         $arrow = '&#10237;' if $port_connect =~ m/^(Trk|Br|Po)/;
-
-         my $vlan = $db_switch{$sw}->{'network'};
-         $vlan .= ' (' . get_current_vlan_id($db_switch{$sw}->{'network'}) . ')' if $db_switch{$sw}->{'network'};
-
-         if (exists $db_switch_output_port{$sw}) {
-
-            my $host_short = format_switchport4sort(split(m/\./xms, $sw, 1), $db_switch_output_port{$sw});
-
-            my $parent_port_hr = format_aggregator4html($port_connect);
-            my $child_port_hr  = format_aggregator4html($db_switch_output_port{$sw});
-
-            fqdn_html_breakable($sw);
-            fqdn_html_breakable($sw_connect);
-
-            print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
-   <td class="bklask-port">$parent_port_hr</td>
-   <td class="bklask-arrow">$arrow $child_port_hr</td>
-   <td sorttable_customkey="$host_short">$sw</td>
-   <td sorttable_customkey="$ip_sort">$ipv4_address</td>
-   <td sorttable_customkey="$mac_sort">$mac_address</td>
-   <td>$vlan</td>
-   <td>$date</td>
-  </tr>
-END_HTML
-            }
-         else {
-            my $parent_port_hr = format_aggregator4html($port_connect);
-
-            fqdn_html_breakable($sw);
-            fqdn_html_breakable($sw_connect);
-
-            print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$switch_hostname_sort">$sw_connect</td>
-   <td class="bklask-port">$parent_port_hr</td>
-   <td class="bklask-arrow">$arrow</td>
-   <td sorttable_customkey="$sw">$sw</td>
-   <td sorttable_customkey="">$ipv4_address</td>
-   <td sorttable_customkey="">$mac_address</td>
-   <td>$vlan</td>
-   <td>$date</td>
-  </tr>
-END_HTML
-            }
-         }
-      }
-
-   print <<'END_HTML';
- </tbody>
- <tfoot>
-  <tr>
-   <th scope="col" class="klask-footer-left">Switch</th>
-   <th scope="col" class="fklask-port">Port</th>
-   <th scope="col" class="fklask-link">Link</th>
-   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
-   <th scope="col" class="fklask-ipv4">IPv4-Address</th>
-   <th scope="col" class="fklask-mac">MAC-Address</th>
-   <th scope="col" class="fklask-vlan">VLAN</th>
-   <th scope="col" class="klask-footer-right">Date</th>
-  </tr>
- </tfoot>
-</table>
-END_HTML
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_bad_vlan_id {
-   local @ARGV = @_;
-
-   my $days_before_alert = $DEFAULT{'days-before-alert'} || 15;
-   my $format = 'txt';
-   my $verbose;
-
-   GetOptions(
-      'day|d=i'    => \$days_before_alert,
-      'format|f=s' => \$format,
-      );
-
-   my %possible_format = (
-      txt  => \&cmd_bad_vlan_id_txt,
-      html => \&cmd_bad_vlan_id_html,
-      none => sub { },
-      );
-   $format = 'txt' if not defined $possible_format{$format};
-
-   test_maindb_environnement();
-
-   my $computerdb = computerdb_load();
-
-   # create a database with the most recent computer by switch port
-   my %switchportdb = ();
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (keys %{$computerdb}) {
-      # to be really improve in the future
-      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} eq ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}); # switch on himself !
-      #next LOOP_ON_IP_ADDRESS if ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}) eq 'unknow';
-      #next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'switch_port_id'} eq '0';
-
-      my $ip_timestamp   = $computerdb->{$ip}{'timestamp'};
-      my $ip_mac         = $computerdb->{$ip}{'mac_address'};
-      my $ip_hostname_fq = $computerdb->{$ip}{'hostname_fq'};
-
-      my $swpt = sprintf "%-28s  %2s",
-         $computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'},
-         $computerdb->{$ip}{'switch_port_hr'};
-      $switchportdb{$swpt} ||= {
-         ip          => $ip,
-         timestamp   => $ip_timestamp,
-         vlan        => $computerdb->{$ip}{'network'},
-         hostname_fq => $ip_hostname_fq,
-         mac_address => $ip_mac,
-         };
-
-      # if float computer, set date 15 day before warning...
-      my $ip_timestamp_mod = $ip_timestamp;
-      my $ip_timestamp_ref = $switchportdb{$swpt}->{'timestamp'};
-      $ip_timestamp_mod -= $days_before_alert * 24 * 3600 if $ip_hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
-      $ip_timestamp_ref -= $days_before_alert * 24 * 3600 if $switchportdb{$swpt}->{'hostname_fq'} =~ m/$RE_FLOAT_HOSTNAME/;
-
-      if ($ip_timestamp_mod > $ip_timestamp_ref) {
-         $switchportdb{$swpt} = {
-            ip          => $ip,
-            timestamp   => $ip_timestamp,
-            vlan        => $computerdb->{$ip}{'network'},
-            hostname_fq => $ip_hostname_fq,
-            mac_address => $ip_mac,
-            };
-         }
-      }
-
-   my @result = ();
-
-   LOOP_ON_RECENT_COMPUTER:
-   for my $swpt (keys %switchportdb) {
-      next LOOP_ON_RECENT_COMPUTER if $swpt =~ m/^\s*0$/;
-      next LOOP_ON_RECENT_COMPUTER if $switchportdb{$swpt}->{'hostname_fq'} !~ m/$RE_FLOAT_HOSTNAME/;
-
-      my $src_ip        = $switchportdb{$swpt}->{'ip'};
-      my $src_timestamp = 0;
-      LOOP_ON_IP_ADDRESS:
-      for my $ip (keys %{$computerdb}) {
-         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'mac_address'} ne $switchportdb{$swpt}->{'mac_address'};
-         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} =~ m/$RE_FLOAT_HOSTNAME/;
-         next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'timestamp'} < $src_timestamp;
-
-         $src_ip        = $ip;
-         $src_timestamp = $computerdb->{$ip}{'timestamp'};
-         }
-
-      # keep only if float computer is the most recent
-      next LOOP_ON_RECENT_COMPUTER if $src_timestamp == 0;
-      next LOOP_ON_RECENT_COMPUTER if $switchportdb{$swpt}->{'timestamp'} < $src_timestamp;
-      next LOOP_ON_RECENT_COMPUTER if $computerdb->{$src_ip}{'hostname_fq'} eq 'unknow';
-
-      my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $switchportdb{$swpt}->{'timestamp'};
-      $year += 1900;
-      $mon++;
-      my $date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
-
-      ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$src_ip}{'timestamp'};
-      $year += 1900;
-      $mon++;
-      my $src_date = sprintf '%04i-%02i-%02i/%02i:%02i', $year, $mon, $mday, $hour, $min;
-
-      my $vlan_id = get_current_vlan_id($computerdb->{$src_ip}{'network'});
-      my ($switch_hostname, $port_hr) = split /\s+/, $swpt, 2;
-
-      push @result, {
-         switch      => $switch_hostname,
-         port_hr     => $port_hr,
-         vlan_bad    => $switchportdb{$swpt}->{'vlan'},
-         vlan_good   => $computerdb->{$src_ip}{'network'},
-         vlan_id     => $vlan_id,
-         date_last   => $date,
-         date_good   => $src_date,
-         mac_address => $computerdb->{$src_ip}{'mac_address'},
-         hostname_fq => $computerdb->{$src_ip}{'hostname_fq'},
-         };
-      }
-
-   $possible_format{$format}->(@result);
-   }
-
-#---------------------------------------------------------------
-sub cmd_bad_vlan_id_txt {
-   my @result = @_;
-
-   my $tb_bad = Text::Table->new(
-      {align  => 'left',   align_title => 'left',   title => 'Switch'},
-      {is_sep => 1,              title => ' ',       body => ' '},
-      {align  => 'right',  align_title => 'right',  title => 'Port'},
-      {is_sep => 1,              title => ' ',       body => ' !'},
-      {align  => 'left',   align_title => 'left',   title => 'VLAN-Bad'},
-      {is_sep => 1,              title => ' ',       body => ' +-> '},
-      {align  => 'left',   align_title => 'left',   title => 'VLAN-Good'},
-      {is_sep => 1,              title => ' ',       body => ' '},
-      {align  => 'left',   align_title => 'left',   title => 'VLAN-ID'},
-      {is_sep => 1,              title => ' ',       body => ' '},
-      {align  => 'left',   align_title => 'left',   title => 'Date-Last'},
-      {is_sep => 1,              title => ' ',       body => '  '},
-      {align  => 'left',   align_title => 'left',   title => 'Date-Good'},
-      {is_sep => 1,              title => ' ',       body => '  '},
-      {align  => 'left',   align_title => 'left',   title => 'MAC-Address'},
-      {is_sep => 1,              title => ' ',       body => ' '},
-      {align  => 'left',   align_title => 'left',   title => 'Hostname-FQ'},
-      );
-
-   for my $item (@result) {
-      $tb_bad->add(
-         $item->{'switch'},
-         $item->{'port_hr'},
-         $item->{'vlan_bad'},
-         $item->{'vlan_good'},
-         '(' . $item->{'vlan_id'} . ')',
-         $item->{'date_last'},
-         $item->{'date_good'},
-         $item->{'mac_address'},
-         $item->{'hostname_fq'},
-         );
-      }
-
-   print $tb_bad->title();
-   print $tb_bad->rule('-');
-   print $tb_bad->body();
-   }
-
-#---------------------------------------------------------------
-sub cmd_bad_vlan_id_html {
-   my @result = @_;
-
-   print <<'END_HTML';
-<table class="sortable">
- <caption>Klask VLAN ID Mismatch Database</caption>
- <thead>
-  <tr>
-   <th scope="col" class="klask-header-left">Switch</th>
-   <th scope="col" class="sorttable_nosort">Port</th>
-   <th scope="col" class="sorttable_alpha">VLAN-Bad</th>
-   <th scope="col" class="sorttable_alpha">VLAN-Good</th>
-   <th scope="col" class="sorttable_alpha">Date-Last</th>
-   <th scope="col" class="sorttable_alpha">Date-Good</th>
-   <th scope="col" class="sorttable_alpha">MAC-Address</th>
-   <th scope="col" class="klask-header-right">Hostname-FQ</th>
-  </tr>
- </thead>
- <tbody>
-END_HTML
-
-   my $typerow = 'even';
-
-   for my $item (@result) {
-
-      $typerow = $typerow eq 'even' ? 'odd' : 'even';
-
-      my $switch_hostname_sort = format_switchport4sort($item->{'switch'}, $item->{'port_hr'});
-      my ($host_short) = split m/ \. /xms, $item->{'hostname_fq'};
-
-      my $vlan_nameid = $item->{'vlan_good'} . ' (' . $item->{'vlan_id'} . ')';
-
-      fqdn_html_breakable(my $hostname_fq_html = $item->{'hostname_fq'});
-
-      print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$switch_hostname_sort">$item->{'switch'}</td>
-   <td class="bklask-port">$item->{'port_hr'}</td>
-   <td>!$item->{'vlan_bad'}</td>
-   <td>$vlan_nameid</td>
-   <td>$item->{'date_last'}</td>
-   <td>$item->{'date_good'}</td>
-   <td>$item->{'mac_address'}</td>
-   <td sorttable_customkey="$host_short">$hostname_fq_html</td>
-  </tr>
-END_HTML
-      }
-   print <<'END_HTML';
- </tbody>
- <tfoot>
-  <tr>
-   <th scope="col" class="klask-footer-left">Switch</th>
-   <th scope="col" class="fklask-nosort">Port</th>
-   <th scope="col" class="fklask-alpha">VLAN-Bad</th>
-   <th scope="col" class="fklask-alpha">VLAN-Good</th>
-   <th scope="col" class="fklask-alpha">Date-Last</th>
-   <th scope="col" class="fklask-alpha">Date-Good</th>
-   <th scope="col" class="fklask-alpha">MAC-Address</th>
-   <th scope="col" class="klask-footer-right">Hostname-FQ</th>
-  </tr>
- </tfoot>
-</table>
-END_HTML
-   }
-
-#---------------------------------------------------------------
-sub cmd_poe_enable {
-   local @ARGV = @_;
-
-   my $verbose;
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $switch_port = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $switch_port eq q{}) {
-      die "Usage: klask poe-enable SWITCH_NAME PORT\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS switch and low port number
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-      print "$error \n" if $error;
-
-      my $result = $session->set_request(
-         -varbindlist => [$oid_search, INTEGER, 8], # Only NEXANS
-         );
-      print $session->error() . "\n" if $session->error_status();
-
-      $session->close;
-      }
-   cmd_poe_status($switch_name, $switch_port);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_poe_disable {
-   local @ARGV = @_;
-
-   my $verbose;
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $switch_port = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $switch_port eq q{}) {
-      die "Usage: klask poe-disable SWITCH_NAME PORT\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS switch and low port number
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-      print "$error \n" if $error;
-
-      my $result = $session->set_request(
-         -varbindlist => [$oid_search, INTEGER, 2], # Only NEXANS
-         );
-      print $session->error() . "\n" if $session->error_status();
-
-      $session->close;
-      }
-   cmd_poe_status($switch_name, $switch_port);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_poe_status {
-   local @ARGV = @_;
-
-   my $verbose;
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $switch_port = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $switch_port eq q{}) {
-      die "Usage: klask poe-status SWITCH_NAME PORT\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search = $OID_NUMBER{'NApoeState'} . ".$switch_port"; # Only NEXANS switch and low port number
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search],
-         );
-
-      if (defined $result and $result->{$oid_search} ne 'noSuchInstance') {
-         my $poe_status = $result->{$oid_search} || 'empty';
-         $poe_status =~ s/8/enable/;
-         $poe_status =~ s/2/disable/;
-         printf "%s  %s poe %s\n", $sw_name, $switch_port, $poe_status;
-         }
-      else {
-         print "Klask do not find PoE status on switch $sw_name on port $switch_port\n";
-         }
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_host_setlocation {
-   local @ARGV = @_;
-
-   my ($verbose, $force);
-   GetOptions(
-      'verbose|v' => \$verbose,
-      'force|f'   => \$force,
-      );
-
-   my $switch_name     = shift @ARGV || q{};
-   my $switch_location = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $switch_location eq q{}) {
-      die "Usage: klask host-setlocation SWITCH_NAME LOCATION\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search = $OID_NUMBER{'sysLocation'};
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-      print "$error \n" if $error;
-
-      my $result = $session->set_request(
-         -varbindlist => [$oid_search, OCTET_STRING, $switch_location],
-         );
-      print $session->error()."\n" if $session->error_status();
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-# not finish - do not use
-sub cmd_port_setvlan {
-   my $switch_name = shift || q{};
-   my $mac_address = shift || q{};
-
-   if ($switch_name eq q{} or $mac_address eq q{}) {
-      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
-      }
-
-   $switch_name = join(',', map {$_->{'hostname'}} @SWITCH_LIST) if $switch_name eq q{*};
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search_port1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
-      my $oid_search_port2 = $OID_NUMBER{'searchPort2'} . '.' . 0 . mac_address_hex2dec($mac_address);
-      print "Klask search OID $oid_search_port1 on switch $sw_name\n";
-      print "Klask search OID $oid_search_port2 on switch $sw_name\n";
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search_port1]
-         );
-      if (not defined $result) {
-         $result = $session->get_request(
-            -varbindlist => [$oid_search_port2]
-            );
-         $result->{$oid_search_port1} = $result->{$oid_search_port2} if defined $result;
-         }
-
-      if (defined $result and $result->{$oid_search_port1} ne 'noSuchInstance') {
-         my $swport = $result->{$oid_search_port1};
-         print "Klask find MAC $mac_address on switch $sw_name port $swport\n";
-         }
-      else {
-         print "Klask do not find MAC $mac_address on switch $sw_name\n";
-         }
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_port_getvlan {
-   local @ARGV = @_;
-
-   my $verbose;
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $switch_port = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $switch_port eq q{}) {
-      die "Usage: klask port-getvlan SWITCH_NAME PORT\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search = $OID_NUMBER{'vlanPortDefault'} . ".$switch_port";
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search],
-         );
-
-      if (defined $result and $result->{$oid_search} ne 'noSuchInstance') {
-         my $vlan_id = $result->{$oid_search} || 'empty';
-         print "Klask VLAN Id $vlan_id on switch $sw_name on port $switch_port\n";
-         }
-      else {
-         print "Klask do not find VLAN Id on switch $sw_name on port $switch_port\n";
-         }
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_vlan_setname {
-   }
-
-#---------------------------------------------------------------
-# snmpset -v 1 -c public sw1-batG0-legi.hmg.priv "$OID_NUMBER{'HPicfReset'}.0" i 2;
-sub cmd_rebootsw {
-   local @ARGV = @_;
-
-   my $verbose;
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-
-   if ($switch_name eq q{}) {
-      die "Usage: klask rebootsw SWITCH_NAME\n";
-      }
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-      print "$error \n" if $error;
-
-      my $result = $session->set_request(
-         -varbindlist => ["$OID_NUMBER{'HPicfReset'}.0", INTEGER, 2],
-         );
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_vlan_getname {
-   my $switch_name = shift || q{};
-   my $vlan_id     = shift || q{};
-
-   if ($switch_name eq q{} or $vlan_id eq q{}) {
-      die "Usage: klask vlan-getname SWITCH_NAME VLAN_ID\n";
-      }
-
-   $switch_name = join(',', map {$_->{'hostname'}} @SWITCH_LIST) if $switch_name eq q{*};
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search_vlan_name = $OID_NUMBER{'vlanName'} . ".$vlan_id";
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search_vlan_name]
-         );
-
-      if (defined $result and $result->{$oid_search_vlan_name} ne 'noSuchInstance') {
-         my $vlan_name = $result->{$oid_search_vlan_name} || 'empty';
-         print "Klask find VLAN $vlan_id on switch $sw_name with name $vlan_name\n";
-         }
-      else {
-         print "Klask do not find VLAN $vlan_id on switch $sw_name\n";
-         }
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_vlan_list {
-   my $switch_name = shift || q{};
-
-   if ($switch_name eq q{}) {
-      die "Usage: klask vlan-list SWITCH_NAME\n";
-      }
-
-   $switch_name = join(',', map {$_->{'hostname'}} @SWITCH_LIST) if $switch_name eq q{*};
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my %vlandb = snmp_get_vlan_list($session);
-      $session->close;
-
-      print "VLAN_ID - VLAN_NAME # $sw_name\n";
-      for my $vlan_id (keys %vlandb) {
-         printf "%7i - %s\n", $vlan_id, $vlandb{$vlan_id};
-         }
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_ip_location {
-   my $computerdb = computerdb_load();
-
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (Net::Netmask::sort_by_ip_address(keys %{$computerdb})) {
-
-      next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'hostname_fq'} eq ($computerdb->{$ip}{'switch_hostname'} || $computerdb->{$ip}{'switch_description'}); # switch on himself !
-
-      my $sw_hostname = $computerdb->{$ip}{'switch_hostname'} || q{};
-      next LOOP_ON_IP_ADDRESS if $sw_hostname eq 'unknow';
-
-      my $sw_location = q{};
-      LOOP_ON_ALL_SWITCH:
-      for my $sw (@SWITCH_LIST) {
-         next LOOP_ON_ALL_SWITCH if $sw_hostname ne $sw->{'hostname'};
-         $sw_location = $sw->{'location'};
-         last;
-         }
-
-      printf "%s: \"%s\"\n", $ip, $sw_location if not $sw_location eq q{};
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_ip_free {
-   local @ARGV = @_;
-
-   my $days_to_death = $DEFAULT{'days-to-death'} || 365 * 2;
-   my $format = 'txt';
-   my $verbose;
-
-   GetOptions(
-      'day|d=i'    => \$days_to_death,
-      'format|f=s' => \$format,
-      'verbose|v'  => \$verbose,
-      );
-
-   my %possible_format = (
-      txt  => \&cmd_ip_free_txt,
-      html => \&cmd_ip_free_html,
-      none => sub { },
-      );
-   $format = 'txt' if not defined $possible_format{$format};
-
-   my @vlan_name = @ARGV;
-   @vlan_name = get_list_network() if not @vlan_name;
-
-   my $computerdb = {};
-   $computerdb = computerdb_load() if -e "$KLASK_DB_FILE";
-   my $timestamp = time;
-
-   my $timestamp_barrier = $timestamp - (3600 * 24 * $days_to_death);
-
-   my %result_ip = ();
-
-   ALL_NETWORK:
-   for my $vlan (@vlan_name) {
-
-      my @ip_list = get_list_ip($vlan);
-
-      LOOP_ON_IP_ADDRESS:
-      for my $ip (@ip_list) {
-
-         if (exists $computerdb->{$ip}) {
-            next LOOP_ON_IP_ADDRESS if $computerdb->{$ip}{'timestamp'} > $timestamp_barrier;
-
-            my $mac_address = $computerdb->{$ip}{'mac_address'};
-            LOOP_ON_DATABASE:
-            for my $ip_db (keys %{$computerdb}) {
-               next LOOP_ON_DATABASE   if $computerdb->{$ip_db}{'mac_address'} ne $mac_address;
-               next LOOP_ON_IP_ADDRESS if $computerdb->{$ip_db}{'timestamp'} > $timestamp_barrier;
-               }
-            }
-
-         my $ip_date_last_detection = '';
-         if (exists $computerdb->{$ip}) {
-            my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $computerdb->{$ip}{'timestamp'};
-            $year += 1900;
-            $mon++;
-            $ip_date_last_detection = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-            }
-
-         my $packed_ip   = scalar gethostbyname($ip);
-         my $hostname_fq = 'unknown';
-            $hostname_fq = scalar gethostbyaddr($packed_ip, AF_INET) || 'unknown'
-               if defined $packed_ip and get_current_scan_mode($vlan) eq 'active';
-
-         next LOOP_ON_IP_ADDRESS if $hostname_fq =~ m/$RE_FLOAT_HOSTNAME/;
-
-         $result_ip{$ip} ||= {};
-         $result_ip{$ip}->{'date_last_detection'} = $ip_date_last_detection;
-         $result_ip{$ip}->{'hostname_fq'}         = $hostname_fq;
-         $result_ip{$ip}->{'vlan'}                = $vlan;
-
-         printf "VERBOSE_1: %-15s %-12s %s\n", $ip, $vlan, $hostname_fq if $verbose;
-         }
-      }
-
-   $possible_format{$format}->(%result_ip);
-   }
-
-#---------------------------------------------------------------
-sub cmd_ip_free_txt {
-   my %result_ip = @_;
-
-   my $tb_computer = Text::Table->new(
-      {align => 'left', align_title => 'left', title => 'IPv4-Address'},
-      {align => 'left', align_title => 'left', title => 'Hostname-FQ'},
-      {align => 'left', align_title => 'left', title => 'VLAN'},
-      {align => 'left', align_title => 'left', title => 'Date'},
-      );
-
-   #printf "%-15s %-40s %-16s %s\n", qw(IPv4-Address Hostname-FQ Date VLAN);
-   #print "-------------------------------------------------------------------------------\n";
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
-      my $vlan_nameid = $result_ip{$ip}->{'vlan'} . '(' . get_current_vlan_id($result_ip{$ip}->{'vlan'}) . ')';
-      #printf "%-15s %-40s %-16s %s\n", $ip, $result_ip{$ip}->{'hostname_fq'}, $result_ip{$ip}->{'date_last_detection'}, $vlan_nameid;
-      $tb_computer->add(
-         $ip,
-         $result_ip{$ip}->{'hostname_fq'},
-         $vlan_nameid,
-         $result_ip{$ip}->{'date_last_detection'},
-         );
-      }
-   print $tb_computer->title();
-   print $tb_computer->rule('-');
-   print $tb_computer->body();
-   }
-
-#---------------------------------------------------------------
-sub cmd_ip_free_html {
-   my %result_ip = @_;
-
-   print <<'END_HTML';
-<table class="sortable">
- <caption>Klask Free IPv4 Database</caption>
- <thead>
-  <tr>
-   <th scope="col" class="klask-header-left">IPv4-Address</th>
-   <th scope="col" class="sorttable_alpha">Hostname-FQ</th>
-   <th scope="col" class="sorttable_alpha">VLAN</th>
-   <th scope="col" class="klask-header-right">Date</th>
-  </tr>
- </thead>
- <tbody>
-END_HTML
-
-   my $typerow = 'even';
-
-   LOOP_ON_IP_ADDRESS:
-   for my $ip (Net::Netmask::sort_by_ip_address(keys %result_ip)) {
-
-      $typerow = $typerow eq 'even' ? 'odd' : 'even';
-
-      my $ip_sort = sprintf '%03i%03i%03i%03i', split m/ \. /xms, $ip;
-      my ($host_short) = split m/ \. /xms, $result_ip{$ip}->{'hostname_fq'};
-
-      my $vlan_nameid = $result_ip{$ip}->{'vlan'} . ' (' . get_current_vlan_id($result_ip{$ip}->{'vlan'}) . ')';
-
-      fqdn_html_breakable(my $hostname_fq_html = $result_ip{$ip}->{'hostname_fq'});
-
-      print <<"END_HTML";
-  <tr class="$typerow">
-   <td sorttable_customkey="$ip_sort">$ip</td>
-   <td sorttable_customkey="$host_short">$hostname_fq_html</td>
-   <td>$vlan_nameid</td>
-   <td>$result_ip{$ip}->{'date_last_detection'}</td>
-  </tr>
-END_HTML
-      }
-   print <<'END_HTML';
- </tbody>
- <tfoot>
-  <tr>
-   <th scope="col" class="klask-footer-left">IPv4-Address</th>
-   <th scope="col" class="fklask-hostname">Hostname-FQ</th>
-   <th scope="col" class="fklask-vlan">VLAN</th>
-   <th scope="col" class="klask-footer-right">Date</th>
-  </tr>
- </tfoot>
-</table>
-END_HTML
-   }
-
-#---------------------------------------------------------------
-sub cmd_enable {
-   local @ARGV = @_;
-
-   my $verbose;
-
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $port_hr     = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $port_hr eq q{}) {
-      die "Usage: klask disable SWITCH_NAME PORT\n";
-      }
-
-   if (not defined $SWITCH_DB{$switch_name}) {
-      die "Switch $switch_name must be defined in klask configuration file\n";
-      }
-
-   my $sw = $SWITCH_DB{$switch_name};
-   my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-   print "$error \n" if $error;
-
-   # Retrieve numeric port value
-   my $port_id = snmp_get_switchport_hr2id($session, normalize_port_human_readable($port_hr), $verbose ? 'yes' : '');
-   die "Error : Port $port_hr does not exist on switch $switch_name\n" if not $port_id =~ m/^\d+$/;
-
-   my $oid_search_portstatus = $OID_NUMBER{'portUpDown'} . '.' . $port_id;
-   print "Info: switch $switch_name port $port_hr SNMP OID $oid_search_portstatus\n" if $verbose;
-
-   my $result = $session->set_request(
-      -varbindlist => [$oid_search_portstatus, INTEGER, 1],
-      );
-   print $session->error()."\n" if $session->error_status();
-
-   $session->close;
-
-   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 1 (up)
-   #snmpset -v 1 -c community X.X.X.X 1.3.6.1.2.1.2.2.1.7.NoPort = 2 (down)
-   #system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 1";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_disable {
-   local @ARGV = @_;
-
-   my $verbose;
-
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $port_hr     = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $port_hr eq q{}) {
-      die "Usage: klask disable SWITCH_NAME PORT\n";
-      }
-
-   if (not defined $SWITCH_DB{$switch_name}) {
-      die "Switch $switch_name must be defined in klask configuration file\n";
-      }
-
-   my $sw = $SWITCH_DB{$switch_name};
-   my ($session, $error) = Net::SNMP->session(snmp_get_rwsession($sw));
-   print "$error \n" if $error;
-
-   # Retrieve numeric port value
-   my $port_id = snmp_get_switchport_hr2id($session, normalize_port_human_readable($port_hr), $verbose ? 'yes' : '');
-   die "Error : Port $port_hr does not exist on switch $switch_name\n" if not $port_id =~ m/^\d+$/;
-
-   my $oid_search_portstatus = $OID_NUMBER{'portUpDown'} . '.' . $port_id;
-   print "Info: switch $switch_name port $port_hr SNMP OID $oid_search_portstatus\n" if $verbose;
-
-   my $result = $session->set_request(
-      -varbindlist => [$oid_search_portstatus, INTEGER, 2],
-      );
-   print $session->error()."\n" if $session->error_status();
-
-   $session->close;
-
-   #system "snmpset -v 1 -c public $switch 1.3.6.1.2.1.2.2.1.7.$port = 2";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_status {
-   local @ARGV = @_;
-
-   my $verbose;
-
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $port_hr     = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $port_hr eq q{}) {
-      die "Usage: klask status SWITCH_NAME PORT\n";
-      }
-
-   if (not defined $SWITCH_DB{$switch_name}) {
-      die "Switch $switch_name must be defined in klask configuration file\n";
-      }
-
-   my $sw = $SWITCH_DB{$switch_name};
-   my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-   print "$error \n" if $error;
-
-   # Retrieve numeric port value
-   my $port_id = snmp_get_switchport_hr2id($session, normalize_port_human_readable($port_hr), $verbose ? 'yes' : '');
-   die "Error : Port $port_hr does not exist on switch $switch_name\n" if not $port_id =~ m/^\d+$/;
-
-   my $oid_search_portstatus = $OID_NUMBER{'portUpDown'} . '.' . $port_id;
-   print "Info: switch $switch_name port $port_hr ($port_id) SNMP OID $oid_search_portstatus\n" if $verbose;
-
-   my $result = $session->get_request(
-      -varbindlist => [$oid_search_portstatus]
-      );
-   print $session->error()."\n" if $session->error_status();
-   if (defined $result) {
-      print "$PORT_UPDOWN{$result->{$oid_search_portstatus}}\n";
-      }
-
-   $session->close;
-
-   #system "snmpget -v 1 -c public $switch_name 1.3.6.1.2.1.2.2.1.7.$port";
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_search_mac_on_switch {
-   local @ARGV = @_;
-
-   my $verbose;
-   my $vlan_id = 0;
-
-   GetOptions(
-      'verbose|v' => \$verbose,
-      'vlan|l=i'  => \$vlan_id,
-      );
-
-   my $switch_name = shift @ARGV || q{};
-   my $mac_address = shift @ARGV || q{};
-
-   if ($switch_name eq q{} or $mac_address eq q{}) {
-      die "Usage: klask search-mac-on-switch SWITCH_NAME MAC_ADDRESS\n";
-      }
-
-   $mac_address = normalize_mac_address($mac_address);
-   $switch_name = join(',', map {$_->{'hostname'}} @SWITCH_LIST) if $switch_name eq q{*} or $switch_name eq q{all};
-
-   for my $sw_name (split /,/, $switch_name) {
-      if (not defined $SWITCH_DB{$sw_name}) {
-         die "Switch $sw_name must be defined in klask configuration file\n";
-         }
-
-      my $oid_search_port1 = $OID_NUMBER{'searchPort1'} . mac_address_hex2dec($mac_address);
-      my $oid_search_port2 = $OID_NUMBER{'searchPort2'} . '.' . $vlan_id . mac_address_hex2dec($mac_address);
-      print "Klask search OID $oid_search_port1 on switch $sw_name\n" if $verbose;
-      print "Klask search OID $oid_search_port2 on switch $sw_name\n" if $verbose;
-
-      my $sw = $SWITCH_DB{$sw_name};
-      my ($session, $error) = Net::SNMP->session(%{$sw->{'snmp_param_session'}});
-      print "$error \n" if $error;
-
-      my $result = $session->get_request(
-         -varbindlist => [$oid_search_port1]
-         );
-      if (not defined $result) {
-         $result = $session->get_request(
-            -varbindlist => [$oid_search_port2]
-            );
-         $result->{$oid_search_port1} = $result->{$oid_search_port2} if defined $result;
-         }
-
-      if (defined $result and $result->{$oid_search_port1} ne 'noSuchInstance') {
-         my $swport_id = $result->{$oid_search_port1};
-         my $swport_hr = snmp_get_switchport_id2hr($session, $swport_id);
-         print "Klask find MAC $mac_address on switch $sw_name port $swport_hr\n";
-         }
-      else {
-         print "Klask do not find MAC $mac_address on switch $sw_name\n" if $verbose;
-         }
-
-      $session->close;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_updatesw {
-   local @ARGV = @_;
-
-   my $verbose;
-
-   GetOptions(
-      'verbose|v' => \$verbose,
-      );
-   
-   update_switchdb(verbose => $verbose);
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportsw {
-   local @ARGV = @_;
-
-   test_switchdb_environnement();
-
-   my $format       = 'txt';
-   my $graph_modulo = 0;
-   my $graph_shift  = 1;
-   my $way          = 'all';
-   my $no_header;
-
-   GetOptions(
-      'format|f=s'  => \$format,
-      'modulo|m=i'  => \$graph_modulo,
-      'shift|s=i'   => \$graph_shift,
-      'way|w=s'     => \$way,
-      'no-header|H' => \$no_header,
-      );
-
-   my %possible_format = (
-      txt => \&cmd_exportsw_txt,
-      dot => \&cmd_exportsw_dot,
-      );
-
-   $format = 'txt' if not defined $possible_format{$format};
-
-   $possible_format{$format}->(modulo => $graph_modulo, shift => $graph_shift, way => $way, 'no-header' => $no_header, @ARGV);
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportsw_txt {
-   my %args = (
-      'way'       => 'all',
-      'no-header' => undef,
-      @_);
-
-   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
-   my $interway          = 0;
-
-   if ($args{'way'} =~ m/all|desc/) {
-      print "\n" if $interway++;
-      # Switch description
-      my %db_switch_global = %{$switch_connection->{'switch_db'}};
-      my $arrow            = '0--------->>>>';
-
-      my $tb_desc = Text::Table->new(
-         {align => 'left',   align_title => 'left',   title => 'Switch'},
-         {align => 'center', align_title => 'center', title => 'Is'},
-         {align => 'left',   align_title => 'left',   title => 'Description'},
-         {align => 'left',   align_title => 'left',   title => 'Model'},
-         {align => 'left',   align_title => 'left',   title => 'Revision'},
-         );
-
-      for my $sw (values %db_switch_global) {
-         $tb_desc->add($sw->{'hostname'}, $arrow, $sw->{'description'}, $sw->{'model'}, $sw->{'revision'});
-         }
-
-      print $tb_desc->title()   unless $args{'no-header'};
-      print $tb_desc->rule('-') unless $args{'no-header'};
-      print $tb_desc->body();
-      $tb_desc->clear;
-      }
-
-   my %db_switch_output_port       = %{$switch_connection->{'output_port'}};
-   my %db_switch_parent            = %{$switch_connection->{'parent'}};
-   my %db_switch_connected_on_port = %{$switch_connection->{'connected_on_port'}};
-
-   if ($args{'way'} =~ m/all|child/) {
-      print "\n" if $interway++;
-      # Switch output port and parent port connection
-      my $tb_child = Text::Table->new( # http://www.perlmonks.org/?node_id=988320
-         {align => 'left',   align_title => 'left',   title => 'Child-Switch'},
-         {align => 'right',  align_title => 'right',  title => 'Output-Port'},
-         {align => 'center', align_title => 'center', title => 'Link'},
-         {align => 'left',   align_title => 'left',   title => 'Input-Port'},
-         {align => 'left',   align_title => 'left',   title => 'Parent-Switch'},
-         );
-      for my $sw (sort keys %db_switch_output_port) {
-         my $arrow = '--->';
-         $arrow = '===>' if $db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/;
-         if (exists $db_switch_parent{$sw}) {
-            $tb_child->add($sw, $db_switch_output_port{$sw}, $arrow, $db_switch_parent{$sw}->{'port_hr'}, $db_switch_parent{$sw}->{'switch'});
-
-            }
-         else {
-            $tb_child->add($sw, $db_switch_output_port{$sw}, $arrow, '', 'router');
-            }
-         }
-      my @colrange = map {scalar $tb_child->colrange($_)} (0 .. 4); # force scaler context
-      $tb_child->add(map {' ' x $_} reverse @colrange);             # add empty line to force symetric table output
-      print $tb_child->title()   unless $args{'no-header'};
-      print $tb_child->rule('-') unless $args{'no-header'};
-      print $tb_child->body(0, $tb_child->body_height() - 1);       # remove last fake line
-      $tb_child->clear;
-      }
-
-   if ($args{'way'} =~ m/all|parent/) {
-      print "\n" if $interway++;
-      # Switch parent and children port inter-connection
-      my $tb_parent = Text::Table->new(                             # http://www.perlmonks.org/?node_id=988320
-         {align => 'left',   align_title => 'left',   title => 'Parent-Switch'},
-         {align => 'right',  align_title => 'right',  title => 'Input-Port'},
-         {align => 'center', align_title => 'center', title => 'Link'},
-         {align => 'left',   align_title => 'left',   title => 'Output-Port'},
-         {align => 'left',   align_title => 'left',   title => 'Child-Switch'},
-         );
-      for my $swport (sort keys %db_switch_connected_on_port) {
-         my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-         for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
-            my $arrow = '<---';
-            $arrow = '<===' if $port_connect =~ m/^(Trk|Br|Po)/;
-            if (exists $db_switch_output_port{$sw}) {
-               $tb_parent->add($sw_connect, $port_connect, $arrow, $db_switch_output_port{$sw}, $sw);
-               }
-            else {
-               $tb_parent->add($sw_connect, $port_connect, $arrow, '', $sw);
-               }
-            }
-         }
-      my @colrange = map {scalar $tb_parent->colrange($_)} (0 .. 4); # force scaler context
-      $tb_parent->add(map {' ' x $_} reverse @colrange);             # add empty line to force symetric table output
-      print $tb_parent->title()   unless $args{'no-header'};
-      print $tb_parent->rule('-') unless $args{'no-header'};
-      print $tb_parent->body(0, $tb_parent->body_height() - 1);      # remove last fake line
-      $tb_parent->clear;
-      }
-   return;
-   }
-
-#---------------------------------------------------------------
-sub cmd_exportsw_dot {
-   my %args = (
-      modulo   => 0,
-      shift    => 1,
-      @_);
-
-   my $graph_modulo = $args{'modulo'};
-   my $graph_shift  = $args{'shift'};
-
-   my $switch_connection = YAML::Syck::LoadFile("$KLASK_SW_FILE");
-
-   my %db_switch_output_port       = %{$switch_connection->{'output_port'}};
-   my %db_switch_parent            = %{$switch_connection->{'parent'}};
-   my %db_switch_connected_on_port = %{$switch_connection->{'connected_on_port'}};
-   my %db_switch_link_with         = %{$switch_connection->{'link_with'}};
-   my %db_switch_global            = %{$switch_connection->{'switch_db'}};
-   my $timestamp                   =   $switch_connection->{'timestamp'};
-
-   my $invisible_node = 0; # Count number of invisible node
-
-   my %db_building    = ();
-   my %db_switch_line = (); # Number of line drawed on a switch
-   for my $sw (values %db_switch_global) {
-      my ($building, $location) = split m/ \/ /xms, $sw->{'location'}, 2;
-      $db_building{$building} ||= {};
-      $db_building{$building}->{$location} ||= {};
-      $db_building{$building}->{$location}{$sw->{'hostname'}} = 'y';
-
-      $db_switch_line{$sw} = 0;
-      }
-
-
-   print "digraph G {\n";
-   print "rankdir=LR;\n";
-   #print "splines=polyline;\n";
-
-   print "site [label=\"site\", color=black, fillcolor=gold, shape=invhouse, style=filled];\n";
-   print "internet [label=\"internet\", color=black, fillcolor=cyan, shape=house, style=filled];\n";
-
-   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;
-   $year += 1900;
-   $mon++;
-   my $date = sprintf '%04i-%02i-%02i %02i:%02i', $year, $mon, $mday, $hour, $min;
-   print "\"$date\" [label=\"MAP DATE\\n\\n$date\", color=white, fillcolor=black, shape=polygon, sides=14, style=filled, fontcolor=white];\n";
-   print "site -> \"$date\" [style=invis];\n";
-
-   my $b = 0;
-   for my $building (keys %db_building) {
-      $b++;
-
-      print "\"building$b\" [label=\"$building\", color=black, fillcolor=gold, style=filled];\n";
-      print "site -> \"building$b\" [len=2, color=firebrick];\n";
-
-      my $l = 0;
-      for my $loc (keys %{$db_building{$building}}) {
-         $l++;
-
-         print "\"location$b-$l\" [label=\"$building" . q{/} . join(q{\n}, split(m{ / }xms, $loc)) . "\", color=black, fillcolor=orange, style=filled];\n";
-#         print "\"location$b-$l\" [label=\"$building / $loc\", color=black, fillcolor=orange, style=filled];\n";
-         print "\"building$b\" -> \"location$b-$l\" [len=2, color=firebrick];\n";
-
-         for my $sw (keys %{$db_building{$building}->{$loc}}) {
-
-            my $peripheries = 1;
-            my $color       = 'lightblue';
-            if ($db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/) {
-               $peripheries = 2;
-               $color       = "\"$color:$color\"";
-               }
-            print "\"$sw:$db_switch_output_port{$sw}\" [label=\"".format_aggregator4dot($db_switch_output_port{$sw})."\", color=black, fillcolor=lightblue, peripheries=$peripheries, style=filled];\n";
-
-            my $swname  = $sw;
-               $swname .= q{\n-\n} . "$db_switch_global{$sw}->{'model'}" if exists $db_switch_global{$sw} and exists $db_switch_global{$sw}->{'model'};
-            print "\"$sw\" [label=\"$swname\", color=black, fillcolor=palegreen, shape=rect, style=filled];\n";
-            print "\"location$b-$l\" -> \"$sw\" [len=2, color=firebrick, arrowtail=dot];\n";
-            print "\"$sw\" -> \"$sw:$db_switch_output_port{$sw}\" [len=2, style=bold, color=$color, arrowhead=normal, arrowtail=invdot];\n";
-
-
-            for my $swport (keys %db_switch_connected_on_port) {
-               my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-               next if not $sw_connect eq $sw;
-               next if $port_connect eq $db_switch_output_port{$sw};
-               my $peripheries = 1;
-               my $color       = 'plum';
-               if ($port_connect =~ m/^(Trk|Br|Po)/) {
-                  $peripheries = 2;
-                  $color       = "\"$color:$color\"";
-                  }
-               print "\"$sw:$port_connect\" [label=\"".format_aggregator4dot($port_connect)."\", color=black, fillcolor=plum, peripheries=$peripheries, style=filled];\n";
-               print "\"$sw:$port_connect\" -> \"$sw\" [len=2, style=bold, color=$color, arrowhead=normal, arrowtail=inv];\n";
-
-               #$db_switch_line{$sw}++;
-               #if ($db_switch_line{$sw} % 9 == 0) {
-               #   # Create invisible node
-               #   $invisible_node++;
-               #   my $invisible = '__Invisible_' . $invisible_node;
-               #   print "$invisible [shape=none, label=\"\"]\n";
-               #   print "\"$sw:$port_connect\" -> $invisible [style=invis]\n";
-               #   print "$invisible            -> \"$sw\"    [style=invis]\n";
-               #   }
-               }
-            }
-         }
-      }
-
-   #   print "Switch output port and parent port connection\n";
-   #   print "---------------------------------------------\n";
-   for my $sw (sort keys %db_switch_output_port) {
-      if (exists $db_switch_parent{$sw}) {
-#         printf "   \"%s:%s\" -> \"%s:%s\"\n", $sw, $db_switch_output_port{$sw}, $db_switch_parent{$sw}->{'switch'}, $db_switch_parent{$sw}->{'port_id'};
-         }
-      else {
-         my $style = 'solid';
-         my $color = 'black'; # navyblue
-         if ($db_switch_output_port{$sw} =~ m/^(Trk|Br|Po)/) {
-            $style = 'bold';
-            $color = "\"$color:invis:$color\"";
-            }
-         printf "   \"%s:%s\" -> internet [style=$style, color=$color];\n", $sw, $db_switch_output_port{$sw};
-         }
-      }
-   print "\n";
-
-   # shift graph between 1 or 2 when $graph_shift = 3
-   my $graph_breaker = 1;
-
-   #   print "Switch parent and children port inter-connection\n";
-   #   print "------------------------------------------------\n";
-   for my $swport (sort keys %db_switch_connected_on_port) {
-      my ($sw_connect, $port_connect) = split m/ $SEP_SWITCH_PORT /xms, $swport, 2;
-      for my $sw (keys %{$db_switch_connected_on_port{$swport}}) {
-         my $style = 'solid';
-         my $color = 'black'; # navyblue
-         if ($port_connect =~ m/^(Trk|Br|Po)/) {
-            $style = 'bold';
-            $color = "\"$color:invis:$color\"";
-            }
-         if (exists $db_switch_output_port{$sw}) {
-            printf "   \"%s:%s\" -> \"%s:%s\" [style=$style, color=$color];\n", $sw, $db_switch_output_port{$sw}, $sw_connect, $port_connect;
-
-            next if $graph_modulo == 0; # No shift (invisible nodes) in graph
-            $db_switch_line{$sw_connect}++;
-            if ($db_switch_line{$sw_connect} % $graph_modulo == 0) {
-               # Create invisible node
-               $invisible_node++;
-               my $invisible = '__Invisible_' . $invisible_node;
-               print  "   \"$invisible.a\" [shape=none, label=\"\"];\n";
-               printf "   \"%s:%s\"  -> \"$invisible.a\" [style=invis];\n", $sw, $db_switch_output_port{$sw};
-               $graph_breaker++;
-               if ($graph_shift == 2 or ($graph_shift == 3 and ($graph_breaker % 2) == 0)) {
-                  # Two invisible node
-                  print  "   \"$invisible.b\" [shape=none, label=\"\"];\n";
-                  print  "   \"$invisible.a\" -> \"$invisible.b\" [style=invis];\n";
-                  printf "   \"$invisible.b\" -> \"%s:%s\"  [style=invis];\n", $sw_connect, $port_connect;
-                  }
-               else {
-                  # One invisible node
-                  printf "   \"$invisible.a\" -> \"%s:%s\"  [style=invis];\n", $sw_connect, $port_connect;
-                  }
-               }
-            }
-         else {
-            printf "   \"%s\"   -> \"%s:%s\" [style=$style];\n", $sw, $sw_connect, $port_connect;
-            }
-         }
-      }
-
-   print "}\n";
-   return;
-   }
-
-################################################################
-# documentation
-################################################################
-
-__END__
-
-=head1 NAME
-
-klask - port and search manager for switches, map management
-
-
-=head1 USAGE
-
- klask version
- klask help
-
- klask updatedb [--verbose|-v] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l] [--no-rebuildsw|-R]
- klask exportdb [--format|-f txt|html]
- klask removedb ipv4_addr* computer*
- klask insertdb --ip ipv4_addr --mac mac_addr --network vlan_name
- klask cleandb  [--verbose|-v] [--day number_of_day] [--repair-dns]
-
- klask updatesw [--verbose|-v]
- klask exportsw [--format|-f txt|dot] [--modulo|-m XX] [--shift|-s YY] [--way all|desc|child|parent] [--no-header|-H]
-
- klask searchdb [--kind|-k host|mac] computer [mac-address]
- klask search   computer
- klask search-mac-on-switch [--verbose|-v] [--vlan|-i vlan-id] switch mac_addr
-
- klask ip-free [--verbose|-v] [--day|-d days-to-death] [--format|-f txt|html] [vlan_name]
-
- klask bad-vlan-id [--day|-d days_before_alert] [--format|-f txt|html]
-
- klask enable  [--verbose|-v] switch port
- klask disable [--verbose|-v] switch port
- klask status  [--verbose|-v] switch port
-
- klask poe-enable  [--verbose|-v] switch port
- klask poe-disable [--verbose|-v] switch port
- klask poe-status  [--verbose|-v] switch port
-
- klask vlan-getname switch vlan-id
- klask vlan-list switch
-
-
-=head1 DESCRIPTION
-
-Klask is a small tool to find where is connected a host in a big network
-and on which VLAN.
-Klask mean search in brittany.
-No hight level protocol like CDL, LLDP are use.
-Everything is just done with SNMP request on MAC address.
-
-Limitation : loop cannot be detected and could be problematic when the map is created (C<updatesw> method).
-If you use PVST or MSTP and create loop between VLAN,
-you have to use C<portignore> functionality on switch port to cut manually loop
-(see config file below).
-
-When you use a management port to administrate a switch,
-it's not possible to create the map with this switch because it does not have a MAC address,
-so other switch cannot find the real downlink port...
-One way to work around this problem is, if you have a computer directly connected on the switch,
-to put this IPv4 as the fake ip for the switch.
-The MAC address associated will be use just for the map detection.
-The C<fake-ip> parameter is defined in the config file.
-
-Klask has now a web site dedicated for it: L<http://servforge.legi.grenoble-inp.fr/projects/klask>!
-
-
-=head1 COMMANDS
-
-Some command are defined in the source code but are not documented here.
-Theses could be not well defined, not finished, not well tested...
-You can read the source code and use them at your own risk
-(like for all the Klask code).
-
-=head2 search
-
- klask search   computer
-
-This command takes one or more computer in argument.
-It search a computer on the network and give the port and the switch on which the computer is connected.
-
-=head2 search-mac-on-switch
-
- klask search-mac-on-switch [--verbose|-v] [--vlan|-i vlan-id] switch mac_addr
-
-This command search a MAC address on a switch.
-To search on all switch, you could put C<'*'> or C<all>.
-The VLAN parameter could help.
-
-
-=head2 enable
-
- klask enable  [--verbose|-v] switch port
-
-This command activate a port (or an agrregate bridge port) on a switch by SNMP.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-Warning: You need to have the SNMP write access on the switch in order to modify it's configuration.
-
-
-=head2 disable
-
- klask disable [--verbose|-v] switch port
-
-This command deactivate a port (or an agrregate bridge port) on a switch by SNMP.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-Warning: You need to have the SNMP write access on the switch in order to modify it's configuration.
-
-
-=head2 status
-
- klask status  [--verbose|-v] switch port
-
-This command return the status of a port number on a switch by SNMP.
-The return value could be C<enable> or C<disable> word.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-If it's not possible to change port status with command L</enable> and L</disable>
-(SNMP community read write access),
-it's always possible to have the port status even for bridge agrregate port.
-
-
-=head2 updatedb
-
- klask updatedb [--verbose|-v] [--verb-description|-d] [--chk-hostname|-h] [--chk-location|-l] [--no-rebuildsw|-R]
-
-This command will scan networks and update the computer database.
-To know which are the cmputer scanned, you have to configure the file F</etc/klask/klask.conf>.
-This file is easy to read and write because Klask use YAML format and not XML
-(see L</CONFIGURATION>).
-
-Option are not stable and could be use manually when you have a new kind of switch.
-Maybe some option will be transfered in a future C<checksw> command!
-
-The network parameter C<scan-mode> can have two values: C<active> or C<passive>.
-By default, a network is C<active>.
-This means that an C<fping> command is done at the beginning on all the IPv4 of the network
-and the computers that was not detected in this pass, but where their Klask entry is less than one week,
-will have an C<arping>
-(some OS do not respond to C<ping> but a computer have to respond to C<arping> if it want to interact with other).
-In the scan mode C<passive>, no C<fping> and no C<arping> are done.
-It's good for big subnet with few computer (telephone...).
-The idea of the C<active> scan mode is to force computer to regulary send packet over the network.
-
-At the beginning, the command verify that the switch map checksum is always valid.
-Otherwise, a rebuild procedure will ne done automatically.
-
-=head2 exportdb
-
- klask exportdb [--format|-f txt|html]
-
-This command print the content of the computer database.
-There is actually only two format : TXT and HTML.
-By default, format is TXT.
-It's very easy to have more format, it's just need times...
-
-=head2 removedb
-
- klask removedb ipv4_addr* computer*
-
-This command remove an entry in the database.
-There is only one kind of parameter, the IPv4 of the computers to be removed.
-You can put as many IPv4 as you want...
-
-Computer DNS names are also a valid entry because a DNS resolver is executed at the beginning.
-
-=head2 insertdb
-
- klask insertdb --ip ipv4_addr --mac mac_addr --network vlan_name
-
-This command insert an entry in the database.
-The IPv4 must not exist previously in the database
-and the reverse DNS name resolution must be active for that IPv4.
-
-The date or timestamp of the entry will be C<number_of_day> in the past (by default 15, see L</cleandb>)
-from today or from the oldest entry with the same MAC-Address.
-This command could be use to add pseudo entry and help in the process to detect bad vlan configuration (see L</bad-vlan-id>).
-
-=head2 cleandb
-
- klask cleandb  [--verbose|-v] [--day number_of_day] [--repair-dns]
-
-Remove double entry (same MAC-Address) in the computer database when the older one is older than X day (C<--day>) the new one.
-Computer name beginning by 'float' (regex C<^float>) are not really taken into account but could be remove.
-This could be configure with the global regex parameter C<float-regex> in the configuration file F</etc/klask/klask.conf>.
-This functionality could be use when computer define in VLAN 1
-could have a float IPv4 when they are connected on VLAN 2.
-In the Klask database, the float DNS entries are less important.
-
-When reverse DNS has not been done by the past, option C<--repair-dns> force a reverse DNS check on all unkown host.
-
-=head2 updatesw
-
- klask updatesw [--verbose|-v]
-
-This command build a map of your manageable switch on your network.
-The list of the switches must be given in the file F</etc/klask/klask.conf> (see L</CONFIGURATION>).
-
-The database has a checksum which depend of all the active switches.
-It's use when rebuilding the database in case of change in switch configuration (one more for example).
-
-=head2 exportsw
-
- klask exportsw [--format|-f txt|dot] [--modulo|-m XX] [--shift|-s YY] [--way all|desc|child|parent] [--no-header|-H]
-
-This command print the content of the switch database. There is actually two format.
-One is just TXT for terminal and the other is the DOT format from the graphviz environnement.
-By default, format is TXT.
-
-B<Options for TXT format:>
-
-Tree tables are print :
-C<desc> gives the switches description, model and revision,
-C<child> return the switch to parent switch table and
-C<parent> return the switch parent to parent child table.
-With option C<--way>, you can choose which on to print.
-C<all> will print all and C<child,parent> will print only C<child> and C<parent> table.
-With option C<--no-header>, you can remove the header for each table.
-
-B<Options for DOT format:>
-
- klask exportsw --format dot > /tmp/map.dot
- dot -Tpng /tmp/map.dot > /tmp/map.png
-
-In case you have too many switch connected on one switch,
-the graphviz result graph could be too much vertical.
-With C<--modulo> > 0, you can specify how many switches (connected on one switch) are on the same columns
-before shifting them to one column to the left and back again.
-The C<--shift> parameter must be 1, 2 or 3.
-With C<--shift> egual to 2, the shift will be to two column to the left.
-With 3, it will be 1 to the left and 2 to the left one time over two !
-In practise, we just add virtuals nodes in the dot file,
-that means the result graph is generated with theses virtuals but invisibles nodes...
-
-=head2 ip-free
-
- klask ip-free [--verbose|-v] [--day|-d days-to-death] [--format|-f txt|html] [vlan_name]
-
-This command return IPv4 address that was not use (detected by Klask) at this time.
-The list returned could be limited to just one VLAN.
-IP returned could have been never used or no computer have been detected since the number of days specified
-(2 years by default).
-This parameter could also be define in the configuration file F</etc/klask/klask.conf> (see L</CONFIGURATION>).
-
- default:
-   days-to-death: 730
-
-Computer that does not have the good IPv4 but takes a float one (see L</cleandb>) are taken into account.
-
-
-=head2 bad-vlan-id
-
- klask bad-vlan-id [--day|-d days_before_alert] [--format|-f txt|html]
-
-This command return a list of switch port that are not configure with the good VLAN.
-Computer which are in bad VLAN are detected with the float regex parameter (see L</cleandb>)
-and another prior trace where they had the good IPv4 (good DNS name).
-The computer must stay connected on a bad VLAN more than XX days (15 days by default) before alert.
-This parameter could also define in the configuration file F</etc/klask/klask.conf> (see L</CONFIGURATION>).
-
- default:
-   days-before-alert: 15
-
-This functionality is not need if your switch use RADIUS 802.1X configuration...
-
-
-=head2 poe-enable
-
- klask poe-enable  [--verbose|-v] switch port
-
-This command activate the PoE (Power over Ethernet) on a switch port by SNMP.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-Warning: Only NEXANS switches are supported (we do not have other switch for testing).
-You need to have the SNMP write access on the switch in order to modify it's configuration.
-
-
-=head2 poe-disable
-
- klask poe-disable [--verbose|-v] switch port
-
-This command deactivate the PoE (Power over Ethernet) on a switch port by SNMP.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-Warning: Only NEXANS switches are supported (we do not have other switch for testing).
-You need to have the SNMP write access on the switch in order to modify it's configuration.
-
-
-=head2 poe-status
-
- klask poe-status  [--verbose|-v] switch port
-
-This command return the status of the PoE (Power over Ethernet) on a switch port by SNMP.
-The return value could be C<enable> or C<disable> word.
-So you need to give the switch name and a port on the command line.
-See L</ABBREVIATION FOR PORT>.
-
-If it's not possible to change the PoE status with command L</poe-enable> and L</poe-disable>
-(SNMP community read write access),
-it's always possible to have the PoE port status.
-
-Warning: Only NEXANS switches are supported (we do not have other switch for testing).
-
-
-=head1 CONFIGURATION
-
-Because Klask need many parameters, it's not possible actually to use command line parameters for everything.
-The configuration is done in a F</etc/klask/klask.conf> YAML file.
-This format have many advantage over XML, it's easier to read and to write !
-
-Here an example, be aware with indent, it's important in YAML, do not use tabulation !
-
- default:
-   community: public
-   community-rw: private
-   snmpport: 161
-   float-regex: '(?^msx: ^float )'
-   scan-mode: active
-
- network:
-   labnet:
-     ip-subnet:
-       - add: 192.168.1.0/24
-       - add: 192.168.2.0/24
-     interface: eth0
-     vlan-id: 12
-     main-router: gw1.labnet.local
-
-   schoolnet:
-     ip-subnet:
-       - add: 192.168.3.0/24
-       - add: 192.168.4.0/24
-     interface: eth0.38
-     vlan-id: 13
-     main-router: gw2.schoolnet.local
-     scan-mode: passive
-
-   etunet:
-     ip-subnet:
-       - add: 192.168.5.0/24
-     interface: eth2
-     vlan-id: 14
-     main-router: gw3.etunet.local
-     scan-mode: passive
-
- switch:
-   - hostname: sw1.klask.local
-     location: BatY / 1 floor / K004
-     portignore:
-       - 1
-       - 2
-
-   - hostname: sw2.klask.local
-     location: BatY / 2 floor / K203
-     type: HP2424
-     portignore:
-       - 1
-       - 2
-     fake-ip: 192.168.9.14
-
-   - hostname: sw3.klask.local
-     location: BatY / 2 floor / K203
-
-I think it's pretty easy to understand.
-The default section can be overide in any section, if parameter mean something in theses sections.
-Network to be scan are define in the network section. You must put an add by network.
-Maybe I will make a delete line to suppress specific computers.
-The switch section define your switch.
-You have to write the port number to ignore, this was important if your switchs are cascades
-(right now, method C<updatesw> find them automatically)
-and is still important if you have loop (with PVST or MSTP).
-Just put the ports numbers between switch.
-
-The C<community> parameter is use to get SNMP data on switch.
-It could be overload for each switch.
-By default, it's value is C<public> and you have to configure a readonly word for safety reason.
-Some few command change the switch state as the commands L</enable> and L</disable>.
-In theses rares cases, you need a readwrite SNMP community word define in your configuration file.
-Klask then use since version C<0.6.2> the C<community-rw> parameter which by default is egal to C<private>.
-
-
-=head1 ABBREVIATION FOR PORT
-
-HP Procurve and Nexans switches have a simplistic numbering scheme.
-It's just number: 1, 2, 3... 24.
-On HP8000 chassis, ports names begin with an uppercase letter: A1, A2...
-Nothing is done on theses ports names.
-
-On HP Comware and DELL, port digitization schema use a port speed word (generally a very verbose word)
-followed by tree number.
-In order to have short name,
-we made the following rules:
-
- Bridge-Aggregation     -> Br
- FastEthernet           -> Fa
- Forty-GigabitEthernet  -> Fo
- FortyGigabitEthernet   -> Fo
- GigabitEthernet        -> Gi
- Giga                   -> Gi
- Port-Channel           -> Po
- Ten-GigabitEthernet    -> Te
- TenGigabitEthernet     -> Te
- Ten                    -> Te
-
-All Klask command automatically normalize the port name on standart output
-and also on input command line.
-
-In the case of use an aggregator port (Po, Tk, Br ...),
-the real ports used are also return.
-
-
-=head1 SWITCH SUPPORTED
-
-Here is a list of switches where Klask gives or gave (for old switches) good results.
-We have only a few manageable switches to actually test Klask.
-It is quite possible that switches from other brands will work just as well.
-You just have to do a test on it and add the line of description that goes well in the source code.
-Contact us for any additional information.
-
-In the following list, the names of the switch types written in parentheses are the code names returned by Klask.
-This makes it possible to adjust the code names of the different manufacturers!
-
-HP: J3299A(HP224M), J4120A(HP1600M), J9029A(HP1800-8G), J9449A(HP1810-8G), J4093A(HP2424M), J9279A(HP2510G-24),
-J9280A(HP2510G-48), J4813A(HP2524), J4900A(HP2626A), J4900B(HP2626B), J4899B(HP2650), J9021A(HP2810-24G), J9022A(HP2810-48G),
-J8692A(HP3500-24G), J4903A(HP2824), J4110A(HP8000M), JE074A(HP5120-24G), JE069A(HP5120-48G), JD377A(HP5500-24G), JD374A(HP5500-24F),
-J4121A(HP4000M), J9145A(HP2910-24G), J3298A(HP212M), J9625A(HP2620-24P), JH149A(HP5510-24F).
-
-BayStack: BayStack 350T HW(BS350T)
-
-Nexans: GigaSwitch V3 TP SFP-I 48V ES3(NA3483-6G), GigaSwitch V3 TP.PSE.+ 48/54V ES3(NA3483-6P), GigaSwitch V5 TP(PSE+) SFP-2VI 54VDC(NA5542-7G)
-
-DELL: PC7024(DPC7024), N2048(DN2048), N4032F(DN4032F), N4064F(DN4064F)
-
-H3C and 3COM switches have never not been tested but the new HP Comware switches are exactly the same...
-
-H3C: H3C5500
-
-3COM: 3C17203, 3C17204, 3CR17562-91, 3CR17255-91, 3CR17251-91, 3CR17571-91, 3CRWX220095A, 3CR17254-91, 3CRS48G-24S-91,
-3CRS48G-48S-91, 3C17708, 3C17709, 3C17707, 3CR17258-91, 3CR17181-91, 3CR17252-91, 3CR17253-91, 3CR17250-91, 3CR17561-91,
-3CR17572-91, 3C17702-US, 3C17700.
-
-
-=head1 FILES
-
- /etc/klask/klask.conf
- /var/lib/klask/klaskdb
- /var/lib/klask/switchdb
-
-
-=head1 SEE ALSO
-
-Net::SNMP, Net::Netmask, Net::CIDR::Lite, NetAddr::IP, YAML
-
-=over
-
-=item * L<Web site|http://servforge.legi.grenoble-inp.fr/projects/klask>
-
-=item * L<Online Manual|http://servforge.legi.grenoble-inp.fr/pub/klask/klask.html>
-
-=back
-
-
-=head1 VERSION
-
-$Id$
-
-
-=head1 AUTHOR
-
-Written by Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>, Grenoble - France
-
-=head1 SPECIAL THANKS
-
-The list of people below did not directly contribute to Klask's source code
-but provided me with some data, returned bugs
-or helped me in another small task like having new ideas ...
-Maybe I forgot your contribution in recent years,
-please forgive me in advance and send me an e-mail to correct this.
-
-Kevin Reverchon, Olivier De Marchi, Patrick Begou, Herve Colasuonno, David Gras.
-
-
-=head1 LICENSE AND COPYRIGHT
-
-License GNU GPL version 2 or later and Perl equivalent
-
-Copyright (C) 2005-2020 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Index: unk/klask-sample.conf
===================================================================
--- /trunk/klask-sample.conf	(revision 413)
+++ 	(revision )
@@ -1,27 +1,0 @@
-default:
-   community: public
-   snmpport: 161
-
-network:
-   mysite-1:
-     ip-subnet:
-       - add: 192.168.1.0/24
-       - add: 192.168.2.0/24
-     interface: eth0
-     main-router: gw1.mysite.priv
-
-   mysite-2:
-     ip-subnet:
-       - add: 192.168.33.0/24
-     interface: vlan33
-     main-router: gw33.mysite.priv
-
-switch:
-   - hostname: sw1-batA1-mysite.mysite.priv
-     location: BatA / 1 etage / A101
-
-   - hostname: sw2-batA1-mysite.mysite.priv
-     location: BatA / 1 etage / A101
-
-   - hostname: sw1-batA2-mysite.mysite.priv
-     location: BatA / 2 etage / A201
Index: unk/klask-wrapper
===================================================================
--- /trunk/klask-wrapper	(revision 413)
+++ 	(revision )
@@ -1,48 +1,0 @@
-#!/bin/bash
-#
-# 2011/03/30 gabriel - create
-
-
-check_conf() {
-   if [ ! -r /etc/klask/klask.conf ]
-   then
-      echo "No config file for klask /etc/klask.conf"
-	   exit 1
-   fi
-   if [ ! -r /etc/klask/push-web.conf ]
-   then
-      echo "No config file for klask-push-web /etc/klask/push-web.conf"
-	   exit 1
-   fi
-   if [ ! -x /usr/bin/klask ]
-   then
-      echo "No main program klask"
-	   exit 1
-   fi
-   if [ ! -x /usr/lib/klask/push-web ]
-   then
-      echo "No program klask push-web"
-	   exit 1
-   fi
-   }
-
-case "$1" in
-  update)
-      check_conf
-      /usr/bin/klask updatedb
-      /usr/lib/klask/push-web
-      ;;
-
-  clean)
-      check_conf
-      /usr/bin/klask cleandb
-      /usr/lib/klask/push-web
-      ;;
-
-  *)
-	   echo "Usage: $0 {update|clean}"
-	   exit 1
-      ;;
-esac
-
-exit 0
Index: unk/klask.bash_completion
===================================================================
--- /trunk/klask.bash_completion	(revision 413)
+++ 	(revision )
@@ -1,42 +1,0 @@
-# bash completion for klask
-
-_klask() {
-   local cur prev
-
-   COMPREPLY=()
-   #_get_comp_words_by_ref cur prev
-   cur="${COMP_WORDS[COMP_CWORD]}"
-   prev="${COMP_WORDS[COMP_CWORD-1]}"
-
-   case $prev in
-      -f|--format)
-         COMPREPLY=($(compgen -W "txt html dot" -- "$cur"))
-         return 0
-         ;;
-      -k|--kind)
-         COMPREPLY=($(compgen -W "host mac" -- "$cur"))
-         return 0
-         ;;
-      -i|--vlan)
-         COMPREPLY=($(compgen -W "$(grep vlan-id: /etc/klask/klask.conf|awk '{print $2}'|xargs echo -n)" -- "$cur"))
-         return 0
-         ;;
-      -s|--shift)
-         COMPREPLY=($(compgen -W "1 2 3" -- "$cur"))
-         return 0
-         ;;
-      -w|--way)
-         COMPREPLY=($(compgen -W "all desc child parent" -- "$cur"))
-         return 0
-         ;;
-   esac
-
-   if [[ "$cur" == '-*' || $COMP_CWORD -gt 1 ]]
-   then
-      #COMPREPLY=($(compgen -W '-v --verbose -k --kind -f --format -d --day -i --vlan -m --modulo -s --shift' -- "$cur"))
-      COMPREPLY=($(compgen -W "$(klask  help|grep "^ klask ${COMP_WORDS[1]}" | sed -e 's/[\[]/\n/g;'|grep -- '^-'|cut -f 1 -d ']'|cut -f 1 -d ' '|sed -e 's/|/ /g;'|xargs echo -n)" -- "$cur"))
-   else
-      COMPREPLY=($(compgen -W "$($(which klask || echo /usr/bin/klask) help|grep '^ klask'|awk '{print $2}'|xargs echo -n)" -- "$cur"))
-   fi
-
-   } && complete -F _klask klask
Index: unk/klask.cron
===================================================================
--- /trunk/klask.cron	(revision 413)
+++ 	(revision )
@@ -1,7 +1,0 @@
-#
-# cron-jobs for klask
-#
-
-# m h dom mon dow    user    command
-0,30 7-20 * * *   root   test -x /usr/lib/klask/klask-wrapper && nice -18 /usr/lib/klask/klask-wrapper update >> /dev/null 2>&1
-50   8    * * *   root   test -x /usr/lib/klask/klask-wrapper && nice -18 /usr/lib/klask/klask-wrapper clean >> /dev/null 2>&1
Index: unk/make-package-debian
===================================================================
--- /trunk/make-package-debian	(revision 413)
+++ 	(revision )
@@ -1,88 +1,0 @@
-#!/bin/bash
-#
-# 2018/10/02 Gabriel Moreau <Gabriel.Moreau@univ-grenoble-alpes.fr>
-
-trap '[ -n "${tmp_folder}" -a -d "${tmp_folder}" ] && rm -rf "${tmp_folder}"' QUIT
-
-export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
-export LANG=C
-
-tmp_folder=$(mktemp --directory /tmp/klask-XXXXXX)
-[ -n "${tmp_folder}" -a -d "${tmp_folder}" ] || exit 1
-
-# Set Name and Version
-PKG_NAME=klask
-CODE_VERSION=$(grep 'version->declare' klask | cut -f 2 -d "'") #'
-PKG_VERSION=1
-
-# Create future tree
-mkdir -p ${tmp_folder}/usr/bin
-mkdir -p ${tmp_folder}/usr/lib/klask
-mkdir -p ${tmp_folder}/usr/share/man/man1
-mkdir -p ${tmp_folder}/usr/share/klask
-mkdir -p ${tmp_folder}/etc/klask
-mkdir -p ${tmp_folder}/etc/bash_completion.d
-cp klask                   ${tmp_folder}/usr/bin/
-cp push-web                ${tmp_folder}/usr/lib/klask/
-cp klask-wrapper           ${tmp_folder}/usr/lib/klask/
-cp klask.1.gz              ${tmp_folder}/usr/share/man/man1/
-cp LICENSE.txt             ${tmp_folder}/usr/share/klask/
-cp sorttable-klask.js      ${tmp_folder}/usr/share/klask/
-cp style-klask.css         ${tmp_folder}/usr/share/klask/
-cp klask-sample.conf       ${tmp_folder}/etc/klask/
-cp push-web-sample.conf    ${tmp_folder}/etc/klask/
-cp klask.cron              ${tmp_folder}/etc/klask/
-cp klask.bash_completion   ${tmp_folder}/etc/bash_completion.d/
-chmod -R a+rx,go-w   ${tmp_folder}/usr/bin/klask
-chmod -R a+rx,go-w   ${tmp_folder}/usr/lib/klask/push-web
-chmod -R a+rx,go-w   ${tmp_folder}/usr/lib/klask/klask-wrapper
-chmod -R a+rX,go-w   ${tmp_folder}/usr
-chmod -R a+rX,go-w   ${tmp_folder}/etc
-
-# Data archive
-rm -f ${tmp_folder}/data.tar.gz
-(cd ${tmp_folder}; tar --owner root --group root -czf data.tar.gz ./usr ./etc)
-
-# Control file
-cat <<END > ${tmp_folder}/control
-Package: ${PKG_NAME}
-Version: ${CODE_VERSION}-${PKG_VERSION}
-Section: utils
-Tag: implemented-in::perl, interface::commandline, role::program
-Priority: optional
-Depends: perl, perl-base, perl-modules, libnet-cidr-lite-perl, libnet-netmask-perl, libnet-snmp-perl, libnetaddr-ip-perl, libyaml-perl, libcrypt-des-perl, libcrypt-hcesha-perl, libdigest-hmac-perl, libtext-table-perl, snmp, arping, net-tools, fping, bind9-host, arpwatch
-Suggests: libyaml-shell-perl | yamllint, perl-doc
-Architecture: all
-Installed-Size: $(du -ks ${tmp_folder}|cut -f 1)
-Maintainer: Gabriel Moreau <Gabriel.Moreau@univ-grenoble-alpes.fr>
-Description: Klask maps your local network - Switches and Computers
- Klask means search in Breton and that's exactly what we wanted.
- Klask is a tool whose two main functions are:
- * Find the connections between switches and draw a map of the
-   active hardware on the local network;
- * Make an accurate inventory of the port and switch to which
-   the local network machines are connected.
- Klask is a small tool, in the spirit of UNIX tools, to worry about
- connections on the local network.
-Homepage: http://servforge.legi.grenoble-inp.fr/projects/klask
-END
-
-# Control archive
-rm -f ${tmp_folder}/control.tar.gz
-(cd ${tmp_folder}; tar --owner root --group root -czf control.tar.gz control)
-
-# Format deb package
-echo 2.0 > ${tmp_folder}/debian-binary
-
-# Create package (control before data)
-ar -r ${PKG_NAME}_${CODE_VERSION}-${PKG_VERSION}_all.deb ${tmp_folder}/debian-binary ${tmp_folder}/control.tar.gz ${tmp_folder}/data.tar.gz
-      
-# Clean
-rm -rf ${tmp_folder}
-
-# Prepare reprepro upload
-echo "# Prepare reprepro upload"
-echo "( cd /srv/www/debian/ ; reprepro includedeb wheezy   ~/wheezy/${PKG_NAME}_${CODE_VERSION}-${PKG_VERSION}_all.deb )"
-echo "( cd /srv/www/debian/ ; reprepro includedeb jessie   ~/jessie/${PKG_NAME}_${CODE_VERSION}-${PKG_VERSION}_all.deb )"
-echo "( cd /srv/www/debian/ ; reprepro includedeb stretch ~/stretch/${PKG_NAME}_${CODE_VERSION}-${PKG_VERSION}_all.deb )"
-echo "( cd /srv/www/debian/ ; reprepro dumpreferences ) | grep -i klask"
Index: unk/push-web
===================================================================
--- /trunk/push-web	(revision 413)
+++ 	(revision )
@@ -1,310 +1,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2005-2020 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
-# License GNU GPL version 2 or later
-#
-# 2007/10/23 Gabriel Moreau
-# 2010/11/03 Gabriel Moreau
-# 2011/03/30 Gabriel Moreau - make it generic
-# 2017/11/04 Gabriel Moreau - all reworks - use tab
-
-export PATH=/bin:/sbin:/usr/bin:/usr/sbin
-export LANG=C
-
-SITE_NAME=''
-REMOTE_SERVER=localhost
-REMOTE_USER=''
-REMOTE_FOLDER=/var/www/klask/
-
-GRAPH_MODULO=9
-GRAPH_SHIFT=1
-
-CACHE_TIMEOUT=50
-CACHE_FOLDER='/tmp/klask-push-web'
-
-if [ -f /etc/klask/push-web.conf ]
-then
-   . /etc/klask/push-web.conf
-fi
-
-
-# remote command
-DOT=/usr/bin/dot
-
-mkdir -p ${CACHE_FOLDER}
-if [ ! -d ${CACHE_FOLDER} ]
-then
-   echo "Error: Folder ${CACHE_FOLDER} do not exist !"
-   exit 1
-fi
-cd ${CACHE_FOLDER}
-
-LOCALTIME=$(date "+%Y-%m-%d %H:%M")
-TIMEFILE=$(date "+%Y-%m-%d.%H-%M")
-
-find ${CACHE_FOLDER}/ -type f -a -mtime +${CACHE_TIMEOUT} -delete
-
-klask exportsw --format dot --modulo ${GRAPH_MODULO} --shift ${GRAPH_SHIFT} > ${CACHE_FOLDER}/switch-map.txt.new
-klask exportsw                                                              > ${CACHE_FOLDER}/switch-db.txt.new
-
-klask exportdb    > ${CACHE_FOLDER}/hostname-db.txt
-klask ip-free     > ${CACHE_FOLDER}/ipfree-db.txt
-klask bad-vlan-id > ${CACHE_FOLDER}/badvlan-db.txt
-
-cmp --silent ${CACHE_FOLDER}/switch-db.txt.new ${CACHE_FOLDER}/switch-db.txt || {
-   mv -f ${CACHE_FOLDER}/switch-db.txt.new ${CACHE_FOLDER}/switch-db.${TIMEFILE}.txt;
-   ln -sf ${CACHE_FOLDER}/switch-db.${TIMEFILE}.txt ${CACHE_FOLDER}/switch-db.txt
-   }
-
-cmp --silent ${CACHE_FOLDER}/switch-map.txt.new ${CACHE_FOLDER}/switch-map.txt || {
-   mv -f ${CACHE_FOLDER}/switch-map.txt.new ${CACHE_FOLDER}/switch-map.${TIMEFILE}.txt;
-   ln -sf ${CACHE_FOLDER}/switch-map.${TIMEFILE}.txt ${CACHE_FOLDER}/switch-map.txt
-   }
-
-# keep trace of old switch map
-old_switch_db=$((echo 'command <tt>klask exportsw --format txt</tt>';
-      (cd ${CACHE_FOLDER}/; ls -1 switch-db.20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]-[0-9][0-9].txt;) \
-      | sed -e 's/^switch-db\.//; s/\.txt$//;' \
-      | xargs -r -I {} printf " <a href=\"switch-db.%s.txt\">%s</a>\n" '{}' '{}' \
-      ) \
-   | paste -sd ',' -)
-
-old_switch_map=$((echo 'graphviz';\
-      (cd ${CACHE_FOLDER}/; ls -1 switch-map.20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]-[0-9][0-9].txt;) \
-      | sed -e 's/^switch-map\.//; s/\.txt$//;' \
-      | xargs -r -I {} printf " <a href=\"switch-map.%s.txt\">%s</a>\n" '{}' '{}' \
-      )\
-   | paste -sd ',' -)
-
-
-cat <<END > ${CACHE_FOLDER}/index.html
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <link rel="stylesheet" type="text/css" href="style-klask.css" />
- <!-- <meta http-equiv="refresh" content="1800"> -->
- <title>Klask mapping net-tools</title>
- <noscript>
- </noscript>
- <script src="sorttable-klask.js"></script>
- <script src="svg-pan-zoom-klask.js"></script>
- <script>
-function delayedRefresh() {
- setTimeout( "refresh()", 1800*1000 );
- }
-
-function refresh() {
- history.go(0);
- }
- 
-function selectTab(event, tabName) {
- var i;
- var x = document.getElementsByClassName("selectableTabContent");
- var tablinks = document.getElementsByClassName("selectableTabButton");
- for (i = 0; i < x.length; i++) {
-  x[i].style.display = "none";
-  }
- for (i = 0; i < x.length; i++) {
-  tablinks[i].id = "";
-  }
- document.getElementById(tabName).style.display = "block";
- event.currentTarget.id = "selected";
- }
-
-// https://stackoverflow.com/questions/17591447/how-to-reload-current-page-without-losing-any-form-data
-// http://www.uitrick.com/javascript/how-to-keep-a-global-variable-alive-even-after-page-refresh-javascript/
-// https://blog.udemy.com/javascript-page-refresh/
-
-// localStorage.setItem('bgcolor', document.getElementById('bgcolor').value);
-// localStorage.getItem('bgcolor');
-
-// https://stackoverflow.com/questions/5683087/chaining-getelementbyid
-function getById(id, context) {
- var el = document.getElementById(id);
- return context.contains(el) ? el : null;
- }
-function getByName(name, context) {
- var i;
- var x = document.getElementsByName(name);
- for (i = 0; i < x.length; i++) {
-  if ((x[i].getAttribute('name') == name) && (context.contains(x[i]))) {
-   return x[i];
-   }
-  }
- return null;
- }
-
-// Run on page load
-window.onload = function() {
- if (typeof(Storage) !== "undefined") {
-  // Code for localStorage/sessionStorage.
-  var tabName = localStorage.getItem('navbar-selected');
-  if (tabName == "undefined") { return; }
-  var navbar = document.getElementById('navbar');
-  var tab = getByName(tabName, navbar);
-  var i;
-  var x = document.getElementsByClassName("selectableTabContent");
-  var tablinks = document.getElementsByClassName("selectableTabButton");
-
-  for (i = 0; i < x.length; i++) {
-   x[i].style.display = "none";
-   }
-  for (i = 0; i < x.length; i++) {
-   tablinks[i].id = "";
-   }
-
-  document.getElementById(tabName).style.display = "block";
-  tab.id = "selected";
-  }
- else {
-  // Sorry! No Web Storage support..
-  }
- }
-
-// Before refreshing the page, save the form data to sessionStorage
-window.onbeforeunload = function() {
- if (typeof(Storage) !== "undefined") {
-  // Code for localStorage/sessionStorage.
-  // var navbar = document.getElementById('navbar');
-  var selected = document.getElementById('selected'); // getById('selected', navbar);
-
-  var tabName = selected.getAttribute('name');
-  localStorage.setItem('navbar-selected', tabName);
-  }
- else {
-  // Sorry! No Web Storage support..
-  }
- }
- </script>
-</head>
-<body> <!-- onload="delayedRefresh" -->
-<h1>
-<b><a href="http://servforge.legi.grenoble-inp.fr/projects/klask" class="circle">Klask</a></b>
-Tool for <a href="switch-map.png">mapping</a> the local network - site ${SITE_NAME}
-<a class="refresh" href="./">${LOCALTIME}</a>
-</h1>
-
-<div id="navbar">
- <ul>
-  <li class="selectableTabButton" name="IP_detected"   onclick="selectTab(event, 'IP_detected')" id="selected">IP detected</li>
-  <li class="selectableTabButton" name="IP_available"  onclick="selectTab(event, 'IP_available')">IP available</li>
-  <li class="selectableTabButton" name="VLAN_mismatch" onclick="selectTab(event, 'VLAN_mismatch')">VLAN mismatch</li>
-  <li class="selectableTabButton" name="MAP"           onclick="selectTab(event, 'MAP')">MAP (png)</li>
-  <li class="selectableTabButton" name="RAW_data"      onclick="selectTab(event, 'RAW_data')">RAW data</li>
- </ul>
-</div>
-
-<div id="content">
-
- <div id="IP_detected" class="selectableTabContent">
-  <p>
-  List of all IP addresses detected on the local network (on all VLANs)
-  since the origin of the database
-  (except those that have been deleted manually and voluntarily).
-  </p>
-END
-
-klask exportdb --format html >> ${CACHE_FOLDER}/index.html
-
-cat <<END >> ${CACHE_FOLDER}/index.html
- </div>
-
- <div id="IP_available" class="selectableTabContent" style="display:none">
-  <p>
-  List all free IP addresses (which have never been used or not used for two years).
-  These IP addresses are available to assign to all new computers.
-  </p>
-END
-
-klask ip-free --format html >> ${CACHE_FOLDER}/index.html
-
-cat <<END >> ${CACHE_FOLDER}/index.html
- </div>
-
- <div id="VLAN_mismatch" class="selectableTabContent" style="display:none">
-  <p>
-  List all computers connected to switches where the VLAN is misconfigured.
-  Returns the port and VLAN to configure on these switches.
-  </p>
-END
-
-klask bad-vlan-id --format html >> ${CACHE_FOLDER}/index.html
-
-cat <<END >> ${CACHE_FOLDER}/index.html
- </div>
-
- <div id="MAP" class="selectableTabContent" style="display:none">
-   <p>
-   Direct link to local network map in the formats <a href="switch-map.png">png</a> and <a href="switch-map.svg">svg</a>.
-   Because the map could be big,
-   you will have a better web experience with
-   <a href="https://addons.mozilla.org/en-US/firefox/addon/image-zoom/">Image Zoom<a> plugin (or equivalent) installed.
-   </p>
-   
-   <embed type="image/svg+xml" src="switch-map.svg" id="my-embed-switch-map"/>
-   <script>
-      document.getElementById('my-embed-switch-map').addEventListener('load', function(){
-         // Will get called after embed element was loaded
-         svgPanZoom(document.getElementById('my-embed-switch-map')); 
-         })
-   </script>
-   <img class="map" src="switch-map.png">
- </div>
-
- <div id="RAW_data" class="selectableTabContent" style="display:none">
-  <p>
-   The main raw results of Klask are accessible directly,
-   via <tt>wget</tt> or <tt>curl</tt> tools for example,
-   for any specific treatment:
-   <ul>
-    <li><a href="hostname-db.txt">Computers DB</a> (command <tt>klask exportdb --format txt</tt>),</li>
-    <li><a href="switch-db.txt">Switches DB</a> (${old_switch_db}),</li>
-    <li><a href="ipfree-db.txt">IP available</a> (command <tt>klask ip-free --format txt</tt>),</li>
-    <li><a href="badvlan-db.txt">VLAN mismatch</a> (command <tt>klask bad-vlan-id --format txt</tt>),</li>
-    <li>Map format: <a href="switch-map.txt">dot source</a> (${old_switch_map}), <a href="switch-map.png">png</a>, <a href="switch-map.svg">svg</a>.</li>
-   </ul>
-  </p>
- </div>
-</div>
-
-<p class="copyright">
-Klask (version $(klask version | grep ^Version | cut -f 2 -d ' ')) - $(klask version | grep ^Copyright | cut -f 1 -d '<')
-</p>
-<!-- Try to prefect switch map image -->
-<link rel="prefetch" href="switch-map.png">
-</body>
-</html>
-END
-
-if [ "${REMOTE_SERVER}" = "localhost" ]
-then
-   rsync --times \
-      /usr/share/klask/sorttable-klask.js \
-      /usr/share/klask/svg-pan-zoom-klask.js \
-      /usr/share/klask/style-klask.css \
-      ${CACHE_FOLDER}/index.html \
-      ${CACHE_FOLDER}/*-db.txt \
-      ${CACHE_FOLDER}/switch-map.txt \
-      ${CACHE_FOLDER}/*.20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]-[0-9][0-9].txt \
-      ${REMOTE_FOLDER}/
-
-   [ -x ${DOT} ] && (cd ${REMOTE_FOLDER};
-      [ switch-map.txt -nt switch-map.svg ] && ${DOT} -T svg switch-map.txt > switch-map.svg;
-      [ switch-map.txt -nt switch-map.png ] && ${DOT} -T png switch-map.txt > switch-map.png)
-
-else
-   rsync --times \
-      /usr/share/klask/sorttable-klask.js \
-      /usr/share/klask/svg-pan-zoom-klask.js \
-      /usr/share/klask/style-klask.css \
-      ${CACHE_FOLDER}/index.html \
-      ${CACHE_FOLDER}/*-db.txt \
-      ${CACHE_FOLDER}/switch-map.txt \
-      ${CACHE_FOLDER}/*.20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]-[0-9][0-9].txt \
-      ${REMOTE_USER}@${REMOTE_SERVER}:${REMOTE_FOLDER}/
-
-
-   ssh ${REMOTE_USER}@${REMOTE_SERVER} "(cd ${REMOTE_FOLDER};
-      [ switch-map.txt -nt switch-map.svg ] && ${DOT} -T svg switch-map.txt > switch-map.svg;
-      [ switch-map.txt -nt switch-map.png ] && ${DOT} -T png switch-map.txt > switch-map.png)"
-fi
Index: unk/push-web-sample.conf
===================================================================
--- /trunk/push-web-sample.conf	(revision 413)
+++ 	(revision )
@@ -1,13 +1,0 @@
-
-SITE_NAME="MySite"
-
-REMOTE_SERVER=staticwebserver.mysite.priv
-REMOTE_USER=sysadmin
-
-REMOTE_FOLDER=/var/www/klask
-
-GRAPH_MODULO=9
-GRAPH_SHIFT=1
-
-CACHE_TIMEOUT=50
-CACHE_FOLDER='/var/cache/klask/push-web'
Index: unk/sorttable-klask.js
===================================================================
--- /trunk/sorttable-klask.js	(revision 413)
+++ 	(revision )
@@ -1,504 +1,0 @@
-/*
-  SortTable
-  version 2
-  7th April 2007
-  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
-
-  Instructions:
-  Download this file
-  Add <script src="sorttable.js"></script> to your HTML
-  Add class="sortable" to any table you'd like to make sortable
-  Click on the headers to sort
-
-  Thanks to many, many people for contributions and suggestions.
-  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
-  This basically means: do what you want with it.
-*/
-
-
-var stIsIE = /*@cc_on!@*/false;
-
-sorttable = {
-  init: function() {
-    // quit if this function has already been called
-    if (arguments.callee.done) return;
-    // flag this function so we don't do the same thing twice
-    arguments.callee.done = true;
-    // kill the timer
-    if (_timer) clearInterval(_timer);
-
-    if (!document.createElement || !document.getElementsByTagName) return;
-
-    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
-
-    forEach(document.getElementsByTagName('table'), function(table) {
-      if (table.className.search(/\bsortable\b/) != -1) {
-        sorttable.makeSortable(table);
-      }
-    });
-
-  },
-
-  makeSortable: function(table) {
-    if (table.getElementsByTagName('thead').length == 0) {
-      // table doesn't have a tHead. Since it should have, create one and
-      // put the first table row in it.
-      the = document.createElement('thead');
-      the.appendChild(table.rows[0]);
-      table.insertBefore(the,table.firstChild);
-    }
-    // Safari doesn't support table.tHead, sigh
-    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
-
-    if (table.tHead.rows.length != 1) return; // can't cope with two header rows
-
-    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
-    // "total" rows, for example). This is B&R, since what you're supposed
-    // to do is put them in a tfoot. So, if there are sortbottom rows,
-    // for backwards compatibility, move them to tfoot (creating it if needed).
-    sortbottomrows = [];
-    for (var i=0; i<table.rows.length; i++) {
-      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
-        sortbottomrows[sortbottomrows.length] = table.rows[i];
-      }
-    }
-    if (sortbottomrows) {
-      if (table.tFoot == null) {
-        // table doesn't have a tfoot. Create one.
-        tfo = document.createElement('tfoot');
-        table.appendChild(tfo);
-      }
-      for (var i=0; i<sortbottomrows.length; i++) {
-        tfo.appendChild(sortbottomrows[i]);
-      }
-      delete sortbottomrows;
-    }
-
-    // work through each column and calculate its type
-    headrow = table.tHead.rows[0].cells;
-    for (var i=0; i<headrow.length; i++) {
-      // manually override the type with a sorttable_type attribute
-      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
-        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
-        if (mtch) { override = mtch[1]; }
-	      if (mtch && typeof sorttable["sort_"+override] == 'function') {
-	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
-	      } else {
-	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
-	      }
-	      // make it clickable to sort
-	      headrow[i].sorttable_columnindex = i;
-	      headrow[i].sorttable_tbody = table.tBodies[0];
-	      dean_addEvent(headrow[i],"click", function(e) {
-
-          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
-            // if we're already sorted by this column, just
-            // reverse the table, which is quicker
-            sorttable.reverse(this.sorttable_tbody);
-            this.className = this.className.replace('sorttable_sorted',
-                                                    'sorttable_sorted_reverse');
-            this.removeChild(document.getElementById('sorttable_sortfwdind'));
-            sortrevind = document.createElement('span');
-            sortrevind.id = "sorttable_sortrevind";
-            sortrevind.innerHTML = stIsIE ? '&nbsp;<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
-            this.appendChild(sortrevind);
-            return;
-          }
-          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
-            // if we're already sorted by this column in reverse, just
-            // re-reverse the table, which is quicker
-            sorttable.reverse(this.sorttable_tbody);
-            this.className = this.className.replace('sorttable_sorted_reverse',
-                                                    'sorttable_sorted');
-            this.removeChild(document.getElementById('sorttable_sortrevind'));
-            sortfwdind = document.createElement('span');
-            sortfwdind.id = "sorttable_sortfwdind";
-            sortfwdind.innerHTML = stIsIE ? '&nbsp;<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
-            this.appendChild(sortfwdind);
-            return;
-          }
-
-          // remove sorttable_sorted classes
-          theadrow = this.parentNode;
-          forEach(theadrow.childNodes, function(cell) {
-            if (cell.nodeType == 1) { // an element
-              cell.className = cell.className.replace('sorttable_sorted_reverse','');
-              cell.className = cell.className.replace('sorttable_sorted','');
-            }
-          });
-          sortfwdind = document.getElementById('sorttable_sortfwdind');
-          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
-          sortrevind = document.getElementById('sorttable_sortrevind');
-          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
-
-          this.className += ' sorttable_sorted';
-          sortfwdind = document.createElement('span');
-          sortfwdind.id = "sorttable_sortfwdind";
-          sortfwdind.innerHTML = stIsIE ? '&nbsp;<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
-          this.appendChild(sortfwdind);
-
-	        // build an array to sort. This is a Schwartzian transform thing,
-	        // i.e., we "decorate" each row with the actual sort key,
-	        // sort based on the sort keys, and then put the rows back in order
-	        // which is a lot faster because you only do getInnerText once per row
-	        row_array = [];
-	        col = this.sorttable_columnindex;
-	        rows = this.sorttable_tbody.rows;
-	        for (var j=0; j<rows.length; j++) {
-	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
-	        }
-	        /* If you want a stable sort, uncomment the following line */
-	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
-	        /* and comment out this one */
-	        row_array.sort(this.sorttable_sortfunction);
-
-	        tb = this.sorttable_tbody;
-// gabriel
-memory = "null";
-color = "even";
-	        for (var j=0; j<row_array.length; j++) {
-        // gabriel
-if ( memory != row_array[j][0] ) {
-  memory = row_array[j][0];
-  color = color =="odd" ? "even" : "odd";
-  }
-row_array[j][1].className = color;
-//        row_array[j][1].className = j % 2 ? "even" : "odd";
-	          tb.appendChild(row_array[j][1]);
-	        }
-
-	        delete row_array;
-	      });
-	    }
-    }
-  },
-
-  guessType: function(table, column) {
-    // guess the type of a column based on its first non-blank row
-    sortfn = sorttable.sort_alpha;
-    for (var i=0; i<table.tBodies[0].rows.length; i++) {
-      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
-      if (text != '') {
-        if (text.match(/^-?[£$€]?[\d,.]+%?$/)) {
-          return sorttable.sort_numeric;
-        }
-        // check for a date: dd/mm/yyyy or dd/mm/yy
-        // can have / or . or - as separator
-        // can be mm/dd as well
-        possdate = text.match(sorttable.DATE_RE)
-        if (possdate) {
-          // looks like a date
-          first = parseInt(possdate[1]);
-          second = parseInt(possdate[2]);
-          if (first > 12) {
-            // definitely dd/mm
-            return sorttable.sort_ddmm;
-          } else if (second > 12) {
-            return sorttable.sort_mmdd;
-          } else {
-            // looks like a date, but we can't tell which, so assume
-            // that it's dd/mm (English imperialism!) and keep looking
-            sortfn = sorttable.sort_ddmm;
-          }
-        }
-      }
-    }
-    return sortfn;
-  },
-
-  getInnerText: function(node) {
-    // gets the text we want to use for sorting for a cell.
-    // strips leading and trailing whitespace.
-    // this is *not* a generic getInnerText function; it's special to sorttable.
-    // for example, you can override the cell text with a customkey attribute.
-    // it also gets .value for <input> fields.
-
-    hasInputs = (typeof node.getElementsByTagName == 'function') &&
-                 node.getElementsByTagName('input').length;
-
-    if (node.getAttribute("sorttable_customkey") != null) {
-      return node.getAttribute("sorttable_customkey");
-    }
-    else if (typeof node.textContent != 'undefined' && !hasInputs) {
-      return node.textContent.replace(/^\s+|\s+$/g, '');
-    }
-    else if (typeof node.innerText != 'undefined' && !hasInputs) {
-      return node.innerText.replace(/^\s+|\s+$/g, '');
-    }
-    else if (typeof node.text != 'undefined' && !hasInputs) {
-      return node.text.replace(/^\s+|\s+$/g, '');
-    }
-    else {
-      switch (node.nodeType) {
-        case 3:
-          if (node.nodeName.toLowerCase() == 'input') {
-            return node.value.replace(/^\s+|\s+$/g, '');
-          }
-        case 4:
-          return node.nodeValue.replace(/^\s+|\s+$/g, '');
-          break;
-        case 1:
-        case 11:
-          var innerText = '';
-          for (var i = 0; i < node.childNodes.length; i++) {
-            innerText += sorttable.getInnerText(node.childNodes[i]);
-          }
-          return innerText.replace(/^\s+|\s+$/g, '');
-          break;
-        default:
-          return '';
-      }
-    }
-  },
-
-  reverse: function(tbody) {
-    // reverse the rows in a tbody
-    newrows = [];
-    for (var i=0; i<tbody.rows.length; i++) {
-      newrows[newrows.length] = tbody.rows[i];
-    }
-    for (var i=newrows.length-1; i>=0; i--) {
-       tbody.appendChild(newrows[i]);
-    }
-    delete newrows;
-  },
-
-  /* sort functions
-     each sort function takes two parameters, a and b
-     you are comparing a[0] and b[0] */
-  sort_numeric: function(a,b) {
-    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
-    if (isNaN(aa)) aa = 0;
-    bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
-    if (isNaN(bb)) bb = 0;
-    return aa-bb;
-  },
-  sort_alpha: function(a,b) {
-    if (a[0]==b[0]) return 0;
-    if (a[0]<b[0]) return -1;
-    return 1;
-  },
-  sort_ddmm: function(a,b) {
-    mtch = a[0].match(sorttable.DATE_RE);
-    y = mtch[3]; m = mtch[2]; d = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt1 = y+m+d;
-    mtch = b[0].match(sorttable.DATE_RE);
-    y = mtch[3]; m = mtch[2]; d = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt2 = y+m+d;
-    if (dt1==dt2) return 0;
-    if (dt1<dt2) return -1;
-    return 1;
-  },
-  sort_mmdd: function(a,b) {
-    mtch = a[0].match(sorttable.DATE_RE);
-    y = mtch[3]; d = mtch[2]; m = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt1 = y+m+d;
-    mtch = b[0].match(sorttable.DATE_RE);
-    y = mtch[3]; d = mtch[2]; m = mtch[1];
-    if (m.length == 1) m = '0'+m;
-    if (d.length == 1) d = '0'+d;
-    dt2 = y+m+d;
-    if (dt1==dt2) return 0;
-    if (dt1<dt2) return -1;
-    return 1;
-  },
-
-  shaker_sort: function(list, comp_func) {
-    // A stable sort function to allow multi-level sorting of data
-    // see: http://en.wikipedia.org/wiki/Cocktail_sort
-    // thanks to Joseph Nahmias
-    var b = 0;
-    var t = list.length - 1;
-    var swap = true;
-
-    while(swap) {
-        swap = false;
-        for(var i = b; i < t; ++i) {
-            if ( comp_func(list[i], list[i+1]) > 0 ) {
-                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
-                swap = true;
-            }
-        } // for
-        t--;
-
-        if (!swap) break;
-
-        for(var i = t; i > b; --i) {
-            if ( comp_func(list[i], list[i-1]) < 0 ) {
-                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
-                swap = true;
-            }
-        } // for
-        b++;
-
-    } // while(swap)
-  }
-}
-
-/* ******************************************************************
-   Supporting functions: bundled here to avoid depending on a library
-   ****************************************************************** */
-
-// Dean Edwards/Matthias Miller/John Resig
-
-/* for Mozilla/Opera9 */
-if (document.addEventListener) {
-    document.addEventListener("DOMContentLoaded", sorttable.init, false);
-}
-
-/* for Internet Explorer */
-/*@cc_on @*/
-/*@if (@_win32)
-    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
-    var script = document.getElementById("__ie_onload");
-    script.onreadystatechange = function() {
-        if (this.readyState == "complete") {
-            sorttable.init(); // call the onload handler
-        }
-    };
-/*@end @*/
-
-/* for Safari */
-if (/WebKit/i.test(navigator.userAgent)) { // sniff
-    var _timer = setInterval(function() {
-        if (/loaded|complete/.test(document.readyState)) {
-            sorttable.init(); // call the onload handler
-        }
-    }, 10);
-}
-
-/* for other browsers */
-window.onload = sorttable.init;
-
-// written by Dean Edwards, 2005
-// with input from Tino Zijdel, Matthias Miller, Diego Perini
-
-// http://dean.edwards.name/weblog/2005/10/add-event/
-
-function dean_addEvent(element, type, handler) {
-	if (element.addEventListener) {
-		element.addEventListener(type, handler, false);
-	} else {
-		// assign each event handler a unique ID
-		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
-		// create a hash table of event types for the element
-		if (!element.events) element.events = {};
-		// create a hash table of event handlers for each element/event pair
-		var handlers = element.events[type];
-		if (!handlers) {
-			handlers = element.events[type] = {};
-			// store the existing event handler (if there is one)
-			if (element["on" + type]) {
-				handlers[0] = element["on" + type];
-			}
-		}
-		// store the event handler in the hash table
-		handlers[handler.$$guid] = handler;
-		// assign a global event handler to do all the work
-		element["on" + type] = handleEvent;
-	}
-};
-// a counter used to create unique IDs
-dean_addEvent.guid = 1;
-
-function removeEvent(element, type, handler) {
-	if (element.removeEventListener) {
-		element.removeEventListener(type, handler, false);
-	} else {
-		// delete the event handler from the hash table
-		if (element.events && element.events[type]) {
-			delete element.events[type][handler.$$guid];
-		}
-	}
-};
-
-function handleEvent(event) {
-	var returnValue = true;
-	// grab the event object (IE uses a global event object)
-	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
-	// get a reference to the hash table of event handlers
-	var handlers = this.events[event.type];
-	// execute each event handler
-	for (var i in handlers) {
-		this.$$handleEvent = handlers[i];
-		if (this.$$handleEvent(event) === false) {
-			returnValue = false;
-		}
-	}
-	return returnValue;
-};
-
-function fixEvent(event) {
-	// add W3C standard event methods
-	event.preventDefault = fixEvent.preventDefault;
-	event.stopPropagation = fixEvent.stopPropagation;
-	return event;
-};
-fixEvent.preventDefault = function() {
-	this.returnValue = false;
-};
-fixEvent.stopPropagation = function() {
-  this.cancelBubble = true;
-}
-
-// Dean's forEach: http://dean.edwards.name/base/forEach.js
-/*
-	forEach, version 1.0
-	Copyright 2006, Dean Edwards
-	License: http://www.opensource.org/licenses/mit-license.php
-*/
-
-// array-like enumeration
-if (!Array.forEach) { // mozilla already supports this
-	Array.forEach = function(array, block, context) {
-		for (var i = 0; i < array.length; i++) {
-			block.call(context, array[i], i, array);
-		}
-	};
-}
-
-// generic enumeration
-Function.prototype.forEach = function(object, block, context) {
-	for (var key in object) {
-		if (typeof this.prototype[key] == "undefined") {
-			block.call(context, object[key], key, object);
-		}
-	}
-};
-
-// character enumeration
-String.forEach = function(string, block, context) {
-	Array.forEach(string.split(""), function(chr, index) {
-		block.call(context, chr, index, string);
-	});
-};
-
-// globally resolve forEach enumeration
-var forEach = function(object, block, context) {
-	if (object) {
-		var resolve = Object; // default
-		if (object instanceof Function) {
-			// functions have a "length" property
-			resolve = Function;
-		} else if (object.forEach instanceof Function) {
-			// the object implements a custom forEach method so use that
-			object.forEach(block, context);
-			return;
-		} else if (typeof object == "string") {
-			// the object is a string
-			resolve = String;
-		} else if (typeof object.length == "number") {
-			// the object is array-like
-			resolve = Array;
-		}
-		resolve.forEach(object, block, context);
-	}
-};
-
-
Index: unk/style-klask.css
===================================================================
--- /trunk/style-klask.css	(revision 413)
+++ 	(revision )
@@ -1,184 +1,0 @@
-body {
-   background      : #DBF9CF;
-   }
-
-h1{
-   text-align      : center;
-   font-family     : Verdana;
-   font-size       : 120%;
-   margin          : 2.5ex;
-   }
-
-table.sortable {
-   border-collapse : collapse;
-   font-family     : Verdana;
-   }
-
-table.sortable caption {
-   caption-side    : bottom;
-   text-align      : center;
-   text-decoration : underline;
-   }
-
-table.sortable th {
-   color           : #FFF;
-   background      : #333;
-   }
-
-table.sortable thead th:hover {
-   background      : #AAA;
-   }
-
-table.sortable thead th.sorttable_nosort:hover {
-   background      : #333;
-   }
-
-table.sortable thead th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after { 
-   content         : "\00A0\21C5" 
-   }
-
-table.sortable th.sorttable_sorted,th.sorttable_sorted_reverse {
-   background      : #666;
-   }
-
-th.klask-header-left {
-   border-top-left-radius     : 1ex;
-   }
-
-th.klask-header-right {
-   border-top-right-radius    : 1ex;
-   }
-
-th.klask-footer-left {
-   border-bottom-left-radius  : 1ex;
-   }
-
-th.klask-footer-right {
-   border-bottom-right-radius : 1ex;
-   }
-
-table.sortable td {
-   border          : 1px solid #AAA;
-   border-top      : 1px solid #AAA;
-   border-bottom   : 1px solid #AAA;
-   border-left     : 1px solid #AAA;
-   border-right    : 1px solid #AAA;
-   }
-
-tr.odd td {
-   background      : #DBF9CF;
-   border-collapse : collapse;
-   font-family     : Courier;
-   padding-left    : 0.5ex;
-   padding-right   : 0.5ex;
-   }
-
-tr.even td {
-   background      : #FFF;
-   border-collapse : collapse;
-   font-family     : Courier;
-   padding-left    : 0.5ex;
-   padding-right   : 0.5ex;
-   }
-
-td.bklask-port {
-   text-align      : right;
-   }
-
-td.bklask-arrow {
-   text-align      : left;
-   text-indent     : -5.5ex;
-   padding-left    : 6.5ex !important;
-   }
-
-a.circle {
-   padding         : 2ex;
-   border-radius   : 50%;
-   color           : #FFF;
-   text-align      : center;
-   background      : #333;
-   }
-
-a.circle {
-   text-decoration : none;
-   }
-
-a.circle:hover {
-   background      : #AAA;
-   }
-
-p.copyright {
-   text-align      : right;
-   margin-right    : 10ex;
-   }
-
-
-a.refresh {
-   background      : #333;
-   border-radius   : 1.8ex;
-   color           : white;
-   margin-left     : 2ex;
-   padding         : 0.7ex 1.8ex;
-   text-decoration : none;
-   }
-
-a.refresh:after { 
-   content         : "\00A0\21bb";
-   }
-
-a.refresh:hover {
-   background      : #AAA;
-   }
-
-img.map {
-   width           : 100%;
-   object-fit      : contain;
-   padding-bottom  : 2ex;
-   }
-
-#navbar ul {
-   list-style      : none;
-   padding         : 0;
-   margin          : 0;
-   }
-
-#navbar li {
-   background      : #333;
-   border          : 1px solid #BBB;
-   border-bottom-width     : 0;
-   border-top-left-radius  : 1.5ex;
-   border-top-right-radius : 1.5ex;
-   color           : #FFF;
-   display         : block;
-   float           : left;
-   font-family     : Verdana;
-   font-size       : 110%;
-   margin          : 0;
-   padding         : 0.8ex 1.5ex;
-   text-align      : center;
-   text-decoration : none;
-   width           : 17ex;
-   }
-
-#navbar li:hover {
-   background      : #AAA;
-   }
-
-#navbar #selected {
-   border-color    : #333;
-   position        : relative;
-   top             : 1px;
-   background      : white;
-   color           : black;
-   font-weight     : bold;
-   }
-
-#content {
-   border          : 1px solid #333;
-   clear           : both;
-   padding-left    : 0.5ex;
-   padding-right   : 0.5ex;
-   padding-top     : 0ex;
-   padding-bottom  : 1.5ex;
-   background      : white;
-   }
Index: unk/svg-pan-zoom-klask.js
===================================================================
--- /trunk/svg-pan-zoom-klask.js	(revision 413)
+++ 	(revision )
@@ -1,1900 +1,0 @@
-// svg-pan-zoom v3.5.2
-// https://github.com/ariutta/svg-pan-zoom
-// wget http://ariutta.github.io/svg-pan-zoom/dist/svg-pan-zoom.js
-(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-var svgPanZoom = require('./svg-pan-zoom.js');
-
-// UMD module definition
-(function(window, document){
-  // AMD
-  if (typeof define === 'function' && define.amd) {
-    define('svg-pan-zoom', function () {
-      return svgPanZoom;
-    });
-  // CMD
-  } else if (typeof module !== 'undefined' && module.exports) {
-    module.exports = svgPanZoom;
-
-    // Browser
-    // Keep exporting globally as module.exports is available because of browserify
-    window.svgPanZoom = svgPanZoom;
-  }
-})(window, document)
-
-},{"./svg-pan-zoom.js":4}],2:[function(require,module,exports){
-var SvgUtils = require('./svg-utilities');
-
-module.exports = {
-  enable: function(instance) {
-    // Select (and create if necessary) defs
-    var defs = instance.svg.querySelector('defs')
-    if (!defs) {
-      defs = document.createElementNS(SvgUtils.svgNS, 'defs')
-      instance.svg.appendChild(defs)
-    }
-
-    // Check for style element, and create it if it doesn't exist
-    var styleEl = defs.querySelector('style#svg-pan-zoom-controls-styles');
-    if (!styleEl) {
-      var style = document.createElementNS(SvgUtils.svgNS, 'style')
-      style.setAttribute('id', 'svg-pan-zoom-controls-styles')
-      style.setAttribute('type', 'text/css')
-      style.textContent = '.svg-pan-zoom-control { cursor: pointer; fill: black; fill-opacity: 0.333; } .svg-pan-zoom-control:hover { fill-opacity: 0.8; } .svg-pan-zoom-control-background { fill: white; fill-opacity: 0.5; } .svg-pan-zoom-control-background { fill-opacity: 0.8; }'
-      defs.appendChild(style)
-    }
-
-    // Zoom Group
-    var zoomGroup = document.createElementNS(SvgUtils.svgNS, 'g');
-    zoomGroup.setAttribute('id', 'svg-pan-zoom-controls');
-    zoomGroup.setAttribute('transform', 'translate(' + ( instance.width - 70 ) + ' ' + ( instance.height - 76 ) + ') scale(0.75)');
-    zoomGroup.setAttribute('class', 'svg-pan-zoom-control');
-
-    // Control elements
-    zoomGroup.appendChild(this._createZoomIn(instance))
-    zoomGroup.appendChild(this._createZoomReset(instance))
-    zoomGroup.appendChild(this._createZoomOut(instance))
-
-    // Finally append created element
-    instance.svg.appendChild(zoomGroup)
-
-    // Cache control instance
-    instance.controlIcons = zoomGroup
-  }
-
-, _createZoomIn: function(instance) {
-    var zoomIn = document.createElementNS(SvgUtils.svgNS, 'g');
-    zoomIn.setAttribute('id', 'svg-pan-zoom-zoom-in');
-    zoomIn.setAttribute('transform', 'translate(30.5 5) scale(0.015)');
-    zoomIn.setAttribute('class', 'svg-pan-zoom-control');
-    zoomIn.addEventListener('click', function() {instance.getPublicInstance().zoomIn()}, false)
-    zoomIn.addEventListener('touchstart', function() {instance.getPublicInstance().zoomIn()}, false)
-
-    var zoomInBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
-    zoomInBackground.setAttribute('x', '0');
-    zoomInBackground.setAttribute('y', '0');
-    zoomInBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
-    zoomInBackground.setAttribute('height', '1400');
-    zoomInBackground.setAttribute('class', 'svg-pan-zoom-control-background');
-    zoomIn.appendChild(zoomInBackground);
-
-    var zoomInShape = document.createElementNS(SvgUtils.svgNS, 'path');
-    zoomInShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z');
-    zoomInShape.setAttribute('class', 'svg-pan-zoom-control-element');
-    zoomIn.appendChild(zoomInShape);
-
-    return zoomIn
-  }
-
-, _createZoomReset: function(instance){
-    // reset
-    var resetPanZoomControl = document.createElementNS(SvgUtils.svgNS, 'g');
-    resetPanZoomControl.setAttribute('id', 'svg-pan-zoom-reset-pan-zoom');
-    resetPanZoomControl.setAttribute('transform', 'translate(5 35) scale(0.4)');
-    resetPanZoomControl.setAttribute('class', 'svg-pan-zoom-control');
-    resetPanZoomControl.addEventListener('click', function() {instance.getPublicInstance().reset()}, false);
-    resetPanZoomControl.addEventListener('touchstart', function() {instance.getPublicInstance().reset()}, false);
-
-    var resetPanZoomControlBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
-    resetPanZoomControlBackground.setAttribute('x', '2');
-    resetPanZoomControlBackground.setAttribute('y', '2');
-    resetPanZoomControlBackground.setAttribute('width', '182'); // larger than expected because the whole group is transformed to scale down
-    resetPanZoomControlBackground.setAttribute('height', '58');
-    resetPanZoomControlBackground.setAttribute('class', 'svg-pan-zoom-control-background');
-    resetPanZoomControl.appendChild(resetPanZoomControlBackground);
-
-    var resetPanZoomControlShape1 = document.createElementNS(SvgUtils.svgNS, 'path');
-    resetPanZoomControlShape1.setAttribute('d', 'M33.051,20.632c-0.742-0.406-1.854-0.609-3.338-0.609h-7.969v9.281h7.769c1.543,0,2.701-0.188,3.473-0.562c1.365-0.656,2.048-1.953,2.048-3.891C35.032,22.757,34.372,21.351,33.051,20.632z');
-    resetPanZoomControlShape1.setAttribute('class', 'svg-pan-zoom-control-element');
-    resetPanZoomControl.appendChild(resetPanZoomControlShape1);
-
-    var resetPanZoomControlShape2 = document.createElementNS(SvgUtils.svgNS, 'path');
-    resetPanZoomControlShape2.setAttribute('d', 'M170.231,0.5H15.847C7.102,0.5,0.5,5.708,0.5,11.84v38.861C0.5,56.833,7.102,61.5,15.847,61.5h154.384c8.745,0,15.269-4.667,15.269-10.798V11.84C185.5,5.708,178.976,0.5,170.231,0.5z M42.837,48.569h-7.969c-0.219-0.766-0.375-1.383-0.469-1.852c-0.188-0.969-0.289-1.961-0.305-2.977l-0.047-3.211c-0.03-2.203-0.41-3.672-1.142-4.406c-0.732-0.734-2.103-1.102-4.113-1.102h-7.05v13.547h-7.055V14.022h16.524c2.361,0.047,4.178,0.344,5.45,0.891c1.272,0.547,2.351,1.352,3.234,2.414c0.731,0.875,1.31,1.844,1.737,2.906s0.64,2.273,0.64,3.633c0,1.641-0.414,3.254-1.242,4.84s-2.195,2.707-4.102,3.363c1.594,0.641,2.723,1.551,3.387,2.73s0.996,2.98,0.996,5.402v2.32c0,1.578,0.063,2.648,0.19,3.211c0.19,0.891,0.635,1.547,1.333,1.969V48.569z M75.579,48.569h-26.18V14.022h25.336v6.117H56.454v7.336h16.781v6H56.454v8.883h19.125V48.569z M104.497,46.331c-2.44,2.086-5.887,3.129-10.34,3.129c-4.548,0-8.125-1.027-10.731-3.082s-3.909-4.879-3.909-8.473h6.891c0.224,1.578,0.662,2.758,1.316,3.539c1.196,1.422,3.246,2.133,6.15,2.133c1.739,0,3.151-0.188,4.236-0.562c2.058-0.719,3.087-2.055,3.087-4.008c0-1.141-0.504-2.023-1.512-2.648c-1.008-0.609-2.607-1.148-4.796-1.617l-3.74-0.82c-3.676-0.812-6.201-1.695-7.576-2.648c-2.328-1.594-3.492-4.086-3.492-7.477c0-3.094,1.139-5.664,3.417-7.711s5.623-3.07,10.036-3.07c3.685,0,6.829,0.965,9.431,2.895c2.602,1.93,3.966,4.73,4.093,8.402h-6.938c-0.128-2.078-1.057-3.555-2.787-4.43c-1.154-0.578-2.587-0.867-4.301-0.867c-1.907,0-3.428,0.375-4.565,1.125c-1.138,0.75-1.706,1.797-1.706,3.141c0,1.234,0.561,2.156,1.682,2.766c0.721,0.406,2.25,0.883,4.589,1.43l6.063,1.43c2.657,0.625,4.648,1.461,5.975,2.508c2.059,1.625,3.089,3.977,3.089,7.055C108.157,41.624,106.937,44.245,104.497,46.331z M139.61,48.569h-26.18V14.022h25.336v6.117h-18.281v7.336h16.781v6h-16.781v8.883h19.125V48.569z M170.337,20.14h-10.336v28.43h-7.266V20.14h-10.383v-6.117h27.984V20.14z');
-    resetPanZoomControlShape2.setAttribute('class', 'svg-pan-zoom-control-element');
-    resetPanZoomControl.appendChild(resetPanZoomControlShape2);
-
-    return resetPanZoomControl
-  }
-
-, _createZoomOut: function(instance){
-    // zoom out
-    var zoomOut = document.createElementNS(SvgUtils.svgNS, 'g');
-    zoomOut.setAttribute('id', 'svg-pan-zoom-zoom-out');
-    zoomOut.setAttribute('transform', 'translate(30.5 70) scale(0.015)');
-    zoomOut.setAttribute('class', 'svg-pan-zoom-control');
-    zoomOut.addEventListener('click', function() {instance.getPublicInstance().zoomOut()}, false);
-    zoomOut.addEventListener('touchstart', function() {instance.getPublicInstance().zoomOut()}, false);
-
-    var zoomOutBackground = document.createElementNS(SvgUtils.svgNS, 'rect'); // TODO change these background space fillers to rounded rectangles so they look prettier
-    zoomOutBackground.setAttribute('x', '0');
-    zoomOutBackground.setAttribute('y', '0');
-    zoomOutBackground.setAttribute('width', '1500'); // larger than expected because the whole group is transformed to scale down
-    zoomOutBackground.setAttribute('height', '1400');
-    zoomOutBackground.setAttribute('class', 'svg-pan-zoom-control-background');
-    zoomOut.appendChild(zoomOutBackground);
-
-    var zoomOutShape = document.createElementNS(SvgUtils.svgNS, 'path');
-    zoomOutShape.setAttribute('d', 'M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z');
-    zoomOutShape.setAttribute('class', 'svg-pan-zoom-control-element');
-    zoomOut.appendChild(zoomOutShape);
-
-    return zoomOut
-  }
-
-, disable: function(instance) {
-    if (instance.controlIcons) {
-      instance.controlIcons.parentNode.removeChild(instance.controlIcons)
-      instance.controlIcons = null
-    }
-  }
-}
-
-},{"./svg-utilities":5}],3:[function(require,module,exports){
-var SvgUtils = require('./svg-utilities')
-  , Utils = require('./utilities')
-  ;
-
-var ShadowViewport = function(viewport, options){
-  this.init(viewport, options)
-}
-
-/**
- * Initialization
- *
- * @param  {SVGElement} viewport
- * @param  {Object} options
- */
-ShadowViewport.prototype.init = function(viewport, options) {
-  // DOM Elements
-  this.viewport = viewport
-  this.options = options
-
-  // State cache
-  this.originalState = {zoom: 1, x: 0, y: 0}
-  this.activeState = {zoom: 1, x: 0, y: 0}
-
-  this.updateCTMCached = Utils.proxy(this.updateCTM, this)
-
-  // Create a custom requestAnimationFrame taking in account refreshRate
-  this.requestAnimationFrame = Utils.createRequestAnimationFrame(this.options.refreshRate)
-
-  // ViewBox
-  this.viewBox = {x: 0, y: 0, width: 0, height: 0}
-  this.cacheViewBox()
-
-  // Process CTM
-  var newCTM = this.processCTM()
-
-  // Update viewport CTM and cache zoom and pan
-  this.setCTM(newCTM)
-
-  // Update CTM in this frame
-  this.updateCTM()
-}
-
-/**
- * Cache initial viewBox value
- * If no viewBox is defined, then use viewport size/position instead for viewBox values
- */
-ShadowViewport.prototype.cacheViewBox = function() {
-  var svgViewBox = this.options.svg.getAttribute('viewBox')
-
-  if (svgViewBox) {
-    var viewBoxValues = svgViewBox.split(/[\s\,]/).filter(function(v){return v}).map(parseFloat)
-
-    // Cache viewbox x and y offset
-    this.viewBox.x = viewBoxValues[0]
-    this.viewBox.y = viewBoxValues[1]
-    this.viewBox.width = viewBoxValues[2]
-    this.viewBox.height = viewBoxValues[3]
-
-    var zoom = Math.min(this.options.width / this.viewBox.width, this.options.height / this.viewBox.height)
-
-    // Update active state
-    this.activeState.zoom = zoom
-    this.activeState.x = (this.options.width - this.viewBox.width * zoom) / 2
-    this.activeState.y = (this.options.height - this.viewBox.height * zoom) / 2
-
-    // Force updating CTM
-    this.updateCTMOnNextFrame()
-
-    this.options.svg.removeAttribute('viewBox')
-  } else {
-    this.simpleViewBoxCache()
-  }
-}
-
-/**
- * Recalculate viewport sizes and update viewBox cache
- */
-ShadowViewport.prototype.simpleViewBoxCache = function() {
-  var bBox = this.viewport.getBBox()
-
-  this.viewBox.x = bBox.x
-  this.viewBox.y = bBox.y
-  this.viewBox.width = bBox.width
-  this.viewBox.height = bBox.height
-}
-
-/**
- * Returns a viewbox object. Safe to alter
- *
- * @return {Object} viewbox object
- */
-ShadowViewport.prototype.getViewBox = function() {
-  return Utils.extend({}, this.viewBox)
-}
-
-/**
- * Get initial zoom and pan values. Save them into originalState
- * Parses viewBox attribute to alter initial sizes
- *
- * @return {CTM} CTM object based on options
- */
-ShadowViewport.prototype.processCTM = function() {
-  var newCTM = this.getCTM()
-
-  if (this.options.fit || this.options.contain) {
-    var newScale;
-    if (this.options.fit) {
-      newScale = Math.min(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
-    } else {
-      newScale = Math.max(this.options.width/this.viewBox.width, this.options.height/this.viewBox.height);
-    }
-
-    newCTM.a = newScale; //x-scale
-    newCTM.d = newScale; //y-scale
-    newCTM.e = -this.viewBox.x * newScale; //x-transform
-    newCTM.f = -this.viewBox.y * newScale; //y-transform
-  }
-
-  if (this.options.center) {
-    var offsetX = (this.options.width - (this.viewBox.width + this.viewBox.x * 2) * newCTM.a) * 0.5
-      , offsetY = (this.options.height - (this.viewBox.height + this.viewBox.y * 2) * newCTM.a) * 0.5
-
-    newCTM.e = offsetX
-    newCTM.f = offsetY
-  }
-
-  // Cache initial values. Based on activeState and fix+center opitons
-  this.originalState.zoom = newCTM.a
-  this.originalState.x = newCTM.e
-  this.originalState.y = newCTM.f
-
-  return newCTM
-}
-
-/**
- * Return originalState object. Safe to alter
- *
- * @return {Object}
- */
-ShadowViewport.prototype.getOriginalState = function() {
-  return Utils.extend({}, this.originalState)
-}
-
-/**
- * Return actualState object. Safe to alter
- *
- * @return {Object}
- */
-ShadowViewport.prototype.getState = function() {
-  return Utils.extend({}, this.activeState)
-}
-
-/**
- * Get zoom scale
- *
- * @return {Float} zoom scale
- */
-ShadowViewport.prototype.getZoom = function() {
-  return this.activeState.zoom
-}
-
-/**
- * Get zoom scale for pubilc usage
- *
- * @return {Float} zoom scale
- */
-ShadowViewport.prototype.getRelativeZoom = function() {
-  return this.activeState.zoom / this.originalState.zoom
-}
-
-/**
- * Compute zoom scale for pubilc usage
- *
- * @return {Float} zoom scale
- */
-ShadowViewport.prototype.computeRelativeZoom = function(scale) {
-  return scale / this.originalState.zoom
-}
-
-/**
- * Get pan
- *
- * @return {Object}
- */
-ShadowViewport.prototype.getPan = function() {
-  return {x: this.activeState.x, y: this.activeState.y}
-}
-
-/**
- * Return cached viewport CTM value that can be safely modified
- *
- * @return {SVGMatrix}
- */
-ShadowViewport.prototype.getCTM = function() {
-  var safeCTM = this.options.svg.createSVGMatrix()
-
-  // Copy values manually as in FF they are not itterable
-  safeCTM.a = this.activeState.zoom
-  safeCTM.b = 0
-  safeCTM.c = 0
-  safeCTM.d = this.activeState.zoom
-  safeCTM.e = this.activeState.x
-  safeCTM.f = this.activeState.y
-
-  return safeCTM
-}
-
-/**
- * Set a new CTM
- *
- * @param {SVGMatrix} newCTM
- */
-ShadowViewport.prototype.setCTM = function(newCTM) {
-  var willZoom = this.isZoomDifferent(newCTM)
-    , willPan = this.isPanDifferent(newCTM)
-
-  if (willZoom || willPan) {
-    // Before zoom
-    if (willZoom) {
-      // If returns false then cancel zooming
-      if (this.options.beforeZoom(this.getRelativeZoom(), this.computeRelativeZoom(newCTM.a)) === false) {
-        newCTM.a = newCTM.d = this.activeState.zoom
-        willZoom = false
-      } else {
-        this.updateCache(newCTM);
-        this.options.onZoom(this.getRelativeZoom())
-      }
-    }
-
-    // Before pan
-    if (willPan) {
-      var preventPan = this.options.beforePan(this.getPan(), {x: newCTM.e, y: newCTM.f})
-          // If prevent pan is an object
-        , preventPanX = false
-        , preventPanY = false
-
-      // If prevent pan is Boolean false
-      if (preventPan === false) {
-        // Set x and y same as before
-        newCTM.e = this.getPan().x
-        newCTM.f = this.getPan().y
-
-        preventPanX = preventPanY = true
-      } else if (Utils.isObject(preventPan)) {
-        // Check for X axes attribute
-        if (preventPan.x === false) {
-          // Prevent panning on x axes
-          newCTM.e = this.getPan().x
-          preventPanX = true
-        } else if (Utils.isNumber(preventPan.x)) {
-          // Set a custom pan value
-          newCTM.e = preventPan.x
-        }
-
-        // Check for Y axes attribute
-        if (preventPan.y === false) {
-          // Prevent panning on x axes
-          newCTM.f = this.getPan().y
-          preventPanY = true
-        } else if (Utils.isNumber(preventPan.y)) {
-          // Set a custom pan value
-          newCTM.f = preventPan.y
-        }
-      }
-
-      // Update willPan flag
-      // Check if newCTM is still different
-      if ((preventPanX && preventPanY) || !this.isPanDifferent(newCTM)) {
-        willPan = false
-      } else {
-        this.updateCache(newCTM);
-        this.options.onPan(this.getPan());
-      }
-    }
-
-    // Check again if should zoom or pan
-    if (willZoom || willPan) {
-      this.updateCTMOnNextFrame()
-    }
-  }
-}
-
-ShadowViewport.prototype.isZoomDifferent = function(newCTM) {
-  return this.activeState.zoom !== newCTM.a
-}
-
-ShadowViewport.prototype.isPanDifferent = function(newCTM) {
-  return this.activeState.x !== newCTM.e || this.activeState.y !== newCTM.f
-}
-
-
-/**
- * Update cached CTM and active state
- *
- * @param {SVGMatrix} newCTM
- */
-ShadowViewport.prototype.updateCache = function(newCTM) {
-  this.activeState.zoom = newCTM.a
-  this.activeState.x = newCTM.e
-  this.activeState.y = newCTM.f
-}
-
-ShadowViewport.prototype.pendingUpdate = false
-
-/**
- * Place a request to update CTM on next Frame
- */
-ShadowViewport.prototype.updateCTMOnNextFrame = function() {
-  if (!this.pendingUpdate) {
-    // Lock
-    this.pendingUpdate = true
-
-    // Throttle next update
-    this.requestAnimationFrame.call(window, this.updateCTMCached)
-  }
-}
-
-/**
- * Update viewport CTM with cached CTM
- */
-ShadowViewport.prototype.updateCTM = function() {
-  var ctm = this.getCTM()
-
-  // Updates SVG element
-  SvgUtils.setCTM(this.viewport, ctm, this.defs)
-
-  // Free the lock
-  this.pendingUpdate = false
-
-  // Notify about the update
-  if(this.options.onUpdatedCTM) {
-    this.options.onUpdatedCTM(ctm)
-  }
-}
-
-module.exports = function(viewport, options){
-  return new ShadowViewport(viewport, options)
-}
-
-},{"./svg-utilities":5,"./utilities":7}],4:[function(require,module,exports){
-var Wheel = require('./uniwheel')
-, ControlIcons = require('./control-icons')
-, Utils = require('./utilities')
-, SvgUtils = require('./svg-utilities')
-, ShadowViewport = require('./shadow-viewport')
-
-var SvgPanZoom = function(svg, options) {
-  this.init(svg, options)
-}
-
-var optionsDefaults = {
-  viewportSelector: '.svg-pan-zoom_viewport' // Viewport selector. Can be querySelector string or SVGElement
-, panEnabled: true // enable or disable panning (default enabled)
-, controlIconsEnabled: false // insert icons to give user an option in addition to mouse events to control pan/zoom (default disabled)
-, zoomEnabled: true // enable or disable zooming (default enabled)
-, dblClickZoomEnabled: true // enable or disable zooming by double clicking (default enabled)
-, mouseWheelZoomEnabled: true // enable or disable zooming by mouse wheel (default enabled)
-, preventMouseEventsDefault: true // enable or disable preventDefault for mouse events
-, zoomScaleSensitivity: 0.1 // Zoom sensitivity
-, minZoom: 0.5 // Minimum Zoom level
-, maxZoom: 10 // Maximum Zoom level
-, fit: true // enable or disable viewport fit in SVG (default true)
-, contain: false // enable or disable viewport contain the svg (default false)
-, center: true // enable or disable viewport centering in SVG (default true)
-, refreshRate: 'auto' // Maximum number of frames per second (altering SVG's viewport)
-, beforeZoom: null
-, onZoom: null
-, beforePan: null
-, onPan: null
-, customEventsHandler: null
-, eventsListenerElement: null
-, onUpdatedCTM: null
-}
-
-SvgPanZoom.prototype.init = function(svg, options) {
-  var that = this
-
-  this.svg = svg
-  this.defs = svg.querySelector('defs')
-
-  // Add default attributes to SVG
-  SvgUtils.setupSvgAttributes(this.svg)
-
-  // Set options
-  this.options = Utils.extend(Utils.extend({}, optionsDefaults), options)
-
-  // Set default state
-  this.state = 'none'
-
-  // Get dimensions
-  var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(svg)
-  this.width = boundingClientRectNormalized.width
-  this.height = boundingClientRectNormalized.height
-
-  // Init shadow viewport
-  this.viewport = ShadowViewport(SvgUtils.getOrCreateViewport(this.svg, this.options.viewportSelector), {
-    svg: this.svg
-  , width: this.width
-  , height: this.height
-  , fit: this.options.fit
-  , contain: this.options.contain
-  , center: this.options.center
-  , refreshRate: this.options.refreshRate
-  // Put callbacks into functions as they can change through time
-  , beforeZoom: function(oldScale, newScale) {
-      if (that.viewport && that.options.beforeZoom) {return that.options.beforeZoom(oldScale, newScale)}
-    }
-  , onZoom: function(scale) {
-      if (that.viewport && that.options.onZoom) {return that.options.onZoom(scale)}
-    }
-  , beforePan: function(oldPoint, newPoint) {
-      if (that.viewport && that.options.beforePan) {return that.options.beforePan(oldPoint, newPoint)}
-    }
-  , onPan: function(point) {
-      if (that.viewport && that.options.onPan) {return that.options.onPan(point)}
-    }
-  , onUpdatedCTM: function(ctm) {
-      if (that.viewport && that.options.onUpdatedCTM) {return that.options.onUpdatedCTM(ctm)}
-    }
-  })
-
-  // Wrap callbacks into public API context
-  var publicInstance = this.getPublicInstance()
-  publicInstance.setBeforeZoom(this.options.beforeZoom)
-  publicInstance.setOnZoom(this.options.onZoom)
-  publicInstance.setBeforePan(this.options.beforePan)
-  publicInstance.setOnPan(this.options.onPan)
-  publicInstance.setOnUpdatedCTM(this.options.onUpdatedCTM)
-
-  if (this.options.controlIconsEnabled) {
-    ControlIcons.enable(this)
-  }
-
-  // Init events handlers
-  this.lastMouseWheelEventTime = Date.now()
-  this.setupHandlers()
-}
-
-/**
- * Register event handlers
- */
-SvgPanZoom.prototype.setupHandlers = function() {
-  var that = this
-    , prevEvt = null // use for touchstart event to detect double tap
-    ;
-
-  this.eventListeners = {
-    // Mouse down group
-    mousedown: function(evt) {
-      var result = that.handleMouseDown(evt, prevEvt);
-      prevEvt = evt
-      return result;
-    }
-  , touchstart: function(evt) {
-      var result = that.handleMouseDown(evt, prevEvt);
-      prevEvt = evt
-      return result;
-    }
-
-    // Mouse up group
-  , mouseup: function(evt) {
-      return that.handleMouseUp(evt);
-    }
-  , touchend: function(evt) {
-      return that.handleMouseUp(evt);
-    }
-
-    // Mouse move group
-  , mousemove: function(evt) {
-      return that.handleMouseMove(evt);
-    }
-  , touchmove: function(evt) {
-      return that.handleMouseMove(evt);
-    }
-
-    // Mouse leave group
-  , mouseleave: function(evt) {
-      return that.handleMouseUp(evt);
-    }
-  , touchleave: function(evt) {
-      return that.handleMouseUp(evt);
-    }
-  , touchcancel: function(evt) {
-      return that.handleMouseUp(evt);
-    }
-  }
-
-  // Init custom events handler if available
-  if (this.options.customEventsHandler != null) { // jshint ignore:line
-    this.options.customEventsHandler.init({
-      svgElement: this.svg
-    , eventsListenerElement: this.options.eventsListenerElement
-    , instance: this.getPublicInstance()
-    })
-
-    // Custom event handler may halt builtin listeners
-    var haltEventListeners = this.options.customEventsHandler.haltEventListeners
-    if (haltEventListeners && haltEventListeners.length) {
-      for (var i = haltEventListeners.length - 1; i >= 0; i--) {
-        if (this.eventListeners.hasOwnProperty(haltEventListeners[i])) {
-          delete this.eventListeners[haltEventListeners[i]]
-        }
-      }
-    }
-  }
-
-  // Bind eventListeners
-  for (var event in this.eventListeners) {
-    // Attach event to eventsListenerElement or SVG if not available
-    (this.options.eventsListenerElement || this.svg)
-      .addEventListener(event, this.eventListeners[event], false)
-  }
-
-  // Zoom using mouse wheel
-  if (this.options.mouseWheelZoomEnabled) {
-    this.options.mouseWheelZoomEnabled = false // set to false as enable will set it back to true
-    this.enableMouseWheelZoom()
-  }
-}
-
-/**
- * Enable ability to zoom using mouse wheel
- */
-SvgPanZoom.prototype.enableMouseWheelZoom = function() {
-  if (!this.options.mouseWheelZoomEnabled) {
-    var that = this
-
-    // Mouse wheel listener
-    this.wheelListener = function(evt) {
-      return that.handleMouseWheel(evt);
-    }
-
-    // Bind wheelListener
-    Wheel.on(this.options.eventsListenerElement || this.svg, this.wheelListener, false)
-
-    this.options.mouseWheelZoomEnabled = true
-  }
-}
-
-/**
- * Disable ability to zoom using mouse wheel
- */
-SvgPanZoom.prototype.disableMouseWheelZoom = function() {
-  if (this.options.mouseWheelZoomEnabled) {
-    Wheel.off(this.options.eventsListenerElement || this.svg, this.wheelListener, false)
-    this.options.mouseWheelZoomEnabled = false
-  }
-}
-
-/**
- * Handle mouse wheel event
- *
- * @param  {Event} evt
- */
-SvgPanZoom.prototype.handleMouseWheel = function(evt) {
-  if (!this.options.zoomEnabled || this.state !== 'none') {
-    return;
-  }
-
-  if (this.options.preventMouseEventsDefault){
-    if (evt.preventDefault) {
-      evt.preventDefault();
-    } else {
-      evt.returnValue = false;
-    }
-  }
-
-  // Default delta in case that deltaY is not available
-  var delta = evt.deltaY || 1
-    , timeDelta = Date.now() - this.lastMouseWheelEventTime
-    , divider = 3 + Math.max(0, 30 - timeDelta)
-
-  // Update cache
-  this.lastMouseWheelEventTime = Date.now()
-
-  // Make empirical adjustments for browsers that give deltaY in pixels (deltaMode=0)
-  if ('deltaMode' in evt && evt.deltaMode === 0 && evt.wheelDelta) {
-    delta = evt.deltaY === 0 ? 0 :  Math.abs(evt.wheelDelta) / evt.deltaY
-  }
-
-  delta = -0.3 < delta && delta < 0.3 ? delta : (delta > 0 ? 1 : -1) * Math.log(Math.abs(delta) + 10) / divider
-
-  var inversedScreenCTM = this.svg.getScreenCTM().inverse()
-    , relativeMousePoint = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(inversedScreenCTM)
-    , zoom = Math.pow(1 + this.options.zoomScaleSensitivity, (-1) * delta); // multiplying by neg. 1 so as to make zoom in/out behavior match Google maps behavior
-
-  this.zoomAtPoint(zoom, relativeMousePoint)
-}
-
-/**
- * Zoom in at a SVG point
- *
- * @param  {SVGPoint} point
- * @param  {Float} zoomScale    Number representing how much to zoom
- * @param  {Boolean} zoomAbsolute Default false. If true, zoomScale is treated as an absolute value.
- *                                Otherwise, zoomScale is treated as a multiplied (e.g. 1.10 would zoom in 10%)
- */
-SvgPanZoom.prototype.zoomAtPoint = function(zoomScale, point, zoomAbsolute) {
-  var originalState = this.viewport.getOriginalState()
-
-  if (!zoomAbsolute) {
-    // Fit zoomScale in set bounds
-    if (this.getZoom() * zoomScale < this.options.minZoom * originalState.zoom) {
-      zoomScale = (this.options.minZoom * originalState.zoom) / this.getZoom()
-    } else if (this.getZoom() * zoomScale > this.options.maxZoom * originalState.zoom) {
-      zoomScale = (this.options.maxZoom * originalState.zoom) / this.getZoom()
-    }
-  } else {
-    // Fit zoomScale in set bounds
-    zoomScale = Math.max(this.options.minZoom * originalState.zoom, Math.min(this.options.maxZoom * originalState.zoom, zoomScale))
-    // Find relative scale to achieve desired scale
-    zoomScale = zoomScale/this.getZoom()
-  }
-
-  var oldCTM = this.viewport.getCTM()
-    , relativePoint = point.matrixTransform(oldCTM.inverse())
-    , modifier = this.svg.createSVGMatrix().translate(relativePoint.x, relativePoint.y).scale(zoomScale).translate(-relativePoint.x, -relativePoint.y)
-    , newCTM = oldCTM.multiply(modifier)
-
-  if (newCTM.a !== oldCTM.a) {
-    this.viewport.setCTM(newCTM)
-  }
-}
-
-/**
- * Zoom at center point
- *
- * @param  {Float} scale
- * @param  {Boolean} absolute Marks zoom scale as relative or absolute
- */
-SvgPanZoom.prototype.zoom = function(scale, absolute) {
-  this.zoomAtPoint(scale, SvgUtils.getSvgCenterPoint(this.svg, this.width, this.height), absolute)
-}
-
-/**
- * Zoom used by public instance
- *
- * @param  {Float} scale
- * @param  {Boolean} absolute Marks zoom scale as relative or absolute
- */
-SvgPanZoom.prototype.publicZoom = function(scale, absolute) {
-  if (absolute) {
-    scale = this.computeFromRelativeZoom(scale)
-  }
-
-  this.zoom(scale, absolute)
-}
-
-/**
- * Zoom at point used by public instance
- *
- * @param  {Float} scale
- * @param  {SVGPoint|Object} point    An object that has x and y attributes
- * @param  {Boolean} absolute Marks zoom scale as relative or absolute
- */
-SvgPanZoom.prototype.publicZoomAtPoint = function(scale, point, absolute) {
-  if (absolute) {
-    // Transform zoom into a relative value
-    scale = this.computeFromRelativeZoom(scale)
-  }
-
-  // If not a SVGPoint but has x and y then create a SVGPoint
-  if (Utils.getType(point) !== 'SVGPoint') {
-    if('x' in point && 'y' in point) {
-      point = SvgUtils.createSVGPoint(this.svg, point.x, point.y)
-    } else {
-      throw new Error('Given point is invalid')
-    }
-  }
-
-  this.zoomAtPoint(scale, point, absolute)
-}
-
-/**
- * Get zoom scale
- *
- * @return {Float} zoom scale
- */
-SvgPanZoom.prototype.getZoom = function() {
-  return this.viewport.getZoom()
-}
-
-/**
- * Get zoom scale for public usage
- *
- * @return {Float} zoom scale
- */
-SvgPanZoom.prototype.getRelativeZoom = function() {
-  return this.viewport.getRelativeZoom()
-}
-
-/**
- * Compute actual zoom from public zoom
- *
- * @param  {Float} zoom
- * @return {Float} zoom scale
- */
-SvgPanZoom.prototype.computeFromRelativeZoom = function(zoom) {
-  return zoom * this.viewport.getOriginalState().zoom
-}
-
-/**
- * Set zoom to initial state
- */
-SvgPanZoom.prototype.resetZoom = function() {
-  var originalState = this.viewport.getOriginalState()
-
-  this.zoom(originalState.zoom, true);
-}
-
-/**
- * Set pan to initial state
- */
-SvgPanZoom.prototype.resetPan = function() {
-  this.pan(this.viewport.getOriginalState());
-}
-
-/**
- * Set pan and zoom to initial state
- */
-SvgPanZoom.prototype.reset = function() {
-  this.resetZoom()
-  this.resetPan()
-}
-
-/**
- * Handle double click event
- * See handleMouseDown() for alternate detection method
- *
- * @param {Event} evt
- */
-SvgPanZoom.prototype.handleDblClick = function(evt) {
-  if (this.options.preventMouseEventsDefault) {
-    if (evt.preventDefault) {
-      evt.preventDefault()
-    } else {
-      evt.returnValue = false
-    }
-  }
-
-  // Check if target was a control button
-  if (this.options.controlIconsEnabled) {
-    var targetClass = evt.target.getAttribute('class') || ''
-    if (targetClass.indexOf('svg-pan-zoom-control') > -1) {
-      return false
-    }
-  }
-
-  var zoomFactor
-
-  if (evt.shiftKey) {
-    zoomFactor = 1/((1 + this.options.zoomScaleSensitivity) * 2) // zoom out when shift key pressed
-  } else {
-    zoomFactor = (1 + this.options.zoomScaleSensitivity) * 2
-  }
-
-  var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.svg.getScreenCTM().inverse())
-  this.zoomAtPoint(zoomFactor, point)
-}
-
-/**
- * Handle click event
- *
- * @param {Event} evt
- */
-SvgPanZoom.prototype.handleMouseDown = function(evt, prevEvt) {
-  if (this.options.preventMouseEventsDefault) {
-    if (evt.preventDefault) {
-      evt.preventDefault()
-    } else {
-      evt.returnValue = false
-    }
-  }
-
-  Utils.mouseAndTouchNormalize(evt, this.svg)
-
-  // Double click detection; more consistent than ondblclick
-  if (this.options.dblClickZoomEnabled && Utils.isDblClick(evt, prevEvt)){
-    this.handleDblClick(evt)
-  } else {
-    // Pan mode
-    this.state = 'pan'
-    this.firstEventCTM = this.viewport.getCTM()
-    this.stateOrigin = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
-  }
-}
-
-/**
- * Handle mouse move event
- *
- * @param  {Event} evt
- */
-SvgPanZoom.prototype.handleMouseMove = function(evt) {
-  if (this.options.preventMouseEventsDefault) {
-    if (evt.preventDefault) {
-      evt.preventDefault()
-    } else {
-      evt.returnValue = false
-    }
-  }
-
-  if (this.state === 'pan' && this.options.panEnabled) {
-    // Pan mode
-    var point = SvgUtils.getEventPoint(evt, this.svg).matrixTransform(this.firstEventCTM.inverse())
-      , viewportCTM = this.firstEventCTM.translate(point.x - this.stateOrigin.x, point.y - this.stateOrigin.y)
-
-    this.viewport.setCTM(viewportCTM)
-  }
-}
-
-/**
- * Handle mouse button release event
- *
- * @param {Event} evt
- */
-SvgPanZoom.prototype.handleMouseUp = function(evt) {
-  if (this.options.preventMouseEventsDefault) {
-    if (evt.preventDefault) {
-      evt.preventDefault()
-    } else {
-      evt.returnValue = false
-    }
-  }
-
-  if (this.state === 'pan') {
-    // Quit pan mode
-    this.state = 'none'
-  }
-}
-
-/**
- * Adjust viewport size (only) so it will fit in SVG
- * Does not center image
- */
-SvgPanZoom.prototype.fit = function() {
-  var viewBox = this.viewport.getViewBox()
-    , newScale = Math.min(this.width/viewBox.width, this.height/viewBox.height)
-
-  this.zoom(newScale, true)
-}
-
-/**
- * Adjust viewport size (only) so it will contain the SVG
- * Does not center image
- */
-SvgPanZoom.prototype.contain = function() {
-  var viewBox = this.viewport.getViewBox()
-    , newScale = Math.max(this.width/viewBox.width, this.height/viewBox.height)
-
-  this.zoom(newScale, true)
-}
-
-/**
- * Adjust viewport pan (only) so it will be centered in SVG
- * Does not zoom/fit/contain image
- */
-SvgPanZoom.prototype.center = function() {
-  var viewBox = this.viewport.getViewBox()
-    , offsetX = (this.width - (viewBox.width + viewBox.x * 2) * this.getZoom()) * 0.5
-    , offsetY = (this.height - (viewBox.height + viewBox.y * 2) * this.getZoom()) * 0.5
-
-  this.getPublicInstance().pan({x: offsetX, y: offsetY})
-}
-
-/**
- * Update content cached BorderBox
- * Use when viewport contents change
- */
-SvgPanZoom.prototype.updateBBox = function() {
-  this.viewport.simpleViewBoxCache()
-}
-
-/**
- * Pan to a rendered position
- *
- * @param  {Object} point {x: 0, y: 0}
- */
-SvgPanZoom.prototype.pan = function(point) {
-  var viewportCTM = this.viewport.getCTM()
-  viewportCTM.e = point.x
-  viewportCTM.f = point.y
-  this.viewport.setCTM(viewportCTM)
-}
-
-/**
- * Relatively pan the graph by a specified rendered position vector
- *
- * @param  {Object} point {x: 0, y: 0}
- */
-SvgPanZoom.prototype.panBy = function(point) {
-  var viewportCTM = this.viewport.getCTM()
-  viewportCTM.e += point.x
-  viewportCTM.f += point.y
-  this.viewport.setCTM(viewportCTM)
-}
-
-/**
- * Get pan vector
- *
- * @return {Object} {x: 0, y: 0}
- */
-SvgPanZoom.prototype.getPan = function() {
-  var state = this.viewport.getState()
-
-  return {x: state.x, y: state.y}
-}
-
-/**
- * Recalculates cached svg dimensions and controls position
- */
-SvgPanZoom.prototype.resize = function() {
-  // Get dimensions
-  var boundingClientRectNormalized = SvgUtils.getBoundingClientRectNormalized(this.svg)
-  this.width = boundingClientRectNormalized.width
-  this.height = boundingClientRectNormalized.height
-
-  // Recalculate original state
-  var viewport = this.viewport
-  viewport.options.width = this.width
-  viewport.options.height = this.height
-  viewport.processCTM()
-
-  // Reposition control icons by re-enabling them
-  if (this.options.controlIconsEnabled) {
-    this.getPublicInstance().disableControlIcons()
-    this.getPublicInstance().enableControlIcons()
-  }
-}
-
-/**
- * Unbind mouse events, free callbacks and destroy public instance
- */
-SvgPanZoom.prototype.destroy = function() {
-  var that = this
-
-  // Free callbacks
-  this.beforeZoom = null
-  this.onZoom = null
-  this.beforePan = null
-  this.onPan = null
-  this.onUpdatedCTM = null
-
-  // Destroy custom event handlers
-  if (this.options.customEventsHandler != null) { // jshint ignore:line
-    this.options.customEventsHandler.destroy({
-      svgElement: this.svg
-    , eventsListenerElement: this.options.eventsListenerElement
-    , instance: this.getPublicInstance()
-    })
-  }
-
-  // Unbind eventListeners
-  for (var event in this.eventListeners) {
-    (this.options.eventsListenerElement || this.svg)
-      .removeEventListener(event, this.eventListeners[event], false)
-  }
-
-  // Unbind wheelListener
-  this.disableMouseWheelZoom()
-
-  // Remove control icons
-  this.getPublicInstance().disableControlIcons()
-
-  // Reset zoom and pan
-  this.reset()
-
-  // Remove instance from instancesStore
-  instancesStore = instancesStore.filter(function(instance){
-    return instance.svg !== that.svg
-  })
-
-  // Delete options and its contents
-  delete this.options
-
-  // Delete viewport to make public shadow viewport functions uncallable
-  delete this.viewport
-
-  // Destroy public instance and rewrite getPublicInstance
-  delete this.publicInstance
-  delete this.pi
-  this.getPublicInstance = function(){
-    return null
-  }
-}
-
-/**
- * Returns a public instance object
- *
- * @return {Object} Public instance object
- */
-SvgPanZoom.prototype.getPublicInstance = function() {
-  var that = this
-
-  // Create cache
-  if (!this.publicInstance) {
-    this.publicInstance = this.pi = {
-      // Pan
-      enablePan: function() {that.options.panEnabled = true; return that.pi}
-    , disablePan: function() {that.options.panEnabled = false; return that.pi}
-    , isPanEnabled: function() {return !!that.options.panEnabled}
-    , pan: function(point) {that.pan(point); return that.pi}
-    , panBy: function(point) {that.panBy(point); return that.pi}
-    , getPan: function() {return that.getPan()}
-      // Pan event
-    , setBeforePan: function(fn) {that.options.beforePan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
-    , setOnPan: function(fn) {that.options.onPan = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
-      // Zoom and Control Icons
-    , enableZoom: function() {that.options.zoomEnabled = true; return that.pi}
-    , disableZoom: function() {that.options.zoomEnabled = false; return that.pi}
-    , isZoomEnabled: function() {return !!that.options.zoomEnabled}
-    , enableControlIcons: function() {
-        if (!that.options.controlIconsEnabled) {
-          that.options.controlIconsEnabled = true
-          ControlIcons.enable(that)
-        }
-        return that.pi
-      }
-    , disableControlIcons: function() {
-        if (that.options.controlIconsEnabled) {
-          that.options.controlIconsEnabled = false;
-          ControlIcons.disable(that)
-        }
-        return that.pi
-      }
-    , isControlIconsEnabled: function() {return !!that.options.controlIconsEnabled}
-      // Double click zoom
-    , enableDblClickZoom: function() {that.options.dblClickZoomEnabled = true; return that.pi}
-    , disableDblClickZoom: function() {that.options.dblClickZoomEnabled = false; return that.pi}
-    , isDblClickZoomEnabled: function() {return !!that.options.dblClickZoomEnabled}
-      // Mouse wheel zoom
-    , enableMouseWheelZoom: function() {that.enableMouseWheelZoom(); return that.pi}
-    , disableMouseWheelZoom: function() {that.disableMouseWheelZoom(); return that.pi}
-    , isMouseWheelZoomEnabled: function() {return !!that.options.mouseWheelZoomEnabled}
-      // Zoom scale and bounds
-    , setZoomScaleSensitivity: function(scale) {that.options.zoomScaleSensitivity = scale; return that.pi}
-    , setMinZoom: function(zoom) {that.options.minZoom = zoom; return that.pi}
-    , setMaxZoom: function(zoom) {that.options.maxZoom = zoom; return that.pi}
-      // Zoom event
-    , setBeforeZoom: function(fn) {that.options.beforeZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
-    , setOnZoom: function(fn) {that.options.onZoom = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
-      // Zooming
-    , zoom: function(scale) {that.publicZoom(scale, true); return that.pi}
-    , zoomBy: function(scale) {that.publicZoom(scale, false); return that.pi}
-    , zoomAtPoint: function(scale, point) {that.publicZoomAtPoint(scale, point, true); return that.pi}
-    , zoomAtPointBy: function(scale, point) {that.publicZoomAtPoint(scale, point, false); return that.pi}
-    , zoomIn: function() {this.zoomBy(1 + that.options.zoomScaleSensitivity); return that.pi}
-    , zoomOut: function() {this.zoomBy(1 / (1 + that.options.zoomScaleSensitivity)); return that.pi}
-    , getZoom: function() {return that.getRelativeZoom()}
-      // CTM update
-    , setOnUpdatedCTM: function(fn) {that.options.onUpdatedCTM = fn === null ? null : Utils.proxy(fn, that.publicInstance); return that.pi}
-      // Reset
-    , resetZoom: function() {that.resetZoom(); return that.pi}
-    , resetPan: function() {that.resetPan(); return that.pi}
-    , reset: function() {that.reset(); return that.pi}
-      // Fit, Contain and Center
-    , fit: function() {that.fit(); return that.pi}
-    , contain: function() {that.contain(); return that.pi}
-    , center: function() {that.center(); return that.pi}
-      // Size and Resize
-    , updateBBox: function() {that.updateBBox(); return that.pi}
-    , resize: function() {that.resize(); return that.pi}
-    , getSizes: function() {
-        return {
-          width: that.width
-        , height: that.height
-        , realZoom: that.getZoom()
-        , viewBox: that.viewport.getViewBox()
-        }
-      }
-      // Destroy
-    , destroy: function() {that.destroy(); return that.pi}
-    }
-  }
-
-  return this.publicInstance
-}
-
-/**
- * Stores pairs of instances of SvgPanZoom and SVG
- * Each pair is represented by an object {svg: SVGSVGElement, instance: SvgPanZoom}
- *
- * @type {Array}
- */
-var instancesStore = []
-
-var svgPanZoom = function(elementOrSelector, options){
-  var svg = Utils.getSvg(elementOrSelector)
-
-  if (svg === null) {
-    return null
-  } else {
-    // Look for existent instance
-    for(var i = instancesStore.length - 1; i >= 0; i--) {
-      if (instancesStore[i].svg === svg) {
-        return instancesStore[i].instance.getPublicInstance()
-      }
-    }
-
-    // If instance not found - create one
-    instancesStore.push({
-      svg: svg
-    , instance: new SvgPanZoom(svg, options)
-    })
-
-    // Return just pushed instance
-    return instancesStore[instancesStore.length - 1].instance.getPublicInstance()
-  }
-}
-
-module.exports = svgPanZoom;
-
-},{"./control-icons":2,"./shadow-viewport":3,"./svg-utilities":5,"./uniwheel":6,"./utilities":7}],5:[function(require,module,exports){
-var Utils = require('./utilities')
-  , _browser = 'unknown'
-  ;
-
-// http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
-if (/*@cc_on!@*/false || !!document.documentMode) { // internet explorer
-  _browser = 'ie';
-}
-
-module.exports = {
-  svgNS:  'http://www.w3.org/2000/svg'
-, xmlNS:  'http://www.w3.org/XML/1998/namespace'
-, xmlnsNS:  'http://www.w3.org/2000/xmlns/'
-, xlinkNS:  'http://www.w3.org/1999/xlink'
-, evNS:  'http://www.w3.org/2001/xml-events'
-
-  /**
-   * Get svg dimensions: width and height
-   *
-   * @param  {SVGSVGElement} svg
-   * @return {Object}     {width: 0, height: 0}
-   */
-, getBoundingClientRectNormalized: function(svg) {
-    if (svg.clientWidth && svg.clientHeight) {
-      return {width: svg.clientWidth, height: svg.clientHeight}
-    } else if (!!svg.getBoundingClientRect()) {
-      return svg.getBoundingClientRect();
-    } else {
-      throw new Error('Cannot get BoundingClientRect for SVG.');
-    }
-  }
-
-  /**
-   * Gets g element with class of "viewport" or creates it if it doesn't exist
-   *
-   * @param  {SVGSVGElement} svg
-   * @return {SVGElement}     g (group) element
-   */
-, getOrCreateViewport: function(svg, selector) {
-    var viewport = null
-
-    if (Utils.isElement(selector)) {
-      viewport = selector
-    } else {
-      viewport = svg.querySelector(selector)
-    }
-
-    // Check if there is just one main group in SVG
-    if (!viewport) {
-      var childNodes = Array.prototype.slice.call(svg.childNodes || svg.children).filter(function(el){
-        return el.nodeName !== 'defs' && el.nodeName !== '#text'
-      })
-
-      // Node name should be SVGGElement and should have no transform attribute
-      // Groups with transform are not used as viewport because it involves parsing of all transform possibilities
-      if (childNodes.length === 1 && childNodes[0].nodeName === 'g' && childNodes[0].getAttribute('transform') === null) {
-        viewport = childNodes[0]
-      }
-    }
-
-    // If no favorable group element exists then create one
-    if (!viewport) {
-      var viewportId = 'viewport-' + new Date().toISOString().replace(/\D/g, '');
-      viewport = document.createElementNS(this.svgNS, 'g');
-      viewport.setAttribute('id', viewportId);
-
-      // Internet Explorer (all versions?) can't use childNodes, but other browsers prefer (require?) using childNodes
-      var svgChildren = svg.childNodes || svg.children;
-      if (!!svgChildren && svgChildren.length > 0) {
-        for (var i = svgChildren.length; i > 0; i--) {
-          // Move everything into viewport except defs
-          if (svgChildren[svgChildren.length - i].nodeName !== 'defs') {
-            viewport.appendChild(svgChildren[svgChildren.length - i]);
-          }
-        }
-      }
-      svg.appendChild(viewport);
-    }
-
-    // Parse class names
-    var classNames = [];
-    if (viewport.getAttribute('class')) {
-      classNames = viewport.getAttribute('class').split(' ')
-    }
-
-    // Set class (if not set already)
-    if (!~classNames.indexOf('svg-pan-zoom_viewport')) {
-      classNames.push('svg-pan-zoom_viewport')
-      viewport.setAttribute('class', classNames.join(' '))
-    }
-
-    return viewport
-  }
-
-  /**
-   * Set SVG attributes
-   *
-   * @param  {SVGSVGElement} svg
-   */
-  , setupSvgAttributes: function(svg) {
-    // Setting default attributes
-    svg.setAttribute('xmlns', this.svgNS);
-    svg.setAttributeNS(this.xmlnsNS, 'xmlns:xlink', this.xlinkNS);
-    svg.setAttributeNS(this.xmlnsNS, 'xmlns:ev', this.evNS);
-
-    // Needed for Internet Explorer, otherwise the viewport overflows
-    if (svg.parentNode !== null) {
-      var style = svg.getAttribute('style') || '';
-      if (style.toLowerCase().indexOf('overflow') === -1) {
-        svg.setAttribute('style', 'overflow: hidden; ' + style);
-      }
-    }
-  }
-
-/**
- * How long Internet Explorer takes to finish updating its display (ms).
- */
-, internetExplorerRedisplayInterval: 300
-
-/**
- * Forces the browser to redisplay all SVG elements that rely on an
- * element defined in a 'defs' section. It works globally, for every
- * available defs element on the page.
- * The throttling is intentionally global.
- *
- * This is only needed for IE. It is as a hack to make markers (and 'use' elements?)
- * visible after pan/zoom when there are multiple SVGs on the page.
- * See bug report: https://connect.microsoft.com/IE/feedback/details/781964/
- * also see svg-pan-zoom issue: https://github.com/ariutta/svg-pan-zoom/issues/62
- */
-, refreshDefsGlobal: Utils.throttle(function() {
-    var allDefs = document.querySelectorAll('defs');
-    var allDefsCount = allDefs.length;
-    for (var i = 0; i < allDefsCount; i++) {
-      var thisDefs = allDefs[i];
-      thisDefs.parentNode.insertBefore(thisDefs, thisDefs);
-    }
-  }, this.internetExplorerRedisplayInterval)
-
-  /**
-   * Sets the current transform matrix of an element
-   *
-   * @param {SVGElement} element
-   * @param {SVGMatrix} matrix  CTM
-   * @param {SVGElement} defs
-   */
-, setCTM: function(element, matrix, defs) {
-    var that = this
-      , s = 'matrix(' + matrix.a + ',' + matrix.b + ',' + matrix.c + ',' + matrix.d + ',' + matrix.e + ',' + matrix.f + ')';
-
-    element.setAttributeNS(null, 'transform', s);
-    if ('transform' in element.style) {
-      element.style.transform = s;
-    } else if ('-ms-transform' in element.style) {
-      element.style['-ms-transform'] = s;
-    } else if ('-webkit-transform' in element.style) {
-      element.style['-webkit-transform'] = s;
-    }
-
-    // IE has a bug that makes markers disappear on zoom (when the matrix "a" and/or "d" elements change)
-    // see http://stackoverflow.com/questions/17654578/svg-marker-does-not-work-in-ie9-10
-    // and http://srndolha.wordpress.com/2013/11/25/svg-line-markers-may-disappear-in-internet-explorer-11/
-    if (_browser === 'ie' && !!defs) {
-      // this refresh is intended for redisplaying the SVG during zooming
-      defs.parentNode.insertBefore(defs, defs);
-      // this refresh is intended for redisplaying the other SVGs on a page when panning a given SVG
-      // it is also needed for the given SVG itself, on zoomEnd, if the SVG contains any markers that
-      // are located under any other element(s).
-      window.setTimeout(function() {
-        that.refreshDefsGlobal();
-      }, that.internetExplorerRedisplayInterval);
-    }
-  }
-
-  /**
-   * Instantiate an SVGPoint object with given event coordinates
-   *
-   * @param {Event} evt
-   * @param  {SVGSVGElement} svg
-   * @return {SVGPoint}     point
-   */
-, getEventPoint: function(evt, svg) {
-    var point = svg.createSVGPoint()
-
-    Utils.mouseAndTouchNormalize(evt, svg)
-
-    point.x = evt.clientX
-    point.y = evt.clientY
-
-    return point
-  }
-
-  /**
-   * Get SVG center point
-   *
-   * @param  {SVGSVGElement} svg
-   * @return {SVGPoint}
-   */
-, getSvgCenterPoint: function(svg, width, height) {
-    return this.createSVGPoint(svg, width / 2, height / 2)
-  }
-
-  /**
-   * Create a SVGPoint with given x and y
-   *
-   * @param  {SVGSVGElement} svg
-   * @param  {Number} x
-   * @param  {Number} y
-   * @return {SVGPoint}
-   */
-, createSVGPoint: function(svg, x, y) {
-    var point = svg.createSVGPoint()
-    point.x = x
-    point.y = y
-
-    return point
-  }
-}
-
-},{"./utilities":7}],6:[function(require,module,exports){
-// uniwheel 0.1.2 (customized)
-// A unified cross browser mouse wheel event handler
-// https://github.com/teemualap/uniwheel
-
-module.exports = (function(){
-
-  //Full details: https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel
-
-  var prefix = "", _addEventListener, _removeEventListener, onwheel, support, fns = [];
-
-  // detect event model
-  if ( window.addEventListener ) {
-    _addEventListener = "addEventListener";
-    _removeEventListener = "removeEventListener";
-  } else {
-    _addEventListener = "attachEvent";
-    _removeEventListener = "detachEvent";
-    prefix = "on";
-  }
-
-  // detect available wheel event
-  support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
-            document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
-            "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
-
-
-  function createCallback(element,callback,capture) {
-
-    var fn = function(originalEvent) {
-
-      !originalEvent && ( originalEvent = window.event );
-
-      // create a normalized event object
-      var event = {
-        // keep a ref to the original event object
-        originalEvent: originalEvent,
-        target: originalEvent.target || originalEvent.srcElement,
-        type: "wheel",
-        deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
-        deltaX: 0,
-        delatZ: 0,
-        preventDefault: function() {
-          originalEvent.preventDefault ?
-            originalEvent.preventDefault() :
-            originalEvent.returnValue = false;
-        }
-      };
-
-      // calculate deltaY (and deltaX) according to the event
-      if ( support == "mousewheel" ) {
-        event.deltaY = - 1/40 * originalEvent.wheelDelta;
-        // Webkit also support wheelDeltaX
-        originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
-      } else {
-        event.deltaY = originalEvent.detail;
-      }
-
-      // it's time to fire the callback
-      return callback( event );
-
-    };
-
-    fns.push({
-      element: element,
-      fn: fn,
-      capture: capture
-    });
-
-    return fn;
-  }
-
-  function getCallback(element,capture) {
-    for (var i = 0; i < fns.length; i++) {
-      if (fns[i].element === element && fns[i].capture === capture) {
-        return fns[i].fn;
-      }
-    }
-    return function(){};
-  }
-
-  function removeCallback(element,capture) {
-    for (var i = 0; i < fns.length; i++) {
-      if (fns[i].element === element && fns[i].capture === capture) {
-        return fns.splice(i,1);
-      }
-    }
-  }
-
-  function _addWheelListener( elem, eventName, callback, useCapture ) {
-
-    var cb;
-
-    if (support === "wheel") {
-      cb = callback;
-    } else {
-      cb = createCallback(elem,callback,useCapture);
-    }
-
-    elem[ _addEventListener ]( prefix + eventName, cb, useCapture || false );
-
-  }
-
-  function _removeWheelListener( elem, eventName, callback, useCapture ) {
-
-    var cb;
-
-    if (support === "wheel") {
-      cb = callback;
-    } else {
-      cb = getCallback(elem,useCapture);
-    }
-
-    elem[ _removeEventListener ]( prefix + eventName, cb, useCapture || false );
-
-    removeCallback(elem,useCapture);
-
-  }
-
-  function addWheelListener( elem, callback, useCapture ) {
-    _addWheelListener( elem, support, callback, useCapture );
-
-    // handle MozMousePixelScroll in older Firefox
-    if( support == "DOMMouseScroll" ) {
-        _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture);
-    }
-  }
-
-  function removeWheelListener(elem,callback,useCapture){
-    _removeWheelListener(elem,support,callback,useCapture);
-
-    // handle MozMousePixelScroll in older Firefox
-    if( support == "DOMMouseScroll" ) {
-        _removeWheelListener(elem, "MozMousePixelScroll", callback, useCapture);
-    }
-  }
-
-  return {
-    on: addWheelListener,
-    off: removeWheelListener
-  };
-
-})();
-
-},{}],7:[function(require,module,exports){
-module.exports = {
-  /**
-   * Extends an object
-   *
-   * @param  {Object} target object to extend
-   * @param  {Object} source object to take properties from
-   * @return {Object}        extended object
-   */
-  extend: function(target, source) {
-    target = target || {};
-    for (var prop in source) {
-      // Go recursively
-      if (this.isObject(source[prop])) {
-        target[prop] = this.extend(target[prop], source[prop])
-      } else {
-        target[prop] = source[prop]
-      }
-    }
-    return target;
-  }
-
-  /**
-   * Checks if an object is a DOM element
-   *
-   * @param  {Object}  o HTML element or String
-   * @return {Boolean}   returns true if object is a DOM element
-   */
-, isElement: function(o){
-    return (
-      o instanceof HTMLElement || o instanceof SVGElement || o instanceof SVGSVGElement || //DOM2
-      (o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string')
-    );
-  }
-
-  /**
-   * Checks if an object is an Object
-   *
-   * @param  {Object}  o Object
-   * @return {Boolean}   returns true if object is an Object
-   */
-, isObject: function(o){
-    return Object.prototype.toString.call(o) === '[object Object]';
-  }
-
-  /**
-   * Checks if variable is Number
-   *
-   * @param  {Integer|Float}  n
-   * @return {Boolean}   returns true if variable is Number
-   */
-, isNumber: function(n) {
-    return !isNaN(parseFloat(n)) && isFinite(n);
-  }
-
-  /**
-   * Search for an SVG element
-   *
-   * @param  {Object|String} elementOrSelector DOM Element or selector String
-   * @return {Object|Null}                   SVG or null
-   */
-, getSvg: function(elementOrSelector) {
-    var element
-      , svg;
-
-    if (!this.isElement(elementOrSelector)) {
-      // If selector provided
-      if (typeof elementOrSelector === 'string' || elementOrSelector instanceof String) {
-        // Try to find the element
-        element = document.querySelector(elementOrSelector)
-
-        if (!element) {
-          throw new Error('Provided selector did not find any elements. Selector: ' + elementOrSelector)
-          return null
-        }
-      } else {
-        throw new Error('Provided selector is not an HTML object nor String')
-        return null
-      }
-    } else {
-      element = elementOrSelector
-    }
-
-    if (element.tagName.toLowerCase() === 'svg') {
-      svg = element;
-    } else {
-      if (element.tagName.toLowerCase() === 'object') {
-        svg = element.contentDocument.documentElement;
-      } else {
-        if (element.tagName.toLowerCase() === 'embed') {
-          svg = element.getSVGDocument().documentElement;
-        } else {
-          if (element.tagName.toLowerCase() === 'img') {
-            throw new Error('Cannot script an SVG in an "img" element. Please use an "object" element or an in-line SVG.');
-          } else {
-            throw new Error('Cannot get SVG.');
-          }
-          return null
-        }
-      }
-    }
-
-    return svg
-  }
-
-  /**
-   * Attach a given context to a function
-   * @param  {Function} fn      Function
-   * @param  {Object}   context Context
-   * @return {Function}           Function with certain context
-   */
-, proxy: function(fn, context) {
-    return function() {
-      return fn.apply(context, arguments)
-    }
-  }
-
-  /**
-   * Returns object type
-   * Uses toString that returns [object SVGPoint]
-   * And than parses object type from string
-   *
-   * @param  {Object} o Any object
-   * @return {String}   Object type
-   */
-, getType: function(o) {
-    return Object.prototype.toString.apply(o).replace(/^\[object\s/, '').replace(/\]$/, '')
-  }
-
-  /**
-   * If it is a touch event than add clientX and clientY to event object
-   *
-   * @param  {Event} evt
-   * @param  {SVGSVGElement} svg
-   */
-, mouseAndTouchNormalize: function(evt, svg) {
-    // If no clientX then fallback
-    if (evt.clientX === void 0 || evt.clientX === null) {
-      // Fallback
-      evt.clientX = 0
-      evt.clientY = 0
-
-      // If it is a touch event
-      if (evt.touches !== void 0 && evt.touches.length) {
-        if (evt.touches[0].clientX !== void 0) {
-          evt.clientX = evt.touches[0].clientX
-          evt.clientY = evt.touches[0].clientY
-        } else if (evt.touches[0].pageX !== void 0) {
-          var rect = svg.getBoundingClientRect();
-
-          evt.clientX = evt.touches[0].pageX - rect.left
-          evt.clientY = evt.touches[0].pageY - rect.top
-        }
-      // If it is a custom event
-      } else if (evt.originalEvent !== void 0) {
-        if (evt.originalEvent.clientX !== void 0) {
-          evt.clientX = evt.originalEvent.clientX
-          evt.clientY = evt.originalEvent.clientY
-        }
-      }
-    }
-  }
-
-  /**
-   * Check if an event is a double click/tap
-   * TODO: For touch gestures use a library (hammer.js) that takes in account other events
-   * (touchmove and touchend). It should take in account tap duration and traveled distance
-   *
-   * @param  {Event}  evt
-   * @param  {Event}  prevEvt Previous Event
-   * @return {Boolean}
-   */
-, isDblClick: function(evt, prevEvt) {
-    // Double click detected by browser
-    if (evt.detail === 2) {
-      return true;
-    }
-    // Try to compare events
-    else if (prevEvt !== void 0 && prevEvt !== null) {
-      var timeStampDiff = evt.timeStamp - prevEvt.timeStamp // should be lower than 250 ms
-        , touchesDistance = Math.sqrt(Math.pow(evt.clientX - prevEvt.clientX, 2) + Math.pow(evt.clientY - prevEvt.clientY, 2))
-
-      return timeStampDiff < 250 && touchesDistance < 10
-    }
-
-    // Nothing found
-    return false;
-  }
-
-  /**
-   * Returns current timestamp as an integer
-   *
-   * @return {Number}
-   */
-, now: Date.now || function() {
-    return new Date().getTime();
-  }
-
-  // From underscore.
-  // Returns a function, that, when invoked, will only be triggered at most once
-  // during a given window of time. Normally, the throttled function will run
-  // as much as it can, without ever going more than once per `wait` duration;
-  // but if you'd like to disable the execution on the leading edge, pass
-  // `{leading: false}`. To disable execution on the trailing edge, ditto.
-// jscs:disable
-// jshint ignore:start
-, throttle: function(func, wait, options) {
-    var that = this;
-    var context, args, result;
-    var timeout = null;
-    var previous = 0;
-    if (!options) options = {};
-    var later = function() {
-      previous = options.leading === false ? 0 : that.now();
-      timeout = null;
-      result = func.apply(context, args);
-      if (!timeout) context = args = null;
-    };
-    return function() {
-      var now = that.now();
-      if (!previous && options.leading === false) previous = now;
-      var remaining = wait - (now - previous);
-      context = this;
-      args = arguments;
-      if (remaining <= 0 || remaining > wait) {
-        clearTimeout(timeout);
-        timeout = null;
-        previous = now;
-        result = func.apply(context, args);
-        if (!timeout) context = args = null;
-      } else if (!timeout && options.trailing !== false) {
-        timeout = setTimeout(later, remaining);
-      }
-      return result;
-    };
-  }
-// jshint ignore:end
-// jscs:enable
-
-  /**
-   * Create a requestAnimationFrame simulation
-   *
-   * @param  {Number|String} refreshRate
-   * @return {Function}
-   */
-, createRequestAnimationFrame: function(refreshRate) {
-    var timeout = null
-
-    // Convert refreshRate to timeout
-    if (refreshRate !== 'auto' && refreshRate < 60 && refreshRate > 1) {
-      timeout = Math.floor(1000 / refreshRate)
-    }
-
-    if (timeout === null) {
-      return window.requestAnimationFrame || requestTimeout(33)
-    } else {
-      return requestTimeout(timeout)
-    }
-  }
-}
-
-/**
- * Create a callback that will execute after a given timeout
- *
- * @param  {Function} timeout
- * @return {Function}
- */
-function requestTimeout(timeout) {
-  return function(callback) {
-    window.setTimeout(callback, timeout)
-  }
-}
-
-},{}]},{},[1]);
Index: unk/sys-description.sample.txt
===================================================================
--- /trunk/sys-description.sample.txt	(revision 413)
+++ 	(revision )
@@ -1,107 +1,0 @@
-# From https://github.com/mtoshi/sysdescrparser/blob/master/samples/sample_data.json
-
-A10:
- - AX Series Advanced Traffic Manager AX1000, Advanced Core OS (ACOS) version 2.4.3-p6,
- - AX Series Advanced Traffic Manager AX1000, Advanced Core OS (ACOS) version 2.2.4-p8,
-
-ARISTA:
- - Arista Networks EOS version 4.12.7.1 running on an Arista Networks DCS-7150S-24
- - Arista Networks EOS version 4.6.0 running on an Arista Networks DCS-7124S
- - Arista Networks EOS version 4.14.2F running on an Arista Networks DCS-7504
-
-BROCADE:
- - Brocade Communications Systems, Inc. FESX624+2XG, IronWare Version 07.3.00cT3e1 Compiled on Apr 25 2012 at 17:01:00 labeled as SXS07300c
- - Brocade NetIron CES, IronWare Version V5.2.0cT183 Compiled on Oct 28 2011 at 02:58:44 labeled as V5.2.00c
- - Brocade NetIron MLX (System Mode: MLX), IronWare Version V5.4.0cT163 Compiled on Mar 25 2013 at 17:08:16 labeled as V5.4.00c
- - Brocade Backbone (System Mode: MLX), IronWare Version V5.1.0dT163 Compiled on Aug 19 2011 at 19:00:39 labeled as V5.1.00d
- - Brocade MLXe (System Mode: MLX), IronWare Version V5.3.0T163 Compiled on Jan  3 2012 at 18:01:00 labeled as V5.3.00b460
- - Brocade Communications Systems, Inc. ServerIron ADX 1008-1-PREM, TrafficWork Version 12.4.00T401
- - Brocade Communications Systems, Inc. ServerIron ADX 4000, TrafficWork Version 12.3.01T403
-
-CISCO:
- - Cisco Internetwork Operating System Software ..IOS (tm) C1700 Software (C1700-Y-M), Version 12.2(4)YB, EARLY DEPLOYMENT RELEASE SOFTWARE (fc1)..Synched to technology version 12.2(6.8)T2..TAC Support: http://www.cisco.com/tac..Copyright (c) 1986-2002 by ci
- - Cisco Internetwork Operating System Software ..IOS (tm) C2600 Software (C2600-I-M), Version 12.0(3)T3,  RELEASE SOFTWARE (fc1)..Copyright (c) 1986-1999 by cisco Systems, Inc...Compiled Thu 15-Apr-99 15:41 by kpma
- - Cisco IOS Software, C1700 Software (C1700-ADVSECURITYK9-M), Version 12.3(11)YZ2, RELEASE SOFTWARE (fc2)..Technical Support: http://www.cisco.com/techsupport..Copyright (c) 1986-2007 by Cisco Systems, Inc...Compiled Wed 08-Aug-07 19:22 by dchih
- - Cisco IOS Software, c7600s72033_rp Software (c7600s72033_rp-ADVIPSERVICESK9-M), Version 12.2(33)SRC5, RELEASE SOFTWARE (fc2)
- - Cisco IOS Software, Catalyst 4500 L3 Switch Software (cat4500e-ENTSERVICESK9-M), Version 12.2(50)SG3, RELEASE SOFTWARE (fc2)
- - Cisco NX-OS(tm) cgr1000, Software (cgr1000-uk9), Version 5.2(1)CG2(1), Interim version 5.2(1)CG2(0.167), RELEASE SOFTWARE Copyright (c) 2002-2011 by Cisco Systems, Inc. Compiled 1/1/2012 1:00:00
- - Cisco NX-OS(tm) m9100, Software (m9100-s2ek9-mz), Version 4.1(1c), RELEASE SOFTWARE Copyright (c) 2002-2008 by Cisco Systems, Inc. Compiled 11/24/2008 18:00:00
- - Cisco NX-OS(tm) n3000, Software (n3000-uk9), Version 5.0(3)U1(2), Interim version 5.0(3)U1(1.319), RELEASE SOFTWARE Copyright (c) 2002-2011 by Cisco Systems, Inc. Device Manager Version 5.2(1), Compiled 7/10/2011 6:00:00
- - Cisco NX-OS(tm) n5000, Software (n5000-uk9), Version 4.1(3)N1(1a), RELEASE SOFTWARE Copyright (c) 2002-2008 by Cisco Systems, Inc. Device Manager Version 4.2(1), Compiled 9/17/2009 19:00:00
- - Cisco IOS XR Software (Cisco ASR9K Series), Version 4.2.3[Default] Copyright (c) 2013 by Cisco Systems, Inc.
- - Cisco IOS XR Software (Cisco 12816/PRP), Version 4.3.2[Default] Copyright (c) 2014 by Cisco Systems, Inc.
- - Cisco IOS XR Software (Cisco 12404/PRP), Version 3.6.0[00] Copyright (c) 2007 by Cisco Systems, Inc.
-
-CITRIX:
- - NetScaler NS9.0: Build 69.5, Date: Jun 12 2009, 06:00:53
- - NetScaler NS9.1: Build 97.3.cl, Date: Aug 19 2009, 13:03:59
-
-DELL:
- - Dell Networking N2048, 6.2.0.5, Linux 3.6.5-1289203e
- - Dell Networking N4032F, 6.1.1.7, Linux 2.6.32.9
- - Dell Networking N4064F, 6.3.1.13, Linux 3.7.10-20383c08
- - PowerConnect 7024, 5.1.10.1, VxWorks 6.6
-
-EXTREME:
- - ExtremeXOS version 12.0.3.16 v1203b16 by release-manager on Tue Apr 8 01:08:39 PDT 2008
- - ExtremeWare X0S version 11.2.0.15 v1120b15 by release-manager on Fri Apr 1 18:40:23 PST 2005
- - ExtremeXOS version 12.1.4.2 v1214b2 by release-manager on Wed Dec 9 13:32:31 PST 2009
- - ExtremeXOS version 11.6.3.5 v1163b5 by release-manager on Sun Sep 9 10:46:16 PDT 2007
-
-FOUNDRY:
- - Foundry Networks, Inc. Backbone, IronWare Version 06.6.36T41 Compiled on Jul 20 2001 at 21:38:29 labeled as BIS06636
- - Foundry Networks, Inc. BigIron 15000 Router, IronWare Version 08.0.01vT53 Compiled on Feb 26 2010 at 20:07:21 labeled as B2P08001v
- - Foundry Networks, Inc. BigIron MG8, IronWare Version V2.1.0bT103 Compiled on Apr 23 2005 at 13:32:29 labeled as V2.1.00b
- - Foundry Networks, Inc. BigIron RX, IronWare Version V2.7.2mT143 Compiled on Apr 03 2012 at 19:38:35 labeled as V2.7.02m
- - Foundry Networks, Inc. FES9604, IronWare Version 04.0.00Tc1 Compiled on Dec 18 2007 at 11:24:02 labeled as FEL04000
- - Foundry Networks, Inc. FESX424HF-PREM, IronWare Version 04.3.00T3e3 Compiled on Nov 14 2008 at 15:23:55 labeled as SXR04300
- - Foundry Networks, Inc. NetIron 800 Router, IronWare Version 08.0.01vT53 Compiled on Feb 26 2010 at 20:07:21 labeled as B2P08001v
- - Foundry Networks, Inc. NetIron CES, IronWare Version V3.9.0T183 Compiled on Jul 23 2009 at 14:18:43 labeled as V3.9.00
-
-HP:
- - HP J4813A ProCurve Switch 2524, revision F.05.34, ROM F.02.01  (/sw/code/build/info(s02))
- - HP J4121A ProCurve Switch 4000M, revision C.09.28, ROM C.06.01 (/sw/code/build/vgro(c09))
- - ProCurve J4900B Switch 2626, revision H.08.98, ROM H.08.02 (/sw/code/build/fish(ts_08_5))
- - HP J4900B ProCurve Switch 2626, revision H.08.69, ROM H.08.02 (/sw/code/build/fish(ts_08_5))
- - ProCurve J9022A Switch 2810-48G, revision N.11.15, ROM N.10.01 (/sw/code/build/bass(bh2))
- - ProCurve J9021A Switch 2810-24G, revision N.11.06, ROM N.10.01 (/sw/code/build/bass(bh2))
- - ProCurve J9145A 2910al-24G Switch, revision W.14.03, ROM W.14.04 (/sw/code/build/sbm(t4a_RC3))
- - HPJ3298A HP ProCurve Switch 212M, revision D.05.04, ROM D.05.01 (/sw/code/build/srao(f98))
- - HP J9625A E2620-24-PoEP Switch, revision RA.15.05.0006, ROM RA.15.10 (/sw/code/build/xform(RA_15_05)) (Formerly ProCurve)
- - ProCurve J8692A Switch 3500yl-24G, revision K.12.57, ROM K.12.12 (/sw/code/build/btm(t2g))
- - HP J8692A Switch E3500yl-24G, revision K.15.05.0002, ROM K.15.13 (/sw/code/build/btm(K_15_05)) (Formerly ProCurve)
- - HP Comware Platform Software, Software Version 5.20 Release 2208P01  HP A5120-24G EI Switch with 2 Interface Slots  Copyright (c) 2010-2011 Hewlett-Packard Development Company, L.P.
- - HP Comware Platform Software, Software Version 5.20 Release 2208P01  HP A5120-48G EI Switch with 2 Interface Slots  Copyright (c) 2010-2011 Hewlett-Packard Development Company, L.P.  
- - HP Comware Platform Software, Software Version 5.20.99 Release 2220P02  HP A5500-24G EI Switch with 2 Interface Slots  Copyright (c) 2010-2013 Hewlett-Packard Development Company, L.P. 
- - HP Comware Platform Software, Software Version 5.20 Release 2208P01  HP A5500-24G-SFP EI Switch with 2 Interface Slots  Copyright (c) 2010-2011 Hewlett-Packard Development Company, L.P.
- - HPE Comware Platform Software, Software Version 7.1.045, Release 1122P02  HPE 5510 24G SFP 4SFP+ HI 1-slot Switch JH149A  Copyright (c) 2010-2017 Hewlett Packard Enterprise Development LP
-
-JUNIPER:
- - Juniper Networks, Inc. ex2200-24t-4g internet router, kernel JUNOS 10.2R1.8 #0: 2010-05-27 20:13:49 UTC
- - Juniper Networks, Inc. ex2200-48t-4g internet router, kernel JUNOS 10.2R1.8 #0: 2010-05-27 20:13:49 UTC
- - Juniper Networks, Inc. ex2200-c-12t-2g internet router, kernel JUNOS 11.3R1.7 #0: 2011-08-30 11:49:21 UTC
- - Juniper Networks, Inc. ex3200-24p internet router, kernel JUNOS 11.1R3.5 #0: 2011-06-25 01:18:46 UTC
- - Juniper Networks, Inc. ex4200-24f internet router, kernel JUNOS 10.0S1.1 #0: 2010-01-08 06:28:28 UTC
- - Juniper Networks, Inc. ex4200-24t internet router, kernel JUNOS 10.2R1.8 #0: 2010-05-27 21:05:57 UTC
- - Juniper Networks, Inc. ex4500-40f internet router, kernel JUNOS 11.1R3.5 #0: 2011-06-25 01:18:46 UTC
- - Juniper Networks, Inc. j2300 internet router, kernel JUNOS 8.5-20080528.0 #0: 2008-05-28 06:18:51 UTC
- - Juniper Networks, Inc. j6300 internet router, kernel JUNOS 8.1R1.5 #0: 2006-11-02 00 Build date: 2006-11-02 00:27:54 UTC
- - Juniper Networks, Inc. jsr2320 internet router, kernel JUNOS 9.2R1.10 #0: 2008-08-07 06:45:07 UTC
- - Juniper Networks, Inc. m10 internet router, kernel JUNOS 7.6R3.6 #0: 2006-11-03 04 Build date: 2006-11-03 04:23:40 UTC
- - Juniper Networks, Inc. m20 internet router, kernel JUNOS 6.3R2.2 #0: 2004-08-06 01 Build date: 2004-08-06 01:25:20 UTC
- - Juniper Networks, Inc. m120 internet router, kernel JUNOS 10.0S14 #0: 2011-03-25 07:25:26 UTC
- - Juniper Networks, Inc. mx480 internet router, kernel JUNOS 11.2R1.10 #0: 2011-07-29 07:15:34 UTC
- - Juniper Networks, Inc. srx1400 internet router, kernel JUNOS 10.4R3.4 #0: 2011-03-19 22:06:23 UTC
- - Juniper Networks, Inc. ERX-700 Edge Routing Switch SW Version : (11.1.0 release-0.0 [BuildId 12065]) Build Date : April 20, 2010 20:41 Copyright
- - Juniper Networks, Inc. RX1400 Edge Switch Router SW Version : (10.3.2 patch-0.2 [BuildId 12841]) Build Date : February 3, 2011 16:26 Copyright
-
-MICROSENS:
- - MICROSENS G6 Switch
-
-NEXANS:
- - GigaSwitch V3 TP SFP-I 48V ES3 (HW3/ENHANCED/SECURITY/V4.10C)
- - GigaSwitch V3 TP SFP-I 48/54VDC ES3 (HW3/ENHANCED/SECURITY/V4.14W)
- - GigaSwitch V3 TP(PSE+) SFP-I 48/54V ES3 (HW3/ENHANCED/SECURITY/V4.14W)
- - GigaSwitch V3 TP SFP-I 48/54VDC ES3 (HW3-F21-P06-OFFICE-V5.02R)
- - GigaSwitch V5 TP SFP-VI 54VDC (HW5-F40-P07-OFFICE-V5.01fw)
- - GigaSwitch V5 TP(PSE+) SFP-2VI 54VDC (HW5-F40-P07-OFFICE-V5.01fn)
