diff --git a/.gitattributes b/.gitattributes
index 50f39b7..8191892 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,8 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text eol=lf
*.py text eol=lf
+*.rst text eol=lf
+windows* text eol=crlf
*.zip binary
*.dmg binary
diff --git a/.gitignore b/.gitignore
index 6747499..1777019 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,12 @@
+downloads/tmp
*.tmp
*.pyc
*.data
+
Thumbs.db
hosts
.DS_Store
+
+.idea/
+data/ipv4_mods/*.hosts
+doc/build/
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 0c7d39e..0000000
--- a/README.md
+++ /dev/null
@@ -1,141 +0,0 @@
-Hosts Setup Utility
-===================
-
-
-
-Introduction
-------------
-
-Due to the Chinese government is using the GFW blocking the web access to some
-world famous sites like Googel+, twitter, Facebook, and Wikipedia etc., we
-made this hosts tool to help people to get through the Great Firewall.
-
-Hosts Setup Utility provides basic tools to manage the hosts file on operating
-systems with a desktop environment. Users could use these tool to modify the
-hosts file in order to visit specified websites blocked by Chinese government.
-Tools for users to backup/restore hosts files is also provided.
-
-The home page of this project is .
-
-You can also visit the project page on Google Code to get our latest news
-.
-
-
-License
--------
-
-Licensed under the GNU General Public License, version 3. You should
-have received a copy of the GNU General Public License along with
-this program. If not, see .
-
-
-Usage
------
-
-* Windows(x86/x64): run hostsutl.exe from the binary excutables package to get
- started.
-
- - "Run as Administrator" is needed for operations to change the hosts
- file on Windows Vista or newer.
-
-* Mac OS X: run HostsUtl application from the binary excutables package to get
- started.
-
- - Because of the locale problem with py2app, the automatic language
- selection may not work correctly on Mac OS with binary excutables.
- You can just choose the language on your on choice.
-
-* Linux/X11(Source code): run command "python hostsutl.py" to get started.
-
- - All platforms with python and the PyQT4 extension could use this
- - method to run the source code.
- - A desktop environment with QT and python is needed only for
- Linux/X11 users.
- - "sudo" is needed for operations to change the hosts file.
-
-(Note if the programe is not running with privileges to modify the hosts file,
-a warning message would be shown and you could only do operations like backup
-hosts file and update the local data file.)
-
-
-Requirements
-------------
-
-* Microsoft Windows 2000 or newer for Windows users.
-
-* Mac OS X 10.6 or newer for Macintosh users.
-
-* Linux/X11 desktop with QT for Linux/X11 users.
-
-* Python 2.6/2.7 with PyQT4 extension for develop.
-
-* py2exe or py2app would be required while making binary excutables for
- specified platforms.
-
-
-Available Modules
------------------
-
-* hostsutl.py - contains main parts of Hosts Setup Utility.
-
-* qthostsui.py - contains UI class for the main dialog of Hosts Setup Utility.
-
-* qthosts_rc.py - contains images used by the main dialog.
-
-* retrievedata.py - contains tools to read data from the local hosts data
- file.
-
-* utilities.py - contains basic utilities used by Hosts Setup Utility.
-
-
-Tools for Developers
---------------------
-
-* _build.py - contains tools to make packages for different platforms.
-
- Usage: _build.py [type]
- Options:
- type define the platform to make package for. Optional choices
- could be: py2exe, py2app, py2tar, py2source
- py2exe - Make binary excutables for Windows. The
- py2app - Make binary excutables for Mac OS X. The
- operations of this option depends on the py2app
- distutils extension.
- py2app - Make source code packages for X11 users.
- py2source - Make source code packages for developers.
-
-* _pylupdate4.py : contains tools to update the language files for UI.
-
-* _pyuic4.py : contains tools update the UI code from UI design.
-
-
-The Rest of the Distribution
-----------------------------
-
-* lang/ - This directory contains language files for Hosts Setup Utility.
- The *.qm files would be included in distribution packages.
-
-* img/ - This directory contains images and Icons used by Hosts Setup
- Utility.
- The files in this directory would on be included source code package
- for developers.
-
-* mac_res/ - This directory contains resources to make excutables binaries for
- Mac OS X.
- The files in this directory would on be included source code package
- for developers.
-
-* theme/ - This directory contains QT stylesheet files of the program.
- The stylesheet files (*.qss) are used to create the user interface of
- Hosts Setup Utility.
-
-* hostsutl.pro - Project file for QT.
-
-* qthosts.qrc - Resource file for main dialog designed by QT.
-
-* qthostsui.ui - UI project file for the main dialog.
-
-
-Author/Maintainer
------------------
-huhamhire
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..92cebc0
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,167 @@
+###################
+Hosts Setup Utility
+###################
+
+Introduction
+============
+Since the governments of some countries are using technical methods blocking
+internet access to some websites and web service providers which includes some
+world famous sites like Google, YouTube, twitter, Facebook, and Wikipedia
+etc., we designed this tiny utility in order to help people getting through
+the Internet blockade.
+
+`Hosts Setup Utility` provides basic tools to manage the hosts file on current
+operating systems. It also provides both support for Graphical Desktop
+environment with Graphical User Interface (GUI) and CLI environment with
+Text-based User Interface (TUI).
+
+Users could use these tool to modify the hosts to visit specified websites or
+services blocked by ISP/government. Functions which help users to
+backup/restore hosts files are also provided.
+
+`Hosts Setup Utility` is designed by `huhamhire-hosts team`. For further
+information, please visit our `website `_.
+
+
+Developer Documentation
+=======================
+Developers could find our project documentation by visiting
+``_.
+
+This documentation contains descriptions of modules and functions which are
+used in the current version of `Hosts Setup Utility`. These documents also
+provides some optional methods for developers to help improve this tool.
+
+
+License
+=======
+Licensed under the GNU General Public License, version 3. You should have
+received a copy of the GNU General Public License along with this program.
+If not, see ``_.
+
+
+System Requirements
+===================
+Here are the system requirements needed for using `Hosts Setup Utility`.
+
+
+Graphical User Interface (GUI)
+------------------------------
+System requirements to run `Hosts Setup Utility` on Graphical Desktop are
+listed here:
+
+* Microsoft Windows 2000 or newer for Windows users.
+
+* Mac OS X 10.6 or newer for Macintosh users.
+
+* Linux/X11 desktop with Python 2 and PyQt4 for Linux/X11 users.
+
+* Internet access is required for retrieving the latest hosts data file.
+
+ - On some linux distributions, pre-built packages of PyQt4 can be
+ found in software repositories. For example, you can install PyQt4 on
+ a debian distribution simplly using:
+
+ .. code-block:: bash
+
+ apt-get install python-qt4
+
+
+More requirements are needed for developers:
+
+* Python 2.6/2.7 with PyQt4 extension for developers.
+
+* py2exe or py2app would be required while making binary excutables for
+ specified platforms.
+
+
+Text-based User Interface (TUI)
+-------------------------------
+Any devices with `Python 2` and `Python Standard Library` INSTALLED could run
+`Hosts Setup Utility` in TUI mode from a 80x24 terminal. In addition to this,
+TUI mode could also be operated via SSH on remote devices/machines/servers.
+All you need is a system with Python 2 installed.
+
+
+Get Started
+===========
+Since `Hosts Setup Utility` supports both Graphical Desktop environment with
+Graphical User Interface (GUI) and CLI/terminal environment with Text-based
+User Interface (TUI), users could the way they would like to launch this tool.
+
+However, GUI mode is highly recommended because several features like
+backup/restore hosts file are still not supported in TUI mode currently.
+
+* If the program is not running with privileges to modify the hosts
+ file, a warning message would be shown and you could only do operations
+ like backup hosts file and update the local data file. Plus, TUI mode
+ could not get started in this condition.
+
+
+Graphical User Interface (GUI) Mode
+-----------------------------------
+* Windows(x86/x64): Run hoststool.exe from the binary excutables package to
+ get started.
+
+ - "Run as Administrator" is needed for operations to change the
+ hosts file on Windows Vista or newer.
+
+* Mac OS X: Run HostsUtl application from the binary excutables package to get
+ started.
+
+ - Because of the locale problem with py2app, the automatic language
+ selection may not work correctly on Mac OS with binary executable
+ files. You can just choose the language on your on choice.
+
+* Linux/X11(Source code): Run command "python hoststool.py" to get started.
+
+ - All platforms with Python and the PyQt4 could use this method to run
+ with the source code.
+ - A desktop environment with PyQt4 and python is needed only for
+ Linux/X11 users to start a GUI Session.
+
+
+Text-based User Interface (TUI) Mode
+-----------------------------------
+* Windows Excutable(x86/x64):
+
+ #. Start a command line(could be `cmd` or `Power Shell`).
+
+ - "Run as Administrator" is needed for operations to change the
+ hosts file on Windows Vista or newer.
+
+ #. Change directory to the folder contains binary executable files. of
+ `Hosts Setup Utility`.
+
+ #. Run ``hoststool_tui.exe`` with an argument ``-t`` from the directory to
+ get started.
+
+* Python Source Code:
+
+ `Python Source Code` is very easy to be started through any terminals on any
+ operating systems.
+
+ #. Change your directory to the source script.
+
+ #. Run ``python hoststool.py -t`` in the terminal. Of course, wirte
+ privileges to access the hosts file on current system is required. If
+ not, a warning message box would show up and then terminate current
+ session.
+
+
+User Customized Hosts
+=====================
+
+Users are allowed to add customized hosts list as an independent module to
+make a hosts file. All you need to do is create a simple text file named
+``custom.hosts`` in the working directory, and put your own hosts entries
+into this file. Then you would find a `Customized Hosts` option in the
+function list.
+
+ * Non-ASCII characters are not recommended to be put into the
+ customized hosts file.
+
+
+Author/Maintainer
+=================
+huhamhire ``_
diff --git a/_pyuic4.py b/__version__.py
similarity index 51%
rename from _pyuic4.py
rename to __version__.py
index 97d6cbf..cf7cba5 100644
--- a/_pyuic4.py
+++ b/__version__.py
@@ -1,9 +1,9 @@
-#!/usr/bin/python
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# _pyuic4.py : Tools update the UI code from UI design
+# __version__.py : Version info for Hosts Setup Tool.
#
-# Copyleft (C) 2013 - huhamhire hosts team
+# Copyleft (C) 2014 - huhamhire hosts team
# =====================================================================
# Licensed under the GNU General Public License, version 3. You should
# have received a copy of the GNU General Public License along with
@@ -14,12 +14,6 @@
# PURPOSE.
# =====================================================================
-import os
-for root, dirs, files in os.walk('.'):
- for file in files:
- if file.endswith('.ui'):
- os.system('pyuic4 -o %s.py -x %s' \
- % (file.rsplit('.', 1)[0], file))
- elif file.endswith('.qrc'):
- os.system('pyrcc4 -o %s_rc.py %s' \
- % (file.rsplit('.', 1)[0], file))
+__version__ = "1.9.8"
+__release__ = "beta"
+__revision__ = "$Id$"
\ No newline at end of file
diff --git a/_build.py b/_build.py
index 5ec3ee1..a9cf3ff 100644
--- a/_build.py
+++ b/_build.py
@@ -3,7 +3,7 @@
#
# _build.py : Tools to make packages for different platforms
#
-# Copyleft (C) 2013 - huhamhire hosts team
+# Copyleft (C) 2014 - huhamhire hosts team
# =====================================================================
# Licensed under the GNU General Public License, version 3. You should
# have received a copy of the GNU General Public License along with
@@ -14,16 +14,15 @@
# PURPOSE.
# =====================================================================
-__version__ = "0.9.0"
__author__ = "huhamhire "
import os
import sys
import shutil
-from hostsutl import __version__
+from __version__ import __version__
-SCRIPT = "hostsutl.py"
+SCRIPT = "hoststool.py"
SCRIPT_DIR = os.getcwd() + '/'
RELEASE_DIR = "../release/"
@@ -32,10 +31,10 @@
VERSION = __version__
DESCRIPTION = "HostsUtl - Hosts Setup Utility"
AUTHOR = "Hamhire Hu"
-AUTHOR_EMAIL ="develop@huhamhire.com",
+AUTHOR_EMAIL = "hosts@huhamhire.com",
LICENSE = "Public Domain, Python, BSD, GPLv3 (see LICENSE)",
URL = "https://hosts.huhamhire.com",
-CLASSIFIERS = [
+CLASSIFIERS = [
"Development Status :: 4 - Beta",
"Environment :: MacOS X",
"Environment :: Win32 (MS Windows)",
@@ -60,49 +59,70 @@
"Topic :: System :: Networking",
"Topic :: Software Development :: Documentation",
"Topic :: Text Processing",
- "Topic :: Utilities",
+ "Topic :: CommonUtil",
]
DATA_FILES = [
- ("lang", [
- "lang/en_US.qm",
- "lang/zh_CN.qm",
- "lang/zh_TW.qm",
- ]
- ),
- ("theme", [
- "theme/darkdefault.qss",
- ]
- ),
- "LICENSE",
- "README.md",
- "network.conf",
+ ("gui/lang", [
+ "gui/lang/en_US.qm",
+ "gui/lang/zh_CN.qm",
+ "gui/lang/zh_TW.qm",
+ ]),
+ ("gui/theme", [
+ "gui/theme/default.qss",
+ ]),
+ (".", [
+ "LICENSE",
+ "README.rst",
+ "network.conf",
+ ]),
]
if sys.argv > 1:
tar_flag = 0
+ includes = []
+ excludes = []
+ file_path = lambda rel_path: SCRIPT_DIR + rel_path
if sys.argv[1] == "py2tar":
# Pack up script package for Linux users
- file_path = lambda rel_path: SCRIPT_DIR + rel_path
includes = [
- "*.py", "lang/*.qm", "theme/*.qss", "LICENSE", "README.md",
- "network.conf"]
- excludes = ["_*.py", ".gitattributes"]
+ "*.py",
+ "gui/lang/*.qm",
+ "gui/theme/*.qss",
+ "*/*.py",
+ "LICENSE",
+ "README.rst",
+ "network.conf",
+ ]
+ excludes = [
+ "_build.py",
+ "_pylupdate4.py",
+ "_pyuic4.py",
+ ".gitattributes",
+ ".gitignore",
+ ]
ex_files = []
- prefix = "HostsUtl-x11-gpl-"
+ prefix = "HostsTool-x11-gpl-"
tar_flag = 1
elif sys.argv[1] == "py2source":
# Pack up source package for Linux users
- file_path = lambda rel_path: SCRIPT_DIR + rel_path
includes = ["*"]
- excludes = [".gitattributes"]
+ excludes = [
+ ".gitattributes",
+ ".gitignore",
+ "hostslist.data",
+ ]
ex_files = []
- prefix = "HostsUtl-source-gpl-"
+ prefix = "HostsTool-source-gpl-"
tar_flag = 1
+ else:
+ prefix = "Error"
+ ex_files = []
if tar_flag:
import glob
import tarfile
+
TAR_NAME = prefix + VERSION + ".tar.gz"
RELEASE_PATH = RELEASE_DIR + TAR_NAME
if not os.path.exists(RELEASE_DIR):
@@ -117,24 +137,26 @@
files = glob.glob(file_path(name_format))
for src_file in files:
if src_file not in ex_files:
- tar_path = os.path.join(prefix + VERSION, src_file[rel_len:])
+ tar_path = os.path.join(prefix + VERSION,
+ src_file[rel_len:])
tar.add(src_file, tar_path)
- print src_file
+ print "compressing: %s" % src_file
tar.close()
exit(1)
-from utilities import Utilities
-system = Utilities.check_platform()[0]
+from util import CommonUtil
+
+system = CommonUtil.check_platform()[0]
if system == "Windows":
# Build binary executables for Windows
import struct
import zipfile
from distutils.core import setup
-
import py2exe
+
# Set working directories
WORK_DIR = SCRIPT_DIR + "work/"
- DIR_NAME = "HostsUtl"
+ DIR_NAME = "HostsTool"
DIST_DIR = WORK_DIR + DIR_NAME + '/'
WIN_OPTIONS = {
"includes": ["sip"],
@@ -149,23 +171,32 @@
shutil.rmtree(DIST_DIR)
# Build Executable
print " Building Executable ".center(78, '=')
+ EXE_NAME = SCRIPT.split(".")[0]
setup(
- name = NAME,
- version = VERSION,
- options = {"py2exe": WIN_OPTIONS},
- windows = [
+ name=NAME,
+ version=VERSION,
+ options={"py2exe": WIN_OPTIONS},
+ console=[
+ {"script": SCRIPT,
+ "dest_base": "hoststool_tui",
+ "uac_info": "highestAvailable",
+ },
+ ],
+ windows=[
{"script": SCRIPT,
- "icon_resources": [(1, "img/hosts_utl.ico")]
+ "icon_resources": [(1, "res/img/icons/hosts_utl.ico")],
+ "dest_base": EXE_NAME,
+ "uac_info": "highestAvailable",
},
],
- description = DESCRIPTION,
- author = AUTHOR,
- author_email = AUTHOR_EMAIL,
- license = LICENSE,
- url = URL,
- zipfile = None,
- data_files = DATA_FILES,
- classifiers = CLASSIFIERS,
+ description=DESCRIPTION,
+ author=AUTHOR,
+ author_email=AUTHOR_EMAIL,
+ license=LICENSE,
+ url=URL,
+ zipfile="lib/shared.lib",
+ data_files=DATA_FILES,
+ classifiers=CLASSIFIERS,
)
# Clean work directory after build
shutil.rmtree(SCRIPT_DIR + "build/")
@@ -175,6 +206,8 @@
PLAT = "x64"
elif struct.calcsize("P") * 8 == 32:
PLAT = "x86"
+ else:
+ PLAT = "unknown"
DIR_NAME = DIR_NAME + '-win-gpl-' + VERSION + '-' + PLAT
ZIP_NAME = DIR_NAME + ".zip"
ZIP_FILE = WORK_DIR + ZIP_NAME
@@ -202,13 +235,13 @@
from setuptools import setup
# Set working directories
WORK_DIR = SCRIPT_DIR + "work/"
- RES_DIR = SCRIPT_DIR + "mac_res/"
- APP_NAME = "HostsUtl.app"
+ RES_DIR = SCRIPT_DIR + "res/mac/"
+ APP_NAME = "HostsTool.app"
APP_PATH = WORK_DIR + APP_NAME
DIST_DIR = APP_PATH + "/Contents/"
# Set build configuration
MAC_OPTIONS = {
- "iconfile": "img/hosts_utl.icns",
+ "iconfile": "res/img/icons/hosts_utl.icns",
"includes": ["sip", "PyQt4.QtCore", "PyQt4.QtGui"],
"excludes": [
"PyQt4.QtDBus",
@@ -233,45 +266,45 @@
"plist": {
"CFBundleAllowMixedLocalizations": True,
"CFBundleSignature": "hamh",
- "CFBundleIdentifier": "org.pythonmac.huhamhire.HostsUtl",
- "NSHumanReadableCopyright": "(C) 2013, Huhamhire hosts Team"}
+ "CFBundleIdentifier": "org.pythonmac.huhamhire.HostsTool",
+ "NSHumanReadableCopyright": "(C) 2014, huhamhire hosts Team"}
}
# Clean work space before build
if os.path.exists(APP_PATH):
shutil.rmtree(APP_PATH)
if not os.path.exists(WORK_DIR):
os.mkdir(WORK_DIR)
- # Make daemon APP
+ # Make daemon APP
OSAC_CMD = "osacompile -o %s %sHostsUtl.scpt" % (APP_PATH, RES_DIR)
os.system(OSAC_CMD)
# Build APP
print " Building Application ".center(78, '=')
setup(
- app = [SCRIPT],
- name = NAME,
- version = VERSION,
- options = {"py2app": MAC_OPTIONS},
- setup_requires = ["py2app"],
- description = DESCRIPTION,
- author = AUTHOR,
- author_email = AUTHOR_EMAIL,
- license = LICENSE,
- url = URL,
- data_files = DATA_FILES,
- classifiers = CLASSIFIERS,
+ app=[SCRIPT],
+ name=NAME,
+ version=VERSION,
+ options={"py2app": MAC_OPTIONS},
+ setup_requires=["py2app"],
+ description=DESCRIPTION,
+ author=AUTHOR,
+ author_email=AUTHOR_EMAIL,
+ license=LICENSE,
+ url=URL,
+ data_files=DATA_FILES,
+ classifiers=CLASSIFIERS,
)
# Clean work directory after build
os.remove(DIST_DIR + "Resources/applet.icns")
shutil.copy2(
- SCRIPT_DIR + "img/hosts_utl.icns",
+ SCRIPT_DIR + "res/img/icons/hosts_utl.icns",
DIST_DIR + "Resources/applet.icns")
shutil.copy2(RES_DIR + "Info.plist", DIST_DIR + "Info.plist")
shutil.rmtree(SCRIPT_DIR + "build/")
# Pack APP to DMG file
VDMG_DIR = WORK_DIR + "package_vdmg/"
DMG_TMP = WORK_DIR + "pack_tmp.dmg"
- DMG_RES_DIR = RES_DIR + "dmg_resource/"
- VOL_NAME = "HostsUtl"
+ DMG_RES_DIR = RES_DIR + "dmg/"
+ VOL_NAME = "HostsTool"
DMG_NAME = VOL_NAME + "-mac-gpl-" + VERSION + ".dmg"
DMG_PATH = WORK_DIR + DMG_NAME
# Clean work space before pack up
@@ -281,7 +314,7 @@
os.remove(DMG_TMP)
if os.path.isfile(DMG_PATH):
os.remove(DMG_PATH)
- # Prepare files in DMG package
+ # Prepare files in DMG package
os.mkdir(VDMG_DIR)
shutil.move(APP_PATH, VDMG_DIR)
os.symlink("/Applications", VDMG_DIR + " ")
diff --git a/curseshosts.py b/curseshosts.py
deleted file mode 100644
index fcfe278..0000000
--- a/curseshosts.py
+++ /dev/null
@@ -1,609 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# hcurses.py:
-#
-# Copyleft (C) 2013 - huhamhire
-# =====================================================================
-# Licensed under the GNU General Public License, version 3. You should
-# have received a copy of the GNU General Public License along with
-# this program. If not, see .
-# =====================================================================
-
-__version__ = '1.9.7'
-__revision__ = "$Id$"
-__author__ = "huhamhire "
-
-__all__ = [ 'HostsCursesUI' ]
-
-import curses
-import locale
-
-import os, sys
-import socket
-
-import math
-import urllib
-import json
-
-from zipfile import BadZipfile
-
-from retrievedata import RetrieveData, make_hosts
-from utilities import Utilities
-
-class HostsCursesUI(object):
- __stdscr = ''
- __title = "HOSTS SETUP UTILITY"
- __copyleft = "v%s Copyleft 2011-2013, Huhamhire-hosts Team" % __version__
-
- colorpairs = [(curses.COLOR_WHITE, curses.COLOR_BLUE),
- (curses.COLOR_WHITE, curses.COLOR_RED),
- (curses.COLOR_YELLOW, curses.COLOR_BLUE),
- (curses.COLOR_BLUE, curses.COLOR_WHITE),
- (curses.COLOR_WHITE, curses.COLOR_WHITE),
- (curses.COLOR_BLACK, curses.COLOR_WHITE),
- (curses.COLOR_GREEN, curses.COLOR_WHITE),
- (curses.COLOR_WHITE, curses.COLOR_BLACK),
- (curses.COLOR_RED, curses.COLOR_WHITE),]
- ops_keys = [curses.KEY_F5, curses.KEY_F6, curses.KEY_F10]
- hotkeys = [curses.KEY_UP, curses.KEY_DOWN, 10, 32]
- func_items = [[], []]
- func_selec = [[], []]
- settings = [["Server", 0, []],
- ["IP Version", 0, ["IPv4", "IPv6"]]]
- funckeys = [["", "Select Item"], ["Tab", "Select Field"],
- ["Enter", "Set Item"], ["F5", "Check Update"],
- ["F6", "Get Update"], ["F10", "Apply Changes"],
- ["Esc", "Exit"]]
- subtitles = [["Configure Settings", (1, 2)], ["Status", (8, 2)],
- ["Hosts File", (13, 2)], ["Select Functions", (1, 28)]]
- statusinfo = [["Connection", "N/A", "GREEN"], ["OS", "N/A", "GREEN"]]
- hostsinfo = {"Version": "N/A", "Release": "N/A", "Latest": "N/A"}
- platform = []
-
- filename = "hostslist.data"
- infofile = "hostsinfo.json"
-
- item_sup = 0
- item_inf = 0
-
- def __init__(self):
- locale.setlocale(locale.LC_ALL, '')
- self.__stdscr = curses.initscr()
- curses.start_color()
- curses.noecho()
- curses.cbreak()
- curses.curs_set(0)
- # Set colors
- curses.use_default_colors()
- for i, color in enumerate(self.colorpairs):
- curses.init_pair(i + 1, *color)
-
- def banner(self):
- screen = self.__stdscr.subwin(2, 80, 0, 0)
- screen.bkgd(' ', curses.color_pair(1))
- # Set local variable
- title = curses.A_NORMAL
- title += curses.A_BOLD
- normal = curses.color_pair(4)
- # Print title
- screen.addstr(0, 0, self.__title.center(79), title)
- screen.addstr(1, 0, "Setup".center(10), normal)
- screen.refresh()
-
- def footer(self):
- screen = self.__stdscr.subwin(1, 80, 23, 0)
- screen.bkgd(' ', curses.color_pair(1))
- # Set local variable
- normal = curses.A_NORMAL
- # Copyright info
- copyleft = self.__copyleft
- screen.addstr(0, 0, copyleft.center(79), normal)
- screen.refresh()
-
- def configure_settings(self, pos=None, key_in=None):
- self.__stdscr.keypad(1)
- screen = self.__stdscr.subwin(8, 25, 2, 0)
- screen.bkgd(' ', curses.color_pair(4))
- # Set local variable
- normal = curses.A_NORMAL
- select = curses.color_pair(5)
- select += curses.A_BOLD
-
- id_num = range(len(self.settings))
- if pos != None:
- if key_in == curses.KEY_DOWN:
- pos = list(id_num[1:] + id_num[:1])[pos]
- elif key_in == curses.KEY_UP:
- pos = list(id_num[-1:] + id_num[:-1])[pos]
- elif key_in in [10, 32]:
- self.sub_selection(pos)
- self.info(pos, 0)
- for p, item in enumerate(self.settings):
- item_str = item[0].ljust(12)
- screen.addstr(3 + p, 2, item_str, select if p == pos else normal)
- if p:
- choice = "[%s]" % item[2][item[1]]
- else:
- choice = "[%s]" % item[2][item[1]]["label"]
- screen.addstr(3 + p, 15, ''.ljust(10), normal)
- screen.addstr(3 + p, 15, choice, select if p == pos else normal)
- screen.refresh()
- return pos
-
- def status(self):
- screen = self.__stdscr.subwin(11, 25, 10, 0)
- screen.bkgd(' ', curses.color_pair(4))
- # Set local variable
- normal = curses.A_NORMAL
- green = curses.color_pair(7)
- red = curses.color_pair(9)
- # Status info
- for i, stat in enumerate(self.statusinfo):
- screen.addstr(2 + i, 2, stat[0], normal)
- stat_str = ''.join(['[', stat[1], ']']).ljust(9)
- screen.addstr(2 + i, 15, stat_str,
- green if stat[2] == "GREEN" else red)
- # Hosts file info
- i = 0
- for key, info in self.hostsinfo.items():
- screen.addstr(7 + i, 2, key, normal)
- screen.addstr(7 + i, 15, info, normal)
- i += 1
- screen.refresh()
-
- def select_func(self, pos=None, key_in=None):
- screen = self.__stdscr.subwin(18, 26, 2, 26)
- screen.bkgd(' ', curses.color_pair(4))
- # Set local variable
- normal = curses.A_NORMAL
- select = curses.color_pair(5)
- select += curses.A_BOLD
- list_height = 15
- ip = self.settings[1][1]
- # Key Press Operations
- item_len = len(self.func_items[ip])
- item_sup, item_inf = self.item_sup, self.item_inf
- if pos != None:
- if item_len > list_height:
- if pos <= 1:
- item_sup = 0
- item_inf = list_height - 1
- elif pos >= item_len - 2:
- item_sup = item_len - list_height + 1
- item_inf = item_len
- else:
- item_sup = 0
- item_inf = item_len
- if key_in == curses.KEY_DOWN:
- pos += 1
- if pos >= item_len:
- pos = 0
- if pos not in range(item_sup, item_inf):
- item_sup += 2 if item_sup == 0 else 1
- item_inf += 1
- elif key_in == curses.KEY_UP:
- pos -= 1
- if pos < 0:
- pos = item_len - 1
- if pos not in range(item_sup, item_inf):
- item_inf -= 2 if item_inf == item_len else 1
- item_sup -= 1
- elif key_in in [10, 32]:
- self.func_selec[ip][pos] = not self.func_selec[ip][pos]
- mutex = RetrieveData.get_ids(self.func_items[ip][pos][2])
- for c_id, c in enumerate(self.func_items[ip]):
- if c[0] == self.func_items[ip][pos][0]:
- if c[1] in mutex and self.func_selec[ip][c_id] == 1:
- self.func_selec[ip][c_id] = 0
- self.info(pos, 1)
- else:
- item_sup = 0
- if item_len > list_height:
- item_inf = list_height - 1
- else:
- item_inf = item_len
- # Function list
- items_show = self.func_items[ip][item_sup:item_inf]
- items_selec = self.func_selec[ip][item_sup:item_inf]
- for p, item in enumerate(items_show):
- sel_ch = '+' if items_selec[p] else ' '
- item_str = ("[%s] %s" % (sel_ch, item[3])).ljust(23)
- item_pos = pos - item_sup if pos != None else None
- highlight = select if p == item_pos else normal
- if item_len > list_height:
- if item_inf - item_sup == list_height - 2:
- screen.addstr(4 + p, 2, item_str, highlight)
- elif item_inf == item_len:
- screen.addstr(4 + p, 2, item_str, highlight)
- elif item_sup == 0:
- screen.addstr(3 + p, 2, item_str, highlight)
- else:
- screen.addstr(3 + p, 2, item_str, highlight)
- if item_len > list_height:
- if item_inf - item_sup == list_height - 2:
- screen.addstr(3, 2, " More ".center(23, '.'), normal)
- screen.addch(3, 15, curses.ACS_UARROW)
- screen.addstr(17, 2, " More ".center(23, '.'), normal)
- screen.addch(17, 15, curses.ACS_DARROW)
- elif item_inf == item_len:
- screen.addstr(3, 2, " More ".center(23, '.'), normal)
- screen.addch(3, 15, curses.ACS_UARROW)
- elif item_sup == 0:
- screen.addstr(17, 2, " More ".center(23, '.'), normal)
- screen.addch(17, 15, curses.ACS_DARROW)
- else:
- for line_i in range(list_height - item_len):
- screen.addstr(17 - line_i, 2, ' ' * 23, normal)
- screen.refresh()
-
- self.item_sup, self.item_inf = item_sup, item_inf
- return pos
-
- def info(self, pos, tab):
- screen = self.__stdscr.subwin(18, 24, 2, 52)
- screen.bkgd(' ', curses.color_pair(4))
- normal = curses.A_NORMAL
- if tab:
- ip = self.settings[1][1]
- info_str = self.func_items[ip][pos][3]
- else:
- info_str = self.settings[pos][0]
- # Clear Expired Infomotion
- for i in range(6):
- screen.addstr(1 + i, 2, ''.ljust(22), normal)
- screen.addstr(1, 2, info_str, normal)
- # Key Info Offset
- k_info_y = 10
- k_info_x_key = 2
- k_info_x_text = 10
- # Arrow Keys
- screen.addch(k_info_y, k_info_x_key, curses.ACS_UARROW, normal)
- screen.addch(k_info_y, k_info_x_key + 1, curses.ACS_DARROW, normal)
- # Show Key Info
- for i, keyinfo in enumerate(self.funckeys):
- screen.addstr(k_info_y + i, k_info_x_key, keyinfo[0], normal)
- screen.addstr(k_info_y + i, k_info_x_text, keyinfo[1], normal)
- screen.refresh()
-
- def process_bar(self, done, block, total, mode=1):
- screen = self.__stdscr.subwin(2, 80, 20, 0)
- screen.bkgd(' ', curses.color_pair(4))
- normal = curses.A_NORMAL
- line_width = 76
- prog_len = line_width - 20
- # Progress Bar
- if mode:
- done = done * block
- prog = prog_len * done / total
- progress = ''.join(['=' * int(prog), '-' * int(2 * prog % 2)])
- progress = progress.ljust(prog_len)
- total = Utilities.convert_size(total).ljust(7)
- done = Utilities.convert_size(done).rjust(7)
- else:
- progress = ' ' * prog_len
- done = total = 'N/A'.center(7)
- # Show Progress
- prog_bar = "[%s] %s | %s" % (progress, done, total)
- screen.addstr(1, 2, prog_bar, normal)
- screen.refresh()
-
- def section_daemon(self):
- screen = self.__stdscr.subwin(0, 0, 0, 0)
- screen.keypad(1)
- # Draw Menu
- self.banner()
- self.footer()
- # Key Press Operations
- key_in = None
- tab = 0
- pos = 0
- hot_keys = self.hotkeys
- tab_entry = [self.configure_settings, self.select_func]
- while key_in != 27:
- self.setup_menu()
- self.status()
- self.process_bar(0, 0, 0, 0)
- for i, sec in enumerate(tab_entry):
- tab_entry[i](pos if i == tab else None)
- if key_in == None:
- self.platform = self.check_platform()
- test = self.settings[0][2][0]["test_url"]
- self.check_connection(test)
- key_in = screen.getch()
- if key_in == 9:
- if self.func_items == [[], []]:
- tab = 0
- else:
- tab = not tab
- pos = 0
- elif key_in in hot_keys:
- pos = tab_entry[tab](pos, key_in)
- elif key_in in self.ops_keys:
- i = self.ops_keys.index(key_in)
- if i:
- confirm = self.confirm_win(i)
- else:
- self.check_update()
-
- def sub_selection(self, pos):
- i_len = len(self.settings[pos][2])
- i_pos = self.settings[pos][1]
- # Draw Shadow
- shadow = curses.newwin(i_len + 2, 18, 13 - i_len / 2, 31)
- shadow.bkgd(' ', curses.color_pair(8))
- shadow.refresh()
- # Draw Subwindow
- screen = curses.newwin(i_len + 2, 18, 12 - i_len / 2, 30)
- screen.box()
- screen.bkgd(' ', curses.color_pair(1))
- screen.keypad(1)
- # Set local variable
- normal = curses.A_NORMAL
- select = normal + curses.A_BOLD
- # Title of Subwindow
- screen.addstr(0, 3, self.settings[pos][0].center(12), normal)
- # Key Press Operations
- id_num = range(len(self.settings[pos][2]))
- key_in = None
- while key_in != 27:
- for p, item in enumerate(self.settings[pos][2]):
- item_str = item if pos else item["tag"]
- screen.addstr(1 + p, 2, item_str,
- select if p == i_pos else normal)
- screen.refresh()
- key_in = screen.getch()
- if key_in == curses.KEY_DOWN:
- i_pos = list(id_num[1:] + id_num[:1])[i_pos]
- elif key_in == curses.KEY_UP:
- i_pos = list(id_num[-1:] + id_num[:-1])[i_pos]
- elif key_in in [10, 32]:
- if pos == 0 and i_pos != self.settings[pos][1]:
- test = self.settings[pos][2][i_pos]["test_url"]
- self.check_connection(test)
- self.settings[pos][1] = i_pos
- return
-
- def setup_menu(self):
- screen = self.__stdscr.subwin(21, 80, 2, 0)
- screen.box()
- screen.bkgd(' ', curses.color_pair(4))
- # Configuration Section
- screen.addch(0, 26, curses.ACS_BSSS)
- screen.vline(1, 26, curses.ACS_VLINE, 17)
- # Status Section
- screen.addch(7, 0, curses.ACS_SSSB)
- screen.addch(7, 26, curses.ACS_SBSS)
- screen.hline(7, 1, curses.ACS_HLINE, 25)
- # Select Functions Section
- screen.addch(0, 52, curses.ACS_BSSS)
- screen.vline(1, 52, curses.ACS_VLINE, 17)
- # Process Bar Section
- screen.addch(18, 0, curses.ACS_SSSB)
- screen.addch(18, 79, curses.ACS_SBSS)
- screen.hline(18, 1, curses.ACS_HLINE, 78)
- screen.addch(18, 26, curses.ACS_SSBS)
- screen.addch(18, 52, curses.ACS_SSBS)
- # Section Titles
- title = curses.color_pair(6)
- for s_title in self.subtitles:
- cord = s_title[1]
- screen.addstr(cord[0], cord[1], s_title[0], title)
- screen.hline(cord[0] + 1, cord[1], curses.ACS_HLINE, 23)
- screen.refresh()
-
- def confirm_win(self, op):
- # Draw Shadow
- shadow = curses.newwin(5, 40, 11, 21)
- shadow.bkgd(' ', curses.color_pair(8))
- shadow.refresh()
- # Draw Subwindow
- screen = curses.newwin(5, 40, 10, 20)
- screen.box()
- screen.bkgd(' ', curses.color_pair(2))
- screen.keypad(1)
- # Set local variable
- normal = curses.A_NORMAL
- select = curses.A_REVERSE
- messages = ["Apply Changes to hosts file?",
- "Backup current hosts file?",
- "Restore hosts from a backup?"]
- choices = ["Apply", "Cancel"]
- # Draw subwindow frame
- screen.addstr(1, 2, messages[op].center(36), normal)
- screen.hline(2, 1, curses.ACS_HLINE, 38)
- screen.addch(2, 0, curses.ACS_SSSB)
- screen.addch(2, 39, curses.ACS_SBSS)
- # Apply or Cancel the Operation
- tab = 0
- key_in = None
- while key_in != 27:
- for i, item in enumerate(choices):
- item_str = ''.join(['[', item, ']'])
- screen.addstr(3, 6 + 20 * i, item_str,
- select if i == tab else normal)
- screen.refresh()
- key_in = screen.getch()
- if key_in in [9, curses.KEY_LEFT, curses.KEY_RIGHT]:
- tab = [1, 0][tab]
- if key_in in [ord('a'), ord('c')]:
- key_in -= (ord('a') - ord('A'))
- if key_in in [ord('A'), ord('C')]:
- return [ord('A'), ord('C')].index(key_in)
- if key_in in [10, 32]:
- return tab
-
- def check_connection(self, url):
- # Draw Shadow
- shadow = curses.newwin(3, 30, 11, 21)
- shadow.bkgd(' ', curses.color_pair(8))
- shadow.refresh()
- # Draw Subwindow
- screen = curses.newwin(3, 30, 10, 20)
- screen.box()
- screen.bkgd(' ', curses.color_pair(2))
- screen.keypad(1)
-
- normal = curses.A_NORMAL
- screen.addstr(1, 3, "Checking Server Status...", normal)
- screen.refresh()
-
- conn = Utilities.check_connection(url)
- if conn:
- self.statusinfo[0][1] = "OK"
- self.statusinfo[0][2] = "GREEN"
- else:
- self.statusinfo[0][1] = "Error"
- self.statusinfo[0][2] = "RED"
- self.status()
- return conn
-
- def check_platform(self):
- plat = Utilities.check_platform()
- self.statusinfo[1] = [self.statusinfo[1][0], plat[0],
- "GREEN" if plat[4] else "RED"]
- self.status()
- return plat
-
- def check_update(self):
- # Draw Shadow
- shadow = curses.newwin(3, 30, 11, 21)
- shadow.bkgd(' ', curses.color_pair(8))
- shadow.refresh()
- # Draw Subwindow
- screen = curses.newwin(3, 30, 10, 20)
- screen.box()
- screen.bkgd(' ', curses.color_pair(2))
- screen.keypad(1)
-
- normal = curses.A_NORMAL
- screen.addstr(1, 7, "Checking Update...", normal)
- screen.refresh()
-
- srv_id = self.settings[0][1]
- url = self.settings[0][2][srv_id]["update"] + self.infofile
- try:
- socket.setdefaulttimeout(5)
- urlobj = urllib.urlopen(url)
- j_str = urlobj.read()
- urlobj.close()
- info = json.loads(j_str)
- except:
- info = {"version": "[Error]"}
- self.hostsinfo["Latest"] = info["version"]
- self.status()
- return info
-
-class HostsCurses(object):
- _ipv_id = 0
- _is_root = 0
- _down_flag = 0
- _funcs = [[], []]
- _hostsinfo = []
- _make_cfg = {}
- _make_mode = ""
- _make_path = "./hosts"
- _sys_eol = ""
- _update = {}
- hostsinfo = ["N/A", "N/A"]
-
- choice = [[], []]
- slices = [[], []]
- # OS related configuration
- platform = ''
- hostname = ''
- hostspath = ''
- # Mirror related configuration
- _mirr_id = 0
- mirrors = []
- # Data file related configuration
- filename = "hostslist.data"
- infofile = "hostsinfo.json"
-
- def init_main(self):
- # Set mirrors
- self.mirrors = Utilities.set_network("network.conf")
- self.set_platform()
- # Read data file and set function list
- try:
- RetrieveData.unpack()
- RetrieveData.connect_db()
- self.set_func_list()
- self.set_info()
- except IOError:
- pass
- except BadZipfile:
- pass
- # Check if current session have root privileges
- self.check_root()
-
- def opt_session(self):
- window = HostsCursesUI()
- window.func_items = self.choice
- window.func_selec = self._funcs
- window.hostsinfo["Version"] = self.hostsinfo[0]
- window.hostsinfo["Release"] = self.hostsinfo[1]
- window.settings[0][2] = self.mirrors
-
- window.section_daemon()
-
- def set_platform(self):
- """Set OS info - Public Method
-
- Set the information of current operating system platform.
- """
- system, hostname, path, encode, flag = Utilities.check_platform()
- color = "GREEN" if flag else "RED"
- self.platform = system
- self.hostname = hostname
- self.hostspath = path
- if encode == "win_ansi":
- self._sys_eol = "\r\n"
- else:
- self._sys_eol = "\n"
-
- def set_func_list(self):
- for ip in range(2):
- choice, defaults, slices = RetrieveData.get_choice(ip)
- self.choice[ip] = choice
- self.slices[ip] = slices
- funcs = []
- for func in choice:
- if func[1] in defaults[func[0]]:
- funcs.append(1)
- else:
- funcs.append(0)
- self._funcs[ip] = funcs
-
- def set_info(self):
- """Set data file info - Public Method
-
- Set the information of the current local data file.
- """
- info = RetrieveData.get_info()
- ver = info["Version"]
- build = info["Buildtime"]
- build = Utilities.timestamp_to_date(build)
- self.hostsinfo = [ver, build]
-
- def check_root(self):
- """Check root privileges - Public Method
-
- Check if current session is ran with root privileges.
- """
- is_root = Utilities.check_privileges()[1]
- self._is_root = is_root
- if not is_root:
- #self.warning_permission()
- pass
-
-
-class HostsDownload(object):
- def get_file(self, url, path, ui_class):
- socket.setdefaulttimeout(10)
- urllib.urlretrieve(url, path, ui_class.process_bar)
-
-if __name__ == "__main__":
- main = HostsCurses()
- main.init_main()
- main.opt_session()
diff --git a/doc/dev/Makefile b/doc/dev/Makefile
new file mode 100644
index 0000000..14cc5b9
--- /dev/null
+++ b/doc/dev/Makefile
@@ -0,0 +1,178 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = ../../document
+SOURCEDIR = ./
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR)
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR)
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/huhamhire-hosts.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/huhamhire-hosts.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/huhamhire-hosts"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/huhamhire-hosts"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/dev/conf.py b/doc/dev/conf.py
new file mode 100644
index 0000000..91db424
--- /dev/null
+++ b/doc/dev/conf.py
@@ -0,0 +1,268 @@
+# -*- coding: utf-8 -*-
+#
+# huhamhire-hosts documentation build configuration file, created by
+# sphinx-quickstart on Tue Jan 14 10:49:55 2014.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+sys.path.insert(0, os.path.abspath('../../'))
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.pngmath',
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.graphviz',
+ 'sphinx.ext.inheritance_diagram',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Hosts Setup Utility'
+copyright = u'2011-2014, huhamhire-hosts team'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.9.8'
+# The full version, including alpha/beta/rc tags.
+release = '1.9.8 beta'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+#exclude_patterns = []
+
+#unused_docs = ["gpl"]
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+html_theme_options = {
+ "stickysidebar": True,
+ "collapsiblesidebar": False,
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'huhamhire-hostsdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ 'inputenc': '',
+ 'utf8extra': '',
+ 'preamble': '''
+\\hypersetup{unicode=true}
+\\usepackage{CJKutf8}
+\\AtBeginDocument{\\begin{CJK}{UTF8}{}}
+\\AtEndDocument{\\end{CJK}}
+''',
+ 'papersize': 'a4paper',
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'huhamhire-hosts.tex', u'Hosts Setup Utility Documentation',
+ u'huhamhire-hosts team', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+latex_show_pagerefs = True
+
+# If true, show URL addresses after external links.
+latex_show_urls = True
+
+# Documents to append as an appendix to all manuals.
+latex_appendices = ['gpl']
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'huhamhire-hosts', u'Hosts Setup Utility Documentation',
+ [u'huhamhire-hosts team'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'huhamhire-hosts', u'Hosts Setup Utility Documentation',
+ u'huhamhire-hosts team', 'huhamhire-hosts',
+ 'Easy managing hosts file.', 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/doc/dev/gpl.rst b/doc/dev/gpl.rst
new file mode 100644
index 0000000..50dc790
--- /dev/null
+++ b/doc/dev/gpl.rst
@@ -0,0 +1,11 @@
+:orphan:
+
+GNU General Public License
+==========================
+
+`Hosts Setup Utility` is designed by `huhamhire-hosts team` and licensed under
+the `GNU General Public License, version 3(GPLv3)`. For further information,
+please visit our `website `_.
+
+.. include:: ../../LICENSE
+ :literal:
diff --git a/doc/dev/gui.rst b/doc/dev/gui.rst
new file mode 100644
index 0000000..9a28a03
--- /dev/null
+++ b/doc/dev/gui.rst
@@ -0,0 +1,3 @@
+.. automodule:: gui.__doc__
+ :members:
+
diff --git a/doc/dev/hoststool.rst b/doc/dev/hoststool.rst
new file mode 100644
index 0000000..0f36226
--- /dev/null
+++ b/doc/dev/hoststool.rst
@@ -0,0 +1,69 @@
+Hosts Setup Utility
+===================
+
+This chapter contains discriptions for modules, scripts, and configurations
+at the root working directory of `Hosts Setup Utility`.
+
+UtilLauncher
+------------
+.. automodule:: hoststool
+ :members:
+
+
+Packaging Tool
+--------------
+
+A simple script is also provided for developer to make new package of
+`Hosts Setup Utility` distributions. This tool could create distributions for
+`Windows`, `OS X`, `Linux`, and any `Unix-like` systems.
+
+* **_build.py**
+
+Here is the usage for running this script::
+
+ Usage: _build.py [type]
+ Options:
+ type Indicating the mode for making packages. Optional choices
+ could be: py2exe, py2app, py2tar, py2source
+ py2exe - Make binary excutables for Windows. The
+ operations of this option depends on the py2exe
+ distutils extension.
+ py2app - Make binary excutables for Mac OS X. The
+ operations of this option depends on the py2app
+ distutils extension.
+ py2app - Make source code packages for X11 users.
+ py2source - Make source code packages for developers.
+
+.. note::
+ * ``py2exe`` option requires a Windows platform with `py2exe` installed.
+ * ``py2app`` option requires an OS X platform with `PyQt4` and `py2app`.
+
+
+Configuration Files
+-------------------
+
+There is only one configuration file stored in the root working directory
+currently, which is used to configure the mirrors servers for
+`Hosts Setup Utility`.
+
+* **network.conf**
+
+Here is an example for setting up a server in the configuration file::
+
+ [Github]
+ label = GITHUB
+ server = github.com
+ update = http://huhamhire.github.com/huhamhire-hosts/update/
+
+
+Hosts Data File
+---------------
+
+`hosts data file` is not included in any distributions. It is usually
+downloaded from the mirror servers of this project.
+
+* **hostslist.data**
+
+In fact, the data file `hostslist.data` is just a zip package containing a
+sqlite database file `hostslist.s3db`. The database file could be loaded by
+any sqlite database managing tools.
diff --git a/doc/dev/index.rst b/doc/dev/index.rst
new file mode 100644
index 0000000..0da3203
--- /dev/null
+++ b/doc/dev/index.rst
@@ -0,0 +1,32 @@
+Hosts Setup Utility Developer Documentation
+===========================================
+
+Since the governments of some countries are using technical methods blocking
+internet access to some websites and web service providers which includes some
+world famous sites like Google, YouTube, twitter, Facebook, and Wikipedia
+etc., we designed this tiny utility in order to help people getting through
+the Internet blockade.
+
+Chapter one of this documentation contains a brief introduction of
+`Hosts Setup Utility` and basic usage to start this tool. While the others
+describing modules and functions that are used in the current version of
+`Hosts Setup Utility`. These chapters also describes some optional methods
+for developers to help improve this tool.
+
+This is the documentation for `Hosts Setup Utility` |release|, last updated
+|today|.
+
+`Hosts Setup Utility` is designed by `huhamhire-hosts team` and licensed under
+the `GNU General Public License, version 3(GPLv3)`. For further information,
+please visit our `website `_.
+
+
+.. toctree::
+ :numbered:
+ :maxdepth: 2
+
+ intro
+ hoststool
+ gui
+ tui
+ util
diff --git a/doc/dev/intro.rst b/doc/dev/intro.rst
new file mode 100644
index 0000000..86f7758
--- /dev/null
+++ b/doc/dev/intro.rst
@@ -0,0 +1,160 @@
+Introduction
+============
+
+Since the governments of some countries are using the blocking the internet
+access to several websites and web service providers which includes some world
+famous sites like Google, YouTube, twitter, Facebook, and Wikipedia etc., we
+designed this tiny utility in order to help people getting through the
+Internet blockade.
+
+`Hosts Setup Utility` provides basic tools to manage the hosts file on current
+operating systems. It also provides both support for Graphical Desktop
+environment with Graphical User Interface (GUI) and CLI environment with
+Text-based User Interface (TUI).
+
+Users could use these tool to modify the hosts to visit specified websites or
+services blocked by ISP/government. Functions which help users to
+backup/restore hosts files are also provided.
+
+
+System Requirements
+-------------------
+
+Here are the system requirements needed for using `Hosts Setup Utility`.
+
+Graphical User Interface (GUI)
+``````````````````````````````
+
+System requirements to run `Hosts Setup Utility` on Graphical Desktop are
+listed here:
+
+* Microsoft Windows 2000 or newer for Windows users.
+
+* Mac OS X 10.6 or newer for Macintosh users.
+
+* Linux/X11 desktop with Python 2 and PyQt4 for Linux/X11 users.
+
+* Internet access is required for retrieving the latest hosts data file.
+
+.. note:: On some linux distributions, pre-built packages of PyQt4 can be
+ found in software repositories. For example, you can install PyQt4 on
+ a debian distribution simplly using:
+
+ .. code-block:: bash
+
+ apt-get install python-qt4
+
+
+More requirements are needed for developers:
+
+* Python 2.6/2.7 with PyQt4 extension for developers.
+
+* py2exe or py2app would be required while making binary excutables for
+ specified platforms.
+
+
+Text-based User Interface (TUI)
+```````````````````````````````
+
+Any devices with `Python 2` and `Python Standard Library` INSTALLED could run
+`Hosts Setup Utility` in TUI mode from a 80x24 terminal. In addition to this,
+TUI mode could also be operated via SSH on remote devices/machines/servers.
+All you need is a system with Python 2 installed.
+
+
+.. _intro-get-started:
+
+Get Started
+-----------
+
+Since `Hosts Setup Utility` supports both Graphical Desktop environment with
+Graphical User Interface (GUI) and CLI/terminal environment with Text-based
+User Interface (TUI), users could the way they would like to launch this tool.
+
+However, GUI mode is highly recommended because several features like
+backup/restore hosts file are still not supported in TUI mode currently.
+
+.. note:: If the program is not running with privileges to modify the hosts
+ file, a warning message would be shown and you could only do operations
+ like backup hosts file and update the local data file. Plus, TUI mode
+ could not get started in this condition.
+
+
+Graphical User Interface (GUI) Mode
+```````````````````````````````````
+
+* Windows(x86/x64): Run hoststool.exe from the binary excutables package to
+ get started.
+
+ .. note::
+ - "Run as Administrator" is needed for operations to change the
+ hosts file on Windows Vista or newer.
+
+* Mac OS X: Run HostsUtl application from the binary excutables package to get
+ started.
+
+ .. note::
+ - Because of the locale problem with py2app, the automatic language
+ selection may not work correctly on Mac OS with binary executable
+ files. You can just choose the language on your on choice.
+
+* Linux/X11(Source code): Run command "python hoststool.py" to get started.
+
+ .. note::
+ - All platforms with Python and the PyQt4 could use this method to run
+ with the source code.
+ - A desktop environment with PyQt4 and python is needed only for
+ Linux/X11 users to start a GUI Session.
+
+
+Text-based User Interface (TUI) Mode
+````````````````````````````````````
+
+* Windows Excutable(x86/x64):
+
+ #. Start a command line(could be `cmd` or `Power Shell`).
+
+ .. note:: "Run as Administrator" is needed for operations to change the
+ hosts file on Windows Vista or newer.
+
+ #. Change directory to the folder contains binary executable files. of
+ `Hosts Setup Utility`.
+
+ #. Run ``hoststool_tui.exe`` with an argument ``-t`` from the directory to
+ get started.
+
+* Python Source Code:
+
+ `Python Source Code` is very easy to be started through any terminals on any
+ operating systems.
+
+ #. Change your directory to the source script.
+
+ #. Run ``python hoststool.py -t`` in the terminal. Of course, wirte
+ privileges to access the hosts file on current system is required. If
+ not, a warning message box would show up and then terminate current
+ session.
+
+.. seealso:: :class:`~hoststool.UtilLauncher`.
+
+
+.. _intro-customize:
+
+User Customized Hosts
+---------------------
+
+Users are allowed to add customized hosts list as an independent module to
+make a hosts file. All you need to do is create a simple text file named
+``custom.hosts`` in the working directory, and put your own hosts entries
+into this file. Then you would find a `Customized Hosts` option in the
+function list.
+
+.. warning:: Non-ASCII characters are not recommended to be put into the
+ customized hosts file.
+
+.. versionadded:: 1.9.8
+
+
+.. note:: Specific user manual is not included in this documentation. For
+ further information, please visit our
+ `website `_.
diff --git a/doc/dev/make.bat b/doc/dev/make.bat
new file mode 100644
index 0000000..b7869b9
--- /dev/null
+++ b/doc/dev/make.bat
@@ -0,0 +1,243 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=../../document
+set SOURCEDIR=./
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SOURCEDIR%
+set I18NSPHINXOPTS=%SPHINXOPTS% %SOURCEDIR%
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\huhamhire-hosts.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\huhamhire-hosts.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/doc/dev/tui.rst b/doc/dev/tui.rst
new file mode 100644
index 0000000..86b8b41
--- /dev/null
+++ b/doc/dev/tui.rst
@@ -0,0 +1,2 @@
+.. automodule:: tui.__doc__
+ :members:
diff --git a/doc/dev/util.rst b/doc/dev/util.rst
new file mode 100644
index 0000000..3251d85
--- /dev/null
+++ b/doc/dev/util.rst
@@ -0,0 +1,2 @@
+.. automodule:: util.__doc__
+ :members:
diff --git a/gui/__doc__.py b/gui/__doc__.py
new file mode 100644
index 0000000..cf9025e
--- /dev/null
+++ b/gui/__doc__.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __doc__.py : Document in reST format of gui module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+"""
+.. _gui-module:
+
+Graphical User Interface (GUI)
+==============================
+
+The following sections describe the objects and methods from the Graphical
+User Interface (GUI) module of huhamhire-hosts HostsUtil. The methods to make
+GUI here are based on `PyQT4 `_.
+
+
+HostsUtil(GUI)
+--------------
+.. autoclass:: gui.hostsutil.HostsUtil
+ :members:
+
+ .. automethod:: gui.hostsutil.HostsUtil.__del__
+
+
+QDialogSlots
+------------
+.. autoclass:: gui.qdialog_slots.QDialogSlots
+ :members:
+
+ .. automethod:: gui.qdialog_slots.QDialogSlots.__init__
+
+
+LangUtil
+--------
+.. autoclass:: gui.language.LangUtil
+ :members:
+
+
+QDialogDaemon
+-------------
+.. autoclass:: gui.qdialog_d.QDialogDaemon
+ :members:
+
+
+QDialogUI
+---------
+.. autoclass:: gui.qdialog_ui.QDialogUI
+ :members:
+
+ .. automethod:: gui.qdialog_ui.QDialogUI.__init__
+
+
+QSubChkConnection
+-----------------
+.. autoclass:: gui._checkconn.QSubChkConnection
+ :members:
+
+ .. automethod:: gui._checkconn.QSubChkConnection.__init__
+
+
+QSubChkUpdate
+-------------
+.. autoclass:: gui._checkupdate.QSubChkUpdate
+ :members:
+
+ .. automethod:: gui._checkupdate.QSubChkUpdate.__init__
+
+
+QSubFetchUpdate
+---------------
+.. autoclass:: gui._update.QSubFetchUpdate
+ :members:
+
+ .. automethod:: gui._update.QSubFetchUpdate.__init__
+
+
+QSubMakeHosts
+-------------
+.. autoclass:: gui._make.QSubMakeHosts
+ :members:
+
+ .. automethod:: gui._make.QSubMakeHosts.__init__
+
+
+.. _qt-resource-modules:
+
+Resource Modules
+----------------
+All Qt Project files and resource files are convented into python Resource
+Modules in order to be used by `Hosts Setup Utility`. Including:
+
+* util_ui.py: :class:`~gui.util_ui.Ui_Util` class organizing layout of the
+ main dialog for `Hosts Setup Utility`. This file is translated from the UI
+ project file `util_ui.ui`.
+
+ .. seealso:: `util_ui.ui` in :ref:`QT Project Files `.
+
+* util_rc.py: Images resources used by the main dialog.
+
+* style_rc.py: Images resources used by the default `Qt Stylesheet`.
+
+ .. seealso:: :ref:`QT Stylesheet `.
+
+.. seealso:: `_pyuic4.py` in :ref:`QT Project Scripts `.
+
+
+.. _qt-project-files:
+
+QT Project Files
+----------------
+Qt Project Files and Qt resources files used to design the Qt user interface
+are provided in the "`gui/pyqt/`" directory, including:
+
+* util.pro: QT Project file containing configuration of current project.
+
+* util.qrc: Resource file for main dialog generated by `Qt Designer`.
+
+* style.qrc: Resource file for the default `Qt Style Sheet` generated by
+ `Qt Designer`.
+
+ .. seealso:: :ref:`QT Stylesheet `.
+
+* util_ui.ui: UI project file for the main dialog designed with
+ `Qt designer`.
+
+.. seealso::
+ `Qt Designer `_.
+
+
+.. _qt-project-scripts:
+
+QT Project Scripts
+------------------
+Scripts to help managing the Qt project are also provided in the "`gui/pyqt/`"
+directory:
+
+* _pylupdate4.py: Contains tools to update the language files for UI.
+
+ .. note:: This script would parse files declared as `SOURCES` in the Qt
+ project file (`gui/pyqt/util.pro`), and generate/update language files
+ declared as `TRANSLATIONS` in the Qt project file.
+
+ .. seealso:: :ref:`Language Files `.
+
+* _pyuic4.py: Tools used to update the `UI module` and `Resource Modules`
+ from `UI file` and `Resources Files` designed with `Qt designer`.
+
+ .. seealso:: :ref:`Resource Modules `.
+
+
+.. _qt-language-files:
+
+Language Files
+--------------
+Since this GUI module of `Hosts Setup Utility` is based on :mod:`PyQt4`,
+it is very easy to internationalization this utility with the ways Qt
+provided.
+
+The `Qt Language Files` are stored in "`gui/lang/`" directory with file
+suffixes "`.qm`" and "`.ts`".
+
+.. note:: **File Types**
+
+ `Hosts Setup Utility` makes use of two kinds of files:
+
+ * TS: `translation source files`
+ are human-readable XML files containing source phrases and their
+ translations. These files are usually created and updated by `lupdate`
+ and are specific to an application.
+
+ * QM: `Qt message files`
+ are binary files that contain translations used by an application at
+ run-time. These files are generated by lrelease, but can also be
+ generated by `Qt Linguist`.
+
+.. note:: You can use `_pylupdate4.py` provided in "`gui/pyqt/`" directory to
+ generate/update `TS` files with the Qt project file. And then use
+ `Qt Linguist` to make a `QM` file.
+
+ .. seealso:: `_pylupdate4.py` in
+ :ref:`QT Project Scripts`.
+
+ .. seealso:: `Qt Linguist
+ `_.
+
+
+.. _qt-stylesheet:
+
+QT Stylesheet
+-------------
+With the GUI module of `Hosts Setup Utility` is based on :mod:`PyQt4`, Qt
+style sheets are also supported to customize the appearance of GUI widgets.
+
+The `Qt Style Sheets` are stored in "`gui/theme/`" directory with file suffix
+"`.qss`".
+
+You can create yourown Qt style sheet and use it in method
+:meth:`~gui.qdialog_ui.QDialogUI.set_stylesheet` to customize new appearance
+of GUI widgets.
+
+.. seealso:: Method :meth:`~gui.qdialog_ui.QDialogUI.set_stylesheet` in
+ :class:`~gui.qdialog_ui.QDialogUI` class.
+
+.. note:: Qt Style Sheets are a powerful mechanism that allows you to
+ customize the appearance of widgets, in addition to what is already
+ possible by subclassing QStyle. The concepts, terminology, and syntax
+ of Qt Style Sheets are heavily inspired by HTML Cascading Style Sheets
+ (CSS) but adapted to the world of widgets.
+
+ .. seealso::
+ `QT Stylesheet `_.
+"""
\ No newline at end of file
diff --git a/gui/__init__.py b/gui/__init__.py
new file mode 100644
index 0000000..1c4ab73
--- /dev/null
+++ b/gui/__init__.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __init__.py : Declare modules to be called in gui module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+from hostsutil import HostsUtil
+
+__all__ = ["HostsUtil"]
diff --git a/gui/__list_trans.py b/gui/__list_trans.py
new file mode 100644
index 0000000..6de1382
--- /dev/null
+++ b/gui/__list_trans.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __list_trans.py : Name of items from the function list to be localized
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+from util_ui import _translate
+
+# Name of items from the function list to be localized
+# __list_trans (list): A list containing names of function list items
+# for translator to translate.
+__list_trans = [
+ _translate("Util", "customize", None),
+ _translate("Util", "google", None),
+ _translate("Util", "google-apis", None),
+ _translate("Util", "google(cn)", None),
+ _translate("Util", "google(hk)", None),
+ _translate("Util", "google(us)", None),
+ _translate("Util", "google-apis(cn)", None),
+ _translate("Util", "google-apis(us)", None),
+ _translate("Util", "activation-helper", None),
+ _translate("Util", "facebook", None),
+ _translate("Util", "twitter", None),
+ _translate("Util", "youtube", None),
+ _translate("Util", "wikipedia", None),
+ _translate("Util", "institutions", None),
+ _translate("Util", "steam", None),
+ _translate("Util", "github", None),
+ _translate("Util", "dropbox", None),
+ _translate("Util", "wordpress", None),
+ _translate("Util", "others", None),
+ _translate("Util", "adblock-hostsx", None),
+ _translate("Util", "adblock-mvps", None),
+ _translate("Util", "adblock-mwsl", None),
+ _translate("Util", "adblock-yoyo", None), ]
diff --git a/gui/_checkconn.py b/gui/_checkconn.py
new file mode 100644
index 0000000..a416ebe
--- /dev/null
+++ b/gui/_checkconn.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# _checkconn.py: Check connect to a specified server.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+from PyQt4 import QtCore
+
+import sys
+sys.path.append("..")
+from util import CommonUtil
+
+
+class QSubChkConnection(QtCore.QThread):
+ """
+ QSubChkConnection is a subclass of :class:`PyQt4.QtCore.QThread`. This
+ class contains methods to check the network connection with a specified
+ server.
+
+ .. inheritance-diagram:: gui._checkconn.QSubChkConnection
+ :parts: 1
+
+ .. note:: The instance of this class should be created in an individual
+ thread. And an instance of class should be set as :attr:`parent`
+ here.
+
+ :ivar PyQt4.QtCore.pyqtSignal trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which indicates current status.
+
+ .. note:: The signal :attr:`trigger` should be a integer flag:
+
+ ====== ========
+ signal Status
+ ====== ========
+ -1 Checking
+ 0 Failed
+ 1 OK
+ ====== ========
+ """
+ trigger = QtCore.pyqtSignal(int)
+
+ def __init__(self, parent):
+ """
+ Initialize a new instance of this class. Retrieve mirror settings from
+ the main dialog to check the connection.
+
+ :param parent: An instance of :class:`~gui.qdialog_d.QDialogDaemon`
+ class to fetch settings from.
+ :type parent: :class:`~gui.qdialog_d.QDialogDaemon`
+
+ .. warning:: :attr:`parent` MUST NOT be set as `None`.
+ """
+ super(QSubChkConnection, self).__init__(parent)
+ self.link = parent.mirrors[parent.mirror_id]["test_url"]
+
+ def run(self):
+ """
+ Start operations to check the network connection with a specified
+ server.
+ """
+ self.trigger.emit(-1)
+ status = CommonUtil.check_connection(self.link)
+ self.trigger.emit(status)
\ No newline at end of file
diff --git a/gui/_checkupdate.py b/gui/_checkupdate.py
new file mode 100644
index 0000000..126beca
--- /dev/null
+++ b/gui/_checkupdate.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# _checkupdate.py: Check update info of the latest data file.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import json
+import socket
+import urllib
+from PyQt4 import QtCore
+
+from util_ui import _translate
+
+
+class QSubChkUpdate(QtCore.QThread):
+ """
+ QSubChkConnection is a subclass of :class:`PyQt4.QtCore.QThread`. This
+ class contains methods to retrieve the metadata of the latest hosts data
+ file.
+
+ .. inheritance-diagram:: gui._checkupdate.QSubChkUpdate
+ :parts: 1
+
+ .. note:: The instance of this class should be created in an individual
+ thread. And an instance of class should be set as :attr:`parent`
+ here.
+
+ :ivar PyQt4.QtCore.pyqtSignal trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which indicates current status.
+
+ .. note:: The signal :attr:`trigger` should be a dictionary (`dict`)
+ containing metadata of the latest hosts data file.
+
+ .. seealso:: Method :meth:`~gui.qdialog_d.QDialogDaemon.finish_update`
+ in :class:`~gui.qdialog_d.QDialogDaemon` class.
+ """
+ trigger = QtCore.pyqtSignal(dict)
+
+ def __init__(self, parent):
+ """
+ Initialize a new instance of this class. Retrieve mirror settings from
+ the main dialog to check the update information.
+
+
+ :param parent: An instance of :class:`~gui.qdialog_d.QDialogDaemon`
+ class to fetch settings from.
+ :type parent: :class:`~gui.qdialog_d.QDialogDaemon`
+
+ .. warning:: :attr:`parent` MUST NOT be set as `None`.
+ """
+ super(QSubChkUpdate, self).__init__(parent)
+ self.url = parent.mirrors[parent.mirror_id]["update"] + parent.infofile
+
+ def run(self):
+ """
+ Start operations to retrieve the metadata of the latest hosts data
+ file.
+ """
+ try:
+ socket.setdefaulttimeout(5)
+ urlobj = urllib.urlopen(self.url)
+ j_str = urlobj.read()
+ urlobj.close()
+ info = json.loads(j_str)
+ self.trigger.emit(info)
+ except:
+ info = {"version": unicode(_translate("Util", "[Error]", None))}
+ self.trigger.emit(info)
\ No newline at end of file
diff --git a/gui/_make.py b/gui/_make.py
new file mode 100644
index 0000000..f20b3cb
--- /dev/null
+++ b/gui/_make.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# _make.py: Make a new hosts file.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import time
+from PyQt4 import QtCore
+
+import sys
+sys.path.append("..")
+from util import RetrieveData
+from util import MakeHosts
+
+
+class QSubMakeHosts(QtCore.QThread, MakeHosts):
+ """
+ QSubMakeHosts is a subclass of :class:`PyQt4.QtCore.QThread` and class
+ :class:`~util.makehosts.MakeHosts`. This class contains methods to make a
+ new hosts file for client.
+
+ .. inheritance-diagram:: gui._make.QSubMakeHosts
+ :parts: 1
+
+ .. note:: The instance of this class should be created in an individual
+ thread. And an instance of class should be set as :attr:`parent`
+ here.
+
+ :ivar PyQt4.QtCore.pyqtSignal info_trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which indicates the current operation.
+
+ .. note:: The signal :attr:`info_trigger` should be a tuple of
+ (`mod_name`, mod_num`):
+
+ * mod_name(`str`): Tag of a specified hosts module in current
+ progress.
+ * mod_num(`int`): Number of current module in the operation
+ sequence.
+
+ .. seealso:: Method
+ :meth:`~gui.qdialog_ui.QDialogUI.set_make_progress`
+ in :class:`~gui.qdialog_ui.QDialogUI` class.
+
+ :ivar PyQt4.QtCore.pyqtSignal fina_trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which notifies statistics to the main dialog.
+
+ .. note:: The signal :attr:`fina_trigger` should be a tuple of
+ (`time`, count`):
+
+ * time(`str`): Total time uesd while generating the new hosts
+ file.
+ * count(`int`): Total number of hosts entries inserted into the
+ new hosts file.
+
+ .. seealso:: Method :meth:`~gui.qdialog_d.QDialogDaemon.finish_make`
+ in :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ :ivar PyQt4.QtCore.pyqtSignal move_trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to notify the main dialog while new
+ hosts is being moved to specified path on current operating system.
+
+ .. note:: This signal does not send any data.
+
+ .. seealso:: Method :meth:`~gui.qdialog_d.QDialogDaemon.move_hosts`
+ in :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ .. seealso:: :class:`util.makehosts.MakeHosts` class.
+ """
+ info_trigger = QtCore.pyqtSignal(str, int)
+ fina_trigger = QtCore.pyqtSignal(str, int)
+ move_trigger = QtCore.pyqtSignal()
+
+ def __init__(self, parent):
+ """
+ Initialize a new instance of this class. Retrieve configuration from
+ the main dialog to make a new hosts file.
+
+ :param parent: An instance of :class:`~gui.qdialog_d.QDialogDaemon`
+ class to fetch settings from.
+ :type parent: :class:`~gui.qdialog_d.QDialogDaemon`
+
+ .. warning:: :attr:`parent` MUST NOT be set as `None`.
+ """
+ QtCore.QThread.__init__(self, parent)
+ MakeHosts.__init__(self, parent)
+
+ def run(self):
+ """
+ Start operations to retrieve data from the data file and generate new
+ hosts file.
+ """
+ start_time = time.time()
+ self.make()
+ end_time = time.time()
+ total_time = "%.4f" % (end_time - start_time)
+ self.fina_trigger.emit(total_time, self.count)
+ if self.make_mode == "system":
+ self.move_trigger.emit()
+
+ def get_hosts(self, make_cfg):
+ """
+ Make the new hosts file by the configuration defined by `make_cfg`
+ from function list on the main dialog.
+
+ :param make_cfg: Module settings in byte word format.
+ :type make_cfg: dict
+
+ .. seealso:: :attr:`make_cfg` in :class:`~tui.curses_d.CursesDaemon`
+ class.
+ """
+ for part_id in sorted(make_cfg.keys()):
+ mod_cfg = make_cfg[part_id]
+ if not RetrieveData.chk_mutex(part_id, mod_cfg):
+ return
+ mods = RetrieveData.get_ids(mod_cfg)
+ for mod_id in mods:
+ self.mod_num += 1
+ hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
+ self.info_trigger.emit(mod_name, self.mod_num)
+ if part_id == 0x02:
+ self.write_localhost_mod(hosts)
+ elif part_id == 0x04:
+ self.write_customized()
+ else:
+ self.write_common_mod(hosts, mod_name)
diff --git a/gui/_update.py b/gui/_update.py
new file mode 100644
index 0000000..dea9c74
--- /dev/null
+++ b/gui/_update.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# _update.py: Fetch the latest data file.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+import socket
+import urllib
+from PyQt4 import QtCore
+
+from util_ui import _translate
+
+import sys
+sys.path.append("..")
+from util import CommonUtil
+
+
+class QSubFetchUpdate(QtCore.QThread):
+ """
+ QSubFetchUpdate is a subclass of :class:`PyQt4.QtCore.QThread`. This
+ class contains methods to retrieve the latest hosts data file from a
+ server.
+
+ .. inheritance-diagram:: gui._update.QSubFetchUpdate
+ :parts: 1
+
+ .. note:: The instance of this class should be created in an individual
+ thread. And an instance of class should be set as :attr:`parent`
+ here.
+
+ :ivar PyQt4.QtCore.pyqtSignal prog_trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which indicates current downloading progress.
+
+ .. note:: The signal :attr:`prog_trigger` should be a tuple of
+ (`progress`, message`):
+
+ * progress(`int`): An number between `0` and `100` which indicates
+ current download progress.
+ * message(`str`): Message to be displayed to users on the progress
+ bar.
+
+ :ivar PyQt4.QtCore.pyqtSignal finish_trigger: An instance of
+ :class:`PyQt4.QtCore.pyqtSignal` to emit signal to the main dialog
+ which notifies if current operation is finished.
+
+ .. note:: The signal :attr:`finish_trigger` should be a tuple of
+ (`refresh_flag`, error_flag`):
+
+ * refresh_flag(`int`): An flag indicating whether to refresh
+ function list in the main dialog or not.
+
+ ============ ==============
+ refresh_flag Operation
+ ============ ==============
+ 1 Refresh
+ 0 Do not refresh
+ ============ ==============
+ * error_flag(`int`): An flag indicating whether the downloading
+ progress is successfully finished or not.
+
+ ========== =======
+ error_flag Status
+ ========== =======
+ 1 Error
+ 0 Success
+ ========== =======
+
+ .. seealso:: Method :meth:`~gui.qdialog_d.QDialogDaemon.finish_fetch`
+ in :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ """
+ prog_trigger = QtCore.pyqtSignal(int, str)
+ finish_trigger = QtCore.pyqtSignal(int, int)
+
+ def __init__(self, parent):
+ """
+ Initialize a new instance of this class. Fetch download settings from
+ the main dialog to retrieve new hosts data file.
+
+ :param parent: An instance of :class:`~gui.qdialog_d.QDialogDaemon`
+ class to fetch settings from.
+ :type parent: :class:`~gui.qdialog_d.QDialogDaemon`
+
+ .. warning:: :attr:`parent` MUST NOT be set as `None`.
+ """
+ super(QSubFetchUpdate, self).__init__(parent)
+ self.url = parent.mirrors[parent.mirror_id]["update"] + \
+ parent.filename
+ self.path = "./" + parent.filename
+ self.tmp_path = self.path + ".download"
+ self.filesize = parent._update["size"]
+
+ def run(self):
+ """
+ Start operations to retrieve the new hosts data file.
+ """
+ self.prog_trigger.emit(0, unicode(_translate(
+ "Util", "Connecting...", None)))
+ self.fetch_file()
+
+ def fetch_file(self):
+ """
+ Retrieve the latest data file to a specified local path with a url.
+ """
+ socket.setdefaulttimeout(10)
+ try:
+ urllib.urlretrieve(self.url, self.tmp_path, self.set_progress)
+ self.replace_old()
+ self.finish_trigger.emit(1, 0)
+ except:
+ self.finish_trigger.emit(1, 1)
+
+ def set_progress(self, done, block, total):
+ """
+ Send message to the main dialog to set the progress bar.
+
+ :param done: Block count of packaged retrieved.
+ :type done: int
+ :param block: Block size of the data pack retrieved.
+ :type block: int
+ :param total: Total size of the hosts data file.
+ :type total: int
+ """
+ done = done * block
+ if total <= 0:
+ total = self.filesize
+ prog = 100 * done / total
+ done = CommonUtil.convert_size(done)
+ total = CommonUtil.convert_size(total)
+ text = unicode(_translate(
+ "Util", "Downloading: %s / %s", None)) % (done, total)
+ self.prog_trigger.emit(prog, text)
+
+ def replace_old(self):
+ """
+ Replace the old hosts data file with the new one.
+ """
+ if os.path.isfile(self.path):
+ os.remove(self.path)
+ os.rename(self.tmp_path, self.path)
\ No newline at end of file
diff --git a/gui/hostsutil.py b/gui/hostsutil.py
new file mode 100644
index 0000000..e099654
--- /dev/null
+++ b/gui/hostsutil.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# hostsutil.py : Main entrance to GUI module of Hosts Setup Utility.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import sys
+
+from zipfile import BadZipfile
+
+from qdialog_slots import QDialogSlots
+
+sys.path.append("..")
+from util import RetrieveData, CommonUtil
+
+# Path to store language files
+LANG_DIR = "./gui/lang/"
+
+
+class HostsUtil(QDialogSlots):
+ """
+ HostsUtil class is the main entrance to the Graphical User Interface (GUI)
+ module of `Hosts Setup Utility`. This class contains methods to launch the
+ main dialog of this utility.
+
+ .. note:: This class is subclass of
+ :class:`~gui.qdialog_slots.QDialogSlots` class.
+
+ Typical usage to start a GUI session::
+
+ import gui
+
+ util = gui.HostsUtil()
+ util.start()
+
+ :ivar int init_flag: Times of the main dialog being initialized. This
+ value would be referenced for translator to set the language of the
+ main dialog.
+ :ivar str filename: Filename of the hosts data file containing data to
+ make hosts files from. Default by "`hostslist.data`".
+ :ivar str infofile: Filename of the info file containing metadata of the
+ hosts data file formatted in JSON. Default by "`hostslist.json`".
+
+ .. seealso:: :attr:`filename` and :attr:`infofile` in
+ :class:`~tui.curses_ui.CursesUI` class.
+ """
+ init_flag = 0
+ # Data file related configuration
+ filename = "hostslist.data"
+ infofile = "hostsinfo.json"
+
+ def __init__(self):
+ super(HostsUtil, self).__init__()
+
+ def __del__(self):
+ """
+ Clear up the temporary data file while TUI session is finished.
+ """
+ try:
+ RetrieveData.clear()
+ except:
+ pass
+
+ def start(self):
+ """
+ Start the GUI session.
+
+ .. note:: This method is the trigger to launch a GUI session of
+ `Hosts Setup Utility`.
+ """
+ if not self.init_flag:
+ self.init_main()
+ self.show()
+ sys.exit(self.app.exec_())
+
+ def init_main(self):
+ """
+ Set up the elements on the main dialog. Check the environment of
+ current operating system and current session.
+
+ * Load server list from a configuration file under working directory.
+ * Try to load the hosts data file under working directory if it
+ exists.
+
+ .. note:: IF hosts data file does not exists correctly in current
+ working directory, a warning message box would popup. And
+ operations to change the hosts file on current system could be
+ done only until a new data file has been downloaded.
+
+ .. seealso:: Method :meth:`~tui.hostsutil.HostsUtil.__init__` in
+ :class:`~tui.hostsutil.HostsUtil` class.
+ """
+ self.ui.SelectMirror.clear()
+ self.set_version()
+ # Set mirrors
+ self.mirrors = CommonUtil.set_network("network.conf")
+ self.set_mirrors()
+ # Read data file and set function list
+ try:
+ RetrieveData.unpack()
+ RetrieveData.connect_db()
+ self.set_func_list(1)
+ self.refresh_func_list()
+ self.set_info()
+ except IOError:
+ self.warning_no_datafile()
+ except BadZipfile:
+ self.warning_incorrect_datafile()
+ # Check if current session have root privileges
+ self.check_writable()
+ self.init_flag += 1
+
+
+if __name__ == "__main__":
+ HostsUtlMain = HostsUtil()
+ HostsUtlMain.start()
diff --git a/lang/de_DE.ts b/gui/lang/de_DE.ts
similarity index 64%
rename from lang/de_DE.ts
rename to gui/lang/de_DE.ts
index 8114403..723e529 100644
--- a/lang/de_DE.ts
+++ b/gui/lang/de_DE.ts
@@ -1,404 +1,434 @@
-
+
- HostsUtlMain
+ Util
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/gui/lang/en_US.qm b/gui/lang/en_US.qm
new file mode 100644
index 0000000..7cf3284
Binary files /dev/null and b/gui/lang/en_US.qm differ
diff --git a/lang/en_US.ts b/gui/lang/en_US.ts
similarity index 61%
rename from lang/en_US.ts
rename to gui/lang/en_US.ts
index 89d19d9..bb10557 100644
--- a/lang/en_US.ts
+++ b/gui/lang/en_US.ts
@@ -1,314 +1,352 @@
- HostsUtlMain
+ Util
-
-
-
+
+
+ Google Web Service (CN)
-
-
-
+
+
+ Google Web Service (HK)
-
-
-
+
+
+ Google Web Service (US)
-
-
-
+
+
+ Google API Service (CN)
-
-
+
+
+ Google API Service (US)
+
+
+
+
-
-
+
+
+ Facebook
+
+
+
+
+ twitter
+
+
+
+
+ YouTube
+
+
+
+
+ Wikipedia
+
+
+
+
+ Academy and Research institutions
+
+
+
+
+ Steam
+
+
+
+
+ Other services
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
- Google Web Service (CN)
+
+
+
-
-
- Google Web Service (US)
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
- Google API Service (CN)
-
-
-
-
- Google API Service (US)
-
-
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+ Google Web Service
-
-
- Google Web Service (HK)
+
+
+ Google API Service
-
-
- Facebook
+
+
+ Customized Hosts
-
-
- twitter
+
+
+ GitHub
-
-
- YouTube
+
+
+ Dropbox
-
-
- Academy and Research institutions
+
+
+ WordPress
diff --git a/gui/lang/zh_CN.qm b/gui/lang/zh_CN.qm
new file mode 100644
index 0000000..76f19ae
Binary files /dev/null and b/gui/lang/zh_CN.qm differ
diff --git a/lang/zh_CN.ts b/gui/lang/zh_CN.ts
similarity index 67%
rename from lang/zh_CN.ts
rename to gui/lang/zh_CN.ts
index 12407aa..29c4be6 100644
--- a/lang/zh_CN.ts
+++ b/gui/lang/zh_CN.ts
@@ -1,193 +1,163 @@
- HostsUtlMain
+ Util
-
+ Google Web服务(北京)
-
+ Google Web服务(香港)
-
+ Google Web服务(美国)
-
+ Google 应用程序服务(北京)
-
+ Google 应用程序服务(美国)
-
+ 屏蔽部分破解软件激活服务器
-
+ Facebook
-
+ twitter
-
+ YouTube
-
+ 维基百科
-
+ 教育科研机构
-
+ Steam 游戏平台
-
+ 其他墙外站点
-
+ 广告屏蔽-hostsx 列表
-
+ 广告屏蔽-mvps 列表
-
+ 广告屏蔽-mwsl 列表
-
+ 广告屏蔽-yoyo 列表
-
+ 备份 hosts 文件
-
+ 备份文件(*.bak)
-
+ 还原备份的 hosts 文件
-
+ [错误]
-
+ 正在连接...
-
+ 导出 hosts 文件
-
+ hosts 文件
-
-
- 正在生成 hosts 文件...
-
-
-
+
- 正在将新的 hosts 配置到目标路径
+ 正在将新的 hosts 配置到目标路径
%s
-
+ 清理临时文件
-
+ 操作完成
-
-
- [正常]
-
-
-
-
- [失败]
-
-
-
-
- 功能列表
-
-
-
-
- 应用选定的模块: %s(%s/%s)
-
-
-
-
- 操作进度
-
-
-
+ 注意:共有 %i 条 hosts 条目在
%s秒内被插入到 hosts 文件中。
-
+ 操作成功完成!
-
+ 错误
-
+
@@ -196,17 +166,42 @@ fetch a new data file.
新的数据文件。
-
+ 下载完成
-
+
+
+ [正常]
+
+
+
+
+ [失败]
+
+
+
+
+ 功能列表
+
+
+
+
+ 应用选定的模块: %s(%s/%s)
+
+
+
+
+ 操作进度
+
+
+ 警告
-
+
工具。
-
+ 在从服务器获取数据是发生错误。
请在更换服务器之后尝试先前的操作。
-
+
@@ -233,12 +228,12 @@ fetch a new data file.
新的数据文件。
-
+ 注意
-
+
不可逆转。
-
+ 数据文件已经是最新版本。
-
+ 完成
-
-
- 正在连接服务器...
-
-
-
-
- 正在下载: %s / %s
+
+
+ 正在生成 hosts 文件...
-
+ hosts 文件配置工具
-
+ 设置
-
+ 服务器
-
+ IP 协议版本
-
+ 状态
-
+ 连接
-
+ 无状态
-
+ 操作系统
-
+ 数据文件信息
-
+ 当前版本
-
+ 发布日期
-
+ 最新版本
-
+ 备份当前系统的 hosts 文件。
-
+ 下载数据文件
-
+ 下载最新数据文件。
-
+ 还原备份
-
+ 还原先前备份的 hosts 文件。
-
+ 更改 hosts
-
+ 对 hosts 文件进行修改。
-
+ 退出
-
+ 关闭本工具。
-
+ 检查更新/刷新
-
+ 在线检查数据文件的最新版本。
-
+ 保存为 ANSI 格式
-
+ 以 ANSI 的编码方式导出 hosts 文件。
-
+ 保存为 UTF-8 格式
-
+ 以 UTF-8 的编码方式导出 hosts 文件。
-
+ 公共版权 (C) 2011-2014 <a href="https://hosts.huhamhire.com/"><span style="text-decoration: none;color: #b1b1b1;">huhamhire-hosts</span></a>
-
+
-
+
+
+ 正在连接服务器...
+
+
+
+
+ 正在下载: %s / %s
+
+
+ 注意:共有 %i 条 hosts 条目在
%s秒内被插入到 hosts 文件中。
-
+
@@ -433,7 +433,7 @@ fetch a new data file.
新的数据文件。
-
+
工具。
-
+
@@ -453,7 +453,7 @@ fetch a new data file.
新的数据文件。
-
+
若先前未对 hosts 文件进行备份,该操作将
不可逆转。
+
+
+
+ 正在将新的 hosts 配置到目标路径
+ %s
+
+
+
+
+ Google Web服务
+
+
+
+
+ Google 应用程序服务
+
+
+
+
+ 用户自定义列表
+
+
+
+
+ GitHub
+
+
+
+
+ Dropbox
+
+
+
+
+ WordPress
+
diff --git a/gui/lang/zh_TW.qm b/gui/lang/zh_TW.qm
new file mode 100644
index 0000000..0d30797
Binary files /dev/null and b/gui/lang/zh_TW.qm differ
diff --git a/lang/zh_TW.ts b/gui/lang/zh_TW.ts
similarity index 59%
rename from lang/zh_TW.ts
rename to gui/lang/zh_TW.ts
index 3c9adbf..30a9841 100644
--- a/lang/zh_TW.ts
+++ b/gui/lang/zh_TW.ts
@@ -1,419 +1,505 @@
- HostsUtlMain
+ Util
-
+ Google Web服務(大陸)
-
+
+
+ Google Web服務(香港)
+
+
+ Google Web服務(美國)
-
+
+
+ Google API服務(大陸)
+
+
+
+
+ Google API服務(美國)
+
+
+ 遮罩部分破解軟體啟動伺服器
-
+
+
+ Facebook
+
+
+
+
+ twitter
+
+
+
+
+ YouTube
+
+
+
+
+ 維基百科
+
+
+
+
+ 教育科研機構
+
+
+
+
+ Steam 遊戲平台
+
+
+ 其他網站
-
+ 廣告攔截-hostsx 清單
-
+ 廣告攔截-mvps 清單
-
+ 廣告攔截-mwsl 清單
-
+ 廣告攔截-yoyo 清單
-
-
- 正在創建 hosts 檔...
-
-
-
+ 備份 hosts 檔
-
+ 備份檔(*.bak)
-
+ 還原 hosts
-
+ [錯誤]
-
+ 正在連接伺服器...
-
+
+
+ 匯出 hosts 檔
+
+
+
+
+ hosts 檔
+
+
+
- 將新的 hosts 檔案複製到
+ 將新的 hosts 檔案複製到
%s
-
+ 刪除暫存檔案
-
+ 作業完成
-
-
- [好]
-
-
-
-
- [失敗]
-
-
-
-
- 功能清單
-
-
-
-
- 應用選定的模組: %s(%s/%s)
-
-
-
-
- 作業進度
-
-
-
-
-
+ Operation Completed Successfully!作業已成功完成 !
-
+ Error錯誤
-
+ Incorrect Data file!
-Please use the "Download" key to
+Please use the "Download" key to
fetch a new data file.
- 不正確的資料檔案 !
+ 不正確的資料檔案 !
請使用"下載"按鈕來獲得
一個新的資料檔案。
-
+ Download Complete下載已完成
-
+
+ [OK]
+ [好]
+
+
+
+ [Failed]
+ [失敗]
+
+
+
+ Functions
+ 功能清單
+
+
+
+ Applying module: %s(%s/%s)
+ 應用選定的模組: %s(%s/%s)
+
+
+
+ Progress
+ 作業進度
+
+
+ Warning警告
-
- You do not have permissions to change the
+
+ You do not have permissions to change the
hosts file.
Please run this program as Administrator/root
so it can modify your hosts file.
- 您目前沒有許可權以更改 hosts 檔。
+ 您目前沒有許可權以更改 hosts 檔。
請以管理員方式或者根使用者運行本
程式。
-
+ Error retrieving data from the server.
Please try another server.從伺服器中檢索資料時出錯。
請嘗試使用另一台伺服器。
-
+ Data file not found!
-Please use the "Download" key to
+Please use the "Download" key to
fetch a new data file.
- 找不到本地資料檔案!
+ 找不到本地資料檔案!
請使用"下載"按鈕來獲得
一個新的資料檔案。
-
+ Notice消息
-
+
+ Are you sure you want to apply changes
+to the hosts file on your system?
+
+This operation could not be reverted if
+you have not made a backup of your
+current hosts file.
+ 您確認繼續執行當前操作以修改系統 hosts
+檔嗎?
+
+若先前未對 hosts 檔進行備份,該操作將
+不可逆轉。
+
+
+ Data file is up-to-date.當前的資料檔案是最新的。
-
+ Complete完成
-
- Connecting...
- 正在連接...
-
-
-
- Downloading: %s / %s
- 正在下載: %s / %s
+
+ Building hosts file...
+ 正在創建 hosts 檔...
-
+ Hosts Setup Utilityhosts 設置實用程式
-
+ Config配置
-
+ Server伺服器
-
+ IP VersionIP 協定版本
-
+ Status狀態
-
+ Connection連接狀態
-
+ N/A不適用
-
+ OS作業系統
-
+
+ Hosts Info
+ 資料檔案狀態
+
+
+
+ Version
+ 當前版本
+
+
+
+ Release
+ 發佈日期
+
+
+
+ Latest
+ 最新版本
+
+
+ Backup the hosts file of current system.備份當前系統的 hosts 檔。
-
+ Download data file下載資料檔案
-
+ Download the latest data file.下載最新的資料檔案。
-
+ Restore backup還原備份
-
+ Restore a previous backup of hosts file.還原以前的備份的 hosts 檔。
-
+ Apply hosts更改 hosts 檔
-
+ Apply changes to the hosts file.將更改應用到主 hosts 檔。
-
+ Exit退出
-
+ Close this tool.關閉此程式。
-
+ Check update / Refresh檢查更新 / 刷新
-
+ Check the latest version of hosts data file.檢查 hosts 檔案的最新版本。
-
- Hosts Info
- 資料檔案狀態
-
-
-
- Version
- 當前版本
-
-
-
- Release
- 發佈日期
-
-
-
- Latest
- 最新版本
-
-
-
- Are you sure you want to apply changes
-to the hosts file on your system?
-
-This operation could not be reverted if
-you have not made a backup of your
-current hosts file.
- 您確認繼續執行當前操作以修改系統 hosts
-檔嗎?
-
-若先前未對 hosts 檔進行備份,該操作將
-不可逆轉。
-
-
-
+ Save with ANSI保存為 ANSI 格式
-
+ Export to hosts file encoding by ANSI.匯出由 ANSI 編碼的 hosts 檔。
-
+ Save with UTF-8保存為 UTF-8 格式
-
+ Export to hosts file encoding by UTF-8.匯出由 UTF-8 編碼的 hosts 檔。
-
- Export hosts
- 匯出 hosts 檔
+
+ Copyleft (C) 2011-2014 <a href="https://hosts.huhamhire.com/"><span style="text-decoration: none;color: #b1b1b1;">huhamhire-hosts</span></a>
+ 公共版權 (C) 2011-2014 <a href="https://hosts.huhamhire.com/"><span style="text-decoration: none;color: #b1b1b1;">huhamhire-hosts</span></a>
-
- hosts File
- hosts 檔
+
+ Powered by PyQT
+
-
- google-apis(cn)
- Google API服務(大陸)
+
+ Connecting...
+ 正在連接...
-
- google-apis(us)
- Google API服務(美國)
+
+ Downloading: %s / %s
+ 正在下載: %s / %s
-
- wikipedia
- 維基百科
+
+ Notice: %i hosts entries has
+ been applied in %ssecs.
+ 消息:共有 %i 条 hosts 条目在
+ %s秒内被插入到 hosts 文件中。
-
- steam
- Steam 遊戲平台
+
+ Incorrect Data file!
+Please use the "Download" key to
+fetch a new data file.
+ 不正確的資料檔案 !
+請使用"下載"按鈕來獲得
+一個新的資料檔案。
-
- Copyleft (C) 2011-2014 <a href="https://hosts.huhamhire.com/"><span style="text-decoration: none;color: #b1b1b1;">huhamhire-hosts</span></a>
- 公共版權 (C) 2011-2014 <a href="https://hosts.huhamhire.com/"><span style="text-decoration: none;color: #b1b1b1;">huhamhire-hosts</span></a>
+
+ You do not have permissions to change the
+hosts file.
+Please run this program as Administrator/root
+so it can modify your hosts file.
+ 您目前沒有許可權以更改 hosts 檔。
+請以管理員方式或者根使用者運行本
+程式。
-
- Powered by PyQT
-
+
+ Data file not found!
+Please use the "Download" key to
+fetch a new data file.
+ 找不到本地資料檔案!
+請使用"下載"按鈕來獲得
+一個新的資料檔案。
-
- google(hk)
- Google Web服務(香港)
+
+ Are you sure you want to apply changes
+to the hosts file on your system?
+
+This operation could not be reverted if
+you have not made a backup of your
+current hosts file.
+ 您確認繼續執行當前操作以修改系統 hosts
+檔嗎?
+
+若先前未對 hosts 檔進行備份,該操作將
+不可逆轉。
-
- facebook
- Facebook
+
+ Copying new hosts file to
+%s
+ 將新的 hosts 檔案複製到
+ %s
-
- twitter
- twitter
+
+ google
+ Google Web服務
-
- youtube
- YouTube
+
+ google-apis
+ Google API服務
-
- institutions
- 教育科研機構
+
+ customize
+ 使用者自訂清單
+
+
+
+ github
+ GitHub
+
+
+
+ dropbox
+ Dropbox
+
+
+
+ wordpress
+ Wordpress
diff --git a/gui/language.py b/gui/language.py
new file mode 100644
index 0000000..4be7b42
--- /dev/null
+++ b/gui/language.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# language.py : Language utilities used in GUI module.
+#
+# Copyleft (C) 2013 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import locale
+
+
+class LangUtil(object):
+ """
+ LangUtil contains a set of language tools for Hosts Setup Utility to
+ use.
+
+ .. note:: All methods from this class are declared as `classmethod`.
+
+ :ivar dict language: Supported localized language name for a specified
+ locale tag.
+
+ The default declaration of :attr:`language` is::
+
+ language = {"de_DE": u"Deutsch",
+ "en_US": u"English",
+ "ja_JP": u"日本語",
+ "ko_KR": u"한글",
+ "ru_RU": u"Русский",
+ "zh_CN": u"简体中文",
+ "zh_TW": u"正體中文", }
+
+ .. note:: The keys of :attr:`language` are typically in the format of \
+ IETF language tag. For example: en_US, en_GB, etc.
+
+ .. note:: The Hosts Setup Utility would check whether the language
+ files exist in `gui/lang/` folder automatically while loading GUI.
+ If not, the language related wouldn't be available in language
+ selection combobox.
+ """
+ language = {"de_DE": u"Deutsch",
+ "en_US": u"English",
+ "ja_JP": u"日本語",
+ "ko_KR": u"한글",
+ "ru_RU": u"Русский",
+ "zh_CN": u"简体中文",
+ "zh_TW": u"正體中文", }
+
+ @classmethod
+ def get_locale(cls):
+ """
+ Retrieve the default locale tag of current operating system.
+
+ .. note:: This is a `classmethod`.
+
+ :return: Default locale tag of current operating system. If the locale
+ is not found in cls.dictionary dictionary, the return value
+ "en_US" as default.
+ :rtype: str
+ """
+ lc = locale.getdefaultlocale()[0]
+ if lc is None:
+ lc = "en_US"
+ return lc
+
+ @classmethod
+ def get_language_by_locale(cls, l_locale):
+ """
+ Return the name of a specified language by :attr:`l_locale`.
+
+ .. note:: This is a `classmethod`.
+
+ :param l_locale: Locale tag of a specified language.
+ :type l_locale: str
+ :return: Localized name of a specified language.
+ :type: str
+ """
+ try:
+ return cls.language[l_locale]
+ except KeyError:
+ return cls.language["en_US"]
+
+ @classmethod
+ def get_locale_by_language(cls, l_lang):
+ """
+ Get the locale string connecting with a language specified by
+ :attr:`l_lang`.
+
+ .. note:: This is a `classmethod`.
+
+ :param l_lang: Localized name of a specified language.
+ :type l_lang: unicode
+ :return: Locale tag of a specified language.
+ :rtype: str
+ """
+ for locl, lang in cls.language.items():
+ if l_lang == lang:
+ return locl
+ return "en_US"
diff --git a/_pylupdate4.py b/gui/pyqt/_pylupdate4.py
similarity index 79%
rename from _pylupdate4.py
rename to gui/pyqt/_pylupdate4.py
index d52f674..73387b8 100644
--- a/_pylupdate4.py
+++ b/gui/pyqt/_pylupdate4.py
@@ -3,7 +3,7 @@
#
# _pylupdate4.py : Tools to update the language files for UI
#
-# Copyleft (C) 2013 - huhamhire hosts team
+# Copyleft (C) 2013 - huhamhire hosts team
# =====================================================================
# Licensed under the GNU General Public License, version 3. You should
# have received a copy of the GNU General Public License along with
@@ -17,6 +17,6 @@
import os
for root, dirs, files in os.walk('.'):
- for file in files:
- if file.endswith('.pro'):
- os.system('pylupdate4 %s' % file)
+ for f in files:
+ if f.endswith('.pro'):
+ os.system('pylupdate4 %s' % f)
diff --git a/gui/pyqt/_pyuic4.py b/gui/pyqt/_pyuic4.py
new file mode 100644
index 0000000..5e09438
--- /dev/null
+++ b/gui/pyqt/_pyuic4.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# _pyuic4.py : Tools update the UI code from UI design
+#
+# Copyleft (C) 2013 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+import os
+
+PROJ_DIR = '../'
+
+for root, dirs, files in os.walk(PROJ_DIR):
+ for f in files:
+ file_path = os.path.join(root, f)
+ out_path = os.path.join(PROJ_DIR, f.rsplit('.', 1)[0])
+ if f.endswith('.ui'):
+ os.system('pyuic4 -o %s.py -x %s' % (out_path, file_path))
+ print("make: %s.py" % out_path)
+ elif f.endswith('.qrc'):
+ os.system('pyrcc4 -o %s_rc.py %s' % (out_path, file_path))
+ print("make: %s_rc.py" % out_path)
+ else:
+ pass
diff --git a/gui/pyqt/style.qrc b/gui/pyqt/style.qrc
new file mode 100644
index 0000000..a21e688
--- /dev/null
+++ b/gui/pyqt/style.qrc
@@ -0,0 +1,6 @@
+
+
+ ../../res/img/style/checkbox.png
+ ../../res/img/style/down_arrow.png
+
+
diff --git a/gui/pyqt/util.pro b/gui/pyqt/util.pro
new file mode 100644
index 0000000..870bf64
--- /dev/null
+++ b/gui/pyqt/util.pro
@@ -0,0 +1,19 @@
+SOURCES = ../hostsutil.py \
+ ../__list_trans.py \
+ ../_checkconn.py \
+ ../_checkupdate.py \
+ ../_make.py \
+ ../_update.py \
+ ../qdialog_ui.py \
+ ../qdialog_d.py \
+ ../qdialog_slots.py \
+ ../util_ui.py
+TRANSLATIONS = ../lang/de_DE.ts \
+ ../lang/en_US.ts \
+ ../lang/zh_CN.ts \
+ ../lang/zh_TW.ts
+INTERFACES = util_ui.ui
+RESOURCES = util.qrc \
+ style.qrc
+CODECFORTR = UTF-8
+CODECFORSRC = UTF-8
diff --git a/gui/pyqt/util.qrc b/gui/pyqt/util.qrc
new file mode 100644
index 0000000..cee83bb
--- /dev/null
+++ b/gui/pyqt/util.qrc
@@ -0,0 +1,23 @@
+
+
+ ../../res/img/buttons/button_ansi.png
+ ../../res/img/buttons/button_ansi_disabled.png
+ ../../res/img/buttons/button_apply.png
+ ../../res/img/buttons/button_apply_disabled.png
+ ../../res/img/buttons/button_backup.png
+ ../../res/img/buttons/button_backup_disabled.png
+ ../../res/img/buttons/button_download.png
+ ../../res/img/buttons/button_download_disabled.png
+ ../../res/img/buttons/button_exit.png
+ ../../res/img/buttons/button_exit_disabled.png
+ ../../res/img/buttons/button_restore.png
+ ../../res/img/buttons/button_restore_disabled.png
+ ../../res/img/buttons/button_update.png
+ ../../res/img/buttons/button_update_disabled.png
+ ../../res/img/buttons/button_utf8.png
+ ../../res/img/buttons/button_utf8_disabled.png
+
+
+ ../../res/img/icons/utl_icon@256x256.png
+
+
diff --git a/qthostsui.ui b/gui/pyqt/util_ui.ui
similarity index 87%
rename from qthostsui.ui
rename to gui/pyqt/util_ui.ui
index 6d01345..87b2915 100644
--- a/qthostsui.ui
+++ b/gui/pyqt/util_ui.ui
@@ -1,8 +1,8 @@
huhamhire
- HostsUtlMain
-
+ Util
+ true
@@ -39,8 +39,8 @@
Hosts Setup Utility
-
- :/icon/img/utl_icon.png:/icon/img/utl_icon.png
+
+ :/icon/res/img/icons/utl_icon@256x256.png:/icon/res/img/icons/utl_icon@256x256.png
@@ -344,9 +344,9 @@
-
- :/buttons/img/buttons/button_backup.png
- :/buttons/img/buttons/button_backup_disabled.png:/buttons/img/buttons/button_backup.png
+
+ :/buttons/res/img/buttons/button_backup.png
+ :/buttons/res/img/buttons/button_backup_disabled.png:/buttons/res/img/buttons/button_backup.png
@@ -374,9 +374,9 @@
-
- :/buttons/img/buttons/button_download.png
- :/buttons/img/buttons/button_download_disabled.png:/buttons/img/buttons/button_download.png
+
+ :/buttons/res/img/buttons/button_download.png
+ :/buttons/res/img/buttons/button_download_disabled.png:/buttons/res/img/buttons/button_download.png
@@ -404,9 +404,9 @@
-
- :/buttons/img/buttons/button_restore.png
- :/buttons/img/buttons/button_restore_disabled.png:/buttons/img/buttons/button_restore.png
+
+ :/buttons/res/img/buttons/button_restore.png
+ :/buttons/res/img/buttons/button_restore_disabled.png:/buttons/res/img/buttons/button_restore.png
@@ -434,9 +434,9 @@
-
- :/buttons/img/buttons/button_apply.png
- :/buttons/img/buttons/button_apply_disabled.png:/buttons/img/buttons/button_apply.png
+
+ :/buttons/res/img/buttons/button_apply.png
+ :/buttons/res/img/buttons/button_apply_disabled.png:/buttons/res/img/buttons/button_apply.png
@@ -464,9 +464,9 @@
-
- :/buttons/img/buttons/button_exit.png
- :/buttons/img/buttons/button_exit_disabled.png:/buttons/img/buttons/button_exit.png
+
+ :/buttons/res/img/buttons/button_exit.png
+ :/buttons/res/img/buttons/button_exit_disabled.png:/buttons/res/img/buttons/button_exit.png
@@ -494,9 +494,9 @@
-
- :/buttons/img/buttons/button_update.png
- :/buttons/img/buttons/button_update_disabled.png:/buttons/img/buttons/button_update.png
+
+ :/buttons/res/img/buttons/button_update.png
+ :/buttons/res/img/buttons/button_update_disabled.png:/buttons/res/img/buttons/button_update.png
@@ -524,9 +524,9 @@
-
- :/buttons/img/buttons/button_ansi.png
- :/buttons/img/buttons/button_ansi_disabled.png:/buttons/img/buttons/button_ansi.png
+
+ :/buttons/res/img/buttons/button_ansi.png
+ :/buttons/res/img/buttons/button_ansi_disabled.png:/buttons/res/img/buttons/button_ansi.png
@@ -554,9 +554,9 @@
-
- :/buttons/img/buttons/button_utf8.png
- :/buttons/img/buttons/button_utf8_disabled.png:/buttons/img/buttons/button_utf8.png
+
+ :/buttons/res/img/buttons/button_utf8.png
+ :/buttons/res/img/buttons/button_utf8_disabled.png:/buttons/res/img/buttons/button_utf8.png
@@ -638,6 +638,19 @@
Powered by PyQT
+
+
+
+ 380
+ 20
+ 250
+ 25
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+ SelectMirror
@@ -654,14 +667,14 @@
SelectLang
-
+ ButtonExitclicked()
- HostsUtlMain
+ Utilclose()
@@ -677,7 +690,7 @@
SelectIPcurrentIndexChanged(int)
- HostsUtlMain
+ Utilon_IPVersion_changed(int)
@@ -693,7 +706,7 @@
SelectMirrorcurrentIndexChanged(int)
- HostsUtlMain
+ Utilon_Mirror_changed(int)
@@ -709,7 +722,7 @@
FunctionlistitemChanged(QListWidgetItem*)
- HostsUtlMain
+ Utilon_Selection_changed(QListWidgetItem*)
@@ -725,7 +738,7 @@
ButtonApplyclicked()
- HostsUtlMain
+ Utilon_MakeHosts_clicked()
@@ -741,7 +754,7 @@
ButtonBackupclicked()
- HostsUtlMain
+ Utilon_Backup_clicked()
@@ -757,7 +770,7 @@
ButtonRestoreclicked()
- HostsUtlMain
+ Utilon_Restore_clicked()
@@ -773,7 +786,7 @@
ButtonCheckclicked()
- HostsUtlMain
+ Utilon_CheckUpdate_clicked()
@@ -789,7 +802,7 @@
ButtonUpdateclicked()
- HostsUtlMain
+ Utilon_FetchUpdate_clicked()
@@ -805,7 +818,7 @@
SelectLangcurrentIndexChanged(QString)
- HostsUtlMain
+ Utilon_Lang_changed(QString)
@@ -821,7 +834,7 @@
ButtonANSIclicked()
- HostsUtlMain
+ Utilon_MakeANSI_clicked()
@@ -837,7 +850,7 @@
ButtonUTFclicked()
- HostsUtlMain
+ Utilon_MakeUTF8_clicked()
@@ -853,7 +866,7 @@
CopyrightlinkActivated(QString)
- HostsUtlMain
+ Utilon_LinkActivated(QString)
diff --git a/gui/qdialog_d.py b/gui/qdialog_d.py
new file mode 100644
index 0000000..0ad8418
--- /dev/null
+++ b/gui/qdialog_d.py
@@ -0,0 +1,440 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# qdialog_d.py : Operations on the main dialog.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+import shutil
+
+from zipfile import BadZipfile
+from PyQt4 import QtCore, QtGui
+
+from _checkconn import QSubChkConnection
+from _checkupdate import QSubChkUpdate
+from _make import QSubMakeHosts
+from _update import QSubFetchUpdate
+from qdialog_ui import QDialogUI
+from util_ui import _translate
+
+import sys
+sys.path.append("..")
+from util import RetrieveData, CommonUtil
+
+
+class QDialogDaemon(QDialogUI):
+ """
+ QDialogDaemon class contains methods used to manage the operations while
+ modifying the hosts file of current operating system. Including methods
+ to manage operations to update data file, download data file, configure
+ hosts, make hosts file, backup hosts file, and restore backup.
+
+ .. note:: This class is subclass of :class:`~gui.qdialog_ui.QDialogUI`
+ class and parent class of :class:`~gui.qdialog_slots.QDialogSlots`.
+
+ :ivar int_down_flag: An flag indicating the downloading status of
+ current session. 1 represents data file is being downloaded.
+ :ivar dict _update: Update information of the current data file on server.
+ :ivar int _writable: Indicating whether the program is run with admin/root
+ privileges. The value could be `1` or `0`.
+
+ .. seealso:: `_update` and `_writable` in
+ :class:`~tui.curses_d.CursesDaemon` class.
+
+ :ivar list _funcs: Two lists with the information of function list both
+ for IPv4 and IPv6 environment.
+ :ivar list choice: Two lists with the selection of functions both
+ for IPv4 and IPv6 environment.
+ :ivar list slices: Two lists with integers indicating the number of
+ function items from different parts listed in the function list.
+
+ .. seealso:: `_funcs`, `choice`, and `slices` in
+ :class:`~tui.curses_ui.CursesUI` class.
+
+ :ivar dict make_cfg: A set of module selection control bytes used to
+ control whether a specified method is used or not while generate a
+ hosts file.
+
+ .. seealso:: :attr:`make_cfg` in
+ :class:`~tui.curses_d.CursesDaemon` class.
+ :ivar str platform: Platform of current operating system. The value could
+ be `Windows`, `Linux`, `Unix`, `OS X`, and of course `Unknown`.
+ :ivar str hostname: The hostname of current operating system.
+
+ .. note:: This attribute would only be used on linux.
+
+ :ivar str hosts_path: The absolute path to the hosts file on current
+ operating system.
+ :ivar str make_mode: Operation mode for making hosts file. The valid value
+ could be one of `system`, `ansi`, and `utf-8`.
+
+ .. seealso:: :attr:`make_mode` in
+ :class:`~util.makehosts.MakeHosts` class.
+
+ :ivar str sys_eol: The End-Of-Line marker. This maker could typically be
+ one of `CR`, `LF`, or `CRLF`.
+
+ .. seealso:: :attr:`sys_eol` in
+ :class:`~tui.curses_ui.CursesUI` class.
+ """
+ _down_flag = 0
+
+ _update = {}
+ _writable = 0
+
+ _funcs = [[], []]
+ choice = [[], []]
+ slices = [[], []]
+ make_cfg = {}
+ platform = ''
+ hostname = ''
+ hosts_path = ''
+ sys_eol = ''
+
+ make_mode = ''
+
+ def __init__(self):
+ super(QDialogDaemon, self).__init__()
+ self.set_platform()
+ self.set_platform_label()
+
+ def check_writable(self):
+ """
+ Check if current session is ran with root privileges.
+
+ .. note:: IF current session does not has the write privileges to the
+ hosts file of current system, a warning message box would popup.
+
+ .. note:: ALL operation would change the `hosts` file on current
+ system could only be done while current session has write
+ privileges to the file.
+ """
+ writable = CommonUtil.check_privileges()[1]
+ self._writable = writable
+ if not writable:
+ self.warning_permission()
+
+ def check_connection(self):
+ """
+ Operations to check the connection to current server.
+ """
+ thread = QSubChkConnection(self)
+ thread.trigger.connect(self.set_conn_status)
+ thread.start()
+
+ def check_update(self):
+ """
+ Retrieve the metadata of the latest data file from a server.
+ """
+ self.set_update_start_btns()
+ self.set_label_text(self.ui.labelLatestData, unicode(
+ _translate("Util", "Checking...", None)))
+ thread = QSubChkUpdate(self)
+ thread.trigger.connect(self.finish_update)
+ thread.start()
+
+ def fetch_update(self):
+ """
+ Retrieve a new hosts data file from a server.
+ """
+ self.set_fetch_start_btns()
+ thread = QSubFetchUpdate(self)
+ thread.prog_trigger.connect(self.set_down_progress)
+ thread.finish_trigger.connect(self.finish_fetch)
+ thread.start()
+
+ def fetch_update_after_check(self):
+ """
+ Decide whether to retrieve a new data file from server or not after
+ checking update information from a mirror.
+ """
+ if self._update["version"] == \
+ unicode(_translate("Util", "[Error]", None)):
+ self.finish_fetch(error=1)
+ elif self.new_version():
+ self.fetch_update()
+ else:
+ self.info_uptodate()
+ self.finish_fetch()
+
+ def export_hosts(self):
+ """
+ Display the export dialog and get the path to save the exported hosts
+ file.
+
+ :return: Path to export a hosts file.
+ :rtype: str
+ """
+ filename = "hosts"
+ if self.platform == "OS X":
+ filename = "/Users/" + filename
+ filepath = QtGui.QFileDialog.getSaveFileName(
+ self, _translate("Util", "Export hosts", None),
+ QtCore.QString(filename),
+ _translate("Util", "hosts File", None))
+ return filepath
+
+ def make_hosts(self, mode="system"):
+ """
+ Make a new hosts file for current system.
+
+ :param mode: Operation mode for making hosts file. The valid value
+ could be one of `system`, `ansi`, and `utf-8`.
+ Default by `system`.
+ :type mode: str
+ """
+ self.set_make_start_btns()
+ self.set_make_message(unicode(_translate(
+ "Util", "Building hosts file...", None)), 1)
+ # Avoid conflict while making hosts file
+ RetrieveData.disconnect_db()
+ self.make_mode = mode
+ self.set_config_bytes(mode)
+ thread = QSubMakeHosts(self)
+ thread.info_trigger.connect(self.set_make_progress)
+ thread.fina_trigger.connect(self.finish_make)
+ thread.move_trigger.connect(self.move_hosts)
+ thread.start()
+
+ def move_hosts(self):
+ """
+ Move hosts file to the system path after making.
+
+ .. note:: This method is the slot responses to the move_trigger signal
+ from an instance of :class:`~gui._make.QSubMakeHosts` class while
+ making operations are finished.
+
+ .. seealso:: :attr:`move_trigger` in
+ :class:`~gui._make.QSubMakeHosts`.
+ """
+ filepath = "hosts"
+ msg = unicode(
+ _translate("Util", "Copying new hosts file to\n"
+ "%s", None)) % self.hosts_path
+ self.set_make_message(msg)
+ try:
+ shutil.copy2(filepath, self.hosts_path)
+ except IOError:
+ self.warning_permission()
+ os.remove(filepath)
+ return
+ except OSError:
+ pass
+ msg = unicode(
+ _translate("Util", "Remove temporary file", None))
+ self.set_make_message(msg)
+ os.remove(filepath)
+ msg = unicode(
+ _translate("Util", "Operation completed", None))
+ self.set_make_message(msg)
+ self.info_complete()
+
+ def set_platform(self):
+ """
+ Set the information of current operating system platform.
+ """
+ system, hostname, path, encode, flag = CommonUtil.check_platform()
+ self.platform = system
+ self.hostname = hostname
+ self.hosts_path = path
+ self.plat_flag = flag
+ if encode == "win_ansi":
+ self.sys_eol = "\r\n"
+ else:
+ self.sys_eol = "\n"
+
+ def set_config_bytes(self, mode):
+ """
+ Generate the module configuration byte words by the selection from
+ function list on the main dialog.
+
+ :param mode: Operation mode for making hosts file. The valid value
+ could be one of `system`, `ansi`, and `utf-8`.
+
+ .. seealso:: Method
+ :meth:`~gui.qdialog_d.QDialogDaemon.make_hosts`.
+ """
+ ip_flag = self._ipv_id
+ selection = {}
+ if mode == "system":
+ localhost_word = {
+ "Windows": 0x0001, "Linux": 0x0002,
+ "Unix": 0x0002, "OS X": 0x0004}[self.platform]
+ else:
+ localhost_word = 0x0008
+ selection[0x02] = localhost_word
+ ch_parts = [0x08, 0x20 if ip_flag else 0x10, 0x40]
+ # Set customized module if exists
+ if os.path.isfile(self.custom):
+ ch_parts.insert(0, 0x04)
+ slices = self.slices[ip_flag]
+ for i, part in enumerate(ch_parts):
+ part_cfg = self._funcs[ip_flag][slices[i]:slices[i + 1]]
+ part_word = 0
+ for i, cfg in enumerate(part_cfg):
+ part_word += cfg << i
+ selection[part] = part_word
+ self.make_cfg = selection
+
+ def refresh_info(self, refresh=0):
+ """
+ Reload the data file information and show them on the main dialog. The
+ information here includes both metadata and hosts module info from the
+ data file.
+
+ :param refresh: A flag indicating whether the information on main
+ dialog needs to be reloaded or not. The value could be `0` or `1`.
+
+ ======= =============
+ refresh operation
+ ======= =============
+ 0 Do NOT reload
+ 1 Reload
+ ======= =============
+
+ :type refresh: int
+ """
+ if refresh and RetrieveData.conn is not None:
+ RetrieveData.clear()
+ try:
+ RetrieveData.unpack()
+ RetrieveData.connect_db()
+ self.set_func_list(refresh)
+ self.refresh_func_list()
+ self.set_info()
+ except (BadZipfile, IOError, OSError):
+ self.warning_incorrect_datafile()
+
+ def finish_make(self, time, count):
+ """
+ Start operations after making new hosts file.
+
+ .. note:: This method is the slot responses to the fina_trigger signal
+ values :attr:`time`, :attr:`count` from an instance of
+ :class:`~gui._make.QSubMakeHosts` class while making operations
+ are finished.
+
+ :param time: Total time uesd while generating the new hosts file.
+ :type time: str
+ :param count: Total number of hosts entries inserted into the new
+ hosts file.
+ :type count: int
+
+ .. seealso:: :attr:`fina_trigger` in
+ :class:`~gui._make.QSubMakeHosts` class.
+ """
+ self.set_make_finish_btns()
+ RetrieveData.connect_db()
+ msg = unicode(
+ _translate("Util", "Notice: %i hosts entries has "
+ "\n been applied in %ssecs.", None))\
+ % (count, time)
+ self.set_make_message(msg)
+ self.set_down_progress(100, unicode(
+ _translate("Util", "Operation Completed Successfully!", None)))
+
+ def finish_update(self, update):
+ """
+ Start operations after checking update.
+
+ .. note:: This method is the slot responses to the trigger signal
+ value :attr:`update` from an instance of
+ :class:`~gui._checkupdate.QSubChkUpdate` class while checking
+ operations are finished.
+
+ :param update: Metadata of the latest hosts data file on the server.
+ :type update: dict
+
+ .. seealso:: :attr:`trigger` in
+ :class:`~gui._checkupdate.QSubChkUpdate` class.
+ """
+ self._update = update
+ self.set_label_text(self.ui.labelLatestData, update["version"])
+ if self._update["version"] == \
+ unicode(_translate("Util", "[Error]", None)):
+ self.set_conn_status(0)
+ else:
+ self.set_conn_status(1)
+ if self._down_flag:
+ self.fetch_update_after_check()
+ else:
+ self.set_update_finish_btns()
+
+ def finish_fetch(self, refresh=1, error=0):
+ """
+ Start operations after downloading data file.
+
+ .. note:: This method is the slot responses to the finish_trigger
+ signal :attr:`refresh`, :attr:`error` from an instance of
+ :class:`~gui._update.QSubFetchUpdate` class while downloading is
+ finished.
+
+ :param refresh: An flag indicating whether the downloading progress is
+ successfully finished or not. Default by 1.
+ :type refresh: int.
+ :param error: An flag indicating whether the downloading
+ progress is successfully finished or not. Default by 0.
+ :type error: int
+
+ .. seealso:: :attr:`finish_trigger` in
+ :class:`~gui._update.QSubFetchUpdate` class.
+ """
+ self._down_flag = 0
+ if error:
+ # Error occurred while downloading
+ self.set_down_progress(0, unicode(
+ _translate("Util", "Error", None)))
+ try:
+ os.remove(self.filename)
+ except:
+ pass
+ self.warning_download()
+ msg_title = "Warning"
+ msg = unicode(
+ _translate("Util", "Incorrect Data file!\n"
+ "Please use the \"Download\" key to \n"
+ "fetch a new data file.", None))
+ self.set_message(msg_title, msg)
+ self.set_conn_status(0)
+ else:
+ # Data file retrieved successfully
+ self.set_down_progress(100, unicode(
+ _translate("Util", "Download Complete", None)))
+ self.refresh_info(refresh)
+ self.set_fetch_finish_btns(error)
+
+ def new_version(self):
+ """
+ Compare version of local data file to the version from the server.
+
+ :return: A flag indicating whether the local data file is up-to-date
+ or not.
+ :rtype: int
+
+ ====== ============================================
+ Return File status
+ ====== ============================================
+ 1 The version of data file on server is newer.
+ 0 The local data file is up-to-date.
+ ====== ============================================
+ """
+ local_ver = self._cur_ver
+ server_ver = self._update["version"]
+ local_ver = local_ver.split('.')
+ server_ver = server_ver.split('.')
+ for i, ver_num in enumerate(local_ver):
+ if server_ver[i] > ver_num:
+ return 1
+ return 0
\ No newline at end of file
diff --git a/gui/qdialog_slots.py b/gui/qdialog_slots.py
new file mode 100644
index 0000000..fabb8d2
--- /dev/null
+++ b/gui/qdialog_slots.py
@@ -0,0 +1,346 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# qdialog_slots.py : Qt slots response to signals on the main dialog.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import shutil
+import time
+
+from PyQt4 import QtCore, QtGui
+
+from language import LangUtil
+from qdialog_d import QDialogDaemon
+from util_ui import _translate
+
+import sys
+sys.path.append("..")
+from util import RetrieveData
+
+
+class QDialogSlots(QDialogDaemon):
+ """
+ QDialogSlots class provides `Qt slots` to deal with the `Qt signals`
+ emitted by the widgets on the main dialog operated by users.
+
+ .. note:: This class is subclass of :class:`~gui.qdialog_d.QDialogDaemon`
+ class and parent class of :class:`~gui.hostsutil.HostsUtil`.
+
+ :ivar int ipv_id: An flag indicating current IP version setting. The
+ value could be 1 or 0:
+
+ ====== ==========
+ ipv_id IP Version
+ ====== ==========
+ 1 IPv6
+ 0 IPv4
+ ====== ==========
+
+ .. seealso::
+ :meth:`~gui.qdialog_slots.QDialogSlots.on_IPVersion_changed`. in
+ this class.
+
+ :ivar str make_path: Temporary path to store generated hosts file. The
+ default value of :attr:`make_path` is "`./hosts`".
+ :ivar int mirror_id: Index number of current selected server from the
+ mirror list.
+ """
+ _ipv_id = 0
+
+ make_path = "./hosts"
+ mirror_id = 0
+
+ def __init__(self):
+ """
+ Initialize a new instance of this class.
+ """
+ super(QDialogSlots, self).__init__()
+
+ def reject(self):
+ """
+ Close this program while the reject signal is emitted.
+
+ .. note:: This method is the slot responses to the reject signal from
+ an instance of the main dialog.
+ """
+ self.close()
+ return QtGui.QDialog.reject(self)
+
+ def close(self):
+ """
+ Close this program while the close signal is emitted.
+
+ .. note:: This method is the slot responses to the close signal from
+ an instance of the main dialog.
+ """
+ try:
+ RetrieveData.clear()
+ except:
+ pass
+ super(QDialogDaemon, self).close()
+
+ def mouseMoveEvent(self, e):
+ """
+ Allow drag operations to set the new position for current cursor.
+
+ :param e: Current mouse event.
+ :type e: :class:`PyQt4.QtGui.QMouseEvent`
+ """
+
+ if e.buttons() & QtCore.Qt.LeftButton:
+ try:
+ self.move(e.globalPos() - self.dragPos)
+ except AttributeError:
+ pass
+ e.accept()
+
+ def mousePressEvent(self, e):
+ """
+ Allow press operation to set the new position for current dialog.
+
+ :param e: Current mouse event.
+ :type e: :class:`PyQt4.QtGui.QMouseEvent`
+ """
+ if e.button() == QtCore.Qt.LeftButton:
+ self.dragPos = e.globalPos() - self.frameGeometry().topLeft()
+ e.accept()
+
+ def on_Mirror_changed(self, mirr_id):
+ """
+ Change the current server selection.
+
+ .. note:: This method is the slot responses to the signal argument
+ :attr:`mirr_id` from SelectMirror widget while the value is
+ changed.
+
+ :param mirr_id: Index number of current mirror server.
+ """
+ self.mirror_id = mirr_id
+ self.check_connection()
+
+ def on_IPVersion_changed(self, ipv_id):
+ """
+ Change the current IP version setting.
+
+ .. note:: This method is the slot responses to the signal argument
+ :attr:`ipv_id` from SelectIP widget while the value is changed.
+
+ :param ipv_id: An flag indicating current IP version setting. The
+ value could be 1 or 0:
+
+ ====== ==========
+ ipv_id IP Version
+ ====== ==========
+ 1 IPv6
+ 0 IPv4
+ ====== ==========
+
+ :type ipv_id: int
+ """
+ if self._ipv_id != ipv_id:
+ self._ipv_id = ipv_id
+ if not RetrieveData.db_exists():
+ self.warning_no_datafile()
+ else:
+ self.set_func_list(0)
+ self.refresh_func_list()
+
+ def on_Selection_changed(self, item):
+ """
+ Change the current selection of modules to be applied to hosts file.
+
+ .. note:: This method is the slot responses to the signal argument
+ :attr:`item` from Functionlist widget while the item selection is
+ changed.
+
+ :param item: Row number of the item listed in Functionlist which is
+ changed by user.
+ :type item: int
+ """
+ ip_flag = self._ipv_id
+ func_id = self.ui.Functionlist.row(item)
+ if self._funcs[ip_flag][func_id] == 0:
+ self._funcs[ip_flag][func_id] = 1
+ else:
+ self._funcs[ip_flag][func_id] = 0
+ mutex = RetrieveData.get_ids(self.choice[ip_flag][func_id][2])
+ for c_id, c in enumerate(self.choice[ip_flag]):
+ if c[0] == self.choice[ip_flag][func_id][0]:
+ if c[1] in mutex and self._funcs[ip_flag][c_id] == 1:
+ self._funcs[ip_flag][c_id] = 0
+ self.refresh_func_list()
+
+ def on_Lang_changed(self, lang):
+ """
+ Change the UI language setting.
+
+ .. note:: This method is the slot responses to the signal argument
+ :attr:`lang` from SelectLang widget while the value is changed.
+
+ :param lang: The language name which is selected by user.
+
+ .. note:: This string is typically in the format of IETF language
+ tag. For example: en_US, en_GB, etc.
+
+ .. seealso:: :attr:`language` in :class:`~gui.language.LangUtil`
+ class.
+
+ :type lang: str
+ """
+ new_lang = LangUtil.get_locale_by_language(unicode(lang))
+ trans = QtCore.QTranslator()
+ from hostsutil import LANG_DIR
+ trans.load(LANG_DIR + new_lang)
+ self.app.removeTranslator(self._trans)
+ self.app.installTranslator(trans)
+ self._trans = trans
+ self.ui.retranslateUi(self)
+ self.init_main()
+ self.check_connection()
+
+ def on_MakeHosts_clicked(self):
+ """
+ Start operations to make a hosts file.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonApply widget while the button is clicked.
+
+ .. note:: No operations would be called if current session does not
+ have the privileges to change the hosts file.
+ """
+ if not self._writable:
+ self.warning_permission()
+ return
+ if self.question_apply():
+ self.make_path = "./hosts"
+ self.make_hosts("system")
+ else:
+ return
+
+ def on_MakeANSI_clicked(self):
+ """
+ Export a hosts file encoded in ANSI.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonANSI widget while the button is clicked.
+ """
+ self.make_path = self.export_hosts()
+ if unicode(self.make_path) != u'':
+ self.make_hosts("ansi")
+
+ def on_MakeUTF8_clicked(self):
+ """
+ Export a hosts file encoded in UTF-8.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonUTF widget while the button is clicked.
+ """
+ self.make_path = self.export_hosts()
+ if unicode(self.make_path) != u'':
+ self.make_hosts("utf-8")
+
+ def on_Backup_clicked(self):
+ """
+ Backup the hosts file of current operating system.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonBackup widget while the button is clicked.
+ """
+ l_time = time.localtime(time.time())
+ backtime = time.strftime("%Y-%m-%d-%H%M%S", l_time)
+ filename = "hosts_" + backtime + ".bak"
+ if self.platform == "OS X":
+ filename = "/Users/" + filename
+ filepath = QtGui.QFileDialog.getSaveFileName(
+ self, _translate("Util", "Backup hosts", None),
+ QtCore.QString(filename),
+ _translate("Util", "Backup File(*.bak)", None))
+ if unicode(filepath) != u'':
+ shutil.copy2(self.hosts_path, unicode(filepath))
+ self.info_complete()
+
+ def on_Restore_clicked(self):
+ """
+ Restore a previously backed up hosts file.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonRestore widget while the button is clicked.
+ This method would call
+
+ .. note:: No operations would be called if current session does not
+ have the privileges to change the hosts file.
+ """
+ if not self._writable:
+ self.warning_permission()
+ return
+ filename = ''
+ if self.platform == "OS X":
+ filename = "/Users/" + filename
+ filepath = QtGui.QFileDialog.getOpenFileName(
+ self, _translate("Util", "Restore hosts", None),
+ QtCore.QString(filename),
+ _translate("Util", "Backup File(*.bak)", None))
+ if unicode(filepath) != u'':
+ shutil.copy2(unicode(filepath), self.hosts_path)
+ self.info_complete()
+
+ def on_CheckUpdate_clicked(self):
+ """
+ Retrieve update information (metadata) of the latest data file from a
+ specified server.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonCheck widget while the button is clicked.
+ """
+ if self.choice != [[], []]:
+ self.refresh_func_list()
+ self.set_update_click_btns()
+ if self._update == {} or self._update["version"] == \
+ unicode(_translate("Util", "[Error]", None)):
+ self.check_update()
+
+ def on_FetchUpdate_clicked(self):
+ """
+ Retrieve the latest hosts data file.
+
+ .. note:: This method is the slot responses to the signal from
+ ButtonUpdate widget while the button is clicked.
+ This method would call operations to
+
+ .. note:: Method :meth:`~gui.qdialog_slots.on_CheckUpdate_clicked`
+ would be called if no update information has been set,
+
+ .. note:: If the current data is up-to-date, no data file would be
+ retrieved.
+ """
+ self.set_fetch_click_btns()
+ self._down_flag = 1
+ if self._update == {} or self._update["version"] == \
+ unicode(_translate("Util", "[Error]", None)):
+ self.check_update()
+ elif self.new_version():
+ self.fetch_update()
+ else:
+ self.info_uptodate()
+ self.finish_fetch()
+
+ def on_LinkActivated(self, url):
+ """
+ Open external link in browser.
+
+ .. note:: This method is the slot responses to the signal from a Label
+ widget while the text with a hyperlink which is clicked by user.
+ """
+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
\ No newline at end of file
diff --git a/gui/qdialog_ui.py b/gui/qdialog_ui.py
new file mode 100644
index 0000000..c1e9fac
--- /dev/null
+++ b/gui/qdialog_ui.py
@@ -0,0 +1,577 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# qdialog_ui.py : Draw the Graphical User Interface.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+
+from PyQt4 import QtCore, QtGui
+
+import sys
+
+sys.path.append("..")
+from util import RetrieveData, CommonUtil
+from __version__ import __version__, __release__
+from language import LangUtil
+from util_ui import Ui_Util, _translate, _fromUtf8
+
+# Path to store language files
+LANG_DIR = "./gui/lang/"
+
+
+class QDialogUI(QtGui.QDialog, object):
+ """
+ CursesUI class contains methods to draw the Graphical User Interface (GUI)
+ of Hosts Setup Utility. The methods to make GUI here are based on
+ `PyQT4 `_.
+
+ .. note:: This class is subclass of :class:`QtGui.QDialog` class
+ and :class:`object` class.
+
+ :ivar str _cur_ver: Current version of local hosts data file.
+ :ivar QtCore.QTranslator _trans: An instance of
+ :class:`QtCore.QTranslator` object indicating the current UI language
+ setting.
+
+ :ivar QtGui.QApplication app: An instance of :class:`QtGui.QApplication`
+ object to launch the Qt application.
+
+ :ivar list mirrors: Dictionaries containing `tag`, `test url`, and
+ `update url` of mirror servers.
+ :ivar str platform: Platform of current operating system. The value could
+ be `Windows`, `Linux`, `Unix`, `OS X`, and of course `Unknown`.
+ :ivar bool plat_flag: A flag indicating whether current operating system
+ is supported or not.
+
+ :ivar object ui: Form implementation declares layout of the main dialog
+ which is generated from a UI file designed by `Qt Designer`.
+ :ivar str custom: File name of User Customized Hosts File. Customized
+ hosts would be able to select if this file exists. The default file
+ name is ``custom.hosts``.
+
+ .. seealso:: :ref:`User Customized Hosts`.
+ """
+ _cur_ver = ""
+ _trans = None
+
+ app = None
+ mirrors = []
+ platform = ''
+ plat_flag = True
+ ui = None
+
+ custom = "custom.hosts"
+
+ def __init__(self):
+ """
+ Initialize a new instance of this class. Set the UI object and current
+ translator of the main dialog.
+ """
+ self.app = QtGui.QApplication(sys.argv)
+ super(QDialogUI, self).__init__()
+ self.ui = Ui_Util()
+ self.ui.setupUi(self)
+ self.set_style()
+ self.set_stylesheet()
+ # Set default UI language
+ trans = QtCore.QTranslator()
+ trans.load(LANG_DIR + "en_US")
+ self._trans = trans
+ self.app.installTranslator(trans)
+ self.set_languages()
+
+ def set_stylesheet(self):
+ """
+ Set the style sheet of main dialog.
+
+ .. seealso:: :ref:`QT Stylesheet`.
+ """
+ with open("./gui/theme/default.qss", "r") as qss:
+ self.app.setStyleSheet(qss.read())
+
+ def set_style(self):
+ """
+ Set the main dialog with a window style depending on the os platform.
+ """
+ self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+ system = self.platform
+ if system == "Windows":
+ pass
+ elif system == "Linux":
+ # Set window style for sudo users.
+ QtGui.QApplication.setStyle(
+ QtGui.QStyleFactory.create("Cleanlooks"))
+ elif system == "OS X":
+ pass
+
+ def set_languages(self):
+ """
+ Set optional language selection items in the SelectLang widget.
+ """
+ self.ui.SelectLang.clear()
+ langs = LangUtil.language
+ langs_not_found = []
+ for locale in langs:
+ if not os.path.isfile(LANG_DIR + locale + ".qm"):
+ langs_not_found.append(locale)
+ for locale in langs_not_found:
+ langs.pop(locale)
+ LangUtil.language = langs
+ if len(langs) <= 1:
+ self.ui.SelectLang.setEnabled(False)
+ # Block the signal while set the language selecions.
+ self.ui.SelectLang.blockSignals(True)
+ sys_locale = LangUtil.get_locale()
+ if sys_locale not in langs.keys():
+ sys_locale = "en_US"
+ for i, locale in enumerate(sorted(langs.keys())):
+ if sys_locale == locale:
+ select = i
+ lang = langs[locale]
+ self.ui.SelectLang.addItem(_fromUtf8(""))
+ self.ui.SelectLang.setItemText(i, lang)
+ self.ui.SelectLang.blockSignals(False)
+ self.ui.SelectLang.setCurrentIndex(select)
+
+ def set_mirrors(self):
+ """
+ Set optional server list.
+ """
+ for i, mirror in enumerate(self.mirrors):
+ self.ui.SelectMirror.addItem(_fromUtf8(""))
+ self.ui.SelectMirror.setItemText(
+ i, _translate("Util", mirror["tag"], None))
+ self.set_platform_label()
+
+ def set_label_color(self, label, color):
+ """
+ Set the :attr:`color` of a :attr:`label`.
+
+ :param label: Label on the main dialog.
+ :type: :class:`PyQt4.QtGui.QLabel`
+ :param color: Color to be set on the label.
+ :type color: str
+ """
+ if color == "GREEN":
+ rgb = "#37b158"
+ elif color == "RED":
+ rgb = "#e27867"
+ elif color == "BLACK":
+ rgb = "#b1b1b1"
+ else:
+ rgb = "#ffffff"
+ label.setStyleSheet("QLabel {color: %s}" % rgb)
+
+ def set_label_text(self, label, text):
+ """
+ Set the :attr:`text` of a :attr:`label`.
+
+ :param label: Label on the main dialog.
+ :type: :class:`PyQt4.QtGui.QLabel`
+ :param text: Message to be set on the label.
+ :type text: unicode
+ """
+ label.setText(_translate("Util", text, None))
+
+ def set_conn_status(self, status):
+ """
+ Set the information of connection status to the current server
+ selected.
+ """
+ if status == -1:
+ self.set_label_color(self.ui.labelConnStat, "BLACK")
+ self.set_label_text(self.ui.labelConnStat, unicode(
+ _translate("Util", "Checking...", None)))
+ elif status in [0, 1]:
+ if status:
+ color, stat = "GREEN", unicode(_translate(
+ "Util", "[OK]", None))
+ else:
+ color, stat = "RED", unicode(_translate(
+ "Util", "[Failed]", None))
+ self.set_label_color(self.ui.labelConnStat, color)
+ self.set_label_text(self.ui.labelConnStat, stat)
+
+ def set_version(self):
+ version = "".join(['v', __version__, ' ', __release__])
+ self.set_label_text(self.ui.VersionLabel, version)
+
+ def set_info(self):
+ """
+ Set the information of the current local data file.
+ """
+ info = RetrieveData.get_info()
+ ver = info["Version"]
+ self._cur_ver = ver
+ self.set_label_text(self.ui.labelVersionData, ver)
+ build = info["Buildtime"]
+ build = CommonUtil.timestamp_to_date(build)
+ self.set_label_text(self.ui.labelReleaseData, unicode(build))
+
+ def set_down_progress(self, progress, message):
+ """
+ Set :attr:`progress` position of the progress bar with a
+ :attr:`message`.
+
+ :param progress: Progress position to be set on the progress bar.
+ :type progress: int
+ :param message: Message to be set on the progress bar.
+ :type message: str
+ """
+ self.ui.Prog.setProperty("value", progress)
+ self.set_conn_status(1)
+ self.ui.Prog.setFormat(message)
+
+ def set_platform_label(self):
+ """
+ Set the information of the label indicating current operating system
+ platform.
+ """
+ color = "GREEN" if self.plat_flag else "RED"
+ self.set_label_color(self.ui.labelOSStat, color)
+ self.set_label_text(self.ui.labelOSStat, "[%s]" % self.platform)
+
+ def set_func_list(self, new=0):
+ """
+ Draw the function list and decide whether to load the default
+ selection configuration or not.
+
+ :param new: A flag indicating whether to load the default selection
+ configuration or not. Default value is `0`.
+
+ === ===================
+ new Operation
+ === ===================
+ 0 Use user config.
+ 1 Use default config.
+ === ===================
+ :type new: int
+ """
+ self.ui.Functionlist.clear()
+ self.ui.FunctionsBox.setTitle(_translate(
+ "Util", "Functions", None))
+ if new:
+ for ip in range(2):
+ choice, defaults, slices = RetrieveData.get_choice(ip)
+ if os.path.isfile(self.custom):
+ choice.insert(0, [4, 1, 0, "customize"])
+ defaults[0x04] = [1]
+ for i in range(len(slices)):
+ slices[i] += 1
+ slices.insert(0, 0)
+ self.choice[ip] = choice
+ self.slices[ip] = slices
+ funcs = []
+ for func in choice:
+ if func[1] in defaults[func[0]]:
+ funcs.append(1)
+ else:
+ funcs.append(0)
+ self._funcs[ip] = funcs
+
+ def set_list_item_unchecked(self, item_id):
+ """
+ Set a specified item to become unchecked in the function list.
+
+ :param item_id: Index number of a specified item in the function list.
+ :type: int
+ """
+ self._funcs[self._ipv_id][item_id] = 0
+ item = self.ui.Functionlist.item(item_id)
+ item.setCheckState(QtCore.Qt.Unchecked)
+
+ def refresh_func_list(self):
+ """
+ Refresh the items in the function list by user settings.
+ """
+ ip_flag = self._ipv_id
+ self.ui.Functionlist.clear()
+
+ for f_id, func in enumerate(self.choice[self._ipv_id]):
+ item = QtGui.QListWidgetItem()
+ if self._funcs[ip_flag][f_id] == 1:
+ check = QtCore.Qt.Checked
+ else:
+ check = QtCore.Qt.Unchecked
+ item.setCheckState(check)
+ item.setText(_translate("Util", func[3], None))
+ self.ui.Functionlist.addItem(item)
+
+ def set_make_progress(self, mod_name, mod_num):
+ """
+ Start operations to show progress while making hosts file.
+
+ .. note:: This method is the slot responses to the info_trigger signal
+ :attr:`mod_name`, :attr:`mod_num` from an instance of
+ :class:`~gui._make.QSubMakeHosts` class while making operations
+ are proceeding.
+
+ :param mod_name: Tag of a specified hosts module in current progress.
+ :type mod_name: str
+ :param mod_num: Number of current module in the operation sequence.
+ :type mod_num: int
+
+ .. seealso:: :attr:`info_trigger` in
+ :class:`~gui._make.QSubMakeHosts` class.
+ """
+ total_mods_num = self._funcs[self._ipv_id].count(1) + 1
+ prog = 100 * mod_num / total_mods_num
+ self.ui.Prog.setProperty("value", prog)
+ module = unicode(_translate("Util", mod_name, None))
+ message = unicode(_translate(
+ "Util", "Applying module: %s(%s/%s)", None)
+ ) % (module, mod_num, total_mods_num)
+ self.ui.Prog.setFormat(message)
+ self.set_make_message(message)
+
+ def set_message(self, title, message):
+ """
+ Show a message box with a :attr:`message` and a :attr:`title`.
+
+ :param title: Title of the message box to be displayed.
+ :type title: unicode
+ :param message: Message in the message box.
+ :type message: unicode
+ """
+ self.ui.FunctionsBox.setTitle(_translate("Util", title, None))
+ self.ui.Functionlist.clear()
+ item = QtGui.QListWidgetItem()
+ item.setText(message)
+ item.setFlags(QtCore.Qt.ItemIsEnabled)
+ self.ui.Functionlist.addItem(item)
+
+ def set_make_message(self, message, start=0):
+ """
+ List message for the current operating progress while making the new
+ hosts file in function list.
+
+ :param message: Message to be displayed in the function list.
+ :type message: unicode
+ :param start: A flag indicating whether the message is the first one
+ in the making progress or not. Default value is `0`.
+
+ ===== ==============
+ start Status
+ ===== ==============
+ 0 Not the first.
+ 1 First.
+ ===== ==============
+ :type start: int
+ """
+ if start:
+ self.ui.FunctionsBox.setTitle(_translate(
+ "Util", "Progress", None))
+ self.ui.Functionlist.clear()
+ item = QtGui.QListWidgetItem()
+ item.setText("- " + message)
+ item.setFlags(QtCore.Qt.ItemIsEnabled)
+ self.ui.Functionlist.addItem(item)
+
+ def warning_permission(self):
+ """
+ Show permission error warning message box.
+ """
+ QtGui.QMessageBox.warning(
+ self, _translate("Util", "Warning", None),
+ _translate("Util",
+ "You do not have permissions to change the \n"
+ "hosts file.\n"
+ "Please run this program as Administrator/root\n"
+ "so it can modify your hosts file."
+ , None))
+
+ def warning_download(self):
+ """
+ Show download error warning message box.
+ """
+ QtGui.QMessageBox.warning(
+ self, _translate("Util", "Warning", None),
+ _translate("Util",
+ "Error retrieving data from the server.\n"
+ "Please try another server.", None))
+
+ def warning_incorrect_datafile(self):
+ """
+ Show incorrect data file warning message box.
+ """
+ msg_title = "Warning"
+ msg = unicode(_translate("Util",
+ "Incorrect Data file!\n"
+ "Please use the \"Download\" key to \n"
+ "fetch a new data file.", None))
+ self.set_message(unicode(msg_title), msg)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+
+ def warning_no_datafile(self):
+ """
+ Show no data file warning message box.
+ """
+ msg_title = "Warning"
+ msg = unicode(_translate("Util",
+ "Data file not found!\n"
+ "Please use the \"Download\" key to \n"
+ "fetch a new data file.", None))
+ self.set_message(unicode(msg_title), msg)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+
+ def question_apply(self):
+ """
+ Show confirm question message box before applying hosts file.
+
+ :return: A flag indicating whether user has accepted to continue the
+ operations or not.
+
+ ====== =========
+ return Operation
+ ====== =========
+ True Continue
+ False Cancel
+ ====== =========
+ :rtype: bool
+ """
+ msg_title = unicode(_translate("Util", "Notice", None))
+ msg = unicode(_translate("Util",
+ "Are you sure you want to apply changes \n"
+ "to the hosts file on your system?\n\n"
+ "This operation could not be reverted if \n"
+ "you have not made a backup of your \n"
+ "current hosts file.", None))
+ choice = QtGui.QMessageBox.question(
+ self, msg_title, msg,
+ QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
+ QtGui.QMessageBox.No
+ )
+ if choice == QtGui.QMessageBox.Yes:
+ return True
+ else:
+ return False
+
+ def info_uptodate(self):
+ """
+ Draw data file is up-to-date message box.
+ """
+ QtGui.QMessageBox.information(
+ self, _translate("Util", "Notice", None),
+ _translate("Util", "Data file is up-to-date.", None))
+
+ def info_complete(self):
+ """
+ Draw operation complete message box.
+ """
+ QtGui.QMessageBox.information(
+ self, _translate("Util", "Complete", None),
+ _translate("Util", "Operation completed", None))
+
+ def set_make_start_btns(self):
+ """
+ Set button status while making hosts operations started.
+ """
+ self.ui.Functionlist.setEnabled(False)
+ self.ui.SelectIP.setEnabled(False)
+ self.ui.ButtonCheck.setEnabled(False)
+ self.ui.ButtonUpdate.setEnabled(False)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+ self.ui.ButtonExit.setEnabled(False)
+
+ def set_make_finish_btns(self):
+ """
+ Set button status while making hosts operations finished.
+ """
+ self.ui.Functionlist.setEnabled(True)
+ self.ui.SelectIP.setEnabled(True)
+ self.ui.ButtonCheck.setEnabled(True)
+ self.ui.ButtonUpdate.setEnabled(True)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+ self.ui.ButtonExit.setEnabled(True)
+
+ def set_update_click_btns(self):
+ """
+ Set button status while `CheckUpdate` button clicked.
+ """
+ self.ui.ButtonApply.setEnabled(True)
+ self.ui.ButtonANSI.setEnabled(True)
+ self.ui.ButtonUTF.setEnabled(True)
+
+ def set_update_start_btns(self):
+ """
+ Set button status while operations to check update of hosts data file
+ started.
+ """
+ self.ui.SelectMirror.setEnabled(False)
+ self.ui.ButtonCheck.setEnabled(False)
+ self.ui.ButtonUpdate.setEnabled(False)
+
+ def set_update_finish_btns(self):
+ """
+ Set button status while operations to check update of hosts data file
+ finished.
+ """
+ self.ui.SelectMirror.setEnabled(True)
+ self.ui.ButtonCheck.setEnabled(True)
+ self.ui.ButtonUpdate.setEnabled(True)
+
+ def set_fetch_click_btns(self):
+ """
+ Set button status while `FetchUpdate` button clicked.
+ """
+ self.ui.Functionlist.setEnabled(False)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+
+ def set_fetch_start_btns(self):
+ """
+ Set button status while operations to retrieve hosts data file
+ started.
+ """
+ self.ui.SelectMirror.setEnabled(False)
+ self.ui.ButtonCheck.setEnabled(False)
+ self.ui.ButtonUpdate.setEnabled(False)
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+ self.ui.ButtonExit.setEnabled(False)
+
+ def set_fetch_finish_btns(self, error=0):
+ """
+ Set button status while operations to retrieve hosts data file
+ finished.
+
+ :param error: A flag indicating if error occurs while retrieving hosts
+ data file from the server.
+ :type error: int
+ """
+ if error:
+ self.ui.ButtonApply.setEnabled(False)
+ self.ui.ButtonANSI.setEnabled(False)
+ self.ui.ButtonUTF.setEnabled(False)
+ else:
+ self.ui.ButtonApply.setEnabled(True)
+ self.ui.ButtonANSI.setEnabled(True)
+ self.ui.ButtonUTF.setEnabled(True)
+ self.ui.Functionlist.setEnabled(True)
+ self.ui.SelectMirror.setEnabled(True)
+ self.ui.ButtonCheck.setEnabled(True)
+ self.ui.ButtonUpdate.setEnabled(True)
+ self.ui.ButtonExit.setEnabled(True)
diff --git a/style_rc.py b/gui/style_rc.py
similarity index 94%
rename from style_rc.py
rename to gui/style_rc.py
index af6e883..6ee4df0 100644
--- a/style_rc.py
+++ b/gui/style_rc.py
@@ -2,8 +2,8 @@
# Resource object code
#
-# Created: 周六 11月 30 15:06:56 2013
-# by: The Resource Compiler for PyQt (Qt v4.8.4)
+# Created: 周三 1月 22 13:03:07 2014
+# by: The Resource Compiler for PyQt (Qt v4.8.5)
#
# WARNING! All changes made in this file will be lost!
@@ -107,6 +107,10 @@
\x00\x71\
\x00\x73\x00\x73\
\x00\x03\
+\x00\x00\x78\xc3\
+\x00\x72\
+\x00\x65\x00\x73\
+\x00\x03\
\x00\x00\x70\x37\
\x00\x69\
\x00\x6d\x00\x67\
@@ -128,9 +132,10 @@
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x0c\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
-\x00\x00\x00\x18\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04\
-\x00\x00\x00\x28\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x01\x5b\
+\x00\x00\x00\x18\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\
+\x00\x00\x00\x24\x00\x02\x00\x00\x00\x02\x00\x00\x00\x05\
+\x00\x00\x00\x34\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x00\x52\x00\x00\x00\x00\x00\x01\x00\x00\x01\x5b\
"
def qInitResources():
diff --git a/theme/darkdefault.qss b/gui/theme/default.qss
similarity index 98%
rename from theme/darkdefault.qss
rename to gui/theme/default.qss
index a1c4309..7d3a06d 100644
--- a/theme/darkdefault.qss
+++ b/gui/theme/default.qss
@@ -113,7 +113,7 @@ QComboBox::drop-down
QComboBox::down-arrow
{
- image: url(:/qss/img/style/down_arrow.png);
+ image: url(:/qss/res/img/style/down_arrow.png);
}
QGroupBox {
@@ -280,7 +280,7 @@ QCheckBox::indicator{
QCheckBox::indicator:checked
{
- image:url(:/qss/img/style/checkbox.png);
+ image:url(:/qss/res/img/style/checkbox.png);
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled
diff --git a/qthosts_rc.py b/gui/util_rc.py
similarity index 51%
rename from qthosts_rc.py
rename to gui/util_rc.py
index 7c8b411..b727075 100644
--- a/qthosts_rc.py
+++ b/gui/util_rc.py
@@ -2,8 +2,8 @@
# Resource object code
#
-# Created: 周六 11月 30 15:06:55 2013
-# by: The Resource Compiler for PyQt (Qt v4.8.4)
+# Created: 周三 1月 22 13:03:07 2014
+# by: The Resource Compiler for PyQt (Qt v4.8.5)
#
# WARNING! All changes made in this file will be lost!
@@ -1735,13 +1735,13 @@
\x7f\x1d\x67\xb3\x59\xe7\x7f\xca\x1f\x01\x06\x00\x05\x3b\xea\xc8\
\xfa\x24\x85\x20\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\
-\x00\x00\x12\x6c\
+\x00\x00\x69\xec\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
-\x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\
+\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x5c\x72\xa8\x66\
\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\
\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\
-\x79\x71\xc9\x65\x3c\x00\x00\x03\x69\x69\x54\x58\x74\x58\x4d\x4c\
+\x79\x71\xc9\x65\x3c\x00\x00\x03\x6b\x69\x54\x58\x74\x58\x4d\x4c\
\x3a\x63\x6f\x6d\x2e\x61\x64\x6f\x62\x65\x2e\x78\x6d\x70\x00\x00\
\x00\x00\x00\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x62\x65\x67\
\x69\x6e\x3d\x22\xef\xbb\xbf\x22\x20\x69\x64\x3d\x22\x57\x35\x4d\
@@ -1774,263 +1774,1663 @@
\x42\x46\x41\x46\x35\x41\x33\x39\x32\x45\x31\x31\x31\x41\x36\x37\
\x39\x38\x32\x45\x39\x30\x31\x31\x30\x33\x32\x43\x43\x22\x20\x78\
\x6d\x70\x4d\x4d\x3a\x44\x6f\x63\x75\x6d\x65\x6e\x74\x49\x44\x3d\
-\x22\x78\x6d\x70\x2e\x64\x69\x64\x3a\x32\x39\x39\x37\x43\x38\x33\
-\x33\x46\x32\x41\x33\x31\x31\x45\x32\x38\x38\x34\x36\x39\x36\x46\
-\x46\x33\x43\x35\x45\x44\x30\x33\x36\x22\x20\x78\x6d\x70\x4d\x4d\
+\x22\x78\x6d\x70\x2e\x64\x69\x64\x3a\x43\x41\x33\x32\x31\x42\x46\
+\x41\x35\x34\x30\x38\x31\x31\x45\x33\x41\x31\x35\x45\x45\x45\x33\
+\x31\x41\x34\x30\x39\x34\x30\x45\x31\x22\x20\x78\x6d\x70\x4d\x4d\
\x3a\x49\x6e\x73\x74\x61\x6e\x63\x65\x49\x44\x3d\x22\x78\x6d\x70\
-\x2e\x69\x69\x64\x3a\x32\x39\x39\x37\x43\x38\x33\x32\x46\x32\x41\
-\x33\x31\x31\x45\x32\x38\x38\x34\x36\x39\x36\x46\x46\x33\x43\x35\
-\x45\x44\x30\x33\x36\x22\x20\x78\x6d\x70\x3a\x43\x72\x65\x61\x74\
+\x2e\x69\x69\x64\x3a\x43\x41\x33\x32\x31\x42\x46\x39\x35\x34\x30\
+\x38\x31\x31\x45\x33\x41\x31\x35\x45\x45\x45\x33\x31\x41\x34\x30\
+\x39\x34\x30\x45\x31\x22\x20\x78\x6d\x70\x3a\x43\x72\x65\x61\x74\
\x6f\x72\x54\x6f\x6f\x6c\x3d\x22\x41\x64\x6f\x62\x65\x20\x50\x68\
-\x6f\x74\x6f\x73\x68\x6f\x70\x20\x43\x43\x20\x28\x57\x69\x6e\x64\
-\x6f\x77\x73\x29\x22\x3e\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\x44\x65\
-\x72\x69\x76\x65\x64\x46\x72\x6f\x6d\x20\x73\x74\x52\x65\x66\x3a\
-\x69\x6e\x73\x74\x61\x6e\x63\x65\x49\x44\x3d\x22\x78\x6d\x70\x2e\
-\x69\x69\x64\x3a\x63\x62\x39\x36\x32\x37\x37\x63\x2d\x66\x65\x63\
-\x66\x2d\x30\x39\x34\x33\x2d\x39\x63\x33\x39\x2d\x61\x35\x36\x62\
-\x66\x66\x39\x30\x30\x61\x64\x62\x22\x20\x73\x74\x52\x65\x66\x3a\
-\x64\x6f\x63\x75\x6d\x65\x6e\x74\x49\x44\x3d\x22\x78\x6d\x70\x2e\
-\x64\x69\x64\x3a\x30\x43\x31\x42\x46\x41\x46\x35\x41\x33\x39\x32\
-\x45\x31\x31\x31\x41\x36\x37\x39\x38\x32\x45\x39\x30\x31\x31\x30\
-\x33\x32\x43\x43\x22\x2f\x3e\x20\x3c\x2f\x72\x64\x66\x3a\x44\x65\
-\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x20\x3c\x2f\x72\x64\x66\
-\x3a\x52\x44\x46\x3e\x20\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\x65\x74\
-\x61\x3e\x20\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x65\x6e\x64\
-\x3d\x22\x72\x22\x3f\x3e\x48\xbe\xf1\xc7\x00\x00\x0e\x99\x49\x44\
-\x41\x54\x78\xda\xdc\x5a\x6b\x70\x5b\xe5\x99\x7e\xce\x45\xd2\x91\
-\x64\xc9\x96\xed\xc4\x8e\xe3\x6b\xc2\xa5\x04\x48\x42\x20\x4e\xb8\
-\x2c\xdd\xd2\x65\x97\x86\x26\xc4\x21\x85\x24\x04\x36\x24\x69\x68\
-\x3b\x3b\xbd\xb7\x69\x61\xb7\x3b\xf4\x4f\xdb\x9d\x94\x81\x05\x66\
-\xa7\x29\xdb\x2d\xdb\x81\x00\x25\x31\x25\x49\xc3\xad\x74\x98\xc2\
-\xd0\x86\x6d\xbb\x69\x73\xa5\x89\x43\x2e\xb6\xe5\xbb\xee\x3a\xd2\
-\x39\x3a\xa7\xef\xfb\x49\xb2\x53\x88\x65\x39\xbe\x24\xce\xf1\x7c\
-\x23\xc9\x3a\x3a\xe7\x7b\x9f\xef\x79\x9f\xf7\xf2\x1d\xc9\xb6\x6d\
-\x14\x3a\xf8\x7b\xc3\x30\x36\xd0\xeb\xb7\xe9\xa3\x86\x49\x3e\x82\
-\xc1\x60\xea\xa1\x87\x1e\x5a\xb5\x76\xed\xda\xf7\x9a\x9b\x9b\x11\
-\x8b\xc5\x0a\x9e\xaf\x28\x8a\x38\xe7\xc6\x1b\x6f\x84\xaa\xaa\x68\
-\x6b\x6b\x83\xc7\xe3\x19\xf6\x7c\x75\xa4\x09\x90\xe1\x0b\x2c\xcb\
-\x7a\x94\xde\x96\x4c\xb6\xf1\x92\x24\xc1\xe7\xf3\x61\xf3\xe6\xcd\
-\xcf\xb4\xb6\xb6\x2e\xaf\xae\xae\x3e\x38\x73\xe6\x4c\xa4\x52\xa9\
-\x61\x7f\x23\xcb\xb2\x18\xc5\x1e\xc5\x9c\x39\xeb\x7c\x18\x9f\x3f\
-\x32\x99\x0c\x6a\x6a\x6a\x2e\xbd\xe3\x8e\x3b\x76\xef\xdc\xb9\x73\
-\x4e\x5f\x5f\x1f\x34\x4d\x1b\x34\x74\xb8\xc1\xe0\x8d\x17\x00\x99\
-\xf3\x65\x3c\xbb\x1f\x1b\x42\x2e\x88\x86\x86\x86\xc6\x96\x96\x96\
-\xdd\x2f\xbe\xf8\xe2\x9c\x70\x38\x0c\xb7\xdb\x2d\x0c\x65\xca\x9f\
-\x6d\x8c\x1b\x03\x78\x12\xe7\x73\xe4\x8d\xcc\x83\xb0\x72\xe5\xca\
-\x5d\xdb\xb7\x6f\x9f\x43\xda\x00\xaf\xd7\x3b\x2c\x03\xc6\xd3\x05\
-\xce\xeb\xc1\x0c\xc8\xaf\x6a\x3a\x9d\x46\x5d\x5d\x5d\xd3\xd2\xa5\
-\x4b\x77\xbd\xf0\xc2\x0b\x73\xba\xba\xba\x84\xc0\x9d\x8d\x09\x17\
-\x0d\x00\x79\x10\x78\xb0\xa1\x0c\x42\x63\x63\x63\xd3\xea\xd5\xab\
-\x77\x6f\xdb\xb6\xed\xca\x8e\x8e\x0e\x01\x42\xfe\x9c\x33\xc7\x45\
-\x01\xc0\xd9\xe8\xcd\x51\xa0\xbe\xbe\xbe\x71\xcd\x9a\x35\xbb\x9e\
-\x7b\xee\xb9\x2b\xdb\xdb\xdb\x07\x41\xb8\xe8\x5c\xe0\x4c\x06\x7c\
-\x98\x09\xb5\xb5\xb5\x02\x84\xe7\x9f\x7f\x5e\x80\x90\x17\xc6\x62\
-\x57\x7f\xca\x68\xc0\x70\x83\x41\x60\x26\xac\x5a\xb5\x6a\xe7\x8e\
-\x1d\x3b\xae\xe0\xe8\xe0\x74\x3a\xc7\x1f\x80\xf3\x19\x05\x46\x02\
-\x81\xdd\x81\xa2\x43\xd3\x9d\x77\xde\xb9\x8b\x40\xf8\x58\x24\x12\
-\x81\xcb\xe5\xba\xb8\x18\x30\x52\xd2\x93\xd3\x84\x59\xb7\xde\x7a\
-\xeb\xee\x97\x5e\x7a\xe9\x63\xcc\x04\x4e\x83\x8b\x39\xd4\xa9\xa0\
-\x01\x6c\x24\xa5\xe3\xc3\x52\x3b\xcf\x84\xa6\xa6\xa6\x59\x14\x02\
-\x77\x51\xda\xfc\x69\x02\xe1\xb0\xdf\xef\x9f\xda\x0c\xc8\x67\x81\
-\xa6\x69\x8a\x15\xe5\xf8\x3e\x1c\x0b\xf2\xc9\x12\x85\xc8\xd9\x77\
-\xdf\x7d\xf7\xae\xc5\x8b\x17\x5f\x31\x6e\x0c\x28\x54\x31\xca\x92\
-\x0c\x33\x63\x8a\x09\x8c\x54\x59\x9e\xcb\xe1\x70\x38\x06\x2b\xc0\
-\x7c\x78\x1b\xe9\x3e\x14\x0d\x66\x6f\xdd\xba\x75\xd7\xbd\xf7\xde\
-\xfb\x77\xc9\x64\xb2\x63\x4c\xd5\xe0\x48\xd4\x8c\xc5\xa2\x90\x64\
-\x12\x24\xfa\xd3\xdc\x14\x8b\x8b\x98\x60\xb1\x87\xc5\xa5\x38\x15\
-\x43\x54\x11\x10\xc5\x75\x44\xe3\x09\x54\x04\xca\xe0\x22\xa5\x2f\
-\x74\x0f\x66\x42\x79\x79\xf9\xac\x8d\x1b\x37\xd6\x53\xe1\xd4\x31\
-\x61\x1a\xc0\x14\xd5\x75\x1d\x91\x58\x04\x8d\x0d\x8d\x08\x87\x07\
-\xe0\x2b\xf1\x8f\x99\x0d\x7c\x5d\x36\x3e\x91\x4c\x8a\xeb\xb3\xe1\
-\x3d\x03\x21\xd4\x4c\x9f\x86\xb2\xd2\xd2\x11\xc3\x5c\xfe\xde\xeb\
-\xd7\xaf\xcf\x4c\xa8\x06\x70\xa9\x3a\x7d\x7a\x15\xdd\x11\xe8\xe8\
-\xec\x40\x75\xd5\x8c\x71\x59\x7d\xbe\x44\x32\xa9\x0b\x00\x32\x24\
-\x7e\xc7\x3b\x3a\x71\x69\x63\x3d\x1a\x67\xd6\x0c\x1a\xc8\xa2\x38\
-\xdc\xc8\x87\x50\x7e\x3f\xe1\xd5\x20\x0b\x54\x53\xe3\x2c\x18\x69\
-\x03\x47\xde\x3f\x4c\xc9\x49\x4a\xb8\xc6\xb9\xc6\x7d\x5e\xdb\x14\
-\x25\x38\xf1\x64\x42\x20\xa1\xd0\xb5\xaa\x2a\xca\x11\x20\x45\x37\
-\xe8\x5e\x7c\x0e\x7f\x9f\x37\x6e\xa4\x1c\x62\xc2\xa3\x40\xfe\x66\
-\x0c\x42\x45\x79\x25\x0e\x1d\x3e\x88\x28\xb9\x04\x8b\xd7\x39\xf9\
-\x3d\x8d\xa4\x9e\x14\xc6\xcb\xb2\x22\x44\x36\xe0\xf3\x91\xd1\x86\
-\x60\x5c\x22\x91\xc4\x40\x24\x8a\xcc\x38\xe9\xcc\xb8\x84\x41\x06\
-\x80\x27\xe7\xf7\xf9\x71\xe5\x9c\xab\x71\xe0\xe0\x01\xa1\x07\x9c\
-\x96\x8e\xd6\xf7\x4d\xc3\x1c\x0c\x7b\xf9\xff\x95\x50\x8e\xcf\x22\
-\x18\x4f\x24\x10\x89\xc7\xd9\xe3\x08\x18\xe9\xc2\x01\x20\x7f\x70\
-\x38\xe4\x82\x64\xee\xd5\xf3\x05\x08\xfd\x03\x7d\x02\x84\x62\xe9\
-\xc8\xc6\x26\x88\xfa\xa2\x11\x6b\x66\x90\x26\x35\x97\xc8\x5c\xfe\
-\xe3\xf7\x31\xd2\x05\x9d\x5e\x5d\xc4\x2e\x25\xe7\x66\x93\x02\xc0\
-\x68\x7c\x98\x57\x4f\xa3\x5c\x7c\xfe\xbc\x05\x38\x78\xe8\x00\x7a\
-\x7b\xbb\x29\x6c\xb9\x46\xee\xfc\x90\xf1\xe1\x68\x14\x5d\x7d\xfd\
-\x88\xeb\x29\x38\x28\x92\x38\x54\x85\x50\xa1\x54\x97\x58\x91\x24\
-\x17\xe0\x57\x95\xfe\xcf\x8c\x28\xb6\x8e\x38\x2f\x99\x20\xbb\x03\
-\x17\x24\x0b\xe6\x5f\x87\x43\x47\x0e\xa1\xab\xbb\xb3\x60\x81\xa2\
-\x28\x32\xc2\x94\xec\x74\xf7\xf6\xc3\xe7\xf5\xc2\x47\x89\x0b\x83\
-\xc8\x42\x77\x82\x22\xc0\xb1\x93\xa7\x70\x3a\xd8\x45\xee\x91\x46\
-\x65\x59\x19\xe9\x8b\x3a\x6e\xb9\xc6\x84\xd5\x02\x0c\x02\xd3\xff\
-\xba\x05\xcd\x78\xef\xf7\xbf\xa3\x70\x96\x41\xcd\x8c\xda\x8f\xb4\
-\xb4\x39\x67\x88\x50\x32\xd5\x4d\x2b\x5f\xe2\x71\x8b\xcf\x6e\xcd\
-\x45\x54\x4f\x23\x41\x4c\xa8\x9e\x56\x41\x4c\x70\x90\x26\x28\xf0\
-\x72\xbd\xcf\x39\x42\x11\xe1\x6d\x5c\x01\x38\x57\xb4\xf3\x62\xb6\
-\xf0\xda\x45\xd8\xfb\x7f\xbf\x45\x86\xfc\xba\xae\xb6\x81\x14\x5d\
-\xa7\x55\x74\x91\x66\x28\x44\xfb\xfe\xac\xf1\xee\xac\xf1\x5e\x02\
-\x81\x69\x2f\xd1\x68\xa0\xb8\x2f\x53\x96\x99\xa5\x74\x4e\x6c\x47\
-\x61\x7c\x31\xf3\x9e\xf0\x62\x88\x99\xa0\x28\x2a\x16\x2d\x5c\x8c\
-\xb6\x0f\xda\x70\xe2\xd4\x71\x68\x5a\x89\xd0\x07\x3d\xfc\x16\x7a\
-\xfa\xc3\x43\xc6\xf3\x0a\xd3\xb9\x2c\x86\x7e\xaf\x07\x36\x19\x6b\
-\x12\x68\x99\xcc\x50\x82\x33\xde\xc7\xa4\x94\xc3\x16\xd1\x9f\x63\
-\xfa\xe2\xe6\xeb\xf1\xa7\xfd\xfb\xb1\x73\xcf\xaf\xd1\x54\xfa\x0c\
-\xe6\x55\xe9\x30\xdc\x0f\x22\x29\xdf\x4c\xc6\x5b\x90\xd5\xac\x6f\
-\xbb\xc8\xc7\x79\xc9\xed\x49\x98\xdb\xa4\x95\xc3\xbc\x82\x9a\xe6\
-\xc1\x9f\xf7\xbf\x0f\x35\xf6\x28\x5a\x16\x86\x60\x87\x75\x94\x2b\
-\xcf\x10\x03\x4c\xc8\x0e\x8f\x30\xde\xa9\x66\xeb\x08\x7b\x92\xe6\
-\x55\xcc\xde\xe0\x38\x51\x8f\x0a\x27\xd2\xbf\x1b\x2e\x7b\x0b\x73\
-\xff\x91\xf4\xc1\x9a\x05\xb3\xff\x24\x1c\xae\xbf\xc0\xe7\xf8\x31\
-\x62\xea\xd7\xe0\x74\x78\x89\xf6\xc9\xc9\x6d\xb6\x4c\xd6\x8d\x38\
-\x71\x89\x25\xa2\xa8\x68\x58\x06\x13\x0d\x24\x8e\xa7\xa0\x06\x02\
-\xb0\x52\x2a\x9c\xfa\x76\x94\x26\xbe\x0e\xc5\x38\x48\x38\x79\x04\
-\x58\x17\x15\x00\x5c\x1c\x71\x71\xd3\xd3\x1b\x84\x2e\xdd\x84\x36\
-\xf3\x31\x1c\xef\xbf\x99\x98\xd5\x03\xd9\x45\x53\xb0\x64\x28\x99\
-\x77\xe1\x8e\x3e\x00\x67\xea\x67\x70\x6a\xbe\x51\xf5\xf6\x27\x1c\
-\x80\x33\x4b\xcc\x51\xef\xed\x91\xa2\xc7\xa9\xac\xed\xa2\x24\x87\
-\x55\xde\xa1\x70\xb5\x48\x51\xa0\x7f\x1d\xba\xad\x0d\xa4\xfe\x71\
-\xc0\xe9\x87\xed\xbb\x9a\x0a\x21\x27\x5c\xa1\xef\x61\xdf\xef\xfe\
-\x87\x42\xa4\x43\x14\x54\x63\xed\x28\x9f\x37\x06\x88\x3d\x3d\xb1\
-\xf2\x49\x11\xe7\xbd\x6e\x2d\x97\xe4\x68\x28\x0f\xf8\xb0\x60\x4e\
-\x2d\xbc\x55\x9f\x43\x5a\x6e\x86\xa4\x4a\xc8\x44\x3a\xa1\x9f\xda\
-\x47\x61\x30\x85\x3f\xfe\xea\x41\x3c\xf4\xaf\x0f\xa3\x33\x18\x1c\
-\xdc\xfb\x9b\x32\x2e\x90\xef\xd7\x73\xfc\x0f\xe5\x72\x7b\xaf\xa6\
-\x0d\x26\x39\x1c\xe7\xf9\x3b\xaf\x9b\xca\x65\x5b\x42\xca\xf3\x39\
-\xd8\x7a\x08\xae\xe4\x01\xb8\x4a\x3c\xd0\x13\x2e\x2c\x5f\x12\x40\
-\xba\xf3\xa7\x78\xe0\xf3\x5f\x47\x6b\xeb\x0e\xb1\x9a\xe7\x5a\x5e\
-\x4f\x5a\x31\x24\xfa\xf7\x64\xa4\x49\x49\x4b\x9c\xaa\xb6\xfc\x50\
-\x45\x51\xa3\xc2\x4b\x89\x8d\x4c\x29\x2d\xbb\x93\x43\x96\x44\x72\
-\x63\x5b\x09\x98\xea\x7c\x24\x4a\xfe\x03\x7b\x0f\x57\xc0\xd2\xbc\
-\xd8\xfe\x2b\x07\xee\xfa\x66\x25\xee\xba\xa3\x1c\x15\xca\xef\xf1\
-\xfd\x2d\x4f\xe2\x87\x8f\x6c\xa1\xf2\x3a\x34\x58\x59\x5e\x70\x2e\
-\xc0\x49\x8e\x45\xf7\xe3\xde\x5d\x84\x8a\x1a\x23\x9d\x66\xd8\xe0\
-\xa6\x82\xc6\x5f\xe2\x15\xc6\xf3\x74\x32\x94\x1a\x3b\x14\xe9\x6f\
-\x63\x3c\x85\x3d\xc5\x7b\x0b\xf6\xf5\x7e\x0b\x1b\xbe\x61\x21\xe0\
-\x24\x36\x48\x61\x7c\xf7\xa9\x0a\xac\x5c\xe2\xc1\x4c\xdf\x69\xec\
-\xf8\xc5\xeb\xd8\xf2\x43\x06\x21\x3c\xea\x1e\xc3\x84\x03\x20\xf6\
-\xe8\x4c\x03\x51\x32\x9c\x9b\x16\x8a\xd8\xcf\xe7\x56\x79\x46\x0c\
-\x85\xc0\x89\x27\x92\x08\x85\x23\x94\xe1\x29\x38\xdb\xc2\x18\xe9\
-\x08\xd6\xde\xd3\x82\xe9\x97\xaf\xc5\xa3\xcf\xba\xb1\xee\xb6\x2e\
-\x78\x94\x01\x3c\xfe\xf3\x69\xb8\xeb\x36\x27\x6a\xfd\xdd\x78\xf5\
-\xf5\x77\xf0\xf8\x13\xff\x89\x34\x15\x53\x9c\x5a\x5f\x10\x2e\x20\
-\x26\x4f\xb9\x3a\xaf\x7a\x67\x4f\x2f\x22\xd1\x18\x81\x61\x8a\x8e\
-\xae\x83\x26\xc9\x35\x3d\x47\xf4\x8e\xee\x1e\xa1\x03\xf9\x82\xe6\
-\xc3\x23\x9f\xeb\x6f\xda\xb0\x1a\x72\xe9\x3c\xfc\xa8\xb5\x0c\x2d\
-\x37\x06\x89\x09\xfd\x78\x6a\x77\x15\x56\xdf\xee\x40\x43\xa0\x1b\
-\xad\xbf\x78\x03\x3f\xfd\xdf\xa7\x45\x65\x58\xac\x6b\x4e\x38\x03\
-\x74\xa2\xbb\x9e\x4a\xa3\xba\xb2\x42\x54\x6f\x1e\x32\x94\xf7\x08\
-\x38\x02\xb0\xef\x87\xc9\x2d\xd8\x38\xaf\x47\x2b\x58\xc6\x66\xb7\
-\xb6\x66\x61\xdd\xda\xcf\xc0\x50\x67\xe0\xe9\x57\x2a\xb0\x64\x61\
-\x37\x34\xb9\x1f\x3f\xd9\x53\x85\xfb\x96\xbb\x70\x75\x6d\x10\x3f\
-\x7a\xea\x39\xbc\xf6\xfa\x1b\x94\x56\xbb\xcf\xbf\x0b\x30\xc2\x1e\
-\x97\x13\x35\xd3\x2a\x51\xea\x2b\xc9\x7e\xa6\x70\xc7\x83\x97\x3e\
-\x65\x18\xa2\x95\xc5\x1d\xdd\x62\xb6\xac\x79\x0f\x60\xc9\x92\xdb\
-\xb1\x72\xc5\xed\x30\xe5\x0a\x3c\xff\x66\x00\xcb\x9a\xbb\xe0\xb0\
-\xfb\xf1\x44\x6b\x15\x36\xae\xf6\x61\xd9\xc2\x0f\xf0\xc8\x96\x47\
-\x70\xf2\xe4\xe9\x71\x89\x0c\x63\x6e\x8b\xb3\x61\xf9\xa6\x68\xfe\
-\x55\x6c\x8c\x90\x2a\x8a\x16\x16\xd1\xb5\xc4\xeb\xa6\xd5\x1f\x99\
-\xae\xf9\x6b\xac\xbf\x7f\x03\x56\xdd\xbd\x1c\x86\x52\x89\x6d\x6f\
-\x96\xe1\xbe\x5b\xbb\xe1\x56\x43\xf8\xce\xd6\x4a\xdc\xb9\x7c\x06\
-\x1e\xfb\xea\x3e\x94\xa8\x7f\x14\xc9\xd2\x05\xd3\x16\x1f\x4c\x7e\
-\xf8\x61\x26\x5a\xf5\x38\x51\x9a\x9b\x17\x5e\xb7\x07\x5a\xae\x27\
-\x58\x6c\x13\x85\xaf\xb1\xe9\xb3\x0f\xe0\xf3\x9b\xd6\x21\xa5\xd4\
-\x60\xdb\xab\x1a\xfe\x6d\x5d\x3f\x1c\x08\xe3\xbf\x5b\x5d\xb8\x66\
-\xb1\x07\x15\xa5\xfd\x74\xfd\xb1\xd7\x0c\xea\x78\x00\x70\xe6\x76\
-\x96\x4e\x25\x5f\x82\xa8\xcc\x3e\x50\xea\xf7\x51\x28\x74\x12\xca\
-\x59\xa1\x43\x2e\x00\x16\xc2\x82\x4d\xe2\xbd\x3d\x06\x61\xcd\x9a\
-\x7b\xd0\xd0\xd8\x80\xef\xfd\xe0\xbf\xb0\xe7\xad\xdf\xa2\xf5\xb1\
-\x30\xad\x7a\x8c\xe2\xe9\x4c\xe8\x8e\x85\x14\x42\x53\x63\xae\x9b\
-\xc6\xdc\x12\xe3\x89\x3a\x28\x36\x33\xc5\x8f\x1c\x6d\x43\x4c\x4f\
-\x8a\xfd\xbb\x24\xa5\xc0\xe1\xb6\x08\xca\xc9\xff\xcb\x2b\x02\xe2\
-\x7f\x4e\xa7\x43\xd4\x06\x3c\x78\x43\x35\x8f\x86\x70\xa3\x1c\x30\
-\x96\x6d\x09\xf7\xe1\xf7\xb1\x84\x8e\xd9\x97\x5c\x81\x8d\x9f\xbd\
-\x1f\x6f\xbe\x62\x93\x2e\x04\x21\x53\xa1\x14\xb4\xbe\x42\xa2\x3a\
-\x8f\x0a\xa8\x04\x38\xe5\x18\xd3\x3e\xe4\x48\x3f\x4e\xa7\xd3\x2d\
-\xe1\x50\x68\xc7\xd9\x44\xcc\xc6\xdf\x3e\xbe\x22\xf6\xf3\x68\xf5\
-\xb9\x0f\xc8\xdb\x57\xdc\xd2\xe2\x9d\x5c\x7e\xb4\x95\x13\x18\x65\
-\x30\xa7\xb7\x3f\xc4\x9e\xdc\xda\x4b\x43\xa0\xf0\x26\x19\x47\x0d\
-\x06\xd2\x14\xa1\x55\x26\xd0\x18\x2c\x15\x9a\xcb\x43\x39\x45\xbe\
-\x40\xb3\x30\x5c\xf7\xa4\x2c\x10\x68\xa6\xb9\xbc\x37\x26\x06\x38\
-\x55\xe8\x4e\xc5\xc6\xf0\x22\x3e\x44\x6b\xbf\xc7\x81\x52\xaf\x4b\
-\xfc\x4f\x92\xbc\x43\x1a\xc1\x2b\x0a\x13\xb6\x55\xf0\x12\x1f\x9d\
-\x1c\xdd\xd3\xe5\xe5\x87\x9e\xb4\x9c\xd8\x66\x6f\x64\xd9\x46\x4e\
-\x80\x0b\xb7\x0e\x54\x55\x4a\x9e\xb3\x0b\x1c\xed\xcf\x2c\x7a\xf1\
-\x60\x7a\xb3\x82\x4c\x8d\x61\x38\x47\xd5\xa4\x48\x9a\x9c\x08\x51\
-\x1e\x40\x2b\x66\x58\x3c\x24\x52\xf1\xe1\x0d\x3d\x53\x00\xd8\x48\
-\xfa\x39\xfd\xb6\xd0\xf9\xc5\x3d\x09\xaa\x38\xf4\xc7\x03\x6e\xe5\
-\xe8\xfa\x6b\xb4\x7f\x51\x65\xa4\x46\x05\x40\x77\xc2\xae\x7f\xe5\
-\x58\xba\x45\x4c\x5c\x2a\xfe\xd1\x53\x86\x69\xd9\x25\x32\xfe\xd4\
-\xa9\xe3\x40\xd8\x89\xb9\xd3\x64\xcc\x29\x33\xf0\xf2\x31\xb2\x46\
-\x76\x90\x72\xdb\xe2\x24\x35\x87\x27\x7f\xcc\xf0\x57\x52\x7e\x48\
-\xa8\xd6\x4c\xb4\x27\x24\xd1\x1a\x37\xad\xac\x67\x28\xe7\x20\x76\
-\x46\xc6\xfc\xfb\x19\x3e\x6b\xde\x3f\xcf\xd7\xbe\x4c\x66\xa4\x46\
-\x1b\x06\xcd\x4c\x6e\x35\x4c\x9a\x65\xa1\xc1\x3b\xb5\x3c\x4c\x41\
-\x4f\x60\xc5\xa5\x12\x6a\x1c\x54\x0a\xc7\x6d\xd4\xf9\x80\xe5\x8d\
-\x69\xf1\x4c\x5f\xd4\x00\x02\x6e\x09\x25\x0e\x1b\x91\xb4\x2d\x98\
-\xc2\x5e\x51\xeb\x97\xa1\x12\x5b\x42\x34\xc5\x7f\x9a\xa5\xe0\x0b\
-\x57\xe9\xc2\xef\xf5\x0c\x50\xed\x93\xe1\x23\x02\xf2\xfb\x2c\x58\
-\xb9\x7b\x59\x45\x0c\x21\xaa\x88\x15\xe2\x92\x5a\xc8\xfa\x68\x8a\
-\x0b\x1a\xe9\x23\x2b\x3c\x24\x5c\x79\xe5\xe6\x6d\x6d\x5b\xac\x16\
-\xdf\x6a\x80\x3c\xaf\xb6\xd4\x89\xf9\xd3\x80\x2a\xb7\x4d\x9f\x2d\
-\x1c\xed\x35\xf1\xd5\x9b\x80\x4f\xd5\x26\x45\x4f\xe0\xc9\x3f\x64\
-\xf0\xfa\x29\x19\x8f\x7d\x42\x12\x85\xd4\xf4\x52\x0f\xbe\xfb\x76\
-\x0a\xd7\x54\x78\x71\xd9\x74\x0d\x57\x68\x7d\x98\xdf\x54\x89\x19\
-\x94\x00\x55\xfa\xdd\x78\xe1\xa0\x81\x3d\xa7\x35\x78\x1d\x43\x4c\
-\x91\xce\x70\x9b\xb3\x49\x89\x41\xf3\x49\xb8\x0a\xbb\xde\xb0\x00\
-\xf0\x56\x16\xab\x3a\x1c\x43\x37\x82\x9d\xdd\xbf\xcf\x58\x59\x63\
-\x8d\xdc\x2b\x7f\xb6\x04\x53\xb2\x54\x8d\xa7\xbc\xb8\x61\x26\x50\
-\xef\xee\x43\x5d\xa5\x07\x3d\x51\x13\x97\x95\x1a\xf8\x4c\x63\x1c\
-\x4b\x9f\x6e\xc7\x55\x8d\xd3\xf1\xf0\xcd\x4e\xb4\xf7\x45\x09\x24\
-\x3f\x9e\xe9\x94\xb0\xf7\xd4\x00\x0e\xb4\x9b\x78\xed\xa8\x0a\x2b\
-\x16\x47\xeb\x11\x03\x5f\xba\x29\x83\x7d\xed\x12\x76\x1f\x8a\x63\
-\xef\xf1\x38\x82\x89\x52\xb8\x94\x2c\x00\x7c\x1f\x85\xde\x70\x6f\
-\x81\xfc\x9b\x86\x24\xfe\x9f\x9f\xa7\x2d\x5c\x80\x58\xe6\x52\x91\
-\x6d\xb2\x4b\xa3\x04\x20\xc3\x00\xc4\x21\x99\x92\xa0\x9c\x91\xc9\
-\x5e\x50\x50\xde\xca\xae\x78\x1e\x59\xe9\x0c\x7f\xb6\xe9\xbd\x93\
-\x72\xb6\x2d\x6f\x76\xe2\xc7\xfb\x14\x3c\x70\xbd\x8d\xfb\xaf\x92\
-\xe1\x97\x75\x02\xc6\x81\xfd\x03\x32\x2c\x8f\x01\x0f\x95\xc9\xc7\
-\x83\x03\xd8\xfc\x4a\x1a\x9f\xac\x97\xb0\x6c\x51\x19\xba\x93\x61\
-\x84\x13\x29\x24\xd3\xa4\x01\x31\x13\x9b\xf7\xf4\xe0\xb6\x46\xe0\
-\xae\x2b\xfd\x98\x5d\x2e\xe1\x0b\xbf\x0c\xc1\xa6\x5c\x42\xca\x99\
-\x34\x74\x7f\x49\x00\xc2\x40\x70\xbf\x81\x07\x83\xc3\x2c\x4e\xb9\
-\x1c\xe7\x16\x05\xb8\x79\x11\x89\x84\x90\xa0\x0b\x19\xb9\x15\xe6\
-\x1b\x16\xaa\x69\xf2\x00\x28\x19\x0f\x24\xcb\x40\x16\x29\x32\x96\
-\x7e\xf4\xce\xd1\x3e\x9c\xbc\x56\xc3\xcf\x96\xfb\x30\xbd\x44\xc5\
-\x8e\xf7\x4e\xd0\x3d\x6c\xac\x9a\x13\xc0\xcb\xb4\xc2\x97\x54\x10\
-\xd8\xd1\x4e\x02\xa1\x1c\x0b\x08\x8c\xb5\xb3\x43\xf8\xd4\xe5\x2e\
-\xbc\xdf\x63\xe0\x60\x30\x01\x35\x36\x80\x74\x22\x0d\x39\x73\x76\
-\x4e\x1b\xf9\x0c\x53\xca\x0a\x9b\x9a\x53\xcd\x4a\xa7\x36\xf4\xc5\
-\x68\x00\xe0\x7c\x3e\x3c\x10\x82\xcb\x25\xe7\x7e\x5a\x9c\x0c\xb3\
-\x8b\x7c\xf1\xe7\x61\x1c\x1f\x48\x13\x0f\x34\xbc\x71\x20\x86\x63\
-\x6d\x29\xf4\xc6\x14\xac\x7e\xb6\x0d\x2d\x97\xaa\x22\xc3\x7b\xe9\
-\x2f\x26\xe5\xf9\x1e\x7c\xe7\x97\x1f\xe0\xfa\x1a\x19\x0f\xbf\x7c\
-\x0a\xaf\x9d\x04\x15\x4e\x16\xbe\x6d\xf4\xe1\xfd\xce\x38\xde\x3e\
-\x1e\xc1\xed\xb3\x1d\xd8\x79\x2a\x85\x5d\x47\xd3\x62\xcf\xc0\x48\
-\xa6\x8a\xc9\x5d\x85\xe4\xa7\x09\xe0\x90\xd3\x53\x30\xfa\xaa\x85\
-\xf6\xf3\x60\xe8\x48\x59\x02\x8d\x51\x85\x9f\x37\x7a\x6d\x76\x50\
-\x1a\x3a\x8e\x45\x80\x63\x7c\x0d\xd2\x92\x9e\xa8\x8d\xad\x1d\xd9\
-\x30\x98\x0d\xf4\x21\xbc\x3b\x00\xbc\x7b\xc0\xe2\xbe\x9a\x38\x67\
-\x80\xc0\xf9\x49\xbb\x95\xe5\xb3\x94\xc2\x93\x27\x72\x71\xd0\x99\
-\x3d\x7f\x54\x33\x21\xb1\x4d\xa7\x0b\x3f\x49\x32\x3c\x00\x7a\x54\
-\xc6\xe9\xfd\xa8\xae\xaf\xc2\x75\x73\x9b\x84\x26\x4c\x99\x23\x17\
-\x21\x7e\xf3\x87\x23\x48\x07\x55\x2a\x45\x6f\x1e\x3d\x00\x8a\x1e\
-\xda\x8f\xff\x7f\xf5\xc4\xb5\x97\xdc\xd2\xb0\x63\x53\x8b\x78\x3e\
-\x67\xea\xd8\x9f\x15\xc1\x45\x6f\xb7\x22\x78\x3a\xb6\x47\xb2\xd7\
-\xe9\xa3\x06\xe0\x1f\x16\xcd\x3d\x72\xcf\x86\x15\x4b\x4f\x06\xbb\
-\x5b\x63\x29\x73\x76\x3c\x95\x99\x52\x00\x08\xaf\x71\x69\xcf\x3e\
-\xf1\xe0\xea\x4d\x9a\xcb\x69\x8d\xba\x21\xe2\xd1\x9c\xf8\xc1\x57\
-\xee\xfd\x73\x55\x45\xd9\xbf\xa7\x0d\x13\x53\xed\xb0\x44\xd8\xc2\
-\x23\x37\xcc\xbb\x3c\x5d\x28\x72\x15\xac\x06\x03\x7e\x2f\x56\xdc\
-\xd2\x1c\xe5\xa7\x34\x26\xe2\xe9\x8c\x89\x3c\x78\xbe\xf7\x7d\xfa\
-\xe3\x92\x57\x73\x8d\xad\x1f\x00\x2b\xb3\xe2\xe4\xe9\xf6\xed\x92\
-\x24\x4d\x39\x16\xd4\xd5\xd5\x2d\xa2\x97\xbd\x63\xea\x09\x26\x53\
-\xa3\x8c\x81\x17\xd0\x41\x05\xd8\x88\x73\x1f\xb1\x21\x22\xcb\xf2\
-\x3b\xf4\xf2\x16\x31\xe5\xe3\x53\xc9\x03\x88\xb1\xad\x34\xf7\xc3\
-\x23\x9d\xf8\x57\x01\x06\x00\x89\x1b\xb8\x83\xb7\x34\x04\xe3\x00\
+\x6f\x74\x6f\x73\x68\x6f\x70\x20\x43\x43\x20\x28\x4d\x61\x63\x69\
+\x6e\x74\x6f\x73\x68\x29\x22\x3e\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\
+\x44\x65\x72\x69\x76\x65\x64\x46\x72\x6f\x6d\x20\x73\x74\x52\x65\
+\x66\x3a\x69\x6e\x73\x74\x61\x6e\x63\x65\x49\x44\x3d\x22\x78\x6d\
+\x70\x2e\x69\x69\x64\x3a\x39\x31\x66\x35\x61\x38\x61\x34\x2d\x36\
+\x31\x62\x36\x2d\x34\x66\x34\x62\x2d\x62\x38\x61\x38\x2d\x32\x31\
+\x61\x66\x32\x62\x30\x37\x63\x64\x31\x38\x22\x20\x73\x74\x52\x65\
+\x66\x3a\x64\x6f\x63\x75\x6d\x65\x6e\x74\x49\x44\x3d\x22\x78\x6d\
+\x70\x2e\x64\x69\x64\x3a\x30\x43\x31\x42\x46\x41\x46\x35\x41\x33\
+\x39\x32\x45\x31\x31\x31\x41\x36\x37\x39\x38\x32\x45\x39\x30\x31\
+\x31\x30\x33\x32\x43\x43\x22\x2f\x3e\x20\x3c\x2f\x72\x64\x66\x3a\
+\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e\x20\x3c\x2f\x72\
+\x64\x66\x3a\x52\x44\x46\x3e\x20\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\
+\x65\x74\x61\x3e\x20\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x65\
+\x6e\x64\x3d\x22\x72\x22\x3f\x3e\x64\xc0\x1f\x2c\x00\x00\x66\x17\
+\x49\x44\x41\x54\x78\xda\xec\x7d\x07\x80\x1d\x75\xb5\xfe\x37\x73\
+\x7b\xdd\xbb\x7d\x37\xbd\x07\x12\x52\x08\x81\xd0\xa4\x13\xaa\x48\
+\x49\xa4\x17\x69\x02\x8a\xe2\x53\xf9\x8b\xa0\xf2\xb0\x20\x8a\xcf\
+\xf7\xf4\xd9\x50\x9f\x0a\x22\x9d\xa0\x14\xe9\x45\x29\xd2\x09\x25\
+\x05\x42\xfa\x6e\xb6\xef\xbd\x7b\x7b\x99\x99\xff\x39\x67\xe6\x6e\
+\x49\x93\x96\x6c\xc9\xef\xc0\xe4\xee\xde\x3b\xf7\xee\xcc\xdc\x39\
+\xdf\xf9\x4e\xf9\x9d\xa3\x59\x96\x05\x25\x4a\x06\x43\x0e\x3b\xec\
+\x30\x3c\xf5\xd4\x53\xbb\xdc\x79\xf3\x39\x1f\x72\xc8\x21\x43\xe2\
+\x58\xdc\xea\x36\x54\x32\x58\xd2\xdd\xdd\x8d\xda\xda\x5a\xdc\x78\
+\xe3\x8d\x35\x9d\x9d\x9d\x99\x1f\xfd\xe8\x47\x99\x96\x96\x96\x01\
+\xfb\x5c\x75\xd5\x55\x98\x3b\x77\x2e\x72\xb9\xdc\xb0\x3b\x3f\xbf\
+\xdf\x8f\xef\x7c\xe7\x3b\x58\xb1\x62\xc5\x80\xe7\x03\x81\xc0\x90\
+\x39\xc6\x21\x07\x00\xf1\x78\x1c\x6f\xbc\xf1\x86\x7c\xe1\x7b\xed\
+\xb5\x17\xdf\x20\x7e\xd3\x34\xab\xe9\xa5\x1a\xda\x82\xb4\x79\x69\
+\xd3\x94\xfa\x0c\x7f\xd9\x67\x9f\x7d\x90\xcd\x66\x23\xc7\x1d\x77\
+\xdc\x1e\xa4\xf8\x6f\xdd\x77\xdf\x7d\x8f\x13\x28\x88\xa6\xe7\xf3\
+\x79\xd9\x67\xdf\x7d\xf7\xc5\xc2\x85\x0b\x91\x4e\xa7\x87\xdd\xf9\
+\x85\x42\x21\xfc\xec\x67\x3f\xdb\xe2\xf9\x17\x5e\x78\x01\xd3\xa7\
+\x4f\x47\x2c\x16\x53\x00\xb0\xb9\xbc\xf6\xda\x6b\x38\xfc\xf0\xc3\
+\x31\x7e\xfc\x78\xdc\x71\xc7\x1d\x7a\x65\x65\xe5\x78\x02\x83\xc5\
+\x9a\xa6\x9d\xac\xeb\xfa\x74\x06\x01\xfa\x59\x69\xcf\x08\x90\xdf\
+\xfc\xe6\x37\x02\xf4\x1d\x1d\x1d\x08\x87\xc3\xef\x90\xa5\xbf\x64\
+\xe5\xca\x95\xaf\xa6\x52\x29\xd2\xff\xbc\xc9\xfb\xf0\xeb\xac\xfc\
+\xc3\x11\x00\x58\x0c\xc3\xd8\xe2\xb9\xaf\x7c\xe5\x2b\x98\x35\x6b\
+\x96\xdc\xe7\x83\x2d\xfa\x50\xbb\x60\x1c\x93\xf0\xf9\x7c\x78\xf0\
+\xc1\x07\x7d\x0b\x16\x2c\x38\x90\x6e\x80\x2f\xd0\xd3\x27\x91\xd2\
+\x4f\xa4\xcd\x4f\x9b\x0a\x5a\x8c\x10\xc9\x64\x32\xe8\xe9\xe9\x01\
+\x31\x3c\xfe\xde\x77\x3f\xf5\xd4\x53\x7f\x70\xd8\x61\x87\x1d\x44\
+\xca\xee\x25\x9a\xec\xe2\x7d\xbc\x5e\xef\x88\x3c\x77\x97\xcb\xa5\
+\x5c\x80\xad\x09\x23\xfe\x0d\x37\xdc\xa0\xcf\x9c\x39\x73\x36\xdd\
+\x08\x5f\x72\xbb\xdd\x9f\x76\x68\xbf\x92\x11\x26\x0c\xf6\xac\xfc\
+\x72\x23\xba\xdd\xfa\x98\x31\x63\x3e\x75\xde\x79\xe7\x7d\xd3\xe3\
+\xf1\x58\xf7\xdf\x7f\xff\xb3\xe4\x1e\x64\xd7\xad\x5b\x67\x1d\x75\
+\xd4\x51\x02\x16\xc3\x4d\x98\xa9\x0e\x75\xb6\xaa\x0f\xc5\x83\x9a\
+\x30\x61\xc2\x34\x7a\x38\x91\x28\xff\xd1\x4a\xf9\x77\x1d\x30\x60\
+\x65\xd9\x7d\xf7\xdd\x0f\xba\xe4\x92\x4b\xbe\x73\xf0\xc1\x07\xef\
+\x53\x53\x53\x13\xf8\xc6\x37\xbe\xa1\xdd\x79\xe7\x9d\x20\x57\x10\
+\x2a\x63\xb5\x8b\x00\x40\xb1\x58\x3c\xd1\xa1\xfd\x21\xf5\x15\xed\
+\x02\x37\xa1\xae\xf7\x5a\x4b\xfe\xb9\xb1\xb1\x71\xff\x2b\xaf\xbc\
+\xf2\xfb\x8b\x16\x2d\x3a\x98\x18\xa1\xe7\xa2\x8b\x2e\xd2\x96\x2c\
+\x59\x82\xea\xea\xea\x01\x96\x75\x38\x6c\x43\x5d\xdc\x43\xf4\x86\
+\x38\x84\x1e\xa6\x2b\xd5\xd8\x35\x99\x00\xb9\x00\x20\x77\x60\xff\
+\x53\x4e\x39\xe5\xca\x52\xa9\x64\xdc\x7a\xeb\xad\xff\xfc\xdc\xe7\
+\x3e\xc7\xd9\x01\xeb\xe4\x93\x4f\x96\xf4\xa1\x92\x11\x0c\x00\xb0\
+\xd3\x7d\xba\xfa\x7a\x76\x0d\xd9\x9a\xa5\xe4\xe7\x76\xdb\x6d\xb7\
+\x43\xce\x3f\xff\x7c\xcf\xc6\x8d\x1b\xbf\xf1\xf4\xd3\x4f\xbf\x7a\
+\xe1\x85\x17\x72\x6e\xd0\x24\x60\x40\x57\x57\xd7\x90\xb7\xb0\xc3\
+\x81\x01\x0c\x55\x25\x53\xce\x9e\x02\x05\x71\x07\x46\x8f\x1e\x7d\
+\xc0\x35\xd7\x5c\xf3\x7d\xb2\xfc\xfb\x16\x0a\x05\xf7\x05\x17\x5c\
+\xa0\xdd\x7b\xef\xbd\xa8\xaa\xaa\x52\x17\x69\x04\x33\x00\x25\xbb\
+\x38\x03\x28\x0b\xbb\x03\x13\x27\x4e\x3c\xe8\xec\xb3\xcf\xbe\x8a\
+\x00\xe0\x7b\x0f\x3e\xf8\xe0\xcb\x65\x77\x80\x99\xc0\x50\x76\x07\
+\x14\x03\x50\xa2\xe4\x13\x88\x09\xb0\x22\xcd\x9e\x3d\xfb\xc8\x2b\
+\xae\xb8\xe2\x3f\xf7\xd9\x67\x9f\x39\xf4\xb4\x97\x98\x80\xce\x4c\
+\x80\xb3\x03\x4a\x14\x00\x28\x19\xc6\xd6\xff\x83\x44\xd2\xd9\x1d\
+\x18\x37\x6e\xdc\xc1\xdf\xff\xfe\xf7\x7f\xf0\xd9\xcf\x7e\x76\x3f\
+\x62\x03\x2e\x62\x02\x3a\x67\x07\xd8\x1d\x50\x99\x00\xe5\x02\x28\
+\x19\x81\x2e\x40\x7f\xf1\x7a\xbd\xda\xe4\xc9\x93\x0f\x3d\xe7\x9c\
+\x73\x8a\xf4\xeb\xf5\x77\xdd\x75\xd7\x8b\xf4\x73\x8e\x58\x82\xb5\
+\x68\xd1\xa2\x21\x17\x18\x54\x2e\x80\x12\x25\x3b\xc0\x1d\x98\x35\
+\x6b\xd6\x42\x76\x07\x0e\x3a\xe8\xa0\x3d\x43\xa1\x90\xef\xa2\x8b\
+\x2e\xd2\x09\x0c\x54\x60\x50\x01\x80\x92\x91\xea\x06\xf4\xa7\xd3\
+\x1c\x18\x6c\x6c\x6c\xfc\xd4\xb5\xd7\x5e\xfb\xbd\xe3\x8e\x3b\x6e\
+\x41\x36\x9b\x75\x9d\x7f\xfe\xf9\x7a\x39\x3b\xa0\xe8\xff\x08\x71\
+\x01\x54\xe9\xe7\xc8\xb7\xe8\x1f\x85\x2a\x3b\x0b\xc6\xb4\xa9\x53\
+\xa7\xb2\x3b\x90\x8b\xc7\xe3\xdf\x7d\xf4\xd1\x47\x5f\xa7\x9f\xb9\
+\x4e\x60\xc8\xb8\x03\xca\x05\x50\xa2\x64\x07\x01\x7c\xd9\x1d\xd8\
+\x73\xcf\x3d\x8f\xb9\xfa\xea\xab\xaf\xdb\x7b\xef\xbd\x67\xea\xba\
+\xee\x3d\xef\xbc\xf3\xf4\xbb\xef\xbe\x5b\xb9\x03\x0a\x00\x94\x8c\
+\x44\x17\x60\xf3\xcd\xed\x76\x63\xd2\xa4\x49\x47\xdc\x70\xc3\x0d\
+\x3f\xf8\xcc\x67\x3e\xb3\x57\xa1\x50\xd0\x89\x09\xe8\xf7\xdc\x73\
+\xcf\x90\x71\x07\x94\x0b\xa0\x44\xc9\x0e\xa2\xca\xcc\x04\xb8\x67\
+\xc0\x8c\x19\x33\x16\x9e\x7b\xee\xb9\x05\xfa\xfd\x07\x8f\x3d\xf6\
+\xd8\x1b\x67\x9d\x75\x96\x14\x0b\x2d\x5e\xbc\x78\xd0\xdc\x01\x15\
+\x03\x50\xa2\x64\x27\x29\x0a\x7f\xc6\x3e\xfb\xec\x73\x7c\x5d\x5d\
+\x5d\xa0\xad\xad\xed\xaa\x57\x5e\x79\xe5\x4d\x02\x84\x22\x3d\x6f\
+\x0e\x16\x08\xa8\x18\x80\x12\x25\x3b\xd3\x9a\x91\x3b\x30\x66\xcc\
+\x98\x43\xaf\xbf\xfe\xfa\xef\x9f\x71\xc6\x19\xfb\x95\xdd\x01\x95\
+\x22\x54\x0c\x40\xc9\x10\xf7\xff\x3f\x09\x61\x77\xc0\xef\xf7\xeb\
+\xd3\xa7\x4f\x3f\xf2\x94\x53\x4e\x29\x90\xd5\xcf\x3f\xf1\xc4\x13\
+\x4b\xc9\x1d\xc8\x72\x2b\xb9\x9d\xcd\x04\x94\x0b\xa0\x44\xc9\x4e\
+\x96\x72\x76\x60\xaf\xbd\xf6\x3a\xae\xba\xba\x3a\xd0\xd4\xd4\x74\
+\xe5\x6b\xaf\xbd\xf6\xd6\xd9\x67\x9f\x5d\x32\x49\x4e\x3d\xf5\x54\
+\xd5\x4f\x40\xb9\x00\x4a\x46\xba\x70\xb1\xd0\xb8\x71\xe3\x0e\xbd\
+\xe1\x86\x1b\xbe\x7b\xf2\xc9\x27\xef\xcd\xee\xc0\xb9\xe7\x9e\x2b\
+\xee\x80\x6a\x2f\xa6\x18\x80\x92\x21\xe6\x06\xec\x08\x26\xc0\xc5\
+\x42\xb3\x67\xcf\x3e\xe6\x82\x0b\x2e\xb0\x7a\x7a\x7a\xfe\xf3\x9f\
+\xff\xfc\xe7\x9b\x67\x9e\x79\x66\x7e\x67\xb9\x03\xaa\x29\xa8\x12\
+\x25\x43\xc0\x1d\xd8\x73\xcf\x3d\x8f\xbd\xee\xba\xeb\x7e\x30\x7f\
+\xfe\xfc\x59\x4c\x0e\xce\x3a\xeb\x2c\x15\x18\x54\x00\xa0\x64\x57\
+\x01\x01\xc7\x1d\x38\xec\xfb\xdf\xff\xfe\xf7\x88\x01\x48\x76\xe0\
+\x8c\x33\xce\xe8\x75\x07\x94\x0b\xa0\x44\xc9\x08\x73\x01\xb6\xe6\
+\x0e\xec\xb6\xdb\x6e\x0b\xcf\x3e\xfb\x6c\x83\xe4\xfa\x7b\xef\xbd\
+\xf7\x55\x02\x81\x2c\x9c\x62\x21\x1e\x49\x37\xdc\xce\x4b\x31\x00\
+\x25\x4a\x3e\x04\x08\x70\x53\x91\x59\xb3\x66\x1d\xf5\xb5\xaf\x7d\
+\xed\xba\x03\x0f\x3c\x70\xb6\xcb\xe5\xf2\x96\xdd\x01\x9e\xd3\xb7\
+\x2b\x06\x06\x15\x00\x28\xd9\xa5\x84\xeb\x04\x1a\x1b\x1b\x0f\xfe\
+\xf6\xb7\xbf\x7d\xed\x79\xe7\x9d\x27\x9d\x85\x88\x09\x68\xbb\xaa\
+\x3b\xa0\x5c\x00\x25\x23\xde\x05\xd8\x9c\x09\x10\x08\x68\x33\x67\
+\xce\x3c\x8a\xa8\xbf\xd9\xda\xda\xfa\xdd\xa7\x9e\x7a\xea\x0d\x02\
+\x81\x1c\x67\x07\x78\x29\x71\x22\x91\x50\x2e\x80\x12\x25\x23\xdd\
+\x1d\x98\x3b\x77\xee\x31\xc4\x04\xbe\xb7\x60\xc1\x82\x59\xf4\x9c\
+\xe7\xcc\x33\xcf\x14\x77\xa0\xa2\xa2\x62\x97\x71\x07\x14\x00\x28\
+\xd9\x65\x41\x80\x57\x11\x8e\x1d\x3b\xf6\xd0\xab\xaf\xbe\xfa\xda\
+\x73\xce\x39\x47\xdc\x81\xd3\x4f\x3f\x5d\xfa\x09\x70\x4c\x40\xb9\
+\x00\x4a\x94\xec\x04\xfa\x3f\x58\x54\xb9\x9c\x1d\x98\x33\x67\xce\
+\x31\x04\x00\x56\x73\x73\xf3\x75\xaf\xbf\xfe\xfa\x5b\x04\x02\xec\
+\x0e\xc8\x04\x22\x1e\x5f\xae\x5c\x00\x25\x4a\x46\xb8\x3b\xb0\xc7\
+\x1e\x7b\x1c\xcd\xab\x08\xf7\xdf\x7f\xff\xd9\xa6\x69\xba\xd8\x1d\
+\x60\x26\x10\x8d\x46\x47\xb4\x3b\xa0\x00\x40\xc9\x90\x61\x01\x83\
+\xd9\xb1\x87\xb3\x03\xec\x0e\x5c\x71\xc5\x15\xd7\x9c\x75\xd6\x59\
+\x0b\xf2\xf9\x3c\xbb\x03\x32\x86\x8c\x63\x02\x1f\xe7\x38\x15\x00\
+\x28\x51\x32\x0c\x98\x00\xb9\x03\xfa\x9e\x7b\xee\x79\xdc\x79\xe7\
+\x9d\xf7\xff\x0e\x3b\xec\xb0\x39\xc1\x60\xd0\xc7\x31\x01\x06\x81\
+\x48\x24\xa2\x18\xc0\xce\xfc\x32\xca\x8f\x6a\x1b\xf9\xdb\x50\x63\
+\x23\xb3\x67\xcf\x3e\xf6\x86\x1b\x6e\xf8\x1e\xb9\x05\x33\xb8\x6c\
+\x98\x41\xe0\xcd\x37\xdf\x14\x10\x18\x69\xee\x80\x62\x00\x4a\x94\
+\x6c\x26\xec\x0e\x4c\x9a\x34\x69\xe1\x75\xd7\x5d\xf7\x9d\x4f\x7f\
+\xfa\xd3\xd2\x68\xf4\xca\x2b\xaf\xd4\x96\x2e\x5d\xca\x2c\x61\x44\
+\x9d\xab\xca\x02\x28\x19\x12\x56\x77\x08\xba\x03\xda\x82\x05\x0b\
+\x4e\xf8\xf2\x97\xbf\x6c\xb5\xb7\xb7\x5f\xfb\xe4\x93\x4f\xae\x58\
+\xb4\x68\x51\x9e\x40\xc0\x62\x10\x20\x50\x18\x76\xe7\xa5\x18\x80\
+\x12\x25\x1f\x02\x04\x58\x81\xe7\xcd\x9b\x77\xc2\xcf\x7e\xf6\xb3\
+\x1f\x1c\x78\xe0\x81\x33\x57\xad\x5a\xe5\xda\x7b\xef\xbd\xf5\xe5\
+\xcb\x97\x23\x1c\x0e\x8f\x08\x77\x40\x01\x80\x12\x25\xff\x86\x09\
+\x4c\x9e\x3c\xf9\xa8\xab\xae\xba\xea\x9a\x33\xcf\x3c\x73\xc1\xb2\
+\x65\xcb\xdc\x5f\xff\xfa\xd7\x35\x8e\x09\x8c\x04\x77\x40\xb9\x00\
+\x4a\x06\x9d\xfe\x0f\x65\xaa\x5c\xce\x0e\xec\xb7\xdf\x7e\x27\x7a\
+\xbd\x5e\x7d\xfd\xfa\xf5\xec\x0e\x2c\x27\x26\x90\x7f\xf9\xe5\x97\
+\xad\xd9\xb3\x67\x23\x9d\x4e\x2b\x17\x40\x89\x92\x91\x2c\x2e\x97\
+\x8b\xb3\x03\xc7\xfd\xe4\x27\x3f\xf9\xde\x21\x87\x1c\x32\x93\xcb\
+\x86\x2f\xbc\xf0\x42\x9d\x03\x83\xa1\x50\x48\xb9\x00\x4a\x94\x8c\
+\x74\x77\x20\x10\x08\xb8\xa6\x4e\x9d\x7a\x0c\xb9\x00\x57\x1f\x7b\
+\xec\xb1\xf3\x88\x01\xb8\xbf\xf5\xad\x6f\x69\xab\x57\xaf\x96\xae\
+\x43\xca\x05\x50\xa2\xe4\x23\xba\x01\xc3\x05\x04\xc8\x0d\xd0\x3e\
+\xf5\xa9\x4f\x9d\x44\x8f\xee\x8d\x1b\x37\x5e\xf3\xcc\x33\xcf\xac\
+\xe4\x91\x64\x0f\x3f\xfc\xb0\xc5\x4c\x80\xdd\x81\xe1\x72\x3e\x8a\
+\x01\x28\x51\xf2\x11\x01\x6b\xde\xbc\x79\xc7\xdf\x74\xd3\x4d\xd7\
+\x93\x5b\x30\xe3\xd9\x67\x9f\x75\x2d\x58\xb0\x40\x5f\xb2\x64\xc9\
+\xb0\x73\x07\x14\x00\x28\x51\xf2\x11\x98\x00\x67\x07\x66\xcc\x98\
+\x71\xec\x35\xd7\x5c\xf3\xed\x45\x8b\x16\xcd\x7f\xe7\x9d\x77\xdc\
+\x5f\xfd\xea\x57\xb5\xb7\xde\x7a\x0b\xc1\x60\x50\xb9\x00\x4a\x94\
+\x8c\x64\x31\x4d\x53\x66\x11\x1e\x78\xe0\x81\x27\xfa\xfd\x7e\x57\
+\x57\x57\xd7\xb5\xcf\x3f\xff\xfc\xb2\x93\x4f\x3e\x39\x7f\xef\xbd\
+\xf7\x5a\xb3\x66\xcd\x52\x0c\x40\x89\x92\x0f\x43\xab\x87\xdb\xc6\
+\x52\xce\x0e\xdc\x78\xe3\x8d\xd7\x1f\x7d\xf4\xd1\x73\xb8\x58\x68\
+\xfe\xfc\xf9\xfa\x7d\xf7\xdd\xa7\x00\x40\x89\x92\x0f\x42\xa7\xb9\
+\xac\xb6\x54\x2a\x0d\xdb\xe3\x27\xca\x2f\x6b\x07\x2e\xbb\xec\xb2\
+\x6f\x72\xb1\x10\x9d\x8f\x7b\xf1\xe2\xc5\xda\xc3\x0f\x3f\x3c\xe4\
+\x57\x11\x2a\x17\x40\xc9\xa0\x5b\xfd\x6c\x36\x2b\x74\x9a\xcb\x6b\
+\x87\xab\x3b\xc0\x55\x81\x07\x1c\x70\xc0\x09\x04\x06\xee\x35\x6b\
+\xd6\x7c\x6b\xe9\xd2\xa5\xec\x0e\x14\x02\x81\x80\xa9\x18\x80\x12\
+\x25\xdb\x00\x00\xee\xc6\x53\x66\x00\xc3\x29\x7d\xb6\x35\x26\xe0\
+\x74\x16\x3a\xe6\xe7\x3f\xff\xf9\x0d\x07\x1d\x74\xd0\x1c\x02\x36\
+\xbd\xab\xab\x4b\x57\x00\xa0\x44\xc9\x36\x00\x80\x2d\x3f\x6f\xc5\
+\x62\x51\x98\xc0\x70\x77\x67\xb8\xe5\xf8\xb4\x69\xd3\x8e\xf8\xea\
+\x57\xbf\x7a\x0d\xb9\x01\xfb\xd0\x39\x32\xcb\x1e\xb2\xc8\xe6\x1e\
+\xaa\x17\xb2\xff\xa3\x92\x91\x2b\x6c\x35\x49\x69\x90\x4a\xa5\xd0\
+\xdd\xdd\x2d\x03\x3b\xf9\xf7\xe1\x0c\x02\x5c\x15\xb8\xff\xfe\xfb\
+\x1f\x4f\xee\x80\x97\x18\xc0\xb7\x5b\x5b\x5b\xdf\xde\xb0\x61\x43\
+\x36\x91\x48\x0c\x39\x77\x40\x31\x00\x25\x83\x0e\x00\xac\x30\x9c\
+\x3b\x67\x46\xd0\xde\xde\x0e\x52\x18\xa9\xa8\x63\xdf\x7a\xb8\x82\
+\x80\x93\x1d\x58\xf8\xd3\x9f\xfe\xf4\xbf\x2e\xbf\xfc\xf2\x23\x08\
+\xd8\xc2\x43\x91\x09\xa8\x20\xa0\x92\x41\x17\x56\x96\xf2\xd2\xda\
+\x64\x32\x89\x7c\x3e\x2f\x3d\xfb\xf9\x39\x76\x0f\x86\xab\xf0\xf1\
+\x8f\x19\x33\x66\xbf\x99\x33\x67\x5e\x1c\x0a\x85\x5a\xe9\xa9\xd7\
+\x68\x2b\x32\x46\x28\x00\x18\x06\xfe\x29\x6f\x43\xb1\x6f\xdd\x48\
+\x92\x72\xe3\x0d\x4d\x23\x26\x40\x4a\xcf\x69\xb3\x5c\x2e\x27\x40\
+\xd0\xd1\xd1\x21\xc1\xc1\xe1\x7a\xfd\x19\xd8\x78\x23\x10\x58\x18\
+\x0e\x87\x1f\xa1\xa7\x56\xd3\xc6\x73\xc7\x0a\x43\x05\x04\x14\x00\
+\x6c\x83\x96\x66\x73\x59\xc4\xe3\x5d\xa8\xa9\xae\x13\x8a\x3a\x5c\
+\xe9\xe8\x50\x55\x7a\xd3\x32\x85\x11\x9b\x0c\x00\xd0\x9c\x8c\x80\
+\x0b\x1e\xb2\x9a\xac\x19\xa6\x69\xc1\xa0\x6b\xce\xfb\xf2\x63\xa1\
+\x58\x14\xfe\xec\xf5\x78\x69\x3f\xda\x97\x14\x0b\x43\x1c\x18\xf8\
+\x3e\xe2\xc5\x43\x95\x95\x95\x6e\x02\x82\xc9\xf4\xd4\x24\xda\xde\
+\xa7\xad\x8b\x36\x43\x01\xc0\x50\x55\xfe\x6c\x06\xad\x6d\x9b\xc4\
+\x22\xad\x5d\xbf\x1a\x0d\xf5\xa3\x10\x09\x47\xe9\xa6\x34\xd4\x05\
+\xfa\x98\x8a\x5f\x32\x0c\xa6\x57\xf0\xb8\x49\x81\xc9\xea\xbb\x49\
+\x87\x4b\x06\x59\x79\x52\x72\x83\x40\x81\x15\x5f\x7c\x68\x0f\xb9\
+\x05\x08\xc2\x4d\xdf\x81\x2c\xc5\xa5\xf7\xb1\xba\xeb\xf4\x5e\x2f\
+\x01\xb2\xcf\xef\x87\x6f\x18\x2c\xc1\x65\x60\x73\xe2\x1b\x8d\xf4\
+\xeb\x78\xda\xda\x68\x8b\x2b\x00\x18\xa2\x94\x8d\x2d\x7f\xf3\xa6\
+\x8d\x18\x3b\x66\x1c\x2a\x2a\x2a\xd1\xd1\xd9\x86\xb5\x6b\xdf\x47\
+\x5d\xdd\x28\xd4\xd6\xd6\xcb\x8d\xaa\x5c\x82\x0f\x2f\x6c\xc5\x99\
+\x45\x31\x9b\x62\x90\xe5\xeb\x58\x2a\x96\x60\x90\xf2\x97\x5f\x33\
+\xd8\xea\xf3\xef\xa4\xec\x86\xc3\xb8\xf8\x3b\x61\xcb\xaf\xd1\xfb\
+\x5c\xcc\x10\xbc\x9c\x36\xf4\xa0\xa5\xb3\x1b\x95\x91\x30\x2a\xa3\
+\x43\xbf\x55\x37\xc7\x31\x08\x00\xb8\x24\xb0\x8a\xb6\x00\x86\x50\
+\xf0\x5d\x01\x40\x3f\xcb\xcf\x69\xa8\xa6\xe6\xf5\x18\x37\x7e\x3c\
+\xdc\x74\xc3\x15\x8a\x05\x71\x01\xfc\xbe\x20\x56\xbc\xfb\x0e\x52\
+\xa9\x24\xc6\x8f\x9f\xd8\x1b\x1b\x50\xf2\xc1\x95\x9f\x85\xd3\x7b\
+\xcc\xa2\x8a\x85\x22\x59\x7b\x52\xf2\x52\x59\xf1\x0d\x9b\xf2\x93\
+\xf2\x0b\x43\x70\x8a\x6a\xca\xdf\x0b\xf1\x67\x04\x03\x41\x7a\xbf\
+\x0f\x89\x54\x0a\xab\x37\x6c\x44\x4f\x3a\x23\xaf\x57\x0e\x9f\x81\
+\x1d\xac\x6b\x4c\x59\x5c\x43\xea\xbe\x57\xb7\x67\x3f\x00\x20\x9f\
+\xff\xe9\xa7\x9e\x96\x14\x54\x8e\x98\x00\x6f\x0c\x02\x5c\xa2\x3a\
+\x67\xd6\x3c\x14\xf3\x79\xbc\xfd\xf6\x52\x29\x5a\x61\xcb\xa4\xe4\
+\x83\xd1\x7e\xcd\xa1\xed\x25\x52\xf8\x62\xa9\x24\x20\x60\x5b\x7d\
+\x43\x14\x9e\x2d\x3e\x07\xfb\x8a\x04\x00\x9a\x28\xbf\x4b\xde\xe3\
+\x21\x4b\xcf\xd7\xbe\xb6\xa6\x06\x9a\x4b\xc7\xd2\x15\xef\xe2\x1f\
+\x2f\xbd\x8a\xa6\x96\x56\x04\xbc\x3e\x54\x47\x23\xea\x02\x2b\x00\
+\xf8\x64\x84\x6f\xc0\x29\x93\xa7\x62\xf7\xdd\x67\xe0\x81\xfb\x1f\
+\x20\x25\x2f\x48\x34\x3a\x93\xc9\x08\x08\x30\x75\x9d\x33\x77\x2f\
+\x72\x0b\x62\x78\xf5\xf5\x97\x88\x2d\x74\x0d\xeb\x14\xd5\x4e\x74\
+\x82\xe5\x3a\x95\xc4\xba\x93\xe5\x37\x08\x04\x18\x00\x8a\x86\xfc\
+\x6c\x98\x44\xf9\x4b\xc4\x0a\x08\x04\x24\x18\x48\x40\xcc\x41\xbe\
+\x50\x20\x40\x8a\x5f\x8d\x00\xb1\x86\x37\x57\xbe\x8b\x25\x0f\x3f\
+\x8e\xd7\xde\x5e\x46\x6c\x40\xc7\xd4\x09\xe3\x31\x73\xca\x24\xc4\
+\xa2\x61\x58\x50\x4c\x4c\x01\xc0\x27\x08\x02\x47\x1c\xb1\x50\x82\
+\x36\x4f\x3c\xf9\x04\xdd\x8c\x16\x72\xf9\x2c\xb2\x04\x02\x0c\x08\
+\x2c\xbb\x4d\x9f\x81\x69\x53\x77\xc3\x1b\x4b\x5f\xc3\xba\x75\x6b\
+\xca\xfe\x9d\xba\x78\xdb\x61\x56\xa2\xe0\xb4\x99\x6c\xed\xf9\xb1\
+\x64\x08\xed\x2f\x99\xf6\x73\x45\x62\x03\xa4\xf3\x12\xe0\x73\xe9\
+\x6e\x54\xc7\x2a\x11\xa3\xed\xbd\xf5\x1b\x70\xcb\x7d\xf7\xe3\xef\
+\x4f\xfe\x03\xe9\x6c\x16\xd3\x27\x4d\xc0\xbe\x73\xe7\x60\xf2\xb8\
+\xb1\xf0\x79\x3d\xe2\x36\x28\x51\x31\x80\x4f\x94\xae\xf2\x76\xd2\
+\x49\xa7\xe0\x77\xbf\xbb\x09\xaf\xbe\xf6\x1a\xe6\xce\x99\x8b\x4c\
+\x2e\x03\x5b\xc7\x35\x61\x02\x63\x46\x8f\x45\x24\x1c\xc1\x0b\x2f\
+\x3e\x87\xae\x78\x37\x66\xed\x31\x47\xc5\x05\xb6\x61\xfd\xd9\x42\
+\xb3\x95\xb7\x7a\x7d\x7d\x93\x14\x9f\x23\xfe\xf4\x0a\x53\x7f\x93\
+\x5d\x04\x4b\xea\x00\xd8\x4d\xa8\xae\xaa\x42\x73\x7b\x07\x1e\x7f\
+\xee\x5f\x58\xf1\xfe\x6a\xf8\x3c\x6e\xcc\x9c\x3a\x05\x7b\xcf\xda\
+\x03\x63\x1a\xea\xa1\xbb\x34\x61\x0e\x4a\x14\x03\xd8\x31\x01\x2b\
+\xba\x29\xc3\xa1\x30\x16\x2f\xfe\x2c\xfe\xf5\xc2\x8b\x58\xbb\x6e\
+\xad\xe4\xac\x33\x4e\x4c\x80\x5f\x67\x61\x57\xe0\xb0\x43\x8f\x44\
+\x9e\xdc\x84\x17\x5e\x78\x4e\x18\x82\x62\x02\x9b\x29\x3f\xe1\x21\
+\x5b\x7c\xa1\xfa\xb4\x99\x42\xf9\xed\xc0\x9f\x55\xb2\xa3\xff\xbc\
+\x93\x4b\x94\xdf\x0d\xaf\xcf\x87\xbf\x3d\xf5\x0c\x7e\x78\xd3\x1f\
+\xf0\xec\xab\xaf\xa3\xbe\xa6\x0a\xc7\x1f\x76\xb0\x6c\xe3\xc7\x8c\
+\x92\x42\x5a\xa5\xfc\x0a\x00\x76\xb8\x70\x90\x8f\xd3\x80\xc7\x1c\
+\x7d\x0c\x1e\xf9\xfb\x23\x88\xc7\xe3\x44\x61\x8b\xe2\x0e\xe4\xf3\
+\xb9\x5e\x4b\xcf\x45\x29\x07\x1f\x74\x28\x1a\x1b\x1a\xd1\x93\xec\
+\x51\x00\xd0\xab\xfb\xe5\x0a\x4a\xd3\x49\xe9\x31\x08\x14\x9d\x9f\
+\x2d\x3b\xf2\x2f\xc5\x40\xa4\xfc\x9c\x12\xa4\xf7\x04\xc9\xe7\x5f\
+\xdb\xd4\x8c\xbb\x1f\x7e\x02\x01\x9f\x17\x27\x1e\x79\x28\xce\x39\
+\xf1\xd3\x98\x3f\x6b\xa6\x13\x40\x2c\x29\x86\xa5\x00\x60\xe7\x09\
+\xd7\xa3\xef\x35\x6f\x3e\xf6\x9c\x3b\x17\x0f\xff\xfd\xef\x62\xe1\
+\x19\x18\x72\x04\x00\x85\x42\x7e\xc0\xbe\x93\x26\x4d\x42\x28\x18\
+\x52\x37\x68\x2f\x02\x00\xec\x9e\x8b\xa2\x1b\xe5\x60\x1f\xa7\xf9\
+\x2c\x71\x05\x4c\xd3\x2e\xf8\xd1\x9d\x0a\x40\x4d\x40\xb7\x84\xa0\
+\x3f\x80\x86\xda\x6a\x9c\x7e\xdc\xd1\xf8\xf4\xc1\x9f\x42\x2c\x1a\
+\x95\xe7\x55\x15\xa6\x02\x80\x41\x11\x6e\x54\xb1\x70\xe1\x31\x08\
+\x91\x4b\xf0\xd8\x63\x8f\x43\xd3\x35\x61\x02\x0c\x06\xa6\xd1\x57\
+\xc8\xe5\x72\xb9\x7b\x63\x08\xca\xfa\xdb\x65\xbc\x96\xe9\x58\x7e\
+\xd3\xa6\xfe\x6c\xed\xc9\xe6\x4b\xea\x8f\x5f\xe7\xe8\x80\xcb\xa5\
+\xf7\x32\x06\x06\xd7\x48\x28\x88\xc6\xda\x1a\x79\xe4\xfd\x0d\x43\
+\x55\x5e\xee\x92\x00\x50\x0e\xc6\x0d\xf6\x26\x96\x87\x74\x7a\xf1\
+\xa2\xc5\xe8\xee\xe8\x04\xb7\x7c\x0e\xd1\xcd\x69\x95\xef\x74\x47\
+\xbc\x5e\x8f\xfc\x5a\xce\x14\x0c\x95\xe3\x1f\x9c\x0d\x92\xdb\x17\
+\xeb\x2f\x91\x7f\xbb\xc4\xd7\x32\xcc\x5e\x40\xe0\x8b\xea\xee\x57\
+\x47\xc1\x1c\x80\x9f\x0f\x07\xfc\x72\x2d\x57\xad\xdf\x38\xe2\xae\
+\xa3\x02\x80\x61\x2a\x9c\xbf\xe6\x75\x00\x8b\x16\x2f\xc6\xf3\xcf\
+\x3d\x87\x0d\xeb\x36\xc0\xe7\xf3\x4b\x8d\x00\xc7\x06\x78\x2b\xd1\
+\x8d\x1e\xab\xa8\x14\xea\x5b\x2a\x16\x76\xe9\xeb\x25\x96\xde\xa1\
+\xf8\x25\xc3\x49\xf5\x95\x4c\x94\x2c\xdb\x0d\xb0\x1c\xbf\xbf\xbf\
+\xf2\xf3\xff\xd2\x52\x4b\xd3\x51\x55\x51\x81\x8e\xee\x6e\x01\x84\
+\x8f\x1a\x51\x51\xb1\x98\x0f\x2e\x2a\x0d\xf8\x01\x5d\x81\xf1\xe3\
+\x26\xe2\xa8\xa3\x8e\x96\x78\xc0\xb8\xf1\xcb\xc4\xd2\x75\xc7\xbb\
+\xc1\x6b\x3a\x8e\x38\xe2\x08\x4c\x18\x3f\x19\x15\xd1\x18\x7a\x92\
+\x09\x29\x69\x2d\xbb\x05\xbb\x1c\x00\xb0\xf5\x77\x16\xfd\xf4\xd6\
+\xf8\xc3\x74\xd8\x94\x6d\xf9\xcb\x0a\x5a\x56\xfe\xb2\xef\x50\x20\
+\x7f\x7f\x5c\x63\x23\x5e\x7e\xeb\x6d\x64\x09\x60\xfd\xce\x42\xa0\
+\x0f\x05\xd8\xa5\x12\xf2\x85\xa2\x94\x0d\xf7\x07\x1a\x25\x0a\x00\
+\x3e\x76\x50\x70\xee\x9c\x79\xa4\xe8\x13\xe5\x67\x7f\x20\x80\x9e\
+\x9e\x1e\xfc\xf9\xb6\x3f\xe3\xa5\x57\x5e\x94\xe2\xa1\xba\x9a\x06\
+\x44\x89\x2d\xa4\xd2\x49\xf1\x75\xb9\xa4\x75\x97\x63\x4c\x8e\xf5\
+\xb7\xd3\x7f\xf6\x66\x3a\xe5\xc0\xe5\x85\x3d\x5b\x28\xbf\xf3\x8c\
+\x69\x19\xa8\xaf\xae\x44\xa2\x27\x89\x54\x26\x83\x00\x29\x31\xf9\
+\x0f\x1f\x28\xea\x28\x6b\x0c\xe8\x6f\x32\x33\xeb\x49\xa5\x88\x91\
+\x55\x10\x73\x0b\x0d\xf9\x25\xc3\xca\x05\x18\x46\xc2\x37\x33\xe7\
+\xff\xeb\xeb\x49\xd1\x23\x51\x4c\x24\x30\x38\xe5\xa4\x45\x78\xe5\
+\x95\xa5\x78\xf2\xa9\x27\xd1\xda\xbe\x89\x6d\xa0\x14\x09\x71\x39\
+\xeb\xae\x16\xbd\x96\xb8\x09\xf9\xfa\xec\x02\x14\x39\xed\x27\xa9\
+\x3b\x88\xd5\x77\xeb\xfa\x76\x95\x9f\x6f\x44\x8e\x11\x48\x1c\xc0\
+\xe3\x46\x53\x6b\xbb\xb8\x04\xff\x8e\xea\xdb\x73\x05\xf2\xc8\x66\
+\x73\x28\x12\x53\x2b\x4a\x65\xa1\x25\xeb\x0a\x54\xf6\x40\x01\xc0\
+\x27\x4f\x71\x9d\xd4\x16\x3f\xe6\xe9\xc6\x9b\xb1\xdb\xee\x38\xfb\
+\xcc\x73\xf0\xca\xab\x6f\xe0\x89\xa7\x1e\x17\x10\x90\x80\x56\x28\
+\x22\x51\x6e\xcb\xda\x35\x6e\x42\x56\xc6\x32\xe5\x2f\x95\x8a\x12\
+\x17\xb1\x78\x1d\x80\x6b\xa0\xe2\x6f\x4d\xf9\x9d\x22\x4b\x49\x17\
+\xf2\x22\x9f\xa3\x0f\x3a\x10\x95\xb1\x8a\x0f\xa0\xf8\x05\xe9\x24\
+\xcc\x8f\xd2\x44\x84\xfe\xe5\xc5\x46\xd2\x67\xc0\xed\xb6\x1b\x8a\
+\xa8\xb5\x02\x0a\x00\x76\xb4\x6b\xb0\xfb\x6e\xbb\xe1\x73\xe7\x7e\
+\x0e\xaf\xbd\xfa\x16\x1e\x7d\xec\x51\xb4\xb5\x37\x8b\xf5\x0b\x06\
+\x43\x12\x0b\xd8\x15\xd2\x83\xac\x94\x12\xfd\xb7\x9c\xee\x3d\xa4\
+\xd0\x6e\x6d\xcb\x60\xdf\xb6\x94\xbf\x0c\x0e\xac\xcc\x53\xc7\x8c\
+\x42\x7d\x65\x0c\x19\xa2\xf3\xe5\xd9\x01\xe5\xb8\x41\x7f\xc5\xe7\
+\x6b\x2f\xeb\x01\x9c\xd7\xb8\x66\x80\x19\x80\xdb\xa5\x09\x8b\x30\
+\x9c\x2c\x8e\x12\x05\x00\x3b\x1c\x04\xa6\x4f\x9f\x8e\xf3\xcf\x3b\
+\x1f\x6f\x2c\x7d\x07\x8f\x3c\xfe\x28\x5a\xca\x20\x10\x08\xc2\xe5\
+\x76\x8d\x78\x10\x90\xd6\x5d\x6c\xf5\x2d\x3b\xe7\x2f\xf4\xbd\xac\
+\xec\x16\xb6\xd2\x0f\x77\xa0\xf2\xf7\x43\x12\x64\xf3\x05\xa4\x33\
+\x59\x59\x89\xd9\x93\x4c\x4a\x93\x16\xd3\x61\x17\xac\xf8\x39\x51\
+\x7c\xb3\xfc\x66\x79\x64\xd0\xe1\x20\x62\x89\x58\x04\x57\x68\xf6\
+\xae\xcd\x50\x19\x01\x05\x00\x3b\x05\x04\x72\x79\x4c\x9b\x36\x0d\
+\x17\x9e\x7f\x21\xde\x5c\xba\x0c\x8f\x0a\x08\x6c\x94\xda\x80\x80\
+\x3f\x20\x94\x74\xa4\x82\x00\x2b\x1b\x5b\x62\x56\xc2\x62\xd1\x10\
+\xe5\xef\xd3\x3b\xad\xb7\x77\x42\xf9\xfc\xed\x1e\x80\x5b\x2a\x3f\
+\x3f\x57\x6e\xc6\xca\x31\x14\x51\x62\xfa\xdc\x7c\xae\x80\x54\x3a\
+\x8d\x0c\x29\xbf\x9d\x1e\xec\x73\x25\x98\x00\xc8\xdf\x25\xb0\x2d\
+\x39\x8b\x8d\x82\x7e\x1f\x4a\x45\x43\x90\x47\xa9\xbf\x02\x80\x9d\
+\xca\x04\xa6\x4e\x99\x4a\x20\x70\x11\xde\x7a\x6b\x39\x1e\xfa\xfb\
+\xc3\x04\x02\x4d\xb2\x0e\x9e\xd7\xb5\x7b\x3c\xee\x11\x0b\x00\x85\
+\x52\xc1\x29\x84\xb2\x06\x18\x5d\x17\x47\xff\x89\x01\xb9\xe9\xdc\
+\x03\x92\x9a\x73\x6d\xce\x01\x7a\x95\xbf\x4c\x13\x34\x6d\xe0\x67\
+\x73\x05\xa6\x66\x23\x48\xef\x7b\x4c\x27\x66\x20\x81\x3f\x2e\x17\
+\x36\x2c\xb1\xfe\xbc\x4c\xd8\xe5\xd6\x05\x0c\xf8\x6f\x29\x00\x50\
+\x00\x30\x08\x20\x30\x05\x9f\xbf\xf0\x62\xac\x5c\xf1\x1e\xee\x7f\
+\xe0\x01\x6c\x6a\x6d\x92\x12\x62\xbf\x6f\x84\x82\x00\x69\x59\x8e\
+\xac\xb4\xbe\xb9\xcf\xcf\x15\x7e\x96\x6d\xb1\x3d\x2e\x0f\x51\xf7\
+\xa2\xa4\xe8\x6c\x85\xde\xbe\xf2\xf7\xb7\xf2\xfd\x83\x88\xcc\x21\
+\xf8\x33\xd9\xe2\x73\xf9\xb0\xd4\x1b\x58\xf4\xb3\x65\x08\x00\x70\
+\x23\x11\x0e\xd2\x5a\xfd\x16\x1a\x29\x51\x00\xb0\xd3\x41\x60\xd2\
+\xa4\xc9\xb8\xe4\xf3\x97\xe2\xfd\xf7\xd7\xe2\xbe\x25\x4b\xb0\xa9\
+\x65\x83\x80\x80\xcf\xeb\x95\x9e\x02\x23\xc9\xfa\x17\x0b\xb6\x32\
+\xb2\x62\xf7\xc6\x04\x34\xbb\xf6\x3f\x14\x0a\x21\x12\x0e\x4b\xc6\
+\xa4\xb9\xb5\x45\x4a\xa9\x3d\xd2\x49\xc9\xda\xae\xe5\x07\xfa\x5c\
+\x7c\xcd\x21\xf3\x5c\x63\x20\xd5\x85\xe5\xa6\x22\x96\x5d\x66\xcc\
+\x15\x86\x5c\x03\xc0\xbd\x03\x78\xe5\x60\x81\x8e\x87\xd3\x8e\xaa\
+\x10\x48\x01\xc0\xa0\x82\xc0\xf8\x71\x13\x70\xd9\x25\x97\x61\xfd\
+\x86\x66\xdc\x75\xcf\x3d\x68\x12\x10\x28\xd9\x20\xe0\x1d\x19\x20\
+\xc0\x2d\xbc\x32\x32\xd4\xb3\xbf\x7f\x6f\x07\xe0\x22\xa1\x30\x42\
+\xc1\x00\xda\x3a\xda\xb1\x76\x43\x13\xc6\x8f\x1d\x2b\x1d\x7d\xb9\
+\xa1\xa7\xee\x72\x8b\x9f\xbf\x55\xda\xdf\xdf\xec\xf3\xda\x02\xd3\
+\x4e\xef\xf1\xc6\x81\x55\xae\x35\x28\x95\xab\x0c\x65\x6e\x00\xe4\
+\x39\xfe\x5b\xec\x8a\xb0\xab\xc0\xd7\x58\x95\x04\x2b\x00\x18\x54\
+\xe1\x02\x95\xb1\x63\xc6\xe2\x8b\x97\x7d\x11\x2d\xad\x1d\xb8\xf3\
+\xce\xbb\xb0\xb1\x79\x1d\x3d\x9f\x23\x6b\xe5\x95\xf1\x57\xc3\xfa\
+\xe6\x21\xe5\x2f\x4a\xf4\xbd\xdc\x0c\xa5\x4f\x71\xd9\x1d\xc8\x93\
+\x6f\xbe\x7e\x63\x33\xd6\x35\x35\x63\xd2\xf8\xf1\xe2\x26\xbc\xb5\
+\x6c\x85\xd4\x09\xb0\x75\xe6\x36\x5f\x76\xe0\x6f\x4b\xe5\x17\xaa\
+\xcf\x81\xc5\x92\xdd\x30\xb4\x5c\x51\xc8\x0a\x5f\x5e\x57\x50\x32\
+\xec\x25\xc6\xcc\x0a\x82\x01\x3f\x5c\x2e\x8d\x80\xb7\x28\x1d\x9d\
+\x3d\x6e\xb7\x5a\x9d\xa9\x00\x60\x28\x80\x40\x41\x1a\x86\x7c\x89\
+\x40\xa0\xb3\x33\x8e\xdb\xef\xbc\x93\x40\x60\xbd\xf4\x15\x60\xba\
+\xca\x29\xab\xe1\x2c\xdc\x41\xb9\xbc\x90\x87\x8b\x7e\xf4\x7e\xb4\
+\xdb\x20\x70\xc8\xe7\x0b\x98\x4c\xca\xcf\xb1\x8f\xd5\xeb\xd7\x63\
+\xca\x84\x71\xb4\x8d\x17\xd6\xc0\xa9\xbe\xfe\xeb\x02\xfa\x16\x06\
+\x41\xfc\x79\xc3\xa9\x2b\x30\xca\x0d\x44\x7a\x7b\x09\x38\xe5\xc6\
+\xce\x92\x61\x06\x93\x50\xd0\x2f\xeb\x07\x64\x10\x87\xdf\xa7\xac\
+\xff\x70\x07\x80\x91\xb4\x14\x94\x41\xa0\xb6\xb6\x8e\x98\xc0\x65\
+\x48\xc4\x93\xb8\xe3\x6e\x9b\x09\x64\xf3\x59\x59\xfe\xca\x31\x81\
+\xe1\x76\x4e\x9c\xda\x4b\x67\x32\xe4\xdb\x17\x6c\x6b\x4b\xff\xa5\
+\xb3\x39\x02\xb6\x82\x53\xb8\x63\xdf\x5a\x95\xb1\xa8\x44\xff\x79\
+\xbf\xe9\x13\x27\x60\xdc\xa8\x51\x88\x27\x7a\xb0\xb1\xa5\x45\x62\
+\x03\x76\x43\x90\x3e\x67\x9f\x95\xbb\x4c\xf5\x0d\x67\x5a\x50\x7f\
+\xe5\x37\x9c\x91\x61\x76\x97\x21\x3b\xd8\x17\x8b\x46\x48\xf9\xf3\
+\x28\x30\x13\xf0\xf9\xe0\xa5\xe3\x31\x9d\x01\x2e\x6a\x39\xb0\x62\
+\x00\x43\x42\x98\x2a\xd7\xd6\x10\x08\x5c\xfa\x05\xf4\xf4\xa4\x71\
+\xdb\xed\x77\x60\x43\xd3\x5a\x69\x33\xc6\x53\x64\x87\x93\x3b\xe0\
+\x76\xbb\x08\xbc\x72\x02\x00\x7c\xdc\xf1\x64\x0a\xef\xae\x5d\x8f\
+\x35\x4d\x9b\xb0\x81\x14\xbb\xad\x2b\x6e\xa7\xec\x9c\x68\x7f\x81\
+\x68\x79\xa1\x50\x94\x02\x9e\xf7\xd7\xaf\xc3\xb2\xf7\x56\xa1\xa6\
+\xaa\x52\xea\xfe\x2d\xa9\xe4\x73\x18\x83\x94\x11\x9b\xce\x8a\x42\
+\xfb\x77\xa6\xf8\xa6\x69\x39\xb3\x02\x1d\xe5\xb7\xd0\x1b\x03\xe0\
+\xe5\xc3\x05\x72\x29\xb2\xf4\xd9\x3e\xb7\x17\xe1\x60\x40\x51\x7f\
+\x05\x00\x43\x17\x04\x6a\x6a\x6a\x71\x39\x81\x40\xbe\x50\xc2\x9f\
+\xff\x7c\x1b\xd6\x6d\x78\x9f\x7c\xe3\x8c\x80\x80\x6f\x18\x80\x00\
+\x5b\x7e\xb6\xf2\xc9\x64\x9a\x2c\xad\x07\x9d\xf1\x04\xd6\x35\x6f\
+\x12\x1a\x5e\x11\x0a\x4a\x1a\x8e\xb3\x1d\x09\xc7\x35\x60\xdd\x66\
+\x1a\xef\x71\xeb\x52\xb0\x93\x22\xda\x3f\xa6\xa1\x81\x14\x37\xea\
+\x94\xf1\xc2\xd9\xc7\x92\xe8\xbe\x58\x76\xfa\x9d\xfd\x7c\x06\x87\
+\x5e\x17\xa0\xbc\xb1\xf2\x17\x4b\x72\x2c\xdc\x3e\x8c\xf7\x4f\xa6\
+\xd2\xb2\xe0\x28\x16\x09\x0f\x70\x41\x94\x28\x00\x18\x92\x20\x50\
+\x55\x55\x83\x2f\x5c\x72\x99\xd0\xdf\x9b\x6f\xf9\x0b\x56\xaf\x7b\
+\x8f\x40\x80\x14\xca\xe7\x1d\xd2\x20\xc0\xca\x25\xca\xdf\x93\x14\
+\x16\xd0\x1e\x8f\xa3\xa9\xb5\x15\xb5\x95\x95\xa4\x8c\x35\xb2\x80\
+\x27\x1a\x0a\x91\x65\x0f\xd8\xc5\x41\xa4\xa8\x52\x05\x4c\x1a\xce\
+\x00\x51\x15\x8b\x61\x6c\x63\x83\x44\xeb\xb9\x70\xc7\xbe\xf9\xec\
+\x02\x1e\x1e\x17\x66\xf7\x0e\x70\x56\x14\x3a\x9d\x84\x0c\xa3\x2f\
+\x06\xc0\xfb\x71\x00\xd1\xef\xf7\x62\x14\x29\x3f\xbb\x1c\xdd\x74\
+\x2c\x9c\x4d\xa8\x22\x37\xe0\xa3\xf4\x0f\x50\x00\xa0\x64\x50\x40\
+\xa0\x32\x56\x29\x29\x42\x9f\x2f\x80\x3f\xfd\xe9\xcf\x58\xb5\xfa\
+\x5d\x64\xb2\x69\x69\x8d\xed\xf3\x79\x81\x21\x56\xc3\xc6\x2b\x1b\
+\xf3\x74\xdc\x3d\x69\x52\x7e\x8f\x0b\x1d\x9d\xdd\xa4\xfc\x6d\x68\
+\xa8\xa9\x46\x4d\x65\x0c\x01\x3e\x6e\x9e\xda\xeb\xf5\xc8\xf1\xfb\
+\x69\x73\xf1\xd0\x14\xe9\xfb\x67\xb7\x08\x37\x1d\xbf\x5d\x14\x9c\
+\x9e\xe0\x81\x20\x9c\x29\xc8\x16\x0a\x02\x16\xec\xc3\x97\x0a\x86\
+\x3c\x72\x1c\x80\x33\x00\x85\x72\xfa\x8f\x6b\xfc\xbd\x6e\x34\x54\
+\x57\xc9\x50\xd0\x76\x62\x1e\xcc\x3e\xb8\xd2\xb0\x86\xae\x65\x38\
+\x18\x54\xca\xaf\x00\x60\x78\x81\x40\x45\x24\x86\x4b\x3f\x7f\x09\
+\xa2\xd1\x0a\xfc\xf1\xe6\x5b\xf0\xee\xaa\x15\xe4\x57\x27\xc5\xaf\
+\xf6\x49\x9d\xc0\xd0\x00\x01\xb6\xde\x39\x52\x52\x6e\x7d\xee\xd1\
+\xc9\xf2\x93\x8f\xdf\xd4\xd1\x81\xc6\x3a\xb2\xfa\x15\x31\xbb\x42\
+\xaf\x37\xd8\xa5\x49\x9f\x7f\x2e\xc4\xf1\x90\xf2\x33\x10\x30\x5b\
+\xe8\xbf\x2c\x8f\xfd\xf7\x24\x59\xef\x78\x3a\x43\x2e\x41\x8e\x3e\
+\xbb\x88\xbc\xe1\x4c\x0f\x92\xfd\x2c\xc9\x1a\x70\x40\x2f\x16\x89\
+\xa0\x8e\x00\xa6\xb1\xa6\x0a\x15\xe1\x10\x3a\xba\xba\xb0\x62\xf5\
+\x1a\x7a\xec\x96\x7d\x6a\x09\x10\xa2\x21\xa5\xfc\x1f\x39\x9e\xa3\
+\x2e\xc1\x20\x82\x00\xd1\x59\xee\x1b\x70\xc9\x45\x9f\xc7\x6f\xff\
+\xef\xf7\xb8\xf9\xe6\x5b\x71\xe6\x99\xa7\x61\xda\x94\xe9\x88\x84\
+\xa2\xc2\x9d\x39\x8d\x36\x98\x6b\x5a\x7b\x95\x3f\x95\x22\xe5\x77\
+\xa3\xa3\x3b\x61\x2b\x3f\x59\xfe\x4a\x02\x2e\x5b\xf1\xb6\x3c\x3e\
+\xcb\x71\x19\x3c\x6e\xaf\xf8\xf2\x9c\xcb\x07\xec\x74\x1f\x7f\x26\
+\x2b\x37\xbb\xeb\xb6\xde\xd2\xb3\x2e\x9b\x25\x70\x47\x1f\x66\x1b\
+\xc2\x08\x8a\x05\xa4\xd3\x29\x49\x17\x76\xf5\xf4\x88\xaf\xcf\x90\
+\x18\xab\x88\x12\xf8\xd4\x62\x54\x5d\x9d\xe4\xff\x2d\xd5\xf8\x43\
+\x01\xc0\x70\x15\x56\x8c\x40\x30\x8c\x8b\x2e\xb8\x10\xbf\xff\xc3\
+\xef\xf1\xe7\x5b\xff\x82\x33\x4e\x3b\x15\xd3\xa7\xed\x8e\x68\xd8\
+\x6e\x8a\x31\x58\x20\x50\x56\xfe\x24\x2b\xbf\x8b\x68\x3f\x51\xee\
+\xe6\xf6\x76\xa1\xfd\x55\xd1\xe8\x76\x15\x8f\x15\xdd\xed\x72\x8b\
+\x95\xe6\x61\x2a\xe5\xe7\xec\x71\xdf\xa4\xe0\x74\x4e\x5d\xf1\x38\
+\xda\xbb\xe3\xe8\xa4\x8d\x15\x9c\x0b\x85\xb8\x38\x48\xe6\x2f\xd0\
+\x75\xe1\xa5\x3c\x6e\xb7\x2e\xc1\xc6\x68\x38\x8c\x7a\xf2\xfb\x59\
+\xe9\x79\x44\x58\x55\xac\x42\x66\x09\xaa\xae\x3f\x0a\x00\x86\xbd\
+\x70\xce\x3b\xe0\x0f\xe1\x82\xf3\x2e\xc0\x1f\x6f\xf9\x23\x6e\xb9\
+\xf5\x36\x9c\x7e\xea\x62\xcc\xd8\x7d\x96\xb8\x09\x36\x08\xe4\x77\
+\xb2\xcf\xef\x92\xdc\x7d\x4f\x92\x94\xdf\xcd\xca\x4f\xb4\xbf\xad\
+\xa3\x4f\xf9\xad\xed\xf7\xda\x61\xda\x1f\x20\xeb\x5c\x94\x25\xc2\
+\x45\xbb\x50\x88\x9e\x63\x9a\xbf\x7e\x53\x8b\x04\x0f\xbb\x08\x50\
+\xf2\x64\xe5\x79\xd5\x5e\xd0\xe7\x47\x45\x28\x6c\x67\x05\x68\x5f\
+\x8b\x1e\x3d\x04\x20\x95\xd1\x08\x5d\x83\x08\xb9\x1a\x11\x62\x45\
+\x21\x04\xfd\x7e\x61\x46\x52\x17\xa0\x68\xbf\x02\x80\x11\x03\x02\
+\x06\x81\x40\x20\x88\xcf\x9d\x73\x1e\x6e\xfe\xcb\x9f\x71\xeb\x5f\
+\xee\xc0\xa9\xa7\x1a\x98\x3d\x73\x8e\x80\x00\xaf\x83\xe3\xd5\x74\
+\x3b\x83\x09\x94\x95\x3f\x41\x3e\x3f\x5b\x6b\xb6\xd2\x2d\x1d\x65\
+\xe5\x8f\x08\x6f\xdf\xe6\x51\xd0\x0b\xbc\xfc\x97\x2d\x36\x2b\x69\
+\x92\x00\x84\xb3\x00\xcc\x04\x92\x44\xe5\x37\x34\xb7\xa0\x2b\x11\
+\x97\xc6\x21\x13\xc6\x8e\x46\x6d\x55\x25\x7d\x66\x85\x2c\x97\xe6\
+\xa2\x28\xbd\xdc\x3b\x50\x1b\x78\xaa\xe2\x68\x94\x95\x5e\x29\xbe\
+\x02\x80\x91\x09\x02\x06\xfc\xfe\x20\xce\x3d\xf3\x1c\xdc\x7e\xe7\
+\x6d\x04\x02\xb7\xa3\xb4\xa8\x80\x3d\xe7\xce\x97\x66\xa4\x7e\xd2\
+\x8a\xdc\x0e\x76\x07\xf4\x5e\xcb\x9f\x84\x5b\x7c\xfe\x38\x36\x75\
+\x76\xd8\x69\xbe\x70\x44\xfe\xf4\xb6\xd4\x9f\x59\x01\x57\x35\x72\
+\x47\x5e\x56\x70\xee\x9a\xcc\x2d\xd1\xb8\x45\x77\x82\xdc\x88\x96\
+\x8e\x4e\xa9\xdb\xaf\x23\x20\x19\x55\x5b\x8b\xea\xca\x98\xdd\x2c\
+\xc4\x99\x13\x58\xbe\x06\x4a\x14\x00\xec\xd2\x20\xc0\x83\x47\xce\
+\x38\xed\x0c\x49\xa3\xdd\x76\xc7\xdd\xa2\x34\x7b\xcd\xdb\x9b\x68\
+\x70\x15\xfc\x9a\x8f\x7c\xe5\xfc\x0e\x01\x01\x56\xfe\x82\x43\xfb\
+\xd9\xff\x6f\xef\xee\x46\x4b\x67\x27\x1a\x49\x59\x63\x64\xd1\xb7\
+\x67\xf9\x59\x7f\x45\xf9\x63\x51\x69\xd3\x93\x24\x00\xe1\xb5\x0e\
+\x3e\x7f\x00\xdd\x3d\x09\x74\x27\x7a\xc4\xc2\x57\xd1\xeb\x75\x95\
+\x95\x52\xef\xa0\xc6\x7f\x29\x00\x50\xb2\x0d\x10\xf0\xb8\x7d\x38\
+\x6d\xf1\xa9\xb2\x6c\xf8\x8e\xbb\xee\x95\x8c\xc1\x3e\xf3\x17\x90\
+\xd5\xac\x15\x8b\xca\xd1\xf2\x4f\x96\xf6\xeb\xbd\x96\x9f\x7d\x72\
+\x2e\xe7\x6d\x25\xe5\x1f\x55\x67\x2b\xff\xf6\x7d\x7e\x4b\x94\x9b\
+\xa3\xf3\x9c\xe3\x4f\xa6\x53\x52\xd9\xc8\x0d\x50\xda\x89\xee\xf7\
+\x70\xa5\x9e\x9b\xfd\xf9\x28\x62\x91\x90\xf0\x7b\x43\x05\xef\x14\
+\x00\x6c\xf3\x76\x1a\x06\x8b\x28\x76\xb4\x94\x64\xba\x90\x07\xa7\
+\x7c\xe6\x64\x89\xa6\xdf\xbb\xe4\x7e\xc9\x18\x2c\xd8\x7b\x5f\xd4\
+\x54\xd5\x13\x08\xd8\xe3\xc9\x3e\x89\x6b\xc4\x3e\x7b\x39\xe0\xc7\
+\x91\x75\xb6\xfc\x6d\x5d\xdd\x42\xd3\xff\x9d\xf2\xf3\xf3\xbc\xa2\
+\xd1\x56\x7e\xbb\x2c\x97\x47\x7d\xfb\xe8\xf8\x38\x65\x98\xcd\xe4\
+\x84\x19\x54\xd2\xe7\xd8\x75\xfa\xd8\x65\x5a\xa5\x6f\x7e\x3f\x2b\
+\x00\x50\xf2\xa1\x99\x80\x4e\x7e\xf8\x09\xc7\x9f\x20\xe5\xae\x7f\
+\xbb\xff\x21\x01\x81\xfd\x16\xec\x8f\xfa\x9a\xc6\x4f\x04\x04\xec\
+\x80\x5f\x51\x82\x75\x1c\x80\xeb\x8c\xdb\x96\xbf\x4c\xfb\xb7\x17\
+\x69\xe7\xbf\xcb\x96\xbf\x92\x94\x9f\x6b\xf2\x53\xac\xfc\x74\x4c\
+\x7e\xa2\xfd\x9c\xd6\xe3\x05\x43\x1c\xfc\xe3\x69\xbf\x21\x7a\x5e\
+\x15\xeb\x28\x00\x50\xf2\x21\x85\x23\xe9\x2e\x02\x81\xe3\x8e\x39\
+\x5e\x2c\xf5\x43\x7f\x7f\x54\xca\x62\x0f\xd8\xef\x00\x34\xd4\x35\
+\x4a\xf4\x3c\xfb\x11\x41\x80\x3f\x8f\x1b\x6a\xb2\xbf\xce\xd5\x7b\
+\x9d\xf1\x1e\xb4\x74\x74\xa3\xa1\xba\x06\x15\x8e\xe5\xc7\xf6\x2c\
+\x3f\xf9\xf1\xbc\x14\x97\x2d\x7f\x2a\x95\x12\xc5\x0f\xf0\xc8\x34\
+\x02\x02\x0e\x56\xb2\xe5\x0f\x93\xbb\xc2\x65\xc2\x4a\xf9\x15\x00\
+\x28\xf9\x18\x20\xe0\x26\x77\xe0\xe8\x23\x8f\x11\xff\xfc\x91\x47\
+\x1e\x95\x95\x75\x87\x1c\x74\x30\x1a\xeb\x46\x4b\xbe\x9d\xfb\xe5\
+\x7f\x18\x1d\x63\xcb\xcf\xab\xef\x12\x64\xa9\x99\xf6\x77\x26\x12\
+\xd8\xd4\xd1\x85\xfa\xea\x2a\x54\x90\x52\x5b\xff\xc6\xf2\x7b\xca\
+\x3e\x3f\x59\x7e\xae\x12\xe4\x3c\xbe\x28\x3f\xf7\x04\x20\x96\xc2\
+\x01\xc0\x80\xcf\x03\x9f\xea\xcc\xa3\x00\x40\xc9\x27\x07\x02\x47\
+\x1e\xbe\x50\x94\xeb\x6f\xf7\x3f\x28\x2b\xe8\x8e\x38\xfc\x70\x02\
+\x81\x51\x0e\x13\xc8\x6f\x55\xd9\x98\xda\xf3\x56\x6e\xa0\xa1\xbb\
+\x02\xb2\x08\xa7\x27\xd9\x05\xb7\x28\x7f\x0f\x36\xb5\x77\xc8\xf2\
+\x5a\xae\xbd\xdf\x6e\xb4\x9f\xeb\xf4\xc9\xf2\xf3\x72\x5e\xfe\x3c\
+\x51\x7e\xb1\xfc\x7e\xf4\x48\x5d\x7f\x5e\x32\x08\x01\x02\x08\xaf\
+\x4b\x75\xe5\x55\x00\xa0\xe4\x13\x07\x81\x43\x0e\x3e\x4c\xfa\xec\
+\xdf\x7b\xef\x5f\xc5\x8a\x2f\x5c\x78\x24\x46\x11\x08\x70\xf0\x8d\
+\xc7\x69\xf5\x2f\xd1\x95\xf5\xfb\xf4\x5c\x47\x7b\x3b\x6a\xeb\x1b\
+\xe0\xf3\x85\x01\x63\x25\xb2\x69\x1e\xdf\x35\x1a\x1d\x89\x36\xb4\
+\x76\x74\xa2\xa1\xa6\x86\xe8\x3c\xa7\xf0\xcc\xed\xa6\xfa\x78\xc9\
+\x72\x2c\x5a\x21\x41\xca\x54\x32\x25\xeb\xff\xfd\xc1\x00\x92\x99\
+\x0c\x29\x7f\x49\x29\xbf\x02\x00\x25\x3b\x23\x26\x70\xe0\xfe\x07\
+\x49\x9d\xc0\xdd\x77\xdf\x23\x6b\xe4\x8f\x3a\xea\x28\x8c\x69\x1c\
+\x6b\x33\x81\x6c\x4e\x22\xed\xec\x83\xf3\xda\xfd\x3f\xdc\xf2\x27\
+\xbc\xfa\xc6\x9b\xd8\x7b\xcf\xbd\x71\xe9\x67\x4b\x08\xe5\x7f\x01\
+\x7f\x60\x22\x56\xe5\xbe\x8d\xe6\xf6\x20\x46\xd7\xb1\xcf\x1f\xd9\
+\x6e\x6d\xbf\xad\xfc\x76\x2a\x4f\x94\x9f\x2d\x7f\x28\x28\xa9\xbe\
+\x64\x3a\x2b\x2b\xfa\x94\xf2\x2b\x00\x50\xb2\x13\x41\x60\xbf\xbd\
+\xf7\x93\x01\x9c\x5c\x27\xc0\x9d\x73\x8e\x3e\xea\x68\x8c\x69\x18\
+\x8b\x60\xd0\xdf\x1b\xdc\xfb\xe5\x6f\x7e\x85\x17\x49\xf9\x03\xc1\
+\x08\x1a\x83\x77\xc0\x6b\xa4\x01\xcd\x07\x57\x6a\x29\xea\xbd\x3f\
+\x47\xa1\xe1\x3b\x64\xc5\xab\xe8\x53\x0b\xdb\x55\x7e\x8f\xd7\x2d\
+\xab\xff\x98\xf6\xb3\xe5\x0f\x06\x6d\xe5\x4f\x64\xec\xe9\xbc\xba\
+\x52\x7e\x05\x00\x4a\x76\x36\x08\x78\x30\x7f\xfe\x02\x59\x1c\xc3\
+\x73\x07\x78\x38\xe7\xd1\x47\x2d\xc4\x94\x49\xd3\x45\x6b\x59\xf9\
+\x1f\xff\xe7\x73\x88\x56\xd4\xe3\xcc\x23\xd7\xe1\x94\x63\x8b\x30\
+\xbc\x7b\xc2\xcc\xb4\x03\x49\x03\xb1\xe2\x73\x70\x55\x3e\x84\xb6\
+\xfc\x05\xb4\xfb\x36\xd6\x19\x38\x01\xbf\x01\xa9\x3e\xa2\xfc\x9c\
+\x82\xec\x21\xe5\xe7\xfa\x01\x65\xf9\x15\x00\x28\x19\x0c\x10\x20\
+\xab\xcf\xc5\x42\xf3\xf6\x9c\x4f\x20\x60\x11\x08\xdc\x2b\xad\xb8\
+\xdc\x47\xbb\x71\xcb\xad\x77\xe0\xaf\x7f\x7f\x04\xbe\x60\x0d\x4e\
+\x5d\xd8\x84\x53\x16\x76\xc1\x4a\x73\xcd\x7d\x0f\x11\x80\x4a\x68\
+\xa5\x06\x18\xa4\xcc\xa1\xcc\x6d\x08\xf9\xe7\x21\x9d\xdf\x13\xba\
+\x96\x42\xff\x26\x24\x76\x9e\xbf\x7f\x91\x4f\x4a\x06\x9d\x72\xba\
+\xcf\x0e\xf8\x29\xe5\x57\x00\xa0\x64\x50\x85\xfd\x76\x8f\xc7\x87\
+\x3d\x67\xef\x25\xfe\xff\x43\xa4\xf4\x57\x5d\xfd\x6d\xbc\xb9\x72\
+\x15\x2c\xbd\x02\x17\x2f\xee\xc2\x39\x9f\xe9\x81\x55\xd0\xa5\xa8\
+\x47\xcb\x6e\x80\x15\xdd\x0d\xf0\xd7\x13\x08\x24\x01\xfa\x3d\xe6\
+\xbd\x19\x39\xd7\x74\x52\x72\x37\x01\x88\xd1\xe7\xf3\x7b\x9d\x85\
+\x3d\xac\xfc\x44\xfb\x39\xcd\xc7\xee\x05\xd3\x7e\xa5\xfc\x23\x43\
+\x54\x4b\xb0\x11\x02\x02\xa1\x50\x54\xa8\x7f\x57\x57\x1c\xcf\xbe\
+\xf4\x3a\x7a\xd2\x6e\x7c\x7e\x51\x06\x9f\x3f\x2d\x49\x96\x1f\x28\
+\xf5\x14\x61\x66\x8b\xd0\x8a\x69\x1b\x04\x3c\x11\x58\x81\x46\x98\
+\xee\x0a\xf8\xd2\x4f\xa1\xc2\xfd\x20\x4c\x2d\x28\x6e\x80\x58\x7e\
+\x0f\xf9\xfc\xa4\xfc\x9c\xe7\x4f\xa4\x92\x42\xfb\x79\xeb\x49\xe7\
+\x24\xdd\xa8\x94\x5f\x31\x00\x25\x43\x44\x38\xda\x9f\xcb\x66\xf0\
+\x3f\xff\xfb\x0b\x2c\x79\xf0\x51\x64\x4b\x7e\x7c\xf3\xec\x3c\xae\
+\x38\x8f\xdc\x84\xa4\x07\x85\x44\x09\x16\xbd\x6e\x15\x73\xd0\xe8\
+\x1b\x77\x57\x11\x10\xb8\x43\xb0\x7c\xc4\x02\x02\x04\x10\xc9\x95\
+\x88\x14\x6e\x45\xca\xbd\x1f\x72\xc5\x06\x52\x6c\x43\x2c\x3f\x57\
+\x1c\x72\x1f\x40\xee\xe2\x1b\x08\x06\xd1\x93\xca\x22\xcb\x15\x7e\
+\xdc\xec\x83\x94\xdf\xad\xa6\xef\x2a\x00\xd8\x21\x16\x4d\x2d\x06\
+\xfa\xc0\xc2\x79\x7e\x4e\xc9\xfd\xf8\x27\x37\xe2\x57\xbf\xff\x13\
+\x32\x45\x2f\xae\xbb\x3c\x80\xff\x77\x29\x5d\xbf\x6c\x10\xc5\xbc\
+\x9f\x76\xca\x12\x4a\x98\x76\xff\xfd\x5c\x06\xc5\xf6\x14\xbc\xee\
+\x35\xd0\x6a\x08\x04\x42\x63\x09\x18\x7a\xe0\xca\x2c\x47\x2c\x7a\
+\x07\xba\xfd\x57\x21\x16\x71\x89\xe5\x4f\x92\xe5\x0f\x05\x82\x92\
+\xe7\x4f\x48\xaa\x2f\x2f\x2d\xba\x02\x1e\x47\xf9\xd5\xf7\xf3\x81\
+\xef\x67\xe5\x02\x28\xd9\x61\xd6\x7f\xc9\x92\x25\xf8\x9f\x9f\xff\
+\x0a\xa9\x8e\x1c\xbe\x75\x89\x1f\x57\x5f\xee\x81\x95\xd7\x50\x34\
+\xa2\xd0\x7c\x21\x68\xde\x00\x3d\x12\xbd\xf7\x06\xa1\xf9\x83\xe4\
+\x32\xe8\x30\xba\x53\xd0\x12\xef\x02\x9a\x8b\x40\x60\x02\xd1\xff\
+\x08\x02\x99\x7b\x50\x13\x5a\x06\xc3\x0c\x90\xcf\xdf\x23\x3e\x7f\
+\x80\x69\x7f\x2a\x23\x53\x7d\xb8\x3b\x10\x2b\xbf\x4b\xd7\x94\xf2\
+\xab\x18\x80\x92\xa1\x20\x6c\xd5\x0b\x45\x1e\xa4\xe1\xc5\x37\x2f\
+\xf7\xe1\xda\xaf\x04\x80\x3c\xf7\x19\x64\x7e\xe7\x23\x84\xf0\x43\
+\xf3\x04\xfa\x36\x02\x01\xdd\xeb\x07\x67\xfd\x8c\x44\x0f\xf4\xd4\
+\x6a\x58\xde\x18\xac\xf0\x44\xe8\xc5\x0e\x78\x12\x3f\x46\x26\xd7\
+\x86\x60\x20\x26\x3e\xbf\x58\x7e\xa2\xfd\xfd\x95\x5f\x89\x72\x01\
+\x94\x0c\x11\xe1\x99\x7b\x27\x9c\x70\x0a\xc6\x57\x3c\x8c\x63\x3e\
+\x45\x16\xbd\xe8\x86\x41\xcf\xf1\x6c\x4e\x4d\x27\x60\xd0\x83\xd2\
+\xa1\x47\x2b\x1b\x6c\x99\xc7\x6d\x92\xf6\x13\xcd\x2f\x1a\xd0\x7a\
+\xda\xa1\xbb\x89\x15\x04\xc7\xd2\xef\x49\xe8\x89\xc7\x51\x59\xfd\
+\x2b\x18\xd1\x6b\x91\x48\xf5\x48\x47\x5f\xee\x47\xa0\x94\x5f\x01\
+\x80\x92\x21\x28\xa6\xe6\xc5\xa8\x8a\x7f\x62\xdc\xc1\xcb\x60\x21\
+\x8a\x12\x6d\x9a\xa7\x00\x94\x12\xd0\x4b\x1d\x30\xfd\x51\x58\x3e\
+\xee\xc2\xd3\x0f\x04\x60\xda\xd4\xdf\xca\xc3\xcc\xd3\xf3\xc9\x26\
+\xe2\x82\x5e\x58\x15\xd3\x09\x18\x72\xf0\x75\xfd\x2f\xd2\x7a\x35\
+\xb2\xb8\x0c\x2e\x97\x86\x80\xb7\xa4\x94\x5f\x01\x80\x92\x21\xe7\
+\xbf\xb9\xc8\x8a\xf3\x0c\xbe\xcc\x13\x70\xa5\x5b\x48\xad\x4b\x44\
+\xf1\x0d\x52\xf8\x28\xe0\xaa\x81\x56\x88\x43\xcf\x31\xc5\x1f\x0b\
+\x8b\x5c\x01\xa9\xf4\x63\xeb\xaf\x31\x33\xd0\xa0\x95\x98\x0d\x30\
+\x08\x98\xd0\x93\xeb\xa0\x85\x2d\x58\x95\xf3\xc8\x35\x78\x03\xc1\
+\xf6\x6b\x51\x5d\x99\x46\x31\x74\x01\x5c\x1a\xaf\x10\xcc\xab\x0b\
+\xae\x00\x40\xc9\x50\x11\x8e\xfe\x73\x20\x2e\x1e\x4f\x10\x3d\x3f\
+\x01\x9e\xe8\x2a\x78\x92\x2f\x90\x01\x2f\x90\xa5\x27\x65\xf5\x90\
+\x5f\xef\xad\x24\x26\x90\x24\x20\x58\x4b\xbf\xd7\x13\x08\xf8\xe8\
+\x35\x0f\x2c\x8d\xa8\x3f\xd3\x01\xcd\x69\xb1\x6d\x16\x61\x15\x4c\
+\x68\xe9\xf5\xd0\x82\x25\x02\x81\xbd\x60\xba\xa3\x08\x75\xdf\x08\
+\xc3\x78\x17\xb9\xe8\x95\x04\x34\x93\x68\x5f\x5e\x2f\xa0\x9a\x78\
+\x2a\x00\x50\x32\xf8\xca\x4f\x8f\x89\x9e\x24\x3c\xba\x85\x96\xae\
+\x89\x78\x2f\xf3\x45\xcc\x69\x1c\x83\x70\xee\x61\x98\xb9\x0e\xc9\
+\xf9\xc3\x5f\x45\x8a\x4f\xd6\xdb\x20\x40\x28\x36\x43\xd3\x09\x14\
+\xdc\x21\x7a\x74\xf3\x0c\x5e\x9b\x0d\x94\x2c\xbb\xd5\xb7\x49\x00\
+\x40\xba\xad\x65\x68\xbf\x52\x06\x56\x78\x02\x0c\x3f\xb1\x88\xf8\
+\x53\x08\xe4\x96\xa1\x10\xbb\x14\x25\xdf\xb1\xe2\x66\xd8\x40\xa0\
+\x9a\x7a\x8e\x08\x16\xa9\x2e\xc1\xf0\x54\xfe\x78\x4f\x42\xe2\x79\
+\xf1\x54\x0a\xcd\x6d\xab\xe1\x76\x8f\x47\x5b\xf1\x2a\x74\x87\xae\
+\x81\x15\x99\x49\x5f\x6c\xca\x56\xe6\x5c\x1b\xd1\x7d\x17\x41\x3d\
+\x29\xbe\x11\x27\xa3\xdf\x49\xef\x23\x65\xf7\xb8\x68\xf3\x90\xcb\
+\xe0\xa5\x47\x2f\xb8\x42\xc8\x2a\x71\x7a\x8f\xf6\x2d\xd2\x7e\xf1\
+\xb7\xa0\x9b\x05\x58\xd5\xfb\x01\x3e\x1d\xbe\x8e\xab\x11\xe8\xbc\
+\x14\xee\xd2\x53\x36\x73\x80\x0f\x43\x6d\x82\xb1\x12\xc5\x00\x76\
+\x0d\xe5\x4f\x24\xc0\xf3\x73\x58\xf9\x37\xb4\xb4\xa2\xb6\xaa\x0a\
+\x55\x15\xa4\x90\x96\x86\x44\xf1\x64\xe4\xbd\x73\x11\x75\xdf\x8c\
+\x40\xe6\x01\x20\xd7\x0a\xd3\x60\x36\x50\x4b\x2e\x40\x98\xac\x7b\
+\x8a\x3e\x83\x9b\x81\x84\x79\xf8\x1f\xbd\x87\x3f\xd3\x6d\xab\x32\
+\xbb\x03\x86\xc9\xc1\x05\xfb\x0f\xa6\xd7\x49\xf7\x20\x8b\x98\x80\
+\xe5\xde\x03\x7a\x7a\x29\xfc\x6d\x97\xc1\x88\x1c\x87\x42\xe8\x6c\
+\x18\xfa\xee\xf4\x1e\xbe\x85\x8a\xea\xcb\x51\x0c\x40\xc9\x4e\xf1\
+\xf9\x59\xf9\x1d\xcb\xbf\xa1\xb5\x4d\x94\xbf\x3a\x5a\x61\x0f\xe3\
+\xf4\xb2\x4e\xa7\x90\xc9\x8f\xc1\xa6\xd2\xff\x43\x5b\xe0\x07\x28\
+\x56\x1c\x48\xcf\x65\xa0\x67\xd6\x41\x2b\x24\x6c\x17\x80\x19\x80\
+\x45\x0c\x82\x0b\x06\xdc\x6c\xfc\x5d\xfc\x07\xe8\x6e\x70\xcb\x2d\
+\x61\x99\x9a\x6d\xdc\x75\x8f\xd0\x7d\x2d\xb3\x91\xde\xdb\x0e\x04\
+\x1a\xc8\xf0\xc7\xe0\xea\xfa\x33\xb1\x81\xcb\xe0\x2f\xfe\x92\xf6\
+\x5e\x43\x3b\x0e\x9d\x51\xe6\x4a\x14\x03\x18\x79\xca\xef\x94\xdd\
+\xc6\x79\xd4\x16\xf1\xfe\x78\x92\x2d\x7f\x1b\x6a\xaa\x2a\x64\x50\
+\x27\xfb\x02\xdc\x22\x8c\xa5\xc4\x2d\xbd\xac\xb4\x14\x02\x75\x17\
+\x0f\x46\x97\x35\x03\x21\xeb\x6f\x68\xf4\xde\x01\x4f\x61\x3d\x19\
+\xf8\x3a\xb2\xe8\x75\xd0\x8a\x49\x52\xee\x24\xbd\xd5\x47\xc4\x81\
+\x18\x80\x9b\x14\xd8\x74\xd9\xae\xbd\x65\xd8\x6c\x40\x73\x6c\x84\
+\x66\x8f\xef\x02\xbf\x87\x41\xc1\x5f\x4f\xec\xe0\x7d\x78\xcc\xdf\
+\xc1\x55\xb1\x0c\x79\xdf\xa9\x28\xe1\x60\xd8\x01\x42\x55\x21\xa8\
+\x00\x40\xc9\x27\x47\xd1\x48\xf9\x59\x27\x39\xe0\xc7\x74\xad\x9b\
+\x94\x7f\x23\xd3\xfe\xca\x18\xaa\x2b\x62\x7d\x83\x32\x99\xbd\x5b\
+\x26\x29\x78\x89\x36\x7e\x8e\xde\x65\xf5\xa0\xbb\xdb\xc2\xf2\x8e\
+\x43\xd1\x58\x35\x15\x33\x6b\xef\x44\x94\x7c\x78\x33\x67\xc1\xf4\
+\x35\x90\x3b\x10\xa7\x7d\x72\x04\x02\x1e\xb9\x15\x2c\xe1\x83\x9a\
+\xf4\x14\x80\xcb\x6d\xc7\x05\x84\x22\xe8\x70\xc6\xf6\xda\x7f\x28\
+\x10\xa0\xdd\x69\xcb\x76\x40\x4b\xbd\x05\x9f\x46\x68\xe3\x0d\x11\
+\x08\x2c\x80\x94\x21\x2a\x51\x00\xf0\x71\x44\x2d\x06\xea\x53\x7e\
+\x3b\xda\x6f\xb7\xee\xee\x4a\x24\xd1\xd4\xd6\x2e\x13\x75\xab\x2b\
+\xa2\x65\xbd\x17\x10\xe0\x11\xdc\xac\xa3\x16\x29\xbf\x44\xf4\x69\
+\xff\x54\x3a\x45\x2e\x43\x07\x02\x5e\x1d\xdd\xe9\xf1\x78\x36\xfd\
+\x05\xcc\xa8\xab\xc5\x04\xcf\xdd\xb0\x4a\x1e\x58\x9e\x2a\xa2\xf6\
+\xdd\x8e\xe5\x36\xd8\x89\xb0\x03\x7c\xbc\xc4\x57\xf3\xdb\xd6\x9e\
+\x97\x0f\x4a\x4c\xc0\x61\x02\xbc\xd1\x71\xc1\x47\x4c\xc2\x2c\x42\
+\xcb\x75\x42\xf3\x34\xc3\xab\xdf\x05\xc3\x5d\x07\xd3\x1a\xa7\x62\
+\x02\x5b\xb9\x9f\x55\x0c\x40\xc9\x87\x56\x7e\xd6\xe8\x44\x32\xe9\
+\x28\x7f\x0f\x36\x92\xf2\xd7\x55\x57\xd9\xca\x6f\x6d\x79\x63\x09\
+\x6b\xb7\x6c\x5f\x9c\xc7\x7c\x75\x76\x25\xe0\x76\x7b\x64\x96\xa0\
+\x46\x74\x3f\x9e\xc8\xe1\xc9\x65\x9f\xc6\xea\xfc\x69\x70\x15\xc9\
+\x7a\x1b\x39\x58\xae\x20\xa3\x86\xf3\x09\x25\x27\xc2\xef\x95\xca\
+\x40\xd9\x5c\x3e\xe9\x21\x08\xde\xcf\xc5\x96\x3f\x28\xc0\x61\x95\
+\xd1\x87\x0f\x33\x4f\x20\x50\x78\x1f\x5e\xf3\xaf\x84\x15\x05\xe9\
+\x52\x24\x2c\x42\xc9\xd0\xbf\xcf\xd4\x25\x18\xa2\x96\x9f\x14\x88\
+\x6b\xf1\x75\xd2\x4d\xee\xdb\xbf\xb1\xb5\x1d\x0d\xd5\xd5\xb6\xcf\
+\x6f\x6d\xdd\xd3\x66\x95\x63\xbd\x0b\xf3\x2a\x3e\x9f\x4f\xca\x78\
+\x79\xe8\x27\xaf\x17\xc8\xe4\xf2\x64\x9d\x33\xa8\x8c\x78\xd1\x66\
+\x7c\x01\xb9\xd0\x69\xd0\x73\x76\x09\x70\x2f\xb5\x97\x0f\x71\x91\
+\x2b\xe0\xe9\x03\x00\x0e\xf0\x69\x5e\x9b\x0d\xb8\xbc\xb0\x08\x00\
+\x34\x66\x0c\xdd\x2b\xc8\x7b\x48\xd3\x73\x21\x3b\x6d\x98\xeb\x82\
+\xd7\x78\x11\x5a\xfa\x56\x64\x32\x19\x3a\x07\x97\x03\x62\x4a\x14\
+\x00\x28\xf9\x90\x96\x9f\x2c\x38\xd1\x7e\x36\xc6\x5d\x49\x9b\xf6\
+\x37\xd4\x90\xf2\x57\xfc\xbb\xa1\x1d\x4e\xf7\xde\x58\x05\x26\x8e\
+\x1b\x83\x49\xe3\xc6\x12\x10\xf8\x91\xce\x64\x61\x94\x4c\xd4\x54\
+\x56\x61\xe2\x98\x3a\x4c\x18\x55\x87\x42\xe4\x4b\x30\xfc\xb3\xed\
+\x3a\x01\xb6\xec\x65\x36\x21\x8b\x85\x5c\x0e\xed\x77\xdb\x8a\xcf\
+\xd3\x7d\x34\x72\x19\x1c\xb0\xb0\x3a\xdf\x82\x11\x6f\x86\x51\x2c\
+\xd8\xe5\x40\x2e\x72\x17\x0a\x9d\xc4\x54\x3a\xf0\xaf\x87\x7e\x8c\
+\x73\xcf\x39\x17\xf7\xff\xed\x7e\xf9\x28\x9e\x0a\xac\x44\xc5\x00\
+\x94\x7c\x50\xe5\x67\xcb\xcf\xca\x4f\xbf\x77\xa7\x52\xd8\xd8\xde\
+\x26\xe3\xba\x2a\x79\x06\x9f\xb9\x7d\x5f\xd2\x23\x6d\xbc\xa2\x32\
+\x7a\x9b\xe7\x01\xd4\x54\x56\xa2\xbe\xb6\x16\xf9\x37\xde\x24\x00\
+\x28\xa1\xb1\xa1\x0e\x75\x55\x55\xf0\x79\xb8\xab\x70\x23\x72\x91\
+\xcb\x11\xe8\xfc\x0f\x29\x05\x16\x85\x67\x57\x40\x73\x7c\x7d\x38\
+\x20\xa0\x3b\x81\x40\x97\xbd\x59\x9d\xcb\x50\x6c\x59\x49\xcf\xe9\
+\xf4\x5f\x11\x7a\x21\x0f\xcb\x1f\xb6\xb3\x0a\xd9\x16\x94\xf2\x05\
+\xbc\xf4\xe2\x2b\xc4\x38\xb2\x48\x26\xe3\x38\xe9\xa4\x93\x11\x8e\
+\x44\xa4\x75\xb8\x12\xc5\x00\x94\x6c\x47\xf9\x35\x51\xfe\xa4\x58\
+\x7e\x4e\xf5\x35\x33\xed\xe7\x3c\x7f\x24\xba\xfd\x40\x92\x05\xf1\
+\xf5\x63\xb1\x98\x00\x47\x0f\xb1\x06\x1f\xf9\xfd\xdc\x2c\x24\x93\
+\x2f\xa1\xa6\xaa\x1a\x63\x46\xd5\x0b\x8b\xe0\xb1\x62\xf6\xc4\xdf\
+\x02\x4a\xae\x03\x50\x0c\x7f\x9a\x94\xb8\xc3\x0e\xf6\x95\x69\x04\
+\x2b\x3f\xff\xae\x95\x59\x80\xad\xfc\x66\xf7\xbb\x28\x6c\x7c\x1d\
+\x26\xb1\x09\x0e\x34\x5a\x66\x09\x56\x29\x6f\x03\x88\xaf\x02\x20\
+\xe5\x9f\x33\x39\x8b\x39\xd3\x3c\xd8\xd8\xdc\x85\x87\x1e\xfe\x3b\
+\x7e\xfc\x93\x1f\xe3\xbd\x77\xdf\x95\xee\xc2\x5c\xcb\xa0\x44\x01\
+\x80\x92\xcd\x44\x14\x83\xf3\xfb\x64\xf9\xd9\x0a\x77\x93\x02\x37\
+\x73\xc0\xaf\xaa\x52\x26\xf1\x98\xd8\xde\xa0\x4e\x48\x0d\x00\xd3\
+\x7e\x8d\x7e\x61\xf6\xc0\xfe\x3f\x8f\x09\x93\xd6\xdd\xf9\x1c\xb1\
+\x82\x08\x6a\x89\xfe\xbb\xf5\xfe\x83\x3a\x4d\xa9\xe2\x2b\x06\xce\
+\x80\xe9\x69\xb4\x73\xfc\xa2\xf0\x96\x7d\x57\x30\x13\x20\xfa\x6f\
+\xb9\x1d\x17\xa0\x6b\x05\x8a\xeb\x5e\x82\x25\x95\x82\x7a\xef\xdf\
+\x16\x20\x28\xd9\x19\x08\x78\x2b\x51\x53\xed\xc1\x09\xfb\x16\xd0\
+\xd3\x1d\x47\x4b\x6b\x07\x56\xad\x5e\x8d\xeb\x6f\xf8\x21\xee\xbe\
+\xeb\x4e\x64\x33\x19\xf8\xe8\xd8\x54\x80\x50\x01\x80\x92\xb2\x0f\
+\x46\xca\xc5\x0a\x9e\x48\xd8\x3e\x7f\x9c\x18\x40\x73\x7b\xbb\x50\
+\x75\xa6\xfd\xdb\x1b\xf9\x6b\x3a\xca\x5f\x55\xc9\xca\x6f\xd2\x67\
+\x24\x64\x5a\x8f\xdf\x99\xd2\xcb\x03\x40\x39\x83\x10\xf0\x7b\xe1\
+\x96\x32\xe2\xcd\x3f\xab\x08\x43\x9b\x46\x2c\xe0\x54\x80\x27\x06\
+\x99\xce\x70\x10\xa7\x0e\xc0\xe2\x5c\x3f\xd7\x07\xb5\x2f\x45\x61\
+\xc3\xab\x76\xb2\x40\x77\xf7\xc5\x0a\xb8\x24\x91\x63\x12\xc2\x04\
+\x8a\x74\x1e\x6e\xb8\x2b\xaa\xb0\x70\xdf\x2c\x0e\x99\x03\xbc\xb7\
+\xaa\x09\xf1\xce\x2e\xa9\x4f\xb8\xfb\x9e\xbb\x71\xfd\xf5\x3f\xc0\
+\x6b\xaf\xbe\x22\xc7\xa4\x62\x03\x0a\x00\x76\x79\xca\xcf\x4a\xc0\
+\xbe\x71\x9c\x95\x9f\x53\x7d\x64\xbd\x9b\x3b\x3a\xc8\xe7\xaf\x46\
+\x4c\x46\x74\x63\xbb\x01\x3f\x6e\xdd\x5d\x4d\x96\xdf\x12\xe0\xe8\
+\x11\xab\x1f\x08\x04\x91\x64\xe5\x2f\x94\x44\x3f\x7d\xb4\x8f\x4b\
+\xdb\x56\xf7\x5e\x7b\x39\x70\xd1\xfb\x69\x58\x81\x3d\x88\xc2\x93\
+\x2b\x60\x95\x9c\x03\xf4\x0b\x28\x58\x2d\x2f\xc2\x68\x5d\x66\x57\
+\x08\x96\xd7\x08\x70\xb1\x90\xe3\xb2\xf4\xa3\x02\xc2\x0e\x74\x5f\
+\x18\xe3\xa6\x84\x70\xde\xc2\x2e\x4c\x6d\xd4\xb0\x7c\xe5\x5a\x74\
+\x10\x9b\x09\x84\xc3\x58\xb3\x7e\x3d\x7e\xf4\xe3\x1f\xe3\x77\xbf\
+\xbb\x09\x2d\x2d\x9b\xc4\x2d\xd0\x75\xe5\x16\x28\x00\xd8\xc5\x14\
+\x9f\x87\x7a\x72\xb5\x1e\xe7\xea\x79\xbc\xb6\x5b\x94\x3f\x81\x16\
+\xb2\xfc\x8d\x35\x35\x42\xfb\xb7\xe7\xf3\xcb\xac\x3e\xfa\x8c\x2a\
+\x56\x7e\xd8\xeb\x03\x58\xf9\xed\x89\x3d\x59\x49\xfb\xb1\xf2\xfb\
+\x49\xf9\xdd\xff\xb6\x93\x0f\x5b\xee\x46\x62\x01\xa7\x90\x07\xe0\
+\x25\x10\xe8\x06\x4a\xc4\x06\x32\xeb\xa1\xb5\xbd\x02\x2b\xd9\xea\
+\xd4\x03\xb8\x1d\x66\xc0\xab\x08\xdd\xb4\x2f\x3d\xc2\xd5\x0b\x02\
+\x72\xbc\xb4\xf1\x7c\xd1\x70\x4d\x1d\xf6\x99\xa7\xe1\xa2\x63\x93\
+\x88\xfa\x35\xac\x59\xbd\x01\xad\x9b\x5a\xe5\xbc\x83\x91\x30\x1e\
+\x7f\xea\x49\x7c\xf7\xba\xeb\xf0\xe0\xfd\x7f\x45\x2e\x97\x11\x20\
+\x50\x6e\xc1\x20\x31\x50\x75\x09\x76\xbc\xf0\xcd\x5d\x4e\xef\xf1\
+\x38\xef\x6c\x26\x2d\x33\xf5\xd8\x00\xb3\x15\x6f\xed\xec\x46\x5b\
+\x77\x37\x46\xf1\x88\xee\x48\xc4\x09\xd2\x6d\x47\xf9\x1d\x9f\xdf\
+\x72\x7c\xfe\xf2\xac\x3e\x6e\xe0\x59\x28\x16\x65\xbd\x80\xef\x03\
+\x29\x7f\xf9\x43\x4d\x14\x5c\x47\xc0\x1d\x7e\x02\x7a\xf7\x13\xd0\
+\x72\x9b\x48\xe9\x03\xe2\xfb\xeb\xa1\x18\x50\xc8\x42\x33\x4a\x03\
+\xa7\x07\x5b\xc4\x02\x5c\x9a\x64\x03\xca\xba\xeb\x92\xb5\x44\xb4\
+\x8f\xdb\x8b\xea\xf1\x8d\x38\xea\xc0\x26\xb4\xc5\x73\xf8\xe5\xfd\
+\x3e\x34\x6d\x6c\x92\x89\xc5\x91\x8a\x28\x62\x15\x31\xa4\xf3\x39\
+\xfc\xe1\xe6\x5b\xf0\xf2\xcb\xaf\x60\xd1\xe2\xc5\x98\x31\x63\xa6\
+\xb0\x14\x1e\x3e\xaa\x44\x01\xc0\xf0\x56\x78\x68\x7d\x55\x39\xb0\
+\x07\x79\xe6\x72\x39\x14\xc9\x32\x17\x4b\x76\xb5\x9d\x4e\xb4\x9c\
+\x3b\xed\xb6\x77\x27\xb0\xa9\xa3\x13\xa3\xea\x6a\x11\x65\xe5\x37\
+\xb7\x1d\xf2\xb3\x27\xf6\x78\x50\x59\x19\x13\x65\xe2\x8c\x41\xc0\
+\xef\x93\xf6\xdd\x89\xa4\xad\xfc\xba\xfe\x21\x95\xdf\x86\x25\xfa\
+\xbc\x5a\x14\x82\xe7\xc2\x6f\xac\x03\x52\xef\xdb\x77\x86\x27\x42\
+\xac\x9f\xac\x33\x29\xb4\xcb\x28\xc0\x34\x8a\x36\x08\xf4\x1e\x23\
+\xbb\x02\x9a\x64\x0c\xdc\x6e\x0d\xe9\xee\x34\x3a\x7b\x48\xf9\x6b\
+\x5c\x08\x55\x05\x51\x37\xa1\x0e\x9f\x3d\xa2\x05\xad\xf1\x4a\xdc\
+\xf6\x94\x45\xfb\xb4\x49\x2a\xb3\x44\xee\x49\x94\x5c\x9c\x6a\x02\
+\xbc\x15\xab\x56\xe1\x86\x1f\xdd\x80\xe3\x8f\x3d\x0e\xc7\xd0\x16\
+\xe1\xf1\xe3\xa5\x92\x6a\x3b\xbe\xab\x03\xc0\x70\x5a\x0b\x20\xf4\
+\x55\x13\xb5\x67\xc3\x28\xc1\x39\x1e\xa6\xc9\x93\x75\x78\xa2\x6e\
+\xc9\x30\x85\xaa\xf3\xf9\xd8\xbb\xea\x12\x08\xe3\x20\x5d\x73\x7b\
+\x07\x2a\xc2\x21\x84\x83\xc1\x5e\xdf\x7e\xdb\x96\x9f\x95\xbf\x42\
+\xfe\x80\x28\x7f\xc0\x4b\xd4\x3f\x88\x78\x2a\x83\x02\x29\x0d\x2b\
+\xbf\xdf\xe3\x22\xe5\xff\x28\xf5\xe7\x45\x14\xb4\xfd\x80\xc8\x97\
+\xe1\xd3\x6e\x82\x96\x5c\x6e\xc7\x03\x3c\x15\xa4\xdf\x3e\x9b\x0d\
+\xd0\xb9\x80\x03\x7e\x62\xa5\x4d\xbb\xf4\x98\xce\xc5\xe3\x75\x61\
+\xcd\xaa\x34\xfe\xe7\xce\x18\x56\x6e\xaa\x25\x30\xf3\xe1\xe2\x53\
+\x53\x58\x30\xdf\xc2\x98\x62\x01\x17\x1c\xdb\x85\xd6\xee\x1a\x3c\
+\xf6\x5a\x86\x5c\x97\x2e\x39\x49\xbe\x3e\xc1\x50\x48\x80\x80\xad\
+\xfe\x5d\xf7\xde\x8b\xe5\x2b\x96\xe3\xcc\x33\xce\xc2\x94\x69\xd3\
+\x04\x28\x07\x30\x8e\x61\x2c\x43\xf9\x3e\x56\x0c\xe0\x23\x5b\x79\
+\xbb\x29\x67\x59\xf9\xcb\x4a\x2a\x8a\x4f\x8a\x62\xca\xcd\xab\x49\
+\x90\xcb\x43\x8f\x0c\x0c\x7c\x53\xf3\xd3\x9a\x13\x0b\x48\x67\x53\
+\xf2\xc8\x43\x3d\xf5\xed\x68\x7f\x99\xf6\x57\x89\xe5\xb7\xa4\x4a\
+\xd0\x2f\x96\x3f\x88\x1e\xa1\xfd\x76\xb4\x9f\x95\x9f\xe9\xff\x47\
+\xbb\xdf\x38\x2d\xa8\x13\x08\x1c\x03\x33\x5c\x07\xbf\xfb\x0f\xd0\
+\x7b\x9e\x85\x5e\x8a\x43\xf3\xc7\xec\x62\x20\xcb\x4d\x4a\xc9\xa9\
+\x41\xd3\xa9\x48\xb4\xe8\xef\x59\x78\xff\xdd\x1e\x5c\xfd\x4b\x1f\
+\x5e\x5f\x13\x41\xc8\x9b\xc1\xfa\x8d\x39\xac\x6d\x8a\xe1\x3f\xbf\
+\xec\xc6\x81\xfb\x5a\x98\x4a\xee\xc3\x97\x4e\xee\x44\x47\x4f\x2d\
+\xde\x58\x6d\x9f\xb3\x69\x46\x6c\x70\x2c\x15\x11\x0a\x05\x51\x53\
+\x5b\x2b\x6c\xe0\xc6\x1b\x6f\xc4\xe9\xa7\x9f\x86\x03\x3f\x75\x90\
+\xc4\x1b\x94\x4b\xa0\x82\x80\x43\x0f\x35\xc9\x1a\x7a\x7d\x7e\x09\
+\x6a\x71\x4e\x9c\xd7\xe0\xb3\x05\x66\x0a\x5e\x2a\x99\x52\x89\xc7\
+\xd6\xbf\x48\x37\x77\x2e\x57\x40\x36\x9f\x17\x17\x80\xab\xf1\x34\
+\xe7\x3f\x59\x47\x43\x2e\x00\x2b\x36\x2b\x2f\x8f\xfa\xde\x5a\x91\
+\x2f\x03\x06\x07\xfc\x84\xf6\xc3\xee\x06\xc4\x80\x11\x24\xa5\xb1\
+\x47\x74\x17\x6d\xe5\x77\xdb\xca\xff\xf1\xc4\x94\x5e\x00\x25\x6b\
+\x6f\x64\x02\xdf\x46\xb1\xfa\x02\x3a\x37\x37\xf2\xf1\x4d\xd0\x72\
+\xdd\xd0\x4a\x69\xfa\x5b\x26\x74\x3a\x1e\xee\x2b\xe8\x62\xa7\x9f\
+\xce\xf3\xb6\x87\xf2\xf8\xc7\x9b\x41\x54\x04\x8a\xc4\x3e\x48\xa1\
+\x7d\x39\x74\xb4\xb5\xe1\xdb\xff\xed\xc3\x3f\x5f\xa8\x45\x60\x42\
+\x2d\xe6\xcc\xf5\xe3\xab\x9f\xed\xc2\xc4\x7a\x1d\x9b\xda\x7a\x90\
+\xcd\xa6\x91\x49\xa7\xa5\x68\x29\x91\x48\x22\x95\x4c\xc9\x3c\xc2\
+\x1c\xb9\x19\x37\xfd\xf6\xb7\xb8\xf7\x9e\xbb\x50\xa2\xeb\xa9\xd2\
+\x85\x0a\x00\x86\x94\x78\xb8\xa2\x4d\x72\xf7\xa6\x58\x74\x4e\xe3\
+\x95\x78\x23\x2b\xcc\x8f\xf9\x52\x41\x80\xa0\x58\xe4\xc7\x82\xac\
+\xcf\x67\x75\x97\x40\xa0\xe3\x26\xc8\xb2\x5d\xb2\xa0\x21\x7f\x00\
+\x41\x02\x92\x32\x60\x98\x5b\x59\xdd\xe7\xf5\x92\xe5\xaf\x8a\x49\
+\x17\xdf\x44\x3c\x8e\x10\x47\xfb\x43\x01\x19\xd4\xc9\x81\xc4\x32\
+\xed\xff\xe4\xfa\xf6\xdb\x55\x82\xa6\x59\x0f\x33\x70\x29\xee\x78\
+\xfa\x30\x1c\x7d\x5e\x0a\xdf\xfa\x51\x12\x4f\x3d\xbc\x09\x9b\x56\
+\xae\x23\x40\x68\xa7\x53\x28\x49\x2a\xd0\xa2\xe3\xea\xe8\x26\x06\
+\x82\xf2\xec\x00\xe1\x37\xf0\x79\x2d\x74\x92\x7b\x73\xcd\x4f\xbd\
+\x78\xfa\xf9\x06\x84\x26\x36\xe2\x80\x7d\x74\x5c\xb9\xb8\x1b\x95\
+\x21\x0d\x2d\x6d\x71\xb9\x76\xf9\x6c\x4e\x66\x10\x26\x89\xd5\x24\
+\xe2\x09\x78\x5c\x9c\x29\x88\xe0\xae\x25\x4b\x70\xf3\x9f\xfe\x48\
+\x40\x91\x55\x20\xa0\x00\x60\xf0\x7d\x38\xa6\xad\x3e\xbf\x5f\x2e\
+\x19\x2b\x38\xa7\xda\x8a\x85\x92\x58\xf9\xa2\x61\x48\xe0\x8a\x1f\
+\xb9\x20\xc6\x28\xf2\xf3\x36\x75\x65\xa5\xd0\x1c\xc5\xe7\x8d\x57\
+\xe7\x95\x3f\x93\x2b\x00\xb9\xce\xdf\x23\x8b\x6d\x9c\x75\x38\x9a\
+\xb3\x3f\xa7\xf1\xfc\xde\x5e\xda\x1f\xef\xe6\x54\x9f\x53\xe4\xc3\
+\xca\x5f\x2c\x0e\xa0\xfd\x9f\xbc\x14\x09\x90\x7c\xf0\x54\x1e\x8b\
+\xb5\xf1\xc9\xb8\xe9\x81\x0a\x5c\x72\x63\x14\x37\xfe\x31\x88\x57\
+\xfe\xd9\x81\x8e\x35\xeb\x61\xe4\xb3\xd0\xc3\x41\x1c\xb9\x9f\x07\
+\x85\x6c\x37\xb1\x11\xbb\x36\xa8\xec\x24\xf9\x09\x04\xe2\x9d\xed\
+\xb8\xe6\xbf\xbd\x78\xea\xf9\x51\x88\x4e\x6e\xc0\xe1\x07\x18\xb8\
+\x72\x11\x9d\x8b\x47\x43\x5b\x7b\x5c\x5c\x80\x62\xbe\x80\x14\xb1\
+\x81\x24\xb1\x01\x76\x6f\xf8\x5a\x56\x56\x56\xe2\xb1\x27\x9f\xc4\
+\x1f\x7e\xff\x3b\x64\x32\x69\x05\x02\x3b\x48\x5c\xd7\x5e\x7b\xed\
+\x90\x3a\xa0\xf7\xde\x7b\x8f\x2d\xc3\xe7\x76\xdf\x7d\xf7\xf1\xd2\
+\xe4\x62\x90\x85\xad\x32\x5b\x7c\x2e\x61\x65\x4b\x5d\x22\xab\xce\
+\x37\x28\x53\x7d\xd3\xb0\xe9\xbe\x3c\x96\x6c\x0b\x2e\x3f\xf3\x7b\
+\x74\xdb\xe2\x97\xe3\x05\x76\xe1\x8e\x87\x14\x38\x28\xcc\xc0\x76\
+\x04\x20\xa3\xb6\xbd\xd2\x95\xd7\xae\xe7\xe7\xcc\x00\x57\xed\x31\
+\xd8\x70\x56\x40\x52\x7d\x4e\x9e\x9f\x47\x74\x97\x53\x7d\x3b\x56\
+\xf9\x1d\xe0\xa3\x73\x9b\x34\x61\x2c\xc2\x21\x1d\x71\xb2\xfa\xe9\
+\xbc\x86\x67\xdf\x2c\x60\x6d\x4b\x18\x95\xee\x0c\x62\xbe\x6e\x3a\
+\x26\x2f\xa6\xef\x51\x89\x42\x22\x8e\x47\x9f\x2f\x20\x12\x0d\xdb\
+\x41\x48\xe7\xcc\xe9\x10\x91\x49\xa5\xf1\xdc\x1b\x11\x4c\x18\x1b\
+\xc1\x8c\x39\x06\x46\x07\xbb\xe1\x25\xe6\xf0\xd2\x0a\x2f\x32\xd9\
+\x02\x9d\x9b\x4f\xae\x50\xd1\x89\xfe\xcb\x35\x27\xc0\x0d\x47\x23\
+\x78\x97\xee\x87\xce\xb6\x56\xcc\x9a\x35\x5b\xbe\x03\x73\x18\x06\
+\x06\xb9\xce\xe1\xe6\x9b\x6f\x5e\xbd\x7e\xfd\xfa\x77\xe9\xd7\xb5\
+\xb4\x75\x9e\x77\xde\x79\xc6\x84\x09\x13\x54\x10\x70\xa8\x5b\x7e\
+\xb6\x3c\x1c\x7d\x67\x8a\x5f\x60\xeb\x4e\x56\xde\x34\x2c\x19\x9f\
+\xcd\x29\x2d\x01\x00\xde\x2c\x3b\x35\xc6\xd6\xdb\xad\xf5\xe5\xc6\
+\xcb\xea\xc9\x37\x34\xef\xc3\x3e\xaf\xf4\xf0\x13\x73\xef\xdc\xec\
+\xa4\xf4\x2e\xdd\x5e\x2c\xc3\x75\x01\x6e\xba\x61\xbc\x6e\x2f\x4a\
+\x46\x49\xa6\xf4\x72\x9e\xdf\x47\xee\x42\x77\x22\x25\x2c\x83\xd9\
+\xc8\x27\xe3\xf3\x7f\x80\xf3\xa7\x73\x3f\xfd\xb4\x33\xd1\xd2\xd2\
+\x86\xb7\x96\xbd\x4d\xa0\xe4\xc7\x8b\x2b\x9b\xd0\x1a\xaf\xc6\x85\
+\x64\xad\x8f\xca\x6f\xc4\xe8\xdd\xea\x71\xcd\x55\xa3\x51\x32\x9b\
+\xf0\xbf\x77\xb4\x61\xc2\xf8\x06\x3a\x1f\xc3\xa9\x64\xd4\xc4\x1d\
+\x48\x11\x80\x5c\xfb\x3f\xb5\xc8\x5e\x32\x09\xc7\x2d\x28\x62\x71\
+\xbe\x85\x5c\x18\x1d\xbf\x7c\x20\x80\xae\xae\x38\xaa\x6b\x2a\x25\
+\xbb\x91\xc9\x64\xe5\xba\x32\xf8\x70\x90\x90\x17\x38\x3d\xff\xd2\
+\x4b\x02\xc2\xe7\x9f\x7f\x21\xbc\x0c\xc4\x2a\x30\xa8\x00\x60\x67\
+\x59\x7e\x56\x7e\xf6\xb5\xb9\x80\xc7\xe0\xcd\xb2\x2d\x3e\x2b\xb3\
+\x28\xbe\x69\x47\xc3\x59\xa9\xb9\x55\xb7\xab\x9f\x53\x25\xc1\x3e\
+\xfa\x1c\x9f\xcf\x2b\x16\x9d\xa3\xf5\x9a\x56\xea\x05\x16\x8e\x13\
+\x58\xce\x12\x5f\x4d\xe2\x02\xa6\x54\x08\x6a\x64\x09\xd3\xec\x1f\
+\xe7\xf3\x62\xf9\x7d\x3e\x3f\x9a\x5b\x3b\xe4\xc6\xe7\xba\x7e\x9f\
+\x7b\xe7\x0d\xea\x94\xbf\x49\xcc\xe3\xcc\x33\xce\xc4\xf5\x37\xfc\
+\x80\xac\x99\x4f\x5c\x96\x55\xef\xaf\xc7\x7f\x2d\x09\xa3\xbd\xc7\
+\x23\xca\x3c\x61\x46\x11\xd7\x7e\xb3\x91\xae\xd3\x26\xfc\xe2\xae\
+\x16\x4c\x99\x54\x4f\x00\x65\xc2\x3e\x3d\x8d\xde\x67\xa1\xa7\xab\
+\x1d\x3f\xfc\x75\x3d\xf9\x09\xd3\x71\xdc\x9e\x45\x9c\x56\xe8\x44\
+\x22\xa3\xe3\x4f\x8f\xf9\xa0\x75\xc6\x51\x55\x5d\x29\xcc\x26\x97\
+\xcd\x3a\xa0\x6a\x09\x28\x44\xa3\x51\x3c\xff\xaf\x7f\x49\xbd\xc3\
+\x59\x67\x9f\x23\xc1\x47\x05\x02\x0a\x00\x76\xb8\xcf\x2f\xca\x4f\
+\xfe\x29\x53\x53\x43\xf2\xfa\x46\xaf\xd2\x1b\x96\xe1\xe4\xc1\x6d\
+\xaa\xaf\x43\x43\x7f\x83\x5c\x2e\x06\xe2\xd7\x58\xf9\x93\x99\x0c\
+\x36\x36\x35\x93\x35\xf4\x0a\xd5\x67\xe5\xe6\x00\xde\xa8\xfa\x7a\
+\x09\x12\x4a\x01\x10\x37\xde\x75\x02\x88\x70\x5c\x86\x00\x59\xff\
+\x75\x4d\x2d\xe4\xf7\xa7\x89\x8a\x07\xa5\xc8\xc7\xe3\xd2\x77\x6a\
+\x6e\x99\x83\x75\x13\x26\x4e\xc4\xe2\x93\x17\xe1\xb7\xff\xf7\x7b\
+\x34\x8e\x19\x25\xec\x65\xdd\xfa\x66\xfc\xe6\x01\x9f\xb0\x81\xcf\
+\x65\xba\x88\xde\x97\xf0\xbd\xab\x6b\x09\xc4\x3a\xf0\xeb\xbb\x5b\
+\x31\x65\x72\xbd\xa4\x09\xed\xe0\xa6\x0d\x02\xa9\x78\x1b\x7e\x7c\
+\x53\x3d\xf4\x4b\x66\xe2\x98\xd9\x6f\xe2\xfc\x52\x37\x31\xab\x4a\
+\xdc\xfa\x04\x5f\xcb\x38\x2a\xab\x79\x49\xb3\x2e\xd7\x87\x01\xb1\
+\xdc\x00\x25\x10\x0e\xe1\xa9\x67\x9e\xa1\x6b\x10\xc6\x29\x8b\x16\
+\x3b\xa9\x44\x35\x9d\x48\x01\xc0\x0e\x01\x00\x5e\x44\xe3\x95\xe0\
+\x94\x44\xf4\x85\xf6\x1b\xbd\xfe\x3e\xca\x51\x7d\xbd\x4c\xf1\xb7\
+\xae\xfc\x70\x7c\x7f\x56\x68\x0e\x0c\x56\xc7\x2a\xe1\x27\x36\x90\
+\x4e\xa7\xe1\x8f\x84\x51\x53\x55\x25\x37\x39\xbb\x05\xdc\xb9\xc7\
+\x74\x0a\x85\xca\x9d\x79\x43\xa1\x10\x36\xb5\x75\x60\x53\x6b\x2b\
+\x42\xc1\x10\xa2\xec\x0a\x10\x28\x0c\x46\x61\x09\x03\xd3\x81\x07\
+\x1d\x8c\x57\x5e\x7d\x0d\x6f\xbc\xb5\x14\xf5\xf5\x0d\x42\xef\x5b\
+\x36\xb5\xe0\xce\xa7\x81\x4d\x5d\xb5\xf8\x52\xb6\x03\xf3\xe7\x97\
+\x70\x3d\x81\x80\x85\x4e\xfc\xfa\x1e\x02\x81\x89\x75\x70\xbb\x34\
+\xa7\x99\x89\x46\xec\xc1\x42\xa2\xbd\x15\x3f\xfa\x0d\x01\xdf\xc5\
+\xb3\x70\xf4\xbc\x37\x71\x91\xd9\x45\x2e\x56\x25\x6e\x7b\x86\x63\
+\xac\x71\x49\x79\x72\xbb\x91\x42\xbe\x60\x57\x56\x95\x83\x8a\x01\
+\x3f\x1e\x7a\xe4\x61\x44\xe8\xda\x1d\x7d\xec\xf1\xaa\x71\xac\x02\
+\x80\x1d\x40\xfd\x49\xc9\x3d\x44\x73\xd9\xff\xce\x39\xd4\x9f\x23\
+\xd5\x86\x73\xb3\x49\x60\xcf\xb2\x33\xf9\xe5\xd8\xfe\x40\xe5\xc7\
+\x16\x33\x32\x0a\xf9\xa2\x7c\xae\xcf\x6b\xbb\x13\x41\xa2\xd4\xa3\
+\x1a\xea\x91\x24\xab\xbe\x6a\xdd\x7a\x44\xc9\xaa\x85\x02\x41\x72\
+\x07\xfa\x68\x2d\xd7\xf6\xf3\xdf\x5f\xb5\x76\x83\x44\xd6\x63\x91\
+\x10\xed\xe3\x1f\xb4\x1b\x9e\x8f\x9f\xfd\xef\xc5\x8b\x17\x61\xc5\
+\x8a\xe5\xd0\xdd\x2e\x54\xc6\xec\xb6\xe4\x1e\x57\x07\xfe\xf1\x66\
+\x06\x9d\xc9\x5a\x7c\x35\xdd\x85\x83\xf7\x6f\xc1\xf5\xdf\xa8\x25\
+\xc5\x8f\xe3\x97\x77\xb5\x61\xe2\xf8\x06\x52\x7c\x73\x00\x08\xc4\
+\x09\x04\x6e\xf8\x4d\x1d\xac\x8b\x66\xe3\x98\x3d\x97\xe2\x62\x83\
+\x40\xc0\xac\xc2\xdd\xff\x84\x0c\x2d\x89\x55\x56\x48\x11\x55\xb1\
+\x98\x47\x3a\x65\x39\x60\x6a\x49\xa3\x93\x7b\xef\xbb\x0f\xd1\x8a\
+\x18\x0e\x38\xf0\x20\x09\xa8\x42\x81\xc0\x47\x16\x95\x06\xdc\x8c\
+\xfa\xb3\x7f\xc9\x0a\x9d\x2b\xe4\xed\x74\x9f\xe4\xf2\x6d\xab\xef\
+\xb2\x1d\x75\x09\xda\x79\x39\x2d\x65\x61\x2b\xca\xbf\xb9\x6f\x6e\
+\x17\xfd\xf0\x7b\xba\x13\x09\xa4\x32\x69\xb2\xe6\x41\x24\x93\x29\
+\xac\x5a\xb3\x56\x9a\x77\xf0\x92\x5e\xa3\x9f\xf2\xf3\x3a\x01\x3f\
+\x3d\xbf\x66\x7d\x13\x12\xc9\x1e\xa2\xbd\x01\xd9\x67\xb0\x6f\xf4\
+\x22\x01\xd2\xa4\xc9\x53\x70\xcc\xd1\x47\x23\x1e\x8f\x23\x52\x11\
+\x41\x38\x1c\x46\x75\x4d\x2d\x1a\xea\xa2\x78\x67\x8d\x85\x6b\x6f\
+\xae\xc6\x83\x8f\x5b\xe8\x5a\xdb\x82\xef\x7e\x25\x84\x2f\x9d\x9a\
+\xc6\xba\x0d\xad\xe4\x46\xa1\x37\x2b\xc2\xd7\x48\x40\xa0\xa3\x15\
+\xd7\xff\xc6\x8b\x07\x97\xce\xc5\xac\x79\x31\x5c\xf2\xe9\x6e\x9c\
+\x72\x60\x91\xce\xb9\x80\xee\xee\x04\x01\x46\x49\x98\x10\xb3\x0f\
+\x66\x4d\xe9\x54\x8a\xc0\x34\x4f\xe0\x6c\xe0\x2f\xb7\xfd\x05\xef\
+\xbc\xfd\xa6\x44\xd8\x95\x8c\x30\x06\x30\x58\x73\x01\xc4\xc2\x93\
+\xa2\xe6\x88\x7a\xe6\x73\xce\xc2\x1d\x58\x7d\x4d\x73\xb9\xe3\x2e\
+\xf9\xe1\x9c\xe3\xe7\xd7\xb8\x28\x27\xcf\x69\x41\x8e\xcc\x6b\xfa\
+\x56\xa6\x63\x39\x85\x3f\x5c\x48\xeb\x72\xa1\xae\xba\x5a\x02\x82\
+\x5c\x1d\x98\x27\x80\xa9\x20\x2a\x1b\xe3\x8e\x3f\x8e\x2f\x6b\xf3\
+\x0a\xb2\xa8\xc4\x14\xd2\x99\x1c\x56\x6f\xd8\x00\x0f\x59\xc1\x7a\
+\x72\x15\x18\x40\x0c\x63\xf0\x7d\x5e\x66\x44\x47\x1e\x75\x14\x5e\
+\x7e\xf5\x15\xb4\xb4\xb5\x91\x25\xae\x70\xe2\x22\x06\x5c\x6e\x1d\
+\x1b\x5b\x7a\xf0\xbd\xbf\x54\x22\x91\xe9\xc1\x89\x87\x75\xe0\xda\
+\x2f\x55\xc1\xe3\x49\xe1\xbf\x6f\xb7\x30\x76\x54\x1d\x01\xa7\xd6\
+\x1b\x13\x60\x10\x48\x76\x11\x08\xfc\x96\xdd\x81\xb9\x38\x7a\xfe\
+\xeb\xb8\xd8\xe4\xb5\x02\x55\xb8\xef\x79\x66\x1d\x71\x54\x55\x55\
+\x08\x28\xf3\x42\xaa\xb4\x95\x16\xf0\x88\x10\xe8\x70\x4c\xe5\x0f\
+\x7f\xfa\x03\xbe\xf4\xc5\x2f\x61\xec\xb8\xf1\x43\xba\xe7\xe0\x50\
+\x76\x55\x14\x03\xd8\x2c\xf0\xc7\xca\x9c\xcb\xe5\x85\xf6\xdb\xb6\
+\xdb\x12\xc3\xcb\x16\x39\x1c\x0e\xc9\xca\xbd\x27\xff\xf5\x12\x1e\
+\x7c\xea\x1f\x78\x7d\xc5\x4a\x59\xd0\xc3\x5d\x78\x78\x55\xdc\xc0\
+\x52\x5e\x6d\xa0\x3b\xe0\xf4\xf1\xe7\xcf\xe6\xc9\xbe\x49\xb2\x68\
+\x59\x02\x01\xb1\xfc\x9a\xbd\x77\xb9\x8e\xc0\x4f\x56\x6d\x43\x73\
+\xb3\x34\x0a\x89\x91\x95\x65\xa0\x18\x2a\x01\x2f\x83\x5c\xa3\x58\
+\xac\x12\x27\x9f\x78\xb2\xc4\x48\xb8\x23\x11\xc7\x2a\xfc\xbc\x24\
+\x99\x5d\x9b\xc6\x0a\x74\x93\x9e\xfe\xd7\x3d\x51\xdc\xf6\x90\x1f\
+\xeb\x57\x74\xe0\xea\x8b\x7d\xb8\xe2\xd4\x14\xd6\x37\xb5\xa3\x68\
+\x0c\x2c\x16\x62\x10\x48\x77\x93\x3b\xf0\x5b\x2f\x1e\x5b\x31\x17\
+\xb3\xf7\x0e\xe3\x82\xe3\x3a\x71\xea\xc1\x36\x08\xf3\x6c\x03\xfe\
+\x9b\x7c\x7d\x38\x15\x9b\x65\x26\x40\x1b\x67\x4f\x5a\x5a\xdb\x71\
+\xfb\xed\xb7\x23\x45\xcc\x40\xf5\x1b\x54\x00\xf0\xf1\x00\xc0\xe6\
+\xde\xc8\x92\x82\x72\x75\x9f\x3d\x5c\xcb\x92\x4c\x40\x94\x14\x30\
+\x4b\x16\xe6\xd5\xb7\x97\xe1\xc5\x37\xde\x42\x36\x9b\xc3\xf8\xd1\
+\x8d\xa8\x21\x1f\xb8\xa9\xb5\x15\xef\xae\x5d\x2f\x2b\xfe\x58\x71\
+\xed\xd4\xdf\x40\xe5\xd7\xfa\x51\x03\x66\x13\xd5\x95\x31\x7b\xa8\
+\x27\x59\x4f\x59\x4c\x64\xd9\x69\x47\x8e\x0f\x30\x88\xf0\xcf\xeb\
+\x9a\x5b\x24\x17\xce\x83\x40\x74\x19\x0c\x3a\x74\xae\x15\x53\xf2\
+\x79\x7b\xcd\xc7\x82\xbd\xf7\x46\xbc\x3b\x8e\x10\x01\x63\x40\xd2\
+\x95\x3e\x89\x13\x34\xd4\xc7\x90\xcd\x03\x3f\xff\x5b\x18\xb7\x3c\
+\x14\xc2\xea\xe5\x5d\xb8\xfa\x42\x0f\xbe\xb4\x38\x89\xb5\xeb\xdb\
+\xc9\xd7\xd7\xb7\x00\x81\x04\x31\x81\x1b\x7e\xe7\xc7\x53\xab\xe6\
+\x62\xee\x3e\x61\x9c\xb5\xb0\x0b\xa7\x1d\x5c\x14\xcb\xdf\xd1\x19\
+\x97\x98\x0c\x5f\x3c\xae\xc0\x4c\xa7\x33\x52\x5c\xc4\x80\xfd\xe6\
+\xb2\x65\xb8\xe7\xee\xbb\x04\x20\xd5\x1c\x02\x15\x04\xfc\xc8\xca\
+\x2f\xd6\x9f\xe8\x2d\x53\x49\x4e\xf7\x71\x85\x1e\x2f\xb5\xe5\x45\
+\x3e\x1c\x88\x5b\xdf\xb2\x49\x5e\x1b\x5d\x57\x8b\x89\x63\x47\xa3\
+\xa1\xb6\x9a\x68\xbd\x5b\xaa\x02\x9b\x5a\x5a\xb1\x72\xf5\x1a\xa1\
+\xa6\xe3\x1a\xeb\x09\x08\x3c\x52\x34\xa4\x59\x03\x15\xbf\x7c\xc3\
+\x73\x30\x8c\x7f\x67\x70\x29\xbb\x16\x92\xfe\xa3\xcf\xe2\x85\x3e\
+\x9c\xf2\x5b\xdf\xbc\x89\x00\xa6\xc2\xee\xfa\x33\xc4\xd2\x5d\x52\
+\xc7\x40\xe7\x78\xe2\x49\x27\x61\xd9\x8a\xe5\x02\x5c\x11\x02\x01\
+\x5e\xec\xc4\xd9\x0e\x3e\xa7\xba\xba\x4a\xb4\x93\xe2\xde\xf4\x50\
+\x00\xdc\x33\xf4\x4c\xa2\xf3\xd7\x5c\x1c\x23\xe5\x4f\xe1\x7f\xef\
+\xd2\x30\x69\x7c\x2d\xdc\x7a\xbf\x3a\x01\x02\x81\xee\xb6\x56\xfc\
+\xe8\xb7\x75\xd0\x3f\x3f\x07\x9f\xda\x7b\x29\x81\x44\x37\x82\x9e\
+\x0a\xdc\xf2\x94\x86\xf6\x8e\x04\x6a\x6a\x22\xf4\xbd\x78\x25\x0e\
+\x90\x76\x00\x9b\xd9\xc7\x33\xcf\xfe\x03\xe3\xc6\x8d\xc3\xa1\x87\
+\x1f\x21\xaf\x29\x51\x0c\xe0\xc3\xdd\xd0\xdc\xa4\x82\x2c\x38\x5b\
+\x7f\x4e\xf7\xb1\x9f\xcf\xed\xb5\x5a\xba\xe2\x78\xfb\xdd\xf7\xd1\
+\x44\x37\x66\x8c\x94\x7b\xee\xee\xd3\xb1\xcf\x9c\x3d\x30\xba\xbe\
+\xd6\xa6\xa4\xb2\xba\x0f\xc4\x06\x46\x61\xde\x8c\xdd\x25\xc8\xf5\
+\xfa\xb2\x95\xd8\xb0\xa9\xd5\xa9\x0b\xd0\xfa\x29\xbf\xd6\x4b\x09\
+\xb4\x7e\x6e\x81\x28\xbf\xc4\x14\x0c\x29\x02\x62\x20\xea\x88\xc7\
+\xe9\x58\x72\xc2\x14\x38\x6d\x68\x0e\x41\xff\x91\x57\xea\x4d\x98\
+\x38\x09\x9f\x39\xe1\x04\xa4\xc8\x55\x71\x4b\xcd\x42\x50\xea\x16\
+\x5c\x4e\xdb\xb3\xda\x9a\x18\x34\x97\x07\xbf\x7d\x38\x88\x5f\x2f\
+\xa9\xc0\x7b\xcb\x12\xf8\xce\xc5\x2e\x7c\x71\x51\x12\xab\xd7\x6d\
+\x85\x09\xb8\x4c\x74\xb4\xb4\xe1\x87\xbf\x0e\xe1\x1f\x6b\xe6\x62\
+\xce\x82\x30\x16\x1d\x16\xc7\xc5\x47\x67\x10\x70\x15\xd1\xd2\x92\
+\x20\x10\xce\xcb\x75\x65\x45\x67\x16\xc0\x9d\x86\x39\x36\x72\xdf\
+\x5f\xff\x8a\x15\xcb\x97\xa9\xa0\xa0\x02\x80\x0f\x2f\xac\x90\xe5\
+\x95\x75\x42\xf7\xc9\xb7\x7d\x6f\xc3\x46\x34\x13\xbd\xe7\xe5\xba\
+\xd3\x27\x8c\x17\xe5\x1f\xd7\xd0\x20\xc1\x3c\xbe\xe1\xca\x41\x1d\
+\xfe\x97\xe3\x06\xbc\xdf\xcc\x29\x93\x08\x08\x76\x43\x82\x7c\x52\
+\x0e\xe2\xb9\x74\xbd\x9f\xf2\x0f\x64\x02\xe5\x5a\x01\xa6\xae\x45\
+\xa2\xb7\xbc\xa4\xb8\x64\xda\x0b\x84\xda\x3b\xbb\xa5\x24\xb8\x8e\
+\x2b\xe3\x86\x30\xad\x65\xc6\x74\xe8\xa1\x87\x63\xef\xbd\xe6\x21\
+\x1e\xef\xb6\x5d\x81\x60\x08\x7e\x02\x50\xe9\x7d\x48\x0c\xa9\xa6\
+\x3a\x06\x8f\xcf\x85\x9b\x1f\xf7\xe3\x17\xf7\x54\x60\xe5\x3b\x09\
+\x7c\xfb\xf3\x3a\xbe\xe8\xb8\x03\x45\x43\xdf\x2c\x3b\xc0\x20\xd0\
+\x82\xeb\x7f\x1d\xc0\x13\x2b\xe7\x62\xd6\x82\x18\x4e\x3c\xb4\x07\
+\x97\x1e\x9b\x46\x5d\xd4\xc0\x26\x02\x81\x1c\x29\x3d\x5f\xf7\x3c\
+\x01\x36\x67\x53\xd8\x25\xeb\xec\xee\x96\x78\x40\x77\x57\xa7\x5a\
+\x38\xa4\x5c\x80\x0f\x0b\x00\x1a\x82\x7e\x2f\xd2\xa4\xf8\x9b\xc8\
+\xea\x73\xa3\x4e\xee\xb1\x3f\x86\xe8\x3e\x5b\xe1\x00\x59\x61\xd6\
+\x77\x63\x3b\x54\x9c\x01\xa1\xe4\xb0\x07\x06\x01\x43\xda\x80\xe5\
+\x07\xa6\x09\xb5\x2d\xde\x44\xd4\xbf\x24\x0c\xc4\xb6\xf2\xf6\xda\
+\x02\xae\x0f\xe0\xde\x80\xd5\xb1\xd8\xbf\x9d\x06\x34\x98\xc2\x91\
+\x7f\xb6\xfa\xa7\x9e\x76\x3a\xd6\x6f\xdc\x28\x91\xf9\x48\x24\x24\
+\xcf\xb3\xdb\x92\x49\xdb\x33\x02\x6b\xaa\xb8\xc4\x37\x8e\xdb\x9f\
+\xf1\xd0\x6b\x31\x7c\x9e\xdc\x81\x6f\x5d\x18\x25\x30\x4d\xe2\xe7\
+\x77\x59\x18\x37\xba\x96\xac\x7f\xbf\xec\x80\xc7\x42\x17\xb1\xae\
+\x1f\xfc\xba\x1e\xc6\x45\x73\x70\xec\xde\xef\xd0\x73\x1d\x88\x86\
+\x4c\xfc\xe9\xf1\x30\x56\x34\xa5\x50\x55\x65\x22\x1c\x26\x17\x8d\
+\x98\x52\x92\xae\x1b\xd7\x05\xbc\xbf\x66\x35\x96\xdc\x77\x1f\xce\
+\x39\xe7\x5c\xe9\x54\x64\x59\xaa\x52\x50\x31\x80\x0f\x7a\x21\x78\
+\x01\x8f\x65\x2b\x65\x75\x34\x82\x49\x63\x46\x61\x4c\x7d\x1d\x02\
+\x44\x29\xa5\xde\xff\x03\xd2\x70\xbb\xa4\xd7\x2e\x18\xd2\xf5\xad\
+\xd0\xfe\x7e\xd6\x9f\x01\xc3\x70\x56\xbf\x19\x86\x6d\xfd\x39\xe2\
+\xcd\xf5\x07\x15\x04\x00\xdc\xef\x6f\xa8\x57\xba\x71\xec\x62\xdc\
+\xb8\xf1\x38\xf3\xf4\x33\xc4\xff\x67\xbc\xe2\xda\x80\x80\x2c\x60\
+\xf2\x4b\x28\x95\x41\xa0\x8a\x40\x20\x12\xf1\xe0\xce\x7f\xfa\xf0\
+\xdb\xbf\xc6\xb0\xec\xad\x1e\x7c\xe3\x73\x3a\xbe\x72\x5a\x12\x1b\
+\x9b\xdb\x90\x37\xd0\xaf\x33\xb0\x1d\x18\xec\xe9\x20\x26\xf0\x1b\
+\x37\x1e\x58\xba\x07\xa6\xce\xaf\xc7\xa1\xfb\xe6\xf1\xa5\xcf\x24\
+\x70\xd0\xee\x45\xf4\xc4\x53\xe8\xea\xea\x21\xd6\x64\x48\xc5\x60\
+\x4f\x22\x21\x6e\x1c\xc7\x03\x9e\x7d\xf6\x59\x49\xb7\x2a\x51\x0c\
+\xe0\x43\x05\xb6\xc2\xa4\x70\x41\x9f\xb7\x77\x4d\xfe\xc7\xf1\xbd\
+\xed\x51\x5f\xb6\x15\xda\x9a\xf2\xb3\x85\x2c\x39\xae\x84\x29\x0c\
+\xc0\x44\x90\x57\x00\x4a\x67\xa1\x02\xd1\xff\x7a\x3b\xb5\x35\x0c\
+\xaa\xdc\xb8\x54\x7a\x9f\x7d\xf7\x43\x6b\x4b\x2b\x6e\xbf\xfb\x4e\
+\x54\x10\x6b\x0a\x85\xc2\x36\xb3\xe1\x6e\x49\xbc\xee\x81\xae\x45\
+\x55\x65\x25\xd9\xea\x6e\xdc\xf3\x9c\x07\x3e\x4f\x05\xce\x30\x13\
+\xf8\xfa\x39\x11\xba\x3e\x49\xfc\xf4\x36\x60\xec\xe8\xba\x2d\x98\
+\x40\xaa\xbb\x0d\x37\xfc\xaa\x0e\xda\xa5\x33\x70\xdc\x82\x77\xe1\
+\xf5\xb7\x13\x13\x88\x63\x54\x4d\x14\xf7\xbf\x04\xb4\xb7\x1b\x04\
+\x2e\x21\xc9\x9e\x24\xe3\x09\x44\x63\x15\xb8\xef\xaf\xf7\x61\xca\
+\x94\x29\x18\x3d\x7a\x34\x86\xc2\x92\x72\x05\x00\xc3\x05\x04\x80\
+\x81\x7d\xee\x3f\xa6\x5b\xc1\x99\x84\x7c\xc1\x70\xd4\xbe\xdf\xfa\
+\x60\x5e\xf4\x53\x32\x7b\xad\xbf\xe9\x14\x8a\x70\x4e\x3d\x5f\x28\
+\xca\x20\x0f\x76\x01\x38\x86\x60\x0c\x83\x05\x2f\x0c\x66\x7c\x94\
+\x47\x1f\x7b\x2c\xda\x3b\xda\xf0\xc4\xd3\x4f\x13\x08\x54\xd9\x0b\
+\xa8\x1c\x46\x54\x94\x18\x8b\x2e\x53\x8c\x3a\x8d\x1e\x72\x07\x98\
+\x7e\x56\xe0\x34\x10\x08\x9c\xcb\x95\x90\x29\xfc\xf4\x2f\x1a\x26\
+\x8c\xab\x95\x1e\x02\xe5\xec\x00\x33\x81\x54\x77\x3b\x7e\xf4\x9b\
+\x3a\x7a\xff\x54\x1c\xb3\x9f\x5b\x40\x20\x12\x4e\xa0\xb1\x32\x8c\
+\xbb\x9e\x05\x36\xb6\x26\xc9\x55\x2b\x0a\xe0\x30\x68\x36\x11\x80\
+\x2e\x59\x72\x2f\x2e\xbd\xf4\x32\xf9\x1e\xd4\x7a\x01\x05\x00\x83\
+\xe0\x1f\x9b\x72\x33\xda\xdd\x80\x34\xf4\xd7\xff\xa2\x61\x2f\x2b\
+\xd6\x2c\xa7\x91\x28\x2f\x02\xa2\xe7\xb9\xbc\x98\xfb\xe3\x71\x24\
+\xdb\x6e\x92\x31\xbc\xce\xd7\xed\xf1\xe2\xd4\xd3\xce\x90\xee\x3e\
+\x2f\xbe\xfc\x0a\x2a\x24\x85\x69\x83\x5b\x9a\x83\x9d\x45\x6e\x1b\
+\xc6\xee\x40\x94\xe8\x7b\x02\x7f\xf9\x07\x2f\xba\x8a\xd2\x73\x3d\
+\xb8\xea\xfc\x28\x5d\x97\x24\x7e\x7e\x3b\x30\x69\x3c\x2f\x20\x32\
+\x07\x80\x40\x4f\x47\x2b\x7e\xfc\xeb\x7a\x68\xfa\x44\x1c\x7d\x80\
+\x1b\x81\x48\x37\xa2\xd1\x04\xc6\xd4\xe5\x71\xe7\x3f\x2a\xf0\xca\
+\xaa\x1c\x92\xe9\x82\x9d\x92\x8c\x86\xf1\xd8\xe3\x8f\xe1\xc0\x03\
+\x0e\xc4\x9e\xf3\xe6\xa9\xc9\xc4\x0a\x00\x06\xc7\xa5\xe0\x68\x74\
+\xb9\xd0\xa7\x4c\xff\xa5\xff\x9f\x69\x2f\x73\x2d\x5b\x7e\x53\xdc\
+\x04\x4d\x16\x0b\x65\xf3\x39\x04\x02\x7e\xe9\x03\x30\xdc\x2c\x17\
+\xc7\x2f\x42\xe4\xff\x9f\x7f\xc1\x85\x52\x13\xf0\xca\x1b\xaf\x23\
+\x5a\x51\x69\x57\x48\xd2\xff\xe9\x54\x5a\x3a\x2a\xb9\xdc\x0c\x02\
+\x11\x74\x74\xf6\x48\x8e\xdf\xed\xe2\xde\x0b\x09\x7c\xe7\x12\x5e\
+\x5c\x94\xc4\xcf\xee\x00\x26\x4f\xa8\x93\xe7\x7b\x17\x10\x79\x4c\
+\x74\x75\xb4\xe0\x47\xbf\x6c\xa0\xe7\xc6\xe0\xd8\x43\x3c\x98\x1a\
+\x09\xa1\xb2\xa6\x1d\x93\x46\x77\xe0\xd1\x97\x23\x78\xe4\xb5\x00\
+\xd6\xb6\xa5\xd0\xdc\x92\x14\x97\x63\x43\x53\x3b\xe6\xef\xad\xc2\
+\x5c\xc3\x0e\x00\x06\x6b\x2d\xc0\x27\x2d\xb6\x1b\xe0\x16\x3f\x94\
+\xd7\xb8\x9b\x92\x29\xe8\x6b\x7b\x65\xf5\x73\x01\xbc\x5c\xed\x67\
+\xda\x99\x04\x5e\x63\xc0\xcb\x91\xcd\x61\x78\x0d\xf8\x5c\xc3\xe1\
+\x08\x2e\xb8\xe8\x62\x68\xbf\xff\x1d\x5e\x23\x10\x08\x86\xc2\xbd\
+\x95\x91\xbc\xb2\xaf\xc4\x6e\x8e\xcb\x8b\x9a\xea\x0a\xb4\xb5\x27\
+\x08\x04\xfc\x08\xf9\x4b\x74\xad\xe2\xb8\xf6\x8b\xe4\x3a\x20\x85\
+\x5f\xdc\x69\x61\xf2\xf8\x7a\x02\x01\x38\x20\xa0\xd3\x35\x22\x10\
+\xe0\xa5\xc4\xbf\xac\x83\x59\x6a\xc0\xf1\x87\xb7\xa2\x31\x10\x44\
+\x45\x4d\x07\xc6\x8d\x4f\xe0\xd0\xf9\x19\xbc\xb8\x2c\x84\x95\x1b\
+\xc9\x4d\x70\x67\x31\x63\xd4\x0a\x72\xb5\x8e\x76\xc2\x28\xd6\xa0\
+\xde\xcf\x0a\x00\x76\x61\x37\x40\x16\x15\x59\x90\x00\x9f\x69\x95\
+\x01\xae\x2f\xbe\x27\xfd\x07\xbc\x5e\x27\x1b\x60\x4a\xf3\x4f\xdd\
+\x99\xbc\x3b\x1c\x85\x41\x20\x12\xad\xc0\x05\x17\x5c\x84\xc0\xad\
+\x37\xe3\x85\x97\x5f\x96\x75\x02\x65\x49\x27\xd3\xd2\x23\xc1\xed\
+\xf6\xa2\xae\x36\x8a\x4d\xad\x09\xfc\xf1\xf1\x30\x42\x3e\x62\x02\
+\x9e\x6e\x7c\xf7\x3f\xd8\xfa\x67\xf0\xb3\xbf\xb4\x60\xfc\xb8\x06\
+\x02\x43\x07\x04\xc8\xaa\x7b\x3d\xf6\x2a\xc2\x1f\xfe\xaa\x9e\x58\
+\x46\x2d\x3e\xb3\xb0\x0d\x41\x54\x13\x6b\xd2\x50\x3f\x2a\x8f\xbd\
+\xe7\xe7\x60\x14\x75\xf8\x42\x01\xb8\x46\xbd\x87\x9e\x52\x07\xfd\
+\xc5\x0a\xe6\x27\xea\x86\x54\x00\x30\x18\x7e\xb1\x07\x5a\xa1\xe0\
+\x28\xbf\xe5\xac\x31\x70\x5e\xe7\xbc\xbf\x83\x04\x7e\xf2\xf9\x19\
+\x28\xd8\x63\xe0\x49\x41\x1c\x30\x1b\xce\x0c\x88\x2b\x05\xc3\x91\
+\x08\xce\x39\xef\x7c\x84\x42\x11\x3c\xf9\xf4\xd3\xd2\xd0\xc3\x4e\
+\x81\x10\x08\xa4\x2d\x59\xdc\xc3\x20\xc0\x4b\x89\x9b\x9a\x13\xf8\
+\xd3\x93\x61\x84\x83\x06\x3c\xde\x4e\x5c\xfb\xb5\x06\x52\xf6\x38\
+\x7e\x7e\xdb\x26\xd4\x37\x34\xc0\xdf\x6f\x15\x21\x77\x16\x4a\x76\
+\x3b\xfd\x04\x50\x8f\x13\x0f\x6f\x23\xd4\x89\xc0\x6d\xd1\x7b\x19\
+\x2d\x3c\xf4\x77\x82\x63\x61\xb8\x0c\xb8\xf0\x1e\xa9\xfe\x02\x76\
+\xbe\xd4\x0d\xb9\x15\x51\x0e\xd2\x8e\xbc\xb8\x64\xfd\xb9\x72\x90\
+\x17\xfc\x31\xb5\x17\x85\xe6\xa8\xb8\x53\xf8\x23\xff\x31\x48\xd0\
+\x3e\x01\x02\x0a\x6e\x00\xc2\x01\x40\xff\x08\x29\x67\x65\xd0\xe3\
+\x66\xa6\xa7\x9d\x7e\x06\x4e\x38\xfe\x78\x89\x0b\x78\x7d\x5e\x02\
+\x86\xb0\xb8\x09\x1e\x29\xb0\xb2\x88\xfa\xfb\xd0\x58\x1f\xc1\xaa\
+\x16\x1d\xb7\x3d\x1d\xc5\x6b\xaf\x5b\x68\x59\xd9\x8e\x6f\x5e\x59\
+\x8b\x6f\x7f\x3e\x87\xf6\xd6\x16\xe4\x8a\x76\x07\xa6\xf2\x6d\xeb\
+\xf3\x6a\xc8\x24\xda\xf0\xa3\x9b\x3c\x78\xf2\x85\x18\xb4\xb0\x0f\
+\xa6\x1e\xb2\xfb\x36\xf2\x10\x96\x5c\x12\x56\x31\x4e\x00\xd0\xe6\
+\xac\xea\x54\xa2\x00\x60\x27\xfa\xfe\xac\xc8\xbc\x86\x9f\xab\xfa\
+\x0a\xa5\x42\x6f\xb4\xdf\x28\x2b\xbe\xf8\xfe\x90\x6c\x40\xc0\xeb\
+\x11\x57\xa1\xbc\xb4\x78\x24\xd5\xb3\xcb\x34\x24\xd2\xdc\xcf\x9c\
+\x74\x32\x4e\x5d\xfc\x59\xb8\xa4\xfd\xb9\x4b\x8a\x85\x78\x21\x8f\
+\xc7\xeb\xb6\x3b\xfd\xd0\x79\xd7\xd5\x84\xf1\xca\xfb\x3a\x1e\xf8\
+\x57\x14\xcb\xdf\xca\xa3\x7b\x55\x07\x2e\xbd\xbc\x11\xdf\xbc\x20\
+\x8b\x96\x96\x16\xba\x3e\xba\x53\x5c\x65\x07\x14\xe8\xb2\x21\x19\
+\xef\xc2\xed\x7f\xf3\x20\xd9\x03\xb8\x82\x21\xba\xa3\x3d\x36\xdd\
+\x37\x73\xb6\xd5\xb7\x4a\x83\xea\xff\x2b\x17\x60\x17\x53\x7c\x8e\
+\xfc\x33\x7d\xb7\x87\x87\xe4\x25\xaf\xcf\xae\x80\x51\x5e\x3b\xd0\
+\x2f\xf8\xc7\xd6\x9f\x53\x81\x21\xbf\x5f\x16\x02\x59\xf4\x7e\xae\
+\xfe\xe3\xfa\x81\x91\x94\xbb\x2e\x57\x47\x1e\xb9\xf0\x28\x44\xc8\
+\xf2\xdf\x7e\xd7\xed\x88\x27\x92\x02\x02\x7c\xcd\x78\x51\x0f\xa7\
+\xea\x42\x4e\xb3\x95\x47\x97\x66\x50\x17\xab\x40\x38\x1a\xc7\xac\
+\x48\x10\x5f\xb8\x62\x0c\xf2\xf9\x0d\xf8\xe1\x1f\x5b\x31\x7a\x34\
+\xb9\x03\x5e\x53\x2a\x27\xb9\xd0\xca\xed\x26\xb6\xd0\x6e\x20\xd1\
+\x55\x42\x64\x1c\x01\xa7\xdb\xcf\x5d\x4c\x6d\x47\x4b\x35\x0d\x55\
+\x00\xb0\xd3\x2e\xa4\xdb\x2d\x56\x5c\x9a\x56\x10\x05\xe5\x9b\x5e\
+\x5a\x89\x1b\x4e\x3e\xbb\x1c\xfc\x73\xd6\xfb\x8b\xff\x4f\xaf\x71\
+\x96\x80\x97\x00\xe7\xc9\x45\x60\x57\x20\x48\x66\x8d\xcb\x88\x47\
+\x9a\xcd\x92\xb5\x12\xc4\x06\xf6\x3b\xe0\x00\xd4\xd4\xd6\xe0\xe6\
+\x3f\xdf\x82\x75\x1b\x36\x48\x93\x15\x29\xd6\x49\x26\xa5\xa4\x37\
+\x1a\x0d\x09\x68\xfe\xf5\x45\xa0\xb1\x2a\x84\x58\x45\x2b\xa6\x44\
+\x42\xf8\x8f\xaf\x8d\xa5\x6b\xba\x8e\x40\x00\x18\x35\xba\x9e\x40\
+\x53\x93\xe1\x2c\xa9\x9c\x1b\x7b\x8c\x6d\x43\x6d\x84\x2c\x7d\x89\
+\x94\xdf\xe5\x73\x7a\xac\x8b\x0f\x46\xa0\x1a\xa6\xbf\xad\x43\x53\
+\xb7\xa8\x72\x01\x76\x84\xb0\xd2\xfb\x1c\xca\x9e\xce\x66\x44\xf9\
+\x79\x0d\x8a\x51\x32\x7a\x03\x7f\xe5\xb5\x04\x96\x13\xf4\x93\xd2\
+\x5f\xc3\x92\x32\xe1\x28\x47\xab\x39\x55\x48\x37\xb3\xdf\xe3\x91\
+\x55\x80\x23\x55\xf8\x1a\x30\x33\x9a\x3a\x6d\x3a\x2e\xff\xc2\xe5\
+\xd8\x6b\xee\x5c\x7b\x46\x62\x30\x28\xbd\xff\xb9\x99\x88\xae\xb9\
+\x50\x5d\x19\x41\x57\x46\xc7\x5d\xcf\x45\xf0\xf6\x72\x17\xba\xd6\
+\xb5\x00\x45\x17\xbe\xf6\xf5\xb1\xf8\xc6\xe7\x92\x68\xd9\xb4\x01\
+\x6b\x9a\x92\x68\xea\xc8\xe1\x90\x59\xcd\xb8\xec\xb3\x49\xf8\xc2\
+\x01\xbb\x65\x9a\xb4\x66\x73\x89\xf2\x9b\x7a\x94\x9c\x80\x31\x2a\
+\x06\xa0\x18\xc0\x0e\x40\x4e\x5d\x17\xab\xcf\xb7\x16\x17\xef\x94\
+\x9c\x59\x80\x6c\x7d\x64\x5e\x20\x37\xc7\x70\xd2\x7d\x26\xec\x62\
+\x1f\x48\x21\x90\xad\xf8\xbc\x3f\x2b\x7f\x65\x24\x22\x43\x40\xb8\
+\x38\x48\xa6\x02\x71\xf4\x7f\x24\x5f\x38\x06\x01\x3a\xdf\xda\xba\
+\x3a\x5c\x74\xe1\xc5\xf8\xfb\xdf\x1f\xc2\xa3\x8f\x3f\x2e\x2c\xa9\
+\x82\xce\x9d\x17\xf5\x68\x39\x0b\x35\x55\x61\x2c\x6f\x4a\xe3\xfe\
+\x17\xab\x30\x61\x6c\x07\xa2\x75\x71\x78\x2a\x2a\xf1\xb5\xaf\x8c\
+\xc1\x9c\xdd\x5a\xf1\xd4\x8b\x6d\x68\xa8\x71\xe3\xb4\x63\xbc\xa8\
+\x9b\x54\x05\x23\x5f\x2e\xb8\xd2\x6c\x10\x70\x07\x61\xba\xa6\xd1\
+\x77\x30\x01\x2a\x05\x38\xcc\x00\x60\x28\x17\x02\x95\xfd\x7c\x0e\
+\x6c\x31\x55\x95\x06\x15\xb0\xab\xfc\x4a\x4c\x73\x79\x44\xb8\xc9\
+\xa5\xbe\xe8\x5d\x4c\x24\xfe\x3e\x8f\x13\xe3\x49\x39\x9c\x0d\x20\
+\x2c\x88\x92\xbf\x5b\x5f\x5d\x49\xd4\xdf\x44\x86\x7b\x07\x38\xa3\
+\xbe\x76\x95\xba\x75\x06\x01\xb6\xf8\x27\x9e\x74\x32\x1a\x1b\x1b\
+\x71\xef\x5f\x97\xa0\xad\xad\x1d\xb1\xca\x2a\x99\x82\xcc\x53\x87\
+\xa3\xe1\x02\x9e\x79\xc7\x8d\x7d\xa6\x07\x30\x6e\x72\x17\xaa\x43\
+\x51\xba\x3e\x6e\x1c\x79\xfc\x28\x1c\x79\x6c\x11\x72\x91\x0d\x2f\
+\xcc\x7c\xb9\xa8\x42\xb3\x95\xdd\x45\xae\x80\xaf\x01\x39\xed\x50\
+\x62\x5b\x9c\x7a\x2c\x0c\xfa\xfd\xac\x00\x60\xc4\x50\x7e\xb7\xa4\
+\xf1\x32\xd9\xb4\x34\x0e\x61\xcb\xcd\x55\x7e\x96\x65\xd8\xf9\x7d\
+\xd3\xb6\xf8\x56\x39\xf8\xe5\x3c\x9a\x76\x05\x90\x04\xf8\x2a\x2b\
+\xa2\xb4\x85\x91\x2d\x96\x10\x4f\xa7\xc5\x65\xe5\x91\x62\xfa\x2e\
+\xe6\xa8\xf2\x62\x21\x06\xd4\xfd\xf6\x3f\x40\x56\xee\xdd\x76\xc7\
+\x1d\x78\xfb\x9d\xb7\x11\x8b\x55\xa0\xa7\x47\x17\x17\x6a\x63\x53\
+\x02\x4f\x2c\x0d\xe3\xd0\x05\x9d\xa8\x1e\x95\x80\xe5\x8a\xc0\x4c\
+\xf1\x6a\x4d\x4f\xef\xaa\xca\x7e\xaa\x46\x17\xbb\x04\x2d\x50\x8d\
+\xbc\xf7\x50\x14\xcd\x59\xe0\x29\xc7\x4a\x14\x00\x7c\x3c\x04\x87\
+\xdd\xdd\xdf\x1e\xdd\x55\x20\xe5\xcf\x49\x97\x60\x1e\xdd\x95\xcc\
+\x64\xe9\x46\xb6\x9b\x88\x5a\x4e\x9a\xaf\x7f\x80\x45\x46\x82\x13\
+\x5b\xe0\xa6\x22\xe1\x60\x80\xac\x5a\x50\xd8\x43\x3c\x95\x41\x32\
+\x9b\x95\xd7\xf9\x35\x29\x05\xde\x8a\xa5\xd0\x36\xff\xc1\xea\xfb\
+\xd9\xb2\xfa\x5e\xfa\x28\x36\x46\xdb\xca\x70\xd1\x8f\x62\xad\xb4\
+\x6d\xfe\xb2\xf9\x13\xd6\x16\x0f\x96\xe3\x12\x70\x6b\xef\x2f\x5c\
+\xf6\x05\x3c\xf2\xc8\xc3\x78\xe4\xd1\x87\x1d\x70\x00\x12\x89\x1c\
+\x96\xaf\xcb\x63\x7d\xab\x17\xd3\xb3\x3d\xd0\x3c\x74\xad\x5d\x21\
+\xda\x7c\xe8\xdf\x66\xcd\x0e\xbc\x64\xe9\x8e\x0e\xa0\x10\x3c\x12\
+\x19\xeb\x64\x09\xfe\x01\xa5\xad\x9e\xa7\x12\x05\x00\xdb\xa4\x6b\
+\xe5\x7e\x00\x72\x7b\xc9\xba\x7e\xad\x57\x61\xb8\xfd\x77\x67\xa2\
+\x07\x9b\x3a\xc9\x22\x45\xa3\x32\xae\xcb\xe3\xb8\x04\xe5\xae\x3e\
+\xe5\x15\x80\xd2\x16\x4b\x62\x05\x2e\xa7\x01\x88\x89\x1e\xa2\xfb\
+\x99\x7c\x4e\xd8\x82\xd7\xeb\x41\x24\x10\x40\x88\x1b\x7f\xf4\x57\
+\x3e\xa7\x76\xdd\x9e\xae\xdb\x57\x33\xbc\x5d\xe5\xec\x37\x8a\x7c\
+\x80\x2b\xb5\x99\x1a\x32\x1b\x29\x2f\x91\x2d\xa7\xe7\x7a\xcf\x4d\
+\xd3\x06\x9e\x6b\x3f\xc5\x91\xa0\xba\xa6\x95\xa1\x70\x80\x22\x7f\
+\x38\xf8\xb1\x27\x29\x69\xf6\x3f\xfd\x86\xa9\x68\xa8\xae\x0e\xe0\
+\xdc\x73\xcf\xc3\xdc\xb9\x73\x71\xcb\x2d\x37\xe3\xad\xb7\xde\x26\
+\x90\xd5\xd0\xde\x6d\x20\x57\x30\x05\x51\xad\x42\x86\xfe\x61\x45\
+\x67\x06\xe0\x11\x7f\x5f\xde\xed\xa2\xcf\x0a\xc4\x50\x8a\x1c\x8b\
+\xa2\x7e\x29\xdc\x16\x8f\x5d\xcb\xd1\xbe\x9e\x3e\x37\xac\xdf\xf9\
+\x96\xbf\x67\x05\x00\x4a\x06\x58\x44\x1e\xc9\x25\xdd\x7a\x45\x49\
+\x30\xe0\x86\x31\x9d\x7d\xc6\x8d\x1e\x03\xa3\xa9\x19\xeb\x5a\x5b\
+\x65\xb2\x4f\x2c\x1a\x91\x15\x7c\x9c\x0d\x90\xd9\x7d\xa6\x1d\x8d\
+\x36\x9d\xa8\xb7\x45\x16\x8e\xdf\xcb\xb1\x01\x8e\x72\x73\xe6\x20\
+\x93\x4a\xc1\xa0\x9f\x35\x4e\x65\xa5\xd2\x92\xd3\xe6\xc2\x21\x29\
+\x74\x91\x7d\xec\x5e\x79\x0c\x38\xbc\xb2\xcd\xfe\x59\x2f\xab\x90\
+\x7d\xd3\x6b\x5a\xef\x2c\x32\x4d\xeb\xaf\x5c\xfd\x3a\x10\xe8\x7d\
+\xe3\xcb\x74\x47\xf1\x75\xe7\xdc\xca\xca\x6b\x39\xfe\xb3\xad\xf4\
+\x7d\xec\x82\x7f\x36\xcb\xc0\xd3\x0b\x4a\x5a\x6f\xab\x2d\xd3\x01\
+\x28\xcb\x29\x67\xee\xbf\xe6\xa6\x0f\xcc\xb8\x57\x40\x1f\x68\xc8\
+\x75\x2c\x99\x42\xef\x0d\x27\x55\x6a\x96\x47\xae\xcb\x88\x75\x13\
+\x75\x8d\x63\xf0\xb9\x0b\x2f\xc1\x03\x7f\x5d\x82\x67\x9f\x7b\x11\
+\xd3\x8c\x6e\xcc\x9c\xbd\x1e\x18\x5b\x0b\x3d\x41\x00\x50\x48\xdb\
+\x39\x7e\x3e\x56\xbf\x97\x90\x34\x82\x92\x36\x0e\x79\xd7\x89\x30\
+\xdd\x27\xd2\xf3\x01\xb8\x35\x2e\x00\xf2\xf7\x82\x96\xa6\x59\x7d\
+\x6c\xc9\x61\x72\x79\xd5\x41\x58\x01\xc0\xe6\xc2\xca\xcf\x0a\x6a\
+\x6a\x8e\x3f\x4f\x37\x27\x5b\x6e\xa3\x3c\x0a\x9c\x1e\xdb\x3b\x3a\
+\xb1\x61\xdd\x7a\xbc\xff\xfe\x6a\x99\xde\x93\xa7\x9b\x38\x5c\x11\
+\x41\x5d\x6d\x8d\xf4\xa9\xe3\xd5\x7c\x1c\xe0\x72\x91\x22\x97\x41\
+\x80\x17\xc8\x24\xba\xe3\x58\xb7\xbe\x09\x2d\x04\x1e\x19\xf2\xfd\
+\x99\x39\xb0\x2b\xc1\x55\x70\x21\x62\x12\xa1\x50\x00\xe1\x50\x48\
+\xfa\xea\x45\x22\x11\x49\x8d\x55\xd0\xc6\x9f\x1d\xa5\xdf\xb9\xed\
+\x38\x1f\x9f\xe5\xe8\x35\x83\x46\x19\x04\x5c\x7c\x93\xeb\x8e\xe2\
+\xdb\xda\x2e\x37\xbb\x6e\x69\xc2\x42\x4c\x47\x39\xcb\x29\x72\x89\
+\x3b\xf4\x1b\xa6\xd1\x07\x76\x8e\x42\x6b\x70\xea\x15\x9c\xe7\xcd\
+\x72\x40\xb3\xbf\x15\x45\xef\x8a\xc5\xde\x05\x4e\x1a\x7a\x47\x9e\
+\x5b\x4e\xc6\xa3\x6c\x81\xf9\x78\x58\xe9\xba\xe2\x71\x74\x75\x76\
+\xa1\xbb\xbb\x1b\xf1\x78\x52\x22\xff\x89\x9e\xa4\x34\xf8\xe4\x5e\
+\x02\x9c\x4e\xe5\xe9\xdf\x7c\x3e\xde\x68\x2d\x8a\xf9\x00\xbe\x77\
+\x97\x1b\x07\xb4\xb8\x31\x75\x4a\x04\xb5\x81\x12\xbc\xba\x89\x78\
+\x8e\xd8\x41\x8f\x1b\xeb\xd6\x54\xa2\xbd\x6b\x06\xea\x1b\x62\x98\
+\x3c\xe9\x5d\x4c\x9c\xd8\x20\xd7\x8f\x81\x93\xcf\x5d\x26\x3d\xbb\
+\xec\xc7\xf2\x75\xe3\xe3\xca\xe5\x72\xbb\x3c\x0b\x50\x00\xb0\x19\
+\x65\x4e\x26\x93\x4e\x47\x5b\x97\xdc\x28\xba\x33\x05\x58\xb7\xff\
+\x11\xcf\xbe\xa1\xae\x1a\x41\xbf\x07\xb5\xd5\x95\x98\x30\x61\x1c\
+\xda\xba\xba\xd0\x95\x48\xc2\x20\xcb\x56\xc8\x15\xe1\x61\x6a\x4a\
+\x9a\xc0\xe0\xc1\xf1\x02\xbe\xe1\xe3\xdd\x49\x99\x75\xc7\x41\xc0\
+\xa9\x53\x26\x21\x1c\xe4\xd6\xe3\x3e\x29\x02\xe2\x61\xa1\x41\xfa\
+\xd9\x4f\xc0\xc1\xbf\x07\x82\x7e\x29\x09\xe6\x05\x42\xcc\x2a\xb8\
+\x7e\x9e\x47\x8a\x73\xa1\x10\x33\x82\x5e\xf7\xa4\x9f\xc5\xd6\x9c\
+\x51\xe5\xbd\x2d\xc7\xe0\x0c\x32\x75\x14\x5a\x46\x90\x5b\xf6\x7e\
+\x56\xef\x38\x72\xb3\xb7\x46\x41\xce\x55\x3e\xbb\xfc\x68\x7f\x96\
+\xe6\xac\x4a\x14\xb7\x81\x3f\x53\x66\x9e\xeb\xbd\x85\x4d\xae\xde\
+\xa0\x84\xd9\xeb\xb2\x94\x99\x80\x0d\x0e\xae\x3e\x46\x60\x41\xba\
+\x27\x7b\x3c\x5e\x54\x56\x54\x90\x02\x36\x22\x9b\xcd\x22\x9b\xc9\
+\x92\xe2\x67\xec\x81\x1f\xf4\x7b\x3a\x43\x8f\x99\x3c\xd1\xfe\x9c\
+\xdd\x32\x9d\x00\x38\x95\xd5\xb1\x62\x8d\x85\xae\x94\x17\x13\x46\
+\x05\x10\xab\xf0\x21\x91\x02\x36\x6e\x2a\x22\x12\xaa\xc2\x5e\x13\
+\xab\x30\xa6\x31\x86\x1a\xfa\x4e\x82\xe1\x90\x64\x54\x7a\xbb\x01\
+\x59\xf4\x19\x85\x92\x03\x52\x36\xeb\xb0\x9c\x1e\x0c\x8a\x01\x0c\
+\x61\x65\x1c\xac\xf4\x09\x07\xa0\x4a\x32\x17\x10\x03\x7d\xe2\xb2\
+\xab\x40\x0a\xc0\xe3\xba\x78\x68\xc7\x6e\xd3\x26\xf7\xbe\x56\x6e\
+\xee\x61\x4a\xf5\x9f\x35\xc0\xbf\xd6\x9d\x1b\x52\x77\xba\x04\x6d\
+\x1e\x26\xb3\x36\x0b\xc2\xf5\x4f\x85\x4a\x6c\x81\x2d\x2f\xdf\xb8\
+\x9b\x45\x06\xcb\xc1\xc9\xad\x06\xf3\x9c\x0f\xb6\x36\x8b\x21\x58\
+\xdb\x89\x29\xf4\x3f\xdf\xad\x3d\x6e\x3d\x78\xe8\x50\x6c\x6b\xfb\
+\xd1\x80\x32\x54\xf8\xc9\xaf\x0f\x78\x49\x89\xa3\xa1\x01\xe3\xd5\
+\xad\x7e\xd7\x42\xdb\xec\x98\x74\x69\xa1\xae\x09\x33\x30\x4c\x9b\
+\xca\xb8\x5c\xf6\xf3\xba\x66\xe7\x5d\xf8\xba\xf3\xa2\xab\x12\xd7\
+\x65\x38\x2e\x49\x7f\xb6\x52\x3e\xba\x9d\xad\xf8\x2a\x0d\x38\x4c\
+\xe3\x01\xd8\x46\x40\x8d\xef\x42\x06\x89\xcd\x5b\x4d\x7d\xe8\x1b\
+\xcb\xb2\x76\x58\xd1\x4f\xaf\x22\x6d\xe7\x98\xb6\xf7\xda\x07\x01\
+\x8b\x8f\x9a\x2d\xb0\xb6\x1b\xc7\xfc\xa0\xd7\xd0\x92\x89\x43\xfd\
+\x5d\x98\xed\x9d\xd3\x87\xfb\x6c\xc5\x00\x94\x7c\x04\x90\xf8\x08\
+\x1f\xb0\x43\x6f\x49\x6d\x28\x9d\xeb\x27\x7e\x5c\xda\x56\x14\x5c\
+\xc9\x87\x15\xb5\x16\x40\x89\x12\x05\x00\x4a\x94\x28\x51\x00\xa0\
+\x44\x89\x12\x15\x03\x18\x6c\x19\x29\x5d\x81\x95\x28\xe9\x7f\x3f\
+\x2b\x00\xf8\x40\x07\x64\xd0\x56\x32\x7c\x2e\xa0\xe4\x52\x37\x8f\
+\x92\xe1\x2f\x7c\x2f\x5b\xc5\x2c\xaf\x49\xe6\xd2\xc3\x0c\x3f\x7a\
+\xb4\xa1\xd1\xad\x48\x1b\x0c\x64\xba\x79\x69\xfe\x27\xef\x76\x96\
+\xe6\x79\x5d\x5b\x5e\x85\xce\xce\x4e\x5e\x5c\x33\xaf\xb6\xa6\x26\
+\x26\x0d\x1e\x94\x28\x19\xe6\xc2\x65\xdd\x6f\xbc\xb1\xb4\x2b\x91\
+\x48\x74\x40\xd3\xe2\xa4\x76\x99\xb9\x73\xe7\x9a\xb1\x58\x0c\x05\
+\xc3\xd2\x2f\x9a\x17\xb8\x62\x7c\x4c\x5f\xba\xcb\x30\x80\x77\x3b\
+\x8c\xfd\x5f\x69\x2e\xee\xcb\xad\x9e\xb7\x38\x20\x77\x95\xe4\x74\
+\x9a\x5a\x4b\x2a\x44\xa1\x64\xc4\x88\x7f\xd2\xbe\x55\x21\x5d\xaf\
+\x2a\xff\xbe\x2e\x9b\xc5\xea\xe6\x22\x72\x25\x0b\xa7\xed\xe1\xaf\
+\xde\xa5\x5c\x00\x8f\x0b\x59\x2f\x29\xbf\xd7\xbd\xb5\xe4\xad\x21\
+\x95\x22\xba\xa2\xff\x4a\x46\x90\x18\x85\xec\x80\xbe\x44\xdc\x1d\
+\xd9\xc5\xb3\x0e\x20\xcb\x36\x8c\x5d\x0a\x00\x4c\xa7\x2e\xdd\x1c\
+\xc1\x31\xbe\xd2\x66\xe7\x66\xf7\x05\xc0\x76\x6b\x65\xf9\x66\xd8\
+\xfc\x9a\xb8\x34\x55\xbf\x36\x92\xa5\xbc\xa2\x72\x97\x02\x80\xf2\
+\xe2\x32\x73\x84\x46\xf9\xb9\x2a\xad\xca\xcb\x2b\xf4\xcc\x5e\xed\
+\x2f\x9a\x1a\x12\x45\x7d\x9b\x0d\x2a\xf9\xd9\x00\xb1\x9e\x90\xdb\
+\x1a\xd0\xe9\xa3\x87\xde\x53\x30\x87\x17\x08\xf0\xd1\x97\xe8\x98\
+\xbd\x2e\x35\x9a\xfb\xdf\xdc\x29\xbd\x1d\xa3\x77\x2d\x06\xe0\x2c\
+\x1a\x19\x89\xad\x1a\xf9\xdc\x42\xba\x85\x2b\xe6\xbb\x31\x3a\xca\
+\xf9\x0c\x0b\x3e\xf2\x79\x5e\x79\xbf\x13\xd7\xfd\x33\x85\xaa\xfa\
+\x31\xd2\x3e\x6c\x73\xc9\x97\x80\x83\x46\x03\x9f\x9b\xed\xa1\x9f\
+\xed\x3b\xc2\x47\x14\xf1\xaa\x25\xab\xf0\x3e\x46\xc9\xb8\xf0\xa1\
+\xac\x4c\xe5\xb5\x07\x05\x3a\x35\x0f\x31\x9d\x09\x51\xf2\x73\x7b\
+\x8c\xde\x1e\x06\x4a\xb6\x76\xcd\x9c\xe5\xd6\xd8\xd5\x00\xc0\xa1\
+\xc8\xee\x11\x68\x1c\xca\xcd\x40\x2b\xfc\x1a\xaa\xc3\x6e\x14\xe9\
+\x09\x0f\x37\x0a\x29\xf5\x20\xd9\xb6\x09\x95\x8d\xe3\x60\x96\x8c\
+\xad\xbe\xcf\x4b\xc0\x11\x0b\xf3\x30\x51\x27\x56\x42\x9a\x94\x69\
+\x5d\x83\x42\x55\xad\x34\x2a\x19\xca\x00\xc0\x16\x9f\x95\x7f\x7a\
+\x25\x70\xce\x6c\x02\xab\x64\x1b\xfe\xe3\xa1\x2e\xd4\x4e\x98\x02\
+\xcb\x50\x5d\x79\xb7\x77\xbf\xec\x7a\x00\xe0\xb8\x00\x23\x31\xcb\
+\x27\x8d\x6a\xa5\xfd\x97\xd5\xbb\x79\xb8\x57\xa0\x69\xc9\xf9\x1a\
+\xdb\x18\x58\x23\xaf\x71\xfc\x53\xde\xe3\x00\x80\x6e\xf5\xee\xcf\
+\x8f\x43\x55\xff\xf9\x9c\xab\x7c\xc0\x09\x93\x75\x1c\x37\xd5\x85\
+\x68\xd8\x8b\xa7\x5f\xea\x41\x3a\xde\x81\x6a\x6b\x1a\x1d\xbf\x02\
+\x80\x6d\xd1\x26\xd3\x1a\xdc\xef\xd5\x3d\x68\x4a\xe2\x6c\x23\x12\
+\x00\xfa\x35\xeb\xec\xef\x17\x9b\xbd\x0c\x61\x1b\xcc\x61\x1b\xfe\
+\x74\xf9\x33\x87\x2a\x00\x64\x8b\xc0\x19\x73\x34\x7c\x7a\x86\x1b\
+\xf9\x02\x9c\x65\xce\x9a\xb4\x02\x33\x46\x78\xb0\xf7\xe3\xba\x4d\
+\x83\xad\x03\x83\xc6\x00\xd8\x22\x96\x46\xe0\x1a\xce\x32\x03\xe8\
+\xdf\x2a\x0b\x4e\x1b\x2d\x3e\xef\x92\xd9\xd7\xce\x6e\x73\x06\xd0\
+\xbf\xb5\x96\x28\x7f\x79\x9c\xb8\xb5\x6d\x4b\xa1\x6d\x06\x12\x65\
+\xe1\x8e\x38\x6e\xfd\xc3\xdd\x5d\x72\x6c\xfd\x28\xa9\xb4\x14\xe3\
+\x19\x1b\xe5\xc6\x22\xdb\x78\x1f\x3f\xef\x71\x71\xaf\x3f\x9b\xb1\
+\xf4\x3f\xdf\xed\x31\x3d\xd3\xf9\x9b\x76\xe3\x0e\x38\x7d\x0b\x77\
+\x9d\xcc\x07\x9f\xaf\x0c\x8a\xd9\x35\x01\x80\xb6\x11\x12\x1f\xd2\
+\xb6\xe2\xde\x58\x03\x7a\xe5\x59\x03\x15\xc2\xda\x92\x1d\x94\xca\
+\x96\x72\x33\x00\x60\xb0\x28\x3a\x5b\x7f\x00\xd0\x9c\xbf\x95\x33\
+\xec\x9f\xeb\xfd\x16\x62\x7e\xa9\xb1\x90\xb8\x43\x3c\x6b\xa1\x25\
+\x6b\xb7\xef\xe2\x82\xab\xed\x65\x1f\x72\x25\xfb\x33\x6a\xe9\x33\
+\x2a\x7d\x96\x44\xef\x21\xcf\x5b\x48\xe4\x2c\xb4\xe7\xe8\xdd\xda\
+\x96\x9f\xc3\x7b\xf1\xdf\x4f\x17\xe0\x04\x2e\xfb\x8e\x9d\xab\x38\
+\x93\x79\x03\x3d\xf4\x9a\x46\x88\xc8\x01\x4d\x69\xc3\xd5\x2f\x5e\
+\x10\xf2\x00\x0d\x01\x4b\x1e\x59\xf1\xb9\xa5\x7a\x3a\x6f\xa1\x23\
+\x07\xa4\x0c\x97\xd4\x89\xb0\x1b\xd4\xff\xd0\x47\x12\x99\xd0\xac\
+\xc1\x77\xed\x06\x07\x00\xd8\xfa\xd3\xe6\x1a\xca\x31\x00\xeb\x83\
+\x69\xbd\xb6\x19\x08\x88\x92\x6b\xd6\x56\x3f\x44\x5e\x33\x9d\xd9\
+\x80\x56\x9f\xe5\xb3\x1c\x1a\x5d\xdc\x0a\x1f\xe4\xe7\x92\x39\x13\
+\xa6\xc7\xb4\xdb\x5a\x3b\xcf\xe7\x0d\xfb\xe3\x0f\x1f\xab\xe1\xf8\
+\x29\x3a\xa6\x57\xeb\x88\x7a\xfb\x00\xa0\x87\x14\x77\x45\x47\x09\
+\x7f\x5b\x91\xc3\x63\x4d\x3a\x34\xb7\x4f\x82\x8c\xbd\x9d\xb1\x1c\
+\xff\x93\xbf\x87\x43\x46\x69\x38\x79\x37\x1d\xbb\x55\x93\xff\xee\
+\xed\x0f\x00\xa4\xc4\xa4\xc0\xef\x77\x19\xb8\x6f\x79\x16\x4f\x36\
+\xeb\xd0\x3d\x5e\x62\x04\x76\x83\x4f\x4e\x4f\xfe\xbf\x05\x6e\xcc\
+\xa8\xd1\x30\x2a\x64\x49\x26\x43\x5c\x82\x7c\x11\x7b\x4c\x1e\x85\
+\x5b\xce\xaf\x45\xa4\x42\xc3\x5b\x1b\xb2\xf8\xc9\xbf\xb2\x88\xc4\
+\xaa\xe4\x33\xeb\x09\xa8\x4e\x98\x06\xec\x3f\xd6\x85\xda\x80\x0d\
+\x04\xd2\x81\x98\x8e\x25\x5d\xb0\xd0\x49\x00\xf0\xd2\xc6\x02\xee\
+\x5f\x65\x60\x53\xde\x87\x00\xa7\x15\xfa\x75\x4f\xb2\x3e\xe4\x77\
+\x34\x94\x0d\x47\x69\x57\x64\x00\x76\x40\xcc\x0e\x90\x0d\x49\x6b\
+\xae\x6d\xfe\xdc\xd6\x07\x68\x94\xad\x30\xfa\x29\x32\x9f\x92\xa7\
+\xdc\x41\xb8\xdc\x8f\xcf\xb2\x27\x01\x77\xa7\x0d\x78\x32\x26\x8c\
+\xa2\xd9\xdb\x3c\xd3\x79\x19\x19\x02\x80\x8c\x33\xe2\xaa\x7f\xcb\
+\xee\x7c\x89\xbb\xdf\x1a\x28\x79\x0c\x87\x5a\x93\x82\xd1\x8f\xa3\
+\x49\x71\xbe\x75\x80\x1b\xc7\x4c\x75\x83\xc7\x0e\x14\xcd\x3e\x37\
+\xc1\x43\x96\x33\xec\xd7\x30\xbe\xca\x8d\x23\x26\x7b\x71\xd7\x1b\
+\x71\x5c\xf7\x5c\x06\x9d\x7a\xc4\x5e\x98\xe2\xfc\x5d\x0e\x36\x5e\
+\x31\xcf\x85\x2b\x48\x89\xf9\x3d\x45\x01\x64\x97\x93\xba\x23\x20\
+\xa0\xf3\xa8\x08\x02\x13\xe9\x73\x0e\x9b\xe4\xc1\xef\x9e\xef\xc0\
+\x75\x2f\x66\xe0\x0e\x45\xa1\xd3\xb1\x64\x49\xe1\xa7\x55\xba\xb0\
+\x0f\x81\x50\x36\x67\xcf\x3d\xb4\xad\x3f\xb1\x91\x48\x10\x07\x55\
+\x6a\xd0\xbd\x3a\x52\xcd\x6d\x68\x59\xdb\x82\xec\xf4\x05\x98\x1a\
+\x36\x71\xe3\x21\x6e\xcc\xac\xd7\xe5\xef\x19\x96\xdd\xf6\xdc\x6e\
+\xc3\x6e\xc2\x4f\x00\x54\x17\x05\x66\x37\xb8\x71\xf8\xb8\x0c\xbe\
+\xf1\x68\x07\x96\x67\x2a\x11\xf4\xba\x7a\xd9\x87\xbe\x9d\xf6\x5e\
+\xd6\xe6\x74\xc1\x1a\xba\xac\x81\xcf\xa3\x64\x0e\x6e\xcf\xc0\x41\
+\x72\x01\x6c\x85\x30\x77\x72\x0c\xa0\x7f\xf3\x4d\xed\xdf\x18\x7e\
+\x6b\x3b\x74\xc0\x42\xd9\xc7\xef\xa3\xfc\xe5\xc2\x26\xbe\xa9\x35\
+\x97\x29\x33\xec\xfa\x53\x7f\x9f\xd7\x8d\xba\xa0\x8e\x0a\x14\x60\
+\xe9\x25\x99\x7d\x57\xee\x51\x2f\xd6\x96\xb6\xb0\x4b\xef\x6b\xc3\
+\xed\x3c\xb2\x52\xf1\xe4\xe0\x82\xdc\x29\xa6\x7c\x7e\xad\x0f\xf8\
+\xf9\x11\x5e\xec\x37\xde\x8d\x14\x59\x4c\x66\x03\xfc\xf9\x3e\xa7\
+\x83\xaf\xcd\x10\x4c\xb2\xa6\x76\xe7\xdb\x33\xf6\xa9\xc2\xd8\x50\
+\x27\x2e\x78\xa0\x0d\x3d\xfe\x5a\x78\xe8\x6f\x33\xe0\x2c\x9e\xaa\
+\xe3\xeb\xfb\xbb\x85\xc6\x17\xe9\xf7\xa0\xcf\x83\x54\x2a\x85\x78\
+\x22\x25\x5d\x90\x6b\xab\x49\x13\x75\x0f\x7d\x8e\x21\x9d\x82\x2f\
+\xf9\x54\x2d\xd6\xb7\xad\xc2\x2f\xde\x05\xa2\xd1\xb0\x1c\x97\xed\
+\xb8\xf3\x6d\x64\xf4\xb6\x00\x2f\x2b\xa2\x26\xf5\xdc\x1e\xb9\xda\
+\x49\xa2\x38\x51\x42\x9c\xef\xec\xaf\x8b\xf2\x27\xf3\x96\xb4\x45\
+\xe7\x38\x45\x7b\x47\x97\xcc\x59\x8c\x86\x03\xa8\x88\x46\x88\x0d\
+\x99\xc8\xd1\x79\x8c\xaf\x0b\xe3\x3f\x0f\xca\xe3\x8c\xbb\x37\x22\
+\x11\x19\x2b\x0c\x46\xd7\xca\x2d\xd1\xed\x38\xc7\xc7\x31\xfe\xfd\
+\x9b\x8f\x0e\x86\x0a\x72\x0b\x75\x63\x90\x8d\xe0\x20\x01\x80\xe9\
+\x6c\x83\xc3\xd1\xfa\x1a\x66\x6e\xe5\x4b\xe9\xf5\xd3\xb7\x54\xf0\
+\x5e\x3f\xbe\x1c\xb4\xc3\x40\xf4\x96\x81\x13\x9c\xcf\x77\x9b\xce\
+\x5c\x40\x1b\x00\x32\xb9\x02\x66\x11\x25\x7e\xe8\xf2\x5a\xa7\x1e\
+\xd8\xb3\xe5\x2d\x4a\xfb\x79\x89\xea\x66\x8b\xd6\xc0\x46\x9c\xce\
+\xb5\xb2\x9c\x8d\x9b\x15\x7f\xe3\x20\x0f\x29\xbf\x8b\x68\xbe\xad\
+\x70\x41\xbf\x17\xcb\xdf\x5b\x8b\x07\x9e\x5f\x89\xf6\x8c\x85\x29\
+\xa3\x6b\xf0\x99\x7d\x27\xa3\x86\x14\x38\x5f\x24\x3f\x3c\xa3\xe1\
+\x53\x33\xaa\x71\x65\x73\x02\x57\xbe\xd0\x09\xbd\xa2\x0a\x01\x02\
+\xa9\xb3\xf7\xf0\xda\x60\x6c\x6a\x12\xb3\xff\xdf\xdb\x9f\xc0\xed\
+\x2f\x36\x23\xe5\x8e\x42\xf3\xf8\x30\xb9\xda\x8f\xeb\x4e\x9e\x81\
+\xe9\xe3\xab\xed\xef\xca\xe3\xc6\xe5\x07\xd7\xe1\x81\x95\xeb\xd0\
+\x5a\x9a\x48\xcc\x43\xc7\x13\xcb\x3b\xb1\x76\x59\x13\xf6\x9c\x3e\
+\x06\xe3\x1b\x6b\xa4\x2b\x2f\x4f\x42\x6a\x69\x8f\xe3\x99\xd7\xde\
+\x93\x71\x68\x6f\xae\x6e\x85\xe1\x0a\xe1\xa0\xd1\x16\xe6\x8f\x72\
+\x91\xf2\x9b\xd2\xe2\x7c\xe5\xea\x8d\xb8\xfe\xd6\x7f\x62\x65\x92\
+\x8e\xc3\x1b\x92\xe9\xc8\x67\xec\xd3\x80\x4b\x17\x4e\xe7\x55\x61\
+\x04\x29\x1a\x76\x9b\x52\x87\xd3\xa6\x6c\xc2\x0d\xef\x74\x92\x3b\
+\x11\xe3\xe5\xab\x72\xe9\x74\x27\x40\xa9\xa3\x0c\x08\x5a\x2f\x38\
+\xf4\x07\x08\xbd\x77\x50\xca\x40\x25\x2f\x7f\x6d\x83\xe9\x85\x6a\
+\x56\x9f\x21\xd9\xe5\x62\x00\x32\x68\xc3\xf8\x64\x01\xc0\xda\x06\
+\x8d\xdf\x3c\xe0\x66\x59\x7d\xca\x3b\xf0\x77\x27\x4f\xef\x0c\xc4\
+\x28\x53\xea\xb2\xf2\x97\x53\x71\xdb\xfb\xbe\x4a\x92\x05\x30\x07\
+\x06\xae\x78\x28\x28\xdd\xdc\xb5\x95\xee\xed\x46\x7c\xb6\x56\x1e\
+\x5d\x1e\xe1\xc5\x1b\xbb\x03\x0b\xea\xc8\xe7\x9f\x46\x96\x3f\x6f\
+\x03\x45\x30\xe0\xc5\x43\x4f\xbd\x82\x2f\xfc\xdf\x4b\x68\xab\x9c\
+\x01\x6f\xac\x01\xc6\x86\x12\x6e\x5f\xf9\x3e\x7e\x73\xda\x44\x4c\
+\x6c\xac\x10\xf6\x90\x22\xa5\x3b\x7d\xbf\x51\xb8\xfb\xad\x65\x78\
+\x36\x13\xc2\x8c\x2a\x17\xc6\x45\x2d\xf2\xe3\x35\xb1\xee\xa9\x44\
+\x0f\x6e\xfa\x47\x13\xde\x09\xef\x8d\x50\x75\x8d\x54\xf3\xad\xe4\
+\xde\xfc\x8f\x24\xf0\xbb\x93\x74\x74\xf7\xa4\xf0\x5e\x53\x37\xd6\
+\x37\x77\xc2\x4c\x96\x60\xfa\xc6\x90\xc2\x7a\xf1\x5f\x2f\x15\x10\
+\xdf\x94\xc3\x9f\x4e\x2f\x62\xda\x18\xdb\xad\xf0\x11\x00\x2c\x27\
+\xca\x7f\xfe\xff\x2d\x43\x70\xe2\x1e\xa4\x88\x15\x08\xd6\x8f\xc6\
+\x9c\xca\x92\x0c\xf5\xe4\xe3\xf6\xba\x75\x3c\xbb\x74\x35\xee\x58\
+\x13\x85\x7f\xb7\x7d\xe4\x77\xa6\xf8\xdf\x79\xb5\x07\x8d\x35\xed\
+\xe2\x72\xac\xd8\xd8\x85\xf5\xad\x71\xbc\xfb\x5e\x3b\xcc\xcc\x04\
+\x14\xc2\x15\x3c\xa9\x65\x0b\x30\xd7\x7a\x15\xbf\x4f\xe1\xf5\x32\
+\x48\x94\xe7\x3a\x68\x7d\xfb\xf6\xce\x54\xe8\xf7\xfb\x56\xb9\x9e\
+\xf5\xef\x19\xe3\xc7\xcd\x02\x88\xab\xb8\xcb\xad\x05\x30\x9d\xde\
+\xf9\x3b\xc8\x05\xd8\xd6\xe5\x34\x7b\x95\xdc\xea\x4d\x9b\x95\x03\
+\x72\xbd\x8a\xff\x31\x23\xce\xb6\xd1\x36\x37\xcb\x00\xd8\xd6\x4a\
+\x28\xb1\xb5\x5d\x64\xb4\x67\xf7\xf5\x53\x7e\xab\x3c\xd3\x8e\xb6\
+\x22\x59\xf3\x13\xa7\xf9\x10\xf6\x11\xa5\x2e\xd8\x93\x86\x9b\xc9\
+\xbf\xfe\xc6\x9f\x5f\x46\xf7\xf8\x43\x51\x4d\x4a\x66\x39\x0a\xf2\
+\x32\x71\xfc\xef\x3f\x4d\x4a\xbd\x38\x28\x43\x32\x39\xde\x12\x09\
+\xf8\x71\xd2\x34\x0f\x9e\x78\x96\x14\x2a\x56\x2f\xdf\x01\x7f\x3e\
+\x4f\x3e\x0a\x47\xc2\xf8\xdd\x97\x17\x62\x09\xd1\xfb\x17\x5a\x4d\
+\x6c\xe8\x21\x65\xf6\xfa\xf1\x5c\xc2\x8b\x85\x37\xad\x41\xaa\x75\
+\x3d\x5a\x0b\x3e\x68\xc1\x4a\x02\x88\x7a\xa2\xee\x3c\xf8\xa4\x08\
+\x7f\x30\x84\x8a\x09\x33\xe0\x0e\xba\xa4\xe0\xa7\x7c\xcc\xec\x42\
+\xf8\x1b\x27\x20\x36\x71\x26\x2c\xda\x2f\x9d\x2f\xc1\x28\x95\x7a\
+\x5f\xcf\xe4\x8a\x58\x74\xc4\x3c\x84\x1a\x92\x78\x60\x83\x86\xf7\
+\xbb\x4d\xac\x4f\x11\x83\x0a\x54\xe2\xca\x67\x12\xa8\xb8\xef\x2d\
+\x34\x75\x67\x91\x26\x36\xe2\xaf\x18\x87\x60\x55\x0d\x2c\x99\xd5\
+\xb0\x75\xe7\xcc\xd8\x8e\x0b\x20\x60\x40\xff\xb8\xb4\x81\x3f\xcb\
+\xa6\x3b\x63\xd3\x3e\x66\x4c\xf8\xa3\x02\x80\x31\xc8\xa5\x80\x83\
+\xb4\x18\xc8\xfa\x68\x2e\x80\x85\x2d\xb8\x5c\x7f\x0c\x29\xff\xdc\
+\x97\x7f\xb6\x06\x50\xf8\xb2\xe2\x5b\xe8\xa3\xf7\x7d\x53\x6c\xb6\
+\x16\xfc\xfb\x88\x00\x60\x0d\x04\x00\xb6\x36\x3c\x21\xa8\x65\x6d\
+\x87\x33\xad\x67\xeb\xd7\x84\x47\x82\x55\x57\xc5\xb6\x18\x0e\xc2\
+\x6e\x00\x33\xa6\x08\xb9\x16\xb3\xea\x74\x3b\x5b\xc0\x56\x94\x28\
+\xf8\xd3\xaf\xbe\x87\xf7\xb4\xd1\xa8\xae\x6d\x80\x59\xcc\xf5\x7e\
+\x5e\xd8\xab\xe1\x1f\x6d\x5e\xac\x68\x2d\x60\xf7\x46\xbf\xa4\xe9\
+\xf8\x7d\xb3\xc6\x46\x10\x2b\x6c\xc4\xba\x64\x2d\x56\x77\x15\x31\
+\x8a\x58\x49\x81\xdc\x0e\xc2\x13\xcc\x9e\x50\x89\xbd\x26\x5a\x48\
+\x10\x78\xac\x8d\x9b\x78\xa7\xad\x88\x97\x9b\x4c\x3c\xdf\x3c\x0a\
+\xdd\xae\x46\x62\x31\x7e\x89\xc8\x6b\x66\x3f\x7f\x5f\x26\x72\x93\
+\x62\x9b\xbe\x2d\x53\x9f\xa4\xf8\x66\xa9\x20\x8f\x7c\x1a\xaf\x6e\
+\xe2\x79\x80\x86\xcc\x17\xe4\xe8\x77\x80\x5c\x97\x33\xf7\xad\xc1\
+\x67\xf7\x2a\xa1\xb5\xc7\xc0\xf2\x4e\x03\xef\xb4\x14\xf1\xcc\x46\
+\x72\x1b\x5c\x7b\x20\x17\xf3\x20\xe2\xf3\x82\xbb\xe7\x08\xb0\x59\
+\x1f\xbc\xa2\xb0\x3f\xb5\x97\xb4\xab\x33\x6a\x4c\x00\xa1\x6c\xf9\
+\x9d\x19\x8a\x65\x20\xe8\xef\x42\xf4\x07\x8c\xfe\x41\xde\xfe\x71\
+\x9b\x8f\x9d\x75\xe8\x37\x8e\x6d\x17\x73\x01\x9c\x81\x90\xff\x86\
+\x01\x58\x5b\xbb\xb6\xda\xc0\xc0\x8d\x65\x6e\x49\xeb\xcb\x05\x2d\
+\x7d\x00\xd0\x07\x04\x9b\x07\x7e\xb4\xed\xc0\xfc\x47\x67\x00\xd6\
+\x00\x10\x08\xf8\x3c\xf8\xd7\x9b\xab\x70\xd6\x7f\x3f\x83\xc8\xa8\
+\x09\x9b\xcd\xb4\x77\x52\x67\x05\x03\x17\x1d\x30\x06\x57\x2f\xae\
+\xa4\x9f\xcd\x01\x31\x00\x5e\x3c\x54\x34\x4a\x18\x1d\x34\x51\x17\
+\xe0\x88\x7f\x99\x19\x18\x58\xd9\x94\x80\x9b\x68\xbf\x65\x16\x07\
+\xd0\x63\xbe\xc9\x3b\x0b\xc0\x2a\xb2\xac\x33\x1b\xed\xfd\x79\xc4\
+\x56\x5d\xd4\x8f\x06\x6f\x0e\xef\xe6\x8b\xf8\xcd\x4b\x69\xec\x3d\
+\xc6\x0d\x1f\x51\x79\x8e\x15\xc8\xc6\x11\x0a\xaf\x1b\x33\x1a\x80\
+\x59\x8d\x5e\x9c\x3e\x07\xe8\xce\x94\xf0\xf2\x86\x1c\x6e\x79\x23\
+\x8d\x47\xd6\x5b\x70\xf3\xdc\xc3\x7e\x57\x47\x66\x01\x5a\x9b\xb3\
+\x1e\x27\x6e\xc1\x0a\x4f\x1b\x91\x16\x3c\xb5\xbe\x88\x27\x96\x25\
+\x70\xcc\xec\x6a\x24\x72\x86\x04\x82\x53\x86\x4d\xc7\x6b\x2a\x3c\
+\x38\xbc\xd2\x83\x23\xa6\xfa\x71\x19\x7d\x81\x6b\x3a\x8b\x78\x70\
+\x45\x06\x7f\x7c\x33\x81\xa6\xbc\x87\x80\xc7\xf5\xb1\x12\xe6\x72\
+\x6f\x18\x5b\x82\x43\xf9\x1e\xd0\x37\x73\x23\xca\x00\xe0\xd6\xb7\
+\x74\x17\xfa\xbb\x0d\x96\xf5\x6f\xee\xd7\x0f\xc0\x00\x76\xcd\x18\
+\x00\x4f\xd7\xd1\x3f\x9e\x0b\xd0\xdf\xaa\xdb\x75\xf6\xe5\x9f\xad\
+\xed\x2a\xb1\xb5\x03\xa9\x5d\x39\xd7\x3f\x80\x01\x58\x76\x24\xbf\
+\x33\x34\x0e\x46\xe3\x5e\x0e\x95\x1d\x28\x19\xb2\xc2\xd9\xb0\xdb\
+\xb1\xae\x56\x3f\x36\x61\xd9\x80\xc9\xae\x01\xdd\xc1\x2e\x98\x03\
+\x94\x8c\x53\x71\x3c\x2a\xdb\x24\x80\xd8\x62\x91\x01\xed\x93\x2d\
+\x1a\x03\x8e\x85\x2d\x1d\x88\x29\xf8\xb4\x12\x1e\x5c\x6b\xe1\xab\
+\x77\xaf\xc5\x55\x47\xd4\x62\x54\x5d\x8c\x3e\x59\x97\x78\x01\x5b\
+\xe9\x5c\x3f\x63\xeb\xf7\xba\x70\xe4\xf4\x30\x0e\x9b\x12\xc0\x2f\
+\x9f\x69\xc1\xf7\x5e\x24\xa6\x11\x08\xf7\xa6\xe5\x6c\x37\x6a\x20\
+\x00\x88\x75\x93\xe3\x36\x7a\xdd\x92\x8c\xa9\xe3\x2b\x0f\x77\xa1\
+\x90\x4d\xe1\x88\xd9\xf5\xf0\xfa\xfc\xc2\x4a\x8a\x74\x2f\xf0\xf5\
+\x29\xfe\x7f\xf6\xae\x3d\x46\xae\xaa\x8c\xff\xe6\xce\x9d\xf7\xec\
+\xce\xec\xce\x6e\xb7\x5b\xb6\xed\x52\xba\x65\xdb\x42\x6d\x4b\x01\
+\x41\x4c\x45\x7c\x03\x82\x31\xc1\x10\x35\x24\x04\x12\x13\xd1\x48\
+\x9a\xa8\x91\x18\x05\xf5\x0f\x0d\x88\x09\x6a\x48\x8c\x51\x21\x5a\
+\x40\x4b\x83\x36\xb5\x40\x79\x75\xcb\xab\x2f\x4a\xcb\x62\xbb\xbb\
+\xdd\x2e\xbb\x2d\xfb\x7e\xcd\xec\xcc\xdc\x3b\xf7\xe5\x77\xce\xbd\
+\x77\x76\x66\x77\x8b\x15\x85\x99\x9d\x39\xbf\xe4\x64\xe7\x79\xf7\
+\xdc\x33\xdf\xf9\x9d\xdf\xf7\x9d\xef\x9c\x93\xef\x18\x1e\x5c\xd8\
+\xe0\xc7\xb6\xad\x01\x5c\xdf\x36\x83\x6f\xec\x1c\xc2\x9b\x69\x72\
+\x05\x7c\x1f\x4c\xe6\x98\x35\x87\x14\x0a\xad\xd2\x2b\xcd\x2a\x04\
+\x16\xbf\x2d\x54\x0b\xff\x17\x17\xa0\x1a\x63\x00\xcc\x28\xce\x49\
+\x00\x56\xb1\x9c\xf7\x14\x48\x3a\xb3\xc0\x4f\x67\x1d\x5e\x2f\x18\
+\xe1\x0b\x55\xc0\xff\x22\xe1\xff\x77\xf7\x86\x8a\x64\xcd\x3f\xdb\
+\x0f\xec\x20\x4d\xea\xc0\xbc\x83\xcf\x27\x00\x2f\x27\x0a\xa9\xe8\
+\x4c\xc1\x59\xff\xdf\xe0\x2b\xea\x74\x7e\x50\xe6\xec\x09\xbe\xac\
+\xb0\xce\xc9\xd4\x14\x5f\x71\x37\x87\x00\x0c\x92\xfd\x2c\x03\x0f\
+\xf9\x6b\x5a\xf6\xd9\x79\x8c\x80\xe8\x2f\x8b\xba\x3f\x7a\xca\x8f\
+\x17\x7b\xba\x70\x7d\xab\x85\xeb\xd6\x35\x90\x1b\x90\x40\xa2\xae\
+\x16\x12\x9b\x92\x24\xa9\x9e\xd3\xed\xd3\x91\xd9\x48\xcd\x0c\xff\
+\x5b\x9f\x58\x8a\x63\xbd\x6f\xe2\x6f\xc3\x1e\x84\x43\x41\xfb\xda\
+\x6c\x14\x33\xad\xf9\x0a\xc0\x51\x7a\x70\x56\x03\xb2\xb9\x8f\x41\
+\x23\x82\xdb\xff\x31\x8e\x2b\x3b\x06\x70\x63\x7b\x14\x57\x5f\xdc\
+\x88\x95\x4b\xe3\xfc\x74\x64\x36\x31\xca\x09\x81\xfe\xa7\x42\x84\
+\xa8\x10\x23\x5c\xdc\x52\x8b\x9f\x6d\x9d\xc6\x2d\x4f\x0c\x22\x57\
+\xdb\x5c\xa4\x3c\x3e\xb0\xdf\xb0\x30\xb6\x60\x14\x8f\xfa\xae\x42\
+\x90\x1d\x42\x70\x55\x83\x57\x9a\x3f\xa5\x68\xfd\x87\x08\x22\xcf\
+\x23\xd1\x4b\xbb\xc8\xa3\x74\x41\x40\x6e\xd4\xf3\x9d\x6e\xf7\x15\
+\x37\x45\xd2\xcd\x85\x37\x2c\x3b\x7b\xd0\x9a\xe3\xdb\x7f\xd8\x81\
+\x9b\xf3\x51\x00\xf3\x3b\x83\x95\x8f\x7b\xb0\xd1\x15\x0b\xac\x8e\
+\x73\x8f\x1e\xb7\xe6\x12\x00\x58\xac\xc4\x80\x44\x65\x30\xa5\x61\
+\x38\xad\xa3\xb9\xce\xcd\x31\xf0\x60\x4d\x4b\x02\xc6\xf1\x19\xfa\
+\x7e\x63\xd1\x75\x59\xfb\x25\xfc\x16\xda\xea\xbd\x79\x97\x81\x19\
+\xea\xe0\x58\x12\x43\xd3\x2a\xac\x04\xcb\xd8\x23\xff\xdc\x2b\xa3\
+\x5b\x6f\xc6\x83\x6f\xa7\xf1\xf0\xb1\x11\xac\x0e\xbf\x83\x8d\x4b\
+\x24\x5c\xb6\x22\x8a\x4b\x56\xd4\x63\xed\x8a\x04\x22\x35\xf6\x9c\
+\x3f\x0b\x24\x5a\x3e\x92\xe9\xab\x83\x78\xec\xe4\x30\xcc\xe0\x0a\
+\xfe\x3f\xe7\xd6\xbd\xf0\x7e\x4d\xe7\x7e\xd9\x1d\xb1\xce\xcd\xf8\
+\x4b\xf1\xc7\xb1\x67\x32\x8c\x3d\xcf\x4d\xa1\x65\x5f\x37\x56\x47\
+\x73\xf8\xd8\xca\x10\xd6\xb6\xc4\xf0\x91\x95\x09\x2c\x5f\x96\x80\
+\x0e\xc9\x3e\xad\x39\xab\xe1\xd2\xd6\x3a\x5c\xe4\xef\xc2\x1b\x4a\
+\x1d\x42\xfe\x0f\xdf\x64\x17\xb2\x23\xd7\x65\x70\xd5\x80\xad\x14\
+\x3c\x45\xb3\x0d\x85\xf1\x83\x85\x2e\xc2\x42\x60\xa6\x61\x55\x61\
+\x22\x10\x1f\x19\x74\x52\x00\xd2\xbc\x66\x76\xb3\xcd\x75\xb3\x60\
+\x09\xad\x93\x10\x33\x1b\x34\x2c\xdf\x8c\x70\x2b\x9f\xee\x6b\x38\
+\xd3\x77\x56\xfe\x2f\xef\x08\x26\x93\xea\xfa\x02\xa4\xc8\x3e\xe7\
+\x9d\xfd\x2c\xec\xd3\x70\x6d\x32\xd1\x49\x1d\xe8\x98\xce\x99\x38\
+\x76\x36\x83\x4d\x17\xf8\xf9\x67\xb2\x9a\x8e\x6b\xd6\x35\xa3\xed\
+\xa5\xe3\x38\xa5\xa8\x88\xf8\x66\x13\x81\x58\x2a\xf0\xe7\x2e\x0e\
+\x60\x55\x9d\x04\x25\x67\x77\x40\x26\x5f\x5f\xef\xec\xc3\x94\x27\
+\x8c\x35\x11\x3b\xa1\x28\x10\x90\x70\x31\xf9\xdf\xb5\x44\x04\x7f\
+\x3c\x2e\xe3\xa4\x21\xe3\xc4\x88\x82\x3f\xf7\x25\x11\xd6\x4e\x63\
+\x6d\x5d\x2f\x7e\xf5\xd5\x0d\x58\x47\x64\xc0\xd4\x00\x0b\x46\x26\
+\xa2\x3e\x18\xa9\x31\x58\x4d\x2d\xfc\x5e\x78\xdd\xad\xe2\xfb\x65\
+\xaa\x81\xfd\xc6\x16\x57\x7b\x3a\x42\xf4\xcf\x57\xc7\x25\x84\x03\
+\x1e\x24\x42\x5e\x6c\x48\x44\x71\x60\xc0\x83\x67\xcf\xc6\x31\x4a\
+\x9f\xed\xe8\x9c\x01\x0e\x4e\xa0\x51\x3e\x8b\xdb\x2e\x5f\x82\xef\
+\xdd\xbc\x9e\x9f\xf0\xcb\xc4\x4a\x94\x94\x46\xd8\x60\xc7\xaf\x67\
+\x88\x80\xa2\x76\xe0\xa7\xc4\x30\x9c\xa2\xc1\x8d\x0f\x58\x0e\x19\
+\x78\x78\xdc\x80\x3d\x66\x2a\xc1\x39\xa0\x7d\x01\x29\x60\xf1\x6f\
+\x1a\x66\x69\xb7\x04\x29\x11\x01\xd8\xc7\x38\x5b\x5e\xab\x60\x56\
+\xc0\x95\xf9\x76\xa7\xd7\xf3\x89\x38\x56\x51\x04\xb6\xdc\x91\x0f\
+\x02\x3a\x53\x7a\x6e\x87\xb0\xf2\x45\x5f\x50\x01\xd8\xdf\x31\x16\
+\x20\x00\x83\x07\x01\xdd\x28\xf8\xae\x13\x29\xdc\xba\x31\xca\x47\
+\xff\x1c\x11\xc2\x92\x86\x18\xee\xbb\x7e\x05\xee\x7e\x66\x02\xef\
+\x6a\xb5\x5c\x8a\xb2\xef\x5f\xde\xe4\xc3\xf7\xae\x89\xe6\xb3\x09\
+\xd9\xd6\xd4\xd3\xd3\x49\xec\x78\xad\x0f\x88\x6c\xc2\x35\x4d\x16\
+\x7e\xf9\x85\x7a\x72\x57\x98\xa1\x4a\x44\x30\x26\x4e\xf7\x9d\xc6\
+\x13\x03\xe4\x6b\xd3\x88\x2f\x07\x42\x50\xcc\x26\x1c\x4c\x2b\x38\
+\x3d\x65\xe2\x92\xe5\xba\xe3\xeb\x5b\x98\x9c\x9a\xe6\xf7\xe1\xde\
+\x8b\x1b\x04\x74\xeb\xce\x7c\xfa\xda\x20\xcb\x4c\x24\x22\xca\xa8\
+\xa8\x91\x2d\xde\x41\xee\xbf\xae\x0e\x9b\x96\x07\xb9\x8f\xcf\x76\
+\x49\xda\x47\xca\x65\xdf\xbf\xce\x42\xa9\xbd\x00\xfe\x68\x0c\x56\
+\x34\x8e\xa1\x8c\x8e\x97\xc6\x2c\x6c\xd3\x34\x98\x3c\x6c\xef\x41\
+\x26\xa3\x20\x93\x4e\x03\x7e\xa7\xfd\xca\x6c\x5d\xb4\x3b\xc0\x73\
+\x77\x81\x85\x58\x3c\xf6\xd4\xa2\xec\xb8\x06\x2c\xa0\x58\x98\xb0\
+\xe4\xe6\x9f\x70\x02\x70\xa6\x62\xab\x8e\x00\x4c\x27\xf0\xe3\x4e\
+\xc1\xe9\x8e\x71\x99\x05\x9d\xbf\x1c\x24\xfd\xfb\x72\x01\xa4\x62\
+\x9f\xd8\x3c\x87\x4f\x5c\xf4\x3d\xe3\x1c\x2e\x80\xe5\xe4\x4c\xd0\
+\x28\x1a\xa0\xeb\xbe\xd8\xa7\xe1\x9f\x6f\x4d\xe0\xa6\x4d\x8d\x98\
+\xce\x1a\xc8\xa8\x3a\x3e\xbd\xb1\x05\x3b\x1a\x93\x78\xba\x2b\x83\
+\xd1\x8c\x1d\x40\xbb\x61\x6d\x14\x0d\x35\x32\xf9\xd1\x26\x37\xc6\
+\x48\xc0\x87\x5f\xff\x75\x2f\x0e\x4d\x04\x11\x58\x13\xc7\x0b\xbd\
+\x29\xf4\x4f\x84\xd0\xd2\x10\x26\xb5\xa0\xc3\x47\xc3\xd6\x8f\x6f\
+\x6e\x43\xe4\xf9\x11\x1c\x1c\xd7\x78\x42\x4f\x30\xe0\xc5\x17\xaf\
+\xa8\xc5\xd6\xb6\x1a\x64\x34\x7b\x1d\x02\xfb\x81\xf6\xbf\x79\x9a\
+\xa4\x43\x82\x4f\xff\x71\x17\x80\xea\x3e\x99\xca\xe5\xeb\xce\x5c\
+\x8b\x75\xab\x9a\xf1\x9b\x5b\x89\xa4\x7c\x41\xa8\xaa\x8a\xbb\x9f\
+\x1a\xa2\xfa\x49\xd8\xb2\x32\x88\xa4\x6a\x62\x86\xc8\x6b\x73\xdb\
+\x12\xfc\xfa\x46\x1d\xbf\x3b\xa6\x60\x32\x27\xf3\xce\xb1\x72\x99\
+\x0f\xdb\xae\x8e\xf1\xf5\x08\x06\xd9\x07\x9b\x2a\x3c\xd9\x35\x80\
+\xde\x31\x15\xf2\x0a\xd9\x0e\x76\x96\xa9\x21\xcc\xcd\x49\xd0\x1c\
+\x12\x90\x24\x3b\x2b\x31\x1f\x3c\x74\xdd\x04\xf7\x77\xaf\xbe\x59\
+\x00\x16\x58\xd2\xb8\x7c\x9a\x8d\xde\x5b\x15\xb1\x71\x84\xab\x00\
+\x4c\xab\x58\x01\xb0\xe7\xff\xc9\x05\x58\x58\x01\x98\xb3\xa3\xad\
+\x65\xef\xa1\x70\xef\xde\x11\x5c\x44\xd2\x7e\x5d\x6b\x82\x7c\x64\
+\x9d\x27\xd9\xac\x5a\x1a\xc5\xb7\x97\xd9\x23\x3e\xeb\xf1\x39\x32\
+\xac\x2c\xcf\xe1\x97\x50\x13\xf6\xe3\xc9\x3d\x2f\xe3\x67\xbb\xba\
+\x20\xb7\x6d\x85\xdf\xa3\xa3\x2f\x69\xe0\x27\x7b\xce\xe0\x37\xb7\
+\xb4\x52\xe7\xf7\xf1\x29\xc0\xfa\x58\x04\x0f\x7c\xb9\x15\x13\x29\
+\x8d\xe7\xe3\xd7\x86\x64\xfa\xae\x4d\x22\x6c\xba\x2a\x56\x13\x46\
+\xc7\xab\x47\xb1\xfd\xd0\x30\x02\x6d\xed\x24\x48\x72\x76\x10\x90\
+\xea\xdb\x43\x1d\x54\xf2\xd8\xf7\x6a\x07\x71\x25\x7c\xe9\x8a\x16\
+\xbe\x88\xe7\xd0\xdb\x7d\x88\xe5\x86\xf0\xdb\x03\x61\x7c\xb4\xd9\
+\x83\x4f\x5e\xda\x84\x24\x91\x17\x9b\xc1\xb8\xe9\xf2\x16\x7c\x76\
+\x83\x86\xc9\xb4\xce\xeb\xca\x48\x8b\xd5\x5f\xa1\xfa\xf8\x65\x7a\
+\x4c\x76\xf2\xe0\xe3\x2f\x61\x58\x5e\x8a\x28\xdb\x64\xd4\xd0\x17\
+\x8f\x2d\x50\xc9\x15\xc5\x0c\x3c\xb3\xc1\x43\xc7\x5d\xb0\x0f\xbf\
+\xa9\x32\x05\xa0\xd1\xc8\xa1\xa8\x1a\x2c\x59\xca\xa7\xdd\x5a\x56\
+\x65\x6c\x02\xe1\x12\x40\x90\xfc\x71\xb6\xb8\xc6\x4b\x6e\x0e\xfb\
+\x1b\xf0\xc9\x0e\x01\x9c\x3b\x08\xe8\xf5\xb0\xcf\xca\xa4\x20\xec\
+\xd7\x58\xe7\x61\x9d\xca\x26\x00\xc3\x4e\xfe\xa1\xf7\x7a\x32\x32\
+\x6e\xfb\x4b\x2f\xee\xbb\x6e\x0a\xd7\x6e\x68\x21\xb9\xee\xe7\x01\
+\xb6\xac\xb3\xca\x90\x47\xab\xc9\xca\xc2\x41\x2f\xb4\x9c\x8a\xdf\
+\x3f\xf1\x2c\x7e\xf8\xe4\xdb\xc8\xae\xfc\x28\xf9\xfc\x41\xe2\x1f\
+\x0d\x21\xd9\x83\x1d\x3d\xf4\xf7\xf1\x13\xb8\xe7\xf3\x2b\xb0\xb4\
+\x91\x9f\x52\x03\x1a\x9c\x11\x8d\xf8\x51\xe3\x6c\x6a\x62\x2f\x34\
+\xf2\x71\xa3\x7d\xe1\xe5\xc3\xf8\xce\xef\xf6\x21\xbd\x64\x13\x82\
+\xd4\x39\xdd\xce\xc8\x62\x0b\xbb\xbb\x53\xb8\x63\x30\x82\xd5\xcb\
+\x62\xa4\x4a\x6c\xc2\xe3\xea\xc3\x2b\xa3\x2e\x12\x44\x4c\x56\xd1\
+\x4f\x1f\xbf\xeb\xa9\x41\xfc\x22\x93\xc5\x67\x36\x5d\x40\xef\xf9\
+\x90\xd3\xa9\xbe\x74\xad\x86\x98\xcc\xeb\xae\x5b\xac\x83\x10\x69\
+\x85\x24\x4c\x91\xab\xf1\xa3\x3f\xec\xc6\xdf\x4e\x51\x5b\xb6\xad\
+\x84\xa5\x6b\x58\xac\x3b\x02\xe8\xce\x88\x6f\x38\x6e\x80\x1d\x23\
+\xa0\xd7\xf5\x2a\x74\x01\x58\x4a\xab\x92\xcb\x41\xb2\xbc\x8b\x4e\
+\xe2\x9f\xd7\xfd\xd1\xaf\xfc\xca\xc9\x11\x9c\x7e\x27\xcb\x3b\x66\
+\x90\x3a\xff\x91\x93\x03\x7c\x14\xe0\x9d\x66\x81\x51\x8c\xed\x7b\
+\xd1\x3f\xa9\xe0\x99\x43\xbd\x50\x94\x2c\x1f\x05\x83\x64\x21\x13\
+\xc9\x0c\xa4\x80\x65\x7f\xcf\x31\x94\x20\x5d\xa8\x47\x8d\xe0\xb6\
+\x1d\x43\xf8\xd4\xcb\xfd\xb8\x79\x63\x23\x2e\x5d\x1e\x47\x22\x1e\
+\xe1\x6b\x0e\x34\x4d\xc7\x74\x2a\x83\xa3\xdd\x67\xf0\xc8\x73\x6f\
+\x61\xdf\x59\x09\x72\xeb\x55\xf0\x47\x6a\x60\x69\xb9\xfc\xff\x64\
+\xa4\xf4\x48\x97\x81\x83\x7d\xc7\xf0\x95\x4b\x42\xb8\xa6\xbd\x11\
+\xcd\x75\x11\xbe\x03\x31\x1b\x8d\xd9\xb4\xe3\x4c\x3a\x8b\xae\xfe\
+\x51\xec\x39\x70\x12\x4f\x1c\x19\xc5\x4c\xd3\x06\x84\xe2\x8d\x74\
+\x9d\xd9\xce\xe8\x23\xd3\xee\x4f\x99\xb8\x73\x7b\x0f\xbe\xff\xf1\
+\x38\xd5\x25\x86\x48\x38\xc8\xbf\x3f\x3c\x94\xc1\x2b\x47\xbb\xe9\
+\xf7\xd6\x48\x79\x18\x18\xd4\x43\xb8\xfd\xc9\x21\xdc\x70\x70\x00\
+\x37\x6e\x48\x60\x7d\x4b\x1d\x6a\xa2\x41\x9b\x20\x09\x19\x45\xc5\
+\xd8\xe4\x0c\x0f\x56\x6e\xdf\xd7\x85\xc3\xa9\x18\x82\x17\x6e\x74\
+\x94\x86\xb6\xb8\x07\x87\x02\xf7\x80\x11\x81\x41\x2a\x89\x29\x2f\
+\xab\x84\xd2\xb7\x24\x67\x03\x7e\xf6\xa1\x37\x9e\x7f\xa6\x73\xec\
+\x5a\xe6\x63\x56\x26\x48\xc2\x4e\x8f\x03\x53\x43\xb3\xb2\x80\x46\
+\xb9\x60\xe3\x72\xea\xbd\x91\x05\x83\x58\x2c\x20\xa4\x51\xe7\xd4\
+\x47\xcf\x90\x6e\xcc\xb8\x2b\x45\x20\xc7\xea\xe1\xab\x6b\x86\x35\
+\x27\x6b\x92\x8f\x26\x2c\x71\x47\x55\x80\xd4\x04\x96\xfa\xb2\x68\
+\x0c\x1a\x90\xad\x1c\x27\x00\x96\xbd\x77\x36\x4d\xed\x1b\x5f\x86\
+\x40\xfd\x12\x2e\x37\xad\x85\xa2\xe7\xcc\x5d\xa0\x51\xc8\x9c\x99\
+\x46\x8d\x39\x8d\xa5\x81\x1c\xc2\x5e\x93\x4f\x3b\xb2\x0e\x9c\x52\
+\x74\xf4\x27\x49\xa5\x85\x1b\xe0\x6b\x68\x86\xec\x0f\xe6\x13\x7b\
+\x8a\x2f\xe3\xe1\xb3\x0d\x52\x7a\x1c\x17\x05\x67\x10\x95\x54\xe8\
+\x44\xf2\xe3\x33\x54\x0f\xc5\x0b\xdf\x92\x0b\x21\x87\x63\xbc\x1b\
+\xb0\x55\x7e\xb9\x2c\xd5\x3b\x3d\x81\x96\x40\x16\x31\x9f\x0e\x3f\
+\xec\xfd\x0e\x32\x44\x14\xa3\x33\x06\x26\x72\x21\x20\xb1\x0c\xc1\
+\x58\x3d\x9c\x9c\xd9\xca\xb3\x12\x0f\x9b\x8a\x35\xb0\x6f\xdb\xe5\
+\x9f\xf8\xf8\xea\xf8\x4b\x55\xa3\x00\xf8\xbe\xf8\x5c\x0e\xa3\x62\
+\x11\xa8\x8d\x53\xe7\x4b\xcc\xf1\xf3\x8d\x05\x47\x7f\x77\x74\x60\
+\x91\x7a\xef\xb2\x0b\x8b\x17\x38\x38\x99\x80\x0b\xa5\x29\xb3\x13\
+\xa5\x58\x34\x1d\x0d\x4d\x18\x25\xa5\x31\xa4\x39\xe9\xc0\x24\xef\
+\xa5\x7a\x19\x81\x26\xd9\x9e\x86\x22\x17\xe2\xbd\x9a\x9a\xcd\x1e\
+\x7a\x6a\x63\x50\x10\x43\x0f\x91\x47\x5e\x6d\x90\xb6\xf7\xd4\x7a\
+\xe1\x4f\xf8\x9c\xe8\xb5\xc9\x73\xfb\xcf\x55\x7f\x3f\xcb\xd6\x8d\
+\x35\xa0\x5b\x8b\xdb\xd7\xa0\xe7\xbc\x1e\x7e\xd9\xb9\x0f\x3d\x4f\
+\x5e\x81\xa0\x0f\x08\x2d\xc5\xbb\x44\x3e\x67\xb8\x2a\x72\x7c\xe1\
+\x00\xa9\x95\x88\x6c\xbb\x18\xce\x7a\x82\x4a\x85\x65\x47\x7f\xab\
+\x73\x35\xa0\x3b\x7f\x5c\xd1\x78\x3f\xc6\x6b\xbe\x8f\x20\x97\xc9\
+\xfb\x1a\x3f\x6b\x8e\x1c\xea\x3c\x71\x30\x9f\xf9\x7c\x8d\xcb\x9d\
+\x66\xf5\xf1\xac\x16\x6f\xd1\x1b\x7c\x31\xcf\x7f\x61\xa4\x45\xd7\
+\x70\xea\x71\x2e\x07\x8f\x53\x14\x4f\xba\x2f\xfc\x9f\x8c\x68\x54\
+\x54\x05\x0c\xa3\xfa\x32\x01\x51\x05\x0a\x40\x40\xe0\xfc\xc8\xdb\
+\xa8\xce\x54\x60\x9b\x00\xc4\xb1\x97\x02\x82\x00\xaa\x6e\x53\x50\
+\xcb\x12\x04\x20\x20\x50\xbd\x0a\x20\xbf\xce\x5d\xf8\x00\x02\x55\
+\x0e\xab\x0a\x13\x81\x20\x14\x80\x80\xc0\xac\x02\xa8\x36\x17\xc0\
+\xde\x62\x45\x9a\xdd\xa3\xa9\xe8\x3d\xe2\x87\x9c\xbe\xf0\x09\x9a\
+\x02\x02\x8b\x72\x94\x07\x4b\xa7\xcc\x9f\xb7\x50\x7c\xc4\x93\x54\
+\x7d\x04\x60\x0d\xbc\x61\xe0\xc4\x5b\xb0\x42\x81\xf9\x6f\xe6\x72\
+\xb8\xf7\xae\x5b\xb0\xa5\xbd\x15\xd9\x9c\x26\x8c\x47\x60\xd1\x23\
+\x14\x08\xe0\xbb\xf7\xfe\x1c\x9d\x27\xfa\x80\xfa\x66\xc0\x1f\x46\
+\x7e\x1b\xd2\xac\x0a\x2b\x7b\x19\x3d\x68\xac\x22\x05\x90\x99\x50\
+\x90\x1c\x04\xf4\xe0\x02\xef\xa9\xd8\xdc\x1c\xc0\xa7\xdb\x1b\x90\
+\x56\x54\x61\x3d\x02\x8b\x1e\x91\x48\x08\x75\x2a\xd9\xfb\x70\xb7\
+\xbd\x3b\x69\xa8\x76\x76\xe4\xcf\xb0\xcd\x11\x4b\x37\xd2\x95\x86\
+\x00\x24\x6f\x07\x24\x79\x2d\x95\x55\xf3\xde\xf3\x1a\xfc\x50\x4a\
+\x76\x1a\x0d\x2b\x02\x02\x8b\x1e\x32\xf5\x71\x96\xa0\x25\xfb\x59\
+\xb6\x96\x7d\x92\x92\x4d\x00\x49\x7a\x7c\x90\x9e\x0c\x57\x17\x01\
+\x00\x7b\x1d\xcd\xf3\x35\x2a\x4d\xf3\x5c\x84\xc2\xcd\x25\x05\x04\
+\x16\x7b\x08\x60\xe1\x3d\xc1\x58\xca\xe7\x61\x2a\x8f\x52\x19\xa9\
+\x2a\x02\xc8\xaa\xb9\x4e\x92\x3e\x7f\x42\xd0\xcf\x0e\xa1\xbb\x81\
+\x5e\x5a\x4b\x25\x2c\x4c\x45\xa0\x0a\xc0\xa2\xdb\x6c\xc4\xdf\x4f\
+\xe5\x31\x28\xb9\x0e\xc3\x34\x67\x4a\x55\x99\x92\x84\x20\x1f\xbe\
+\xe7\x4e\xe5\x0f\x0f\x7d\xff\x78\xc0\xeb\xfd\x2d\x34\xfd\x61\x7a\
+\xe9\x10\x66\xf7\x4e\x10\x10\xa8\x64\x39\x30\x0a\xcb\x7c\x8a\x1e\
+\x3d\x00\x35\xf7\xf7\x07\x7f\xfa\xcd\xd1\xcb\xda\x57\x95\x4c\xea\
+\x96\x44\x01\x6c\x5c\xd3\xca\xcb\x5b\xbd\x67\x46\x7e\xff\xd8\x9e\
+\x9d\x53\x6a\xae\x86\x94\xc0\x05\xf4\x16\x8b\x09\x88\xe4\x00\x81\
+\x0a\x04\x5f\x97\x69\xc0\xd4\x8e\xc1\x34\x1e\x83\xd7\x7f\x90\xed\
+\x9c\x7a\xd5\xa6\x76\x7e\x94\x7a\x55\x29\x00\x17\xf7\xdf\xfd\x75\
+\x5c\xb6\xb9\x7d\x9c\x64\xd0\xd3\xf4\xf4\x49\x2a\xdd\xc2\x50\x04\
+\x2a\x73\xe4\x37\x75\xe8\xb9\x6e\xe4\x32\x7b\x31\xd6\x73\x1c\xe9\
+\x31\x93\x05\x04\xb3\x4a\x69\x85\xaf\x5c\xea\x76\xb1\xf7\x44\x43\
+\x0f\x95\xed\x60\x51\x51\xe0\x26\x2a\x9b\x60\xaf\x70\x15\x10\xa8\
+\x8c\xd1\xdf\xd0\x46\x90\x9d\x7a\x1d\x93\xea\xeb\x50\x92\x19\x44\
+\x1a\xec\x65\xd0\x25\x86\x5c\x26\x2d\xc4\x68\xf0\x38\x95\x33\x44\
+\x95\x67\x98\x38\xa0\x92\x10\x86\x23\x50\x31\xea\x3f\x97\xee\x83\
+\x3e\xdd\x89\x54\x72\x92\x2f\x82\x29\x71\x06\x60\x59\xb8\x00\x73\
+\xc0\xa6\x45\x46\xa9\xec\xa1\x06\x1a\x11\x91\x00\x81\xca\x52\x00\
+\x6c\x9f\x37\x28\x4e\x1a\x70\xd9\x58\xb7\x54\x5e\xed\xc4\xb6\x49\
+\x55\x86\xa9\x4c\x89\x58\xa0\x40\xe5\xc9\x80\xf2\x33\x6a\xb9\xbc\
+\xda\x88\xda\x47\xcd\x58\x50\xd3\x6c\xb3\x68\x91\x08\x24\x50\x11\
+\x28\x67\x3b\x2e\x39\x01\xb0\x53\x64\x90\x51\xec\x27\x2c\x4d\x32\
+\x93\x86\xc7\xd4\x2c\x78\x84\x02\x10\xa8\x60\x30\xbb\xd7\x14\xe7\
+\x6c\xc0\x2a\x26\x80\xab\x36\xac\x41\x88\x11\x64\xd0\x4f\x0a\xc0\
+\x8b\xf1\x6e\x99\x9f\x1b\x28\x20\x50\xc9\xd8\x48\x76\x1f\x4f\xb4\
+\x20\x11\xab\xa9\x6e\x02\x78\x70\xdb\x6d\x45\xcf\x9f\x7f\x6e\x2f\
+\xfa\xfa\x07\x84\x85\x08\x54\x34\x1e\xfe\xc1\x1d\xb8\xf2\xaa\xab\
+\x4b\x5e\x0f\xa9\xdc\x1a\x46\x21\x69\x24\xc4\xbf\x40\xa5\x83\xbb\
+\xbe\x65\x00\x49\xfc\x14\x02\x02\xd5\x0b\x41\x00\x02\x02\x82\x00\
+\x04\x04\x04\x04\x01\x08\x08\x08\x08\x02\x10\x10\x10\x10\x04\x20\
+\x20\x20\x50\xe1\x90\xcb\xb4\x5e\x92\xd8\x13\x50\xa0\x52\x50\xce\
+\x76\x5c\x96\x0a\x40\xd3\xb4\x7e\x55\x55\xd3\xc2\x74\x04\x16\x33\
+\xd8\xf9\x37\x64\xcb\x66\x7f\x7f\xbf\xaa\x28\x8a\x9b\xde\x6a\x09\
+\x02\x78\xaf\x0a\x49\x12\x26\x26\x26\x76\x8f\x8e\x8e\xbe\xe9\x36\
+\xa2\x80\xc0\x62\x84\xd7\xeb\xc5\xcc\xcc\x8c\xbe\x6b\xd7\xae\x77\
+\x87\x86\x86\xd8\x66\x37\x6c\xff\x7f\xa3\x9c\x48\x40\x2a\xc7\x46\
+\x7b\xed\xb5\xd7\x3a\x0e\x1f\x3e\xfc\xcc\xd4\xd4\xd4\x18\x23\x00\
+\x49\x12\xa1\x0a\x81\xc5\x35\xf2\x33\x3b\x26\x15\x6b\x1e\x39\x72\
+\x64\x72\xe7\xce\x9d\xfd\x34\xa0\xb1\xad\xbf\xa7\xc1\xf6\x04\x28\
+\x23\x02\x28\xbb\x18\x80\x2c\xcb\xd8\xbd\x7b\x37\x6b\xac\xe7\xa8\
+\xe3\xb7\xad\x5f\xbf\xfe\x73\xf1\x78\xbc\xce\xe7\xf3\x49\xac\x51\
+\x45\x5c\x40\xa0\x9c\x3b\xbe\xe3\xc2\x5a\x4c\xf2\x1f\x3d\x7a\x74\
+\x6a\xc7\x8e\x1d\x03\x27\x4e\x9c\x38\xab\xeb\x7a\x3f\xbd\x35\x48\
+\x85\x6d\x01\x6e\x0a\x02\x78\x6f\xbf\x49\xdb\xbf\x7f\x7f\x57\x2a\
+\x95\xda\xbe\x65\xcb\x96\x99\xcd\x9b\x37\x6f\x6d\x6f\x6f\x6f\x6d\
+\x6c\x6c\x0c\x12\x11\x08\x45\x20\x50\x76\x60\x03\x93\x61\x18\xc8\
+\x64\x32\xd6\xa9\x53\xa7\x52\x1d\x1d\x1d\xa3\x7b\xf7\xee\x1d\xee\
+\xec\xec\x3c\x93\xcd\x66\x4f\xc1\xde\xf7\xf2\xac\x20\x80\xf3\x83\
+\x39\x3e\x3e\x3e\xfd\xea\xab\xaf\x1e\xa5\xc6\xf3\x27\x93\x49\xab\
+\xaf\xaf\x6f\x73\x5d\x5d\x5d\x63\x38\x1c\x8e\x10\x09\xb0\x7a\xbb\
+\xc1\x01\x11\x24\x10\x28\x0b\x02\xa0\x51\x5f\x27\x5b\xd5\x7a\x7b\
+\x7b\x93\x07\x0e\x1c\x18\x24\x05\xd0\x4f\xf6\xdb\x4b\x6f\x9f\x74\
+\x08\x80\x29\x5b\x55\xb8\x00\xe7\xd1\x9e\x4c\x49\x51\xe3\x4d\x10\
+\x09\x1c\xa4\x32\x44\xcf\x5f\xa6\xd2\x4a\x65\x09\x15\xb6\x88\xda\
+\x07\x91\xc7\x20\x50\x7e\x76\x6b\x3a\x9d\x7c\x8a\xca\xbb\x54\xd8\
+\xe8\xdf\xeb\x3c\x4e\x97\xd3\xe8\x5f\xce\x04\x80\x82\x86\x1c\x87\
+\xbd\x61\x68\xd6\x79\xcc\xce\x14\x8c\x50\xf1\x0b\x02\x10\x28\x43\
+\x02\x60\x51\x7e\xc5\x21\x80\x61\xa7\xe3\x8f\x39\x9d\x5f\x47\x99\
+\x4d\x03\xca\x65\xde\xa0\x2e\x09\x4c\x50\xc9\x38\x0d\x1a\x72\x46\
+\x7f\xaf\x90\xff\x02\x65\x6a\xb3\x2e\x09\x64\x9c\xa2\x3a\xaf\x97\
+\x5d\x04\xfb\xdf\x02\x0c\x00\x0b\x53\x81\x73\x51\x45\x40\xdb\x00\
\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
"
@@ -2044,6 +3444,10 @@
\x00\x62\
\x00\x75\x00\x74\x00\x74\x00\x6f\x00\x6e\x00\x73\
\x00\x03\
+\x00\x00\x78\xc3\
+\x00\x72\
+\x00\x65\x00\x73\
+\x00\x03\
\x00\x00\x70\x37\
\x00\x69\
\x00\x6d\x00\x67\
@@ -2123,36 +3527,44 @@
\x00\x62\
\x00\x75\x00\x74\x00\x74\x00\x6f\x00\x6e\x00\x5f\x00\x72\x00\x65\x00\x73\x00\x74\x00\x6f\x00\x72\x00\x65\x00\x2e\x00\x70\x00\x6e\
\x00\x67\
-\x00\x0c\
-\x09\xf7\x1c\xa7\
+\x00\x05\
+\x00\x6f\xa6\x53\
+\x00\x69\
+\x00\x63\x00\x6f\x00\x6e\x00\x73\
+\x00\x14\
+\x0d\x75\xab\xe7\
\x00\x75\
-\x00\x74\x00\x6c\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x00\x74\x00\x6c\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x40\x00\x32\x00\x35\x00\x36\x00\x78\x00\x32\x00\x35\x00\x36\x00\x2e\
+\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x15\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\
\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
\x00\x00\x00\x22\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\
-\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x10\x00\x00\x00\x05\
-\x00\x00\x00\xb8\x00\x00\x00\x00\x00\x01\x00\x00\x12\x2c\
-\x00\x00\x02\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x57\x58\
-\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x48\xda\
-\x00\x00\x03\x04\x00\x00\x00\x00\x00\x01\x00\x00\x62\xfc\
-\x00\x00\x02\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x50\x67\
-\x00\x00\x01\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x33\x9e\
-\x00\x00\x01\x64\x00\x00\x00\x00\x00\x01\x00\x00\x25\x54\
-\x00\x00\x00\x68\x00\x00\x00\x00\x00\x01\x00\x00\x04\xfc\
-\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x18\xef\
-\x00\x00\x01\x88\x00\x00\x00\x00\x00\x01\x00\x00\x2c\x15\
-\x00\x00\x01\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x93\
-\x00\x00\x01\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x5d\
-\x00\x00\x02\xdc\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x00\
-\x00\x00\x00\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x02\x30\x00\x00\x00\x00\x00\x01\x00\x00\x41\xe9\
-\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x00\x0a\xa0\
-\x00\x00\x00\x22\x00\x02\x00\x00\x00\x01\x00\x00\x00\x16\
-\x00\x00\x03\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x69\xb1\
+\x00\x00\x00\x2e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x05\
+\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x10\x00\x00\x00\x06\
+\x00\x00\x00\xc4\x00\x00\x00\x00\x00\x01\x00\x00\x12\x2c\
+\x00\x00\x02\xc2\x00\x00\x00\x00\x00\x01\x00\x00\x57\x58\
+\x00\x00\x02\x76\x00\x00\x00\x00\x00\x01\x00\x00\x48\xda\
+\x00\x00\x03\x10\x00\x00\x00\x00\x00\x01\x00\x00\x62\xfc\
+\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x00\x50\x67\
+\x00\x00\x01\xca\x00\x00\x00\x00\x00\x01\x00\x00\x33\x9e\
+\x00\x00\x01\x70\x00\x00\x00\x00\x00\x01\x00\x00\x25\x54\
+\x00\x00\x00\x74\x00\x00\x00\x00\x00\x01\x00\x00\x04\xfc\
+\x00\x00\x00\xfc\x00\x00\x00\x00\x00\x01\x00\x00\x18\xef\
+\x00\x00\x01\x94\x00\x00\x00\x00\x00\x01\x00\x00\x2c\x15\
+\x00\x00\x01\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x1e\x93\
+\x00\x00\x02\x06\x00\x00\x00\x00\x00\x01\x00\x00\x3a\x5d\
+\x00\x00\x02\xe8\x00\x00\x00\x00\x00\x01\x00\x00\x5e\x00\
+\x00\x00\x00\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x02\x3c\x00\x00\x00\x00\x00\x01\x00\x00\x41\xe9\
+\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x01\x00\x00\x0a\xa0\
+\x00\x00\x00\x22\x00\x02\x00\x00\x00\x01\x00\x00\x00\x17\
+\x00\x00\x00\x2e\x00\x02\x00\x00\x00\x01\x00\x00\x00\x18\
+\x00\x00\x03\x3a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x19\
+\x00\x00\x03\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x69\xb1\
"
def qInitResources():
diff --git a/qthostsui.py b/gui/util_ui.py
similarity index 60%
rename from qthostsui.py
rename to gui/util_ui.py
index a393ff8..bafe7e2 100644
--- a/qthostsui.py
+++ b/gui/util_ui.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
-# Form implementation generated from reading ui file 'qthostsui.ui'
+# Form implementation generated from reading ui file '../pyqt\util_ui.ui'
#
-# Created: Sat Nov 30 15:06:56 2013
-# by: PyQt4 UI code generator 4.10.2
+# Created: Wed Jan 22 13:03:09 2014
+# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!
@@ -23,26 +23,26 @@ def _translate(context, text, disambig):
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
-class Ui_HostsUtlMain(object):
- def setupUi(self, HostsUtlMain):
- HostsUtlMain.setObjectName(_fromUtf8("HostsUtlMain"))
- HostsUtlMain.setEnabled(True)
- HostsUtlMain.resize(640, 420)
+class Ui_Util(object):
+ def setupUi(self, Util):
+ Util.setObjectName(_fromUtf8("Util"))
+ Util.setEnabled(True)
+ Util.resize(640, 420)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(HostsUtlMain.sizePolicy().hasHeightForWidth())
- HostsUtlMain.setSizePolicy(sizePolicy)
- HostsUtlMain.setMinimumSize(QtCore.QSize(640, 420))
- HostsUtlMain.setMaximumSize(QtCore.QSize(640, 420))
- HostsUtlMain.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
+ sizePolicy.setHeightForWidth(Util.sizePolicy().hasHeightForWidth())
+ Util.setSizePolicy(sizePolicy)
+ Util.setMinimumSize(QtCore.QSize(640, 420))
+ Util.setMaximumSize(QtCore.QSize(640, 420))
+ Util.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
icon = QtGui.QIcon()
- icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icon/img/utl_icon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- HostsUtlMain.setWindowIcon(icon)
- HostsUtlMain.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
- HostsUtlMain.setSizeGripEnabled(False)
- HostsUtlMain.setModal(False)
- self.mainFrame = QtGui.QFrame(HostsUtlMain)
+ icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/icon/res/img/icons/utl_icon@256x256.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ Util.setWindowIcon(icon)
+ Util.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
+ Util.setSizeGripEnabled(False)
+ Util.setModal(False)
+ self.mainFrame = QtGui.QFrame(Util)
self.mainFrame.setGeometry(QtCore.QRect(0, 40, 640, 351))
self.mainFrame.setFrameShape(QtGui.QFrame.StyledPanel)
self.mainFrame.setFrameShadow(QtGui.QFrame.Raised)
@@ -164,8 +164,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonBackup.setGeometry(QtCore.QRect(0, 10, 48, 48))
self.ButtonBackup.setText(_fromUtf8(""))
icon1 = QtGui.QIcon()
- icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_backup.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_backup_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_backup.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_backup_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonBackup.setIcon(icon1)
self.ButtonBackup.setIconSize(QtCore.QSize(32, 32))
self.ButtonBackup.setObjectName(_fromUtf8("ButtonBackup"))
@@ -173,8 +173,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonUpdate.setGeometry(QtCore.QRect(60, 70, 48, 48))
self.ButtonUpdate.setText(_fromUtf8(""))
icon2 = QtGui.QIcon()
- icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_download.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_download_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_download.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_download_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonUpdate.setIcon(icon2)
self.ButtonUpdate.setIconSize(QtCore.QSize(32, 32))
self.ButtonUpdate.setObjectName(_fromUtf8("ButtonUpdate"))
@@ -182,8 +182,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonRestore.setGeometry(QtCore.QRect(60, 10, 48, 48))
self.ButtonRestore.setText(_fromUtf8(""))
icon3 = QtGui.QIcon()
- icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_restore.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_restore_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_restore.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_restore_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonRestore.setIcon(icon3)
self.ButtonRestore.setIconSize(QtCore.QSize(32, 32))
self.ButtonRestore.setObjectName(_fromUtf8("ButtonRestore"))
@@ -191,8 +191,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonApply.setGeometry(QtCore.QRect(0, 220, 48, 48))
self.ButtonApply.setText(_fromUtf8(""))
icon4 = QtGui.QIcon()
- icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_apply.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_apply_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_apply.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_apply_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonApply.setIcon(icon4)
self.ButtonApply.setIconSize(QtCore.QSize(32, 32))
self.ButtonApply.setObjectName(_fromUtf8("ButtonApply"))
@@ -200,8 +200,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonExit.setGeometry(QtCore.QRect(60, 220, 48, 48))
self.ButtonExit.setText(_fromUtf8(""))
icon5 = QtGui.QIcon()
- icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_exit_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_exit.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_exit_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonExit.setIcon(icon5)
self.ButtonExit.setIconSize(QtCore.QSize(32, 32))
self.ButtonExit.setObjectName(_fromUtf8("ButtonExit"))
@@ -209,8 +209,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonCheck.setGeometry(QtCore.QRect(0, 70, 48, 48))
self.ButtonCheck.setText(_fromUtf8(""))
icon6 = QtGui.QIcon()
- icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_update.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_update_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_update.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_update_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonCheck.setIcon(icon6)
self.ButtonCheck.setIconSize(QtCore.QSize(32, 32))
self.ButtonCheck.setObjectName(_fromUtf8("ButtonCheck"))
@@ -218,8 +218,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonANSI.setGeometry(QtCore.QRect(0, 160, 48, 48))
self.ButtonANSI.setText(_fromUtf8(""))
icon7 = QtGui.QIcon()
- icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_ansi.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_ansi_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_ansi.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_ansi_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonANSI.setIcon(icon7)
self.ButtonANSI.setIconSize(QtCore.QSize(32, 32))
self.ButtonANSI.setObjectName(_fromUtf8("ButtonANSI"))
@@ -227,8 +227,8 @@ def setupUi(self, HostsUtlMain):
self.ButtonUTF.setGeometry(QtCore.QRect(60, 160, 48, 48))
self.ButtonUTF.setText(_fromUtf8(""))
icon8 = QtGui.QIcon()
- icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_utf8.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/img/buttons/button_utf8_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
+ icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_utf8.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/buttons/res/img/buttons/button_utf8_disabled.png")), QtGui.QIcon.Disabled, QtGui.QIcon.Off)
self.ButtonUTF.setIcon(icon8)
self.ButtonUTF.setIconSize(QtCore.QSize(32, 32))
self.ButtonUTF.setObjectName(_fromUtf8("ButtonUTF"))
@@ -241,93 +241,97 @@ def setupUi(self, HostsUtlMain):
self.Prog.setTextVisible(True)
self.Prog.setInvertedAppearance(False)
self.Prog.setObjectName(_fromUtf8("Prog"))
- self.TitleLabel = QtGui.QLabel(HostsUtlMain)
+ self.TitleLabel = QtGui.QLabel(Util)
self.TitleLabel.setGeometry(QtCore.QRect(20, 20, 250, 25))
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
- self.Copyright = QtGui.QLabel(HostsUtlMain)
+ self.Copyright = QtGui.QLabel(Util)
self.Copyright.setGeometry(QtCore.QRect(330, 390, 300, 16))
self.Copyright.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.Copyright.setObjectName(_fromUtf8("Copyright"))
- self.label = QtGui.QLabel(HostsUtlMain)
+ self.label = QtGui.QLabel(Util)
self.label.setGeometry(QtCore.QRect(10, 390, 150, 16))
self.label.setObjectName(_fromUtf8("label"))
+ self.VersionLabel = QtGui.QLabel(Util)
+ self.VersionLabel.setGeometry(QtCore.QRect(380, 20, 250, 25))
+ self.VersionLabel.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.VersionLabel.setObjectName(_fromUtf8("VersionLabel"))
self.labelIP.setBuddy(self.SelectMirror)
self.labelMirror.setBuddy(self.SelectIP)
- self.retranslateUi(HostsUtlMain)
- QtCore.QObject.connect(self.ButtonExit, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.close)
- QtCore.QObject.connect(self.SelectIP, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), HostsUtlMain.on_IPVersion_changed)
- QtCore.QObject.connect(self.SelectMirror, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), HostsUtlMain.on_Mirror_changed)
- QtCore.QObject.connect(self.Functionlist, QtCore.SIGNAL(_fromUtf8("itemChanged(QListWidgetItem*)")), HostsUtlMain.on_Selection_changed)
- QtCore.QObject.connect(self.ButtonApply, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_MakeHosts_clicked)
- QtCore.QObject.connect(self.ButtonBackup, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_Backup_clicked)
- QtCore.QObject.connect(self.ButtonRestore, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_Restore_clicked)
- QtCore.QObject.connect(self.ButtonCheck, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_CheckUpdate_clicked)
- QtCore.QObject.connect(self.ButtonUpdate, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_FetchUpdate_clicked)
- QtCore.QObject.connect(self.SelectLang, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(QString)")), HostsUtlMain.on_Lang_changed)
- QtCore.QObject.connect(self.ButtonANSI, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_MakeANSI_clicked)
- QtCore.QObject.connect(self.ButtonUTF, QtCore.SIGNAL(_fromUtf8("clicked()")), HostsUtlMain.on_MakeUTF8_clicked)
- QtCore.QObject.connect(self.Copyright, QtCore.SIGNAL(_fromUtf8("linkActivated(QString)")), HostsUtlMain.on_LinkActivated)
- QtCore.QMetaObject.connectSlotsByName(HostsUtlMain)
- HostsUtlMain.setTabOrder(self.SelectMirror, self.SelectIP)
- HostsUtlMain.setTabOrder(self.SelectIP, self.Functionlist)
- HostsUtlMain.setTabOrder(self.Functionlist, self.ButtonApply)
- HostsUtlMain.setTabOrder(self.ButtonApply, self.ButtonExit)
- HostsUtlMain.setTabOrder(self.ButtonExit, self.ButtonCheck)
- HostsUtlMain.setTabOrder(self.ButtonCheck, self.ButtonUpdate)
- HostsUtlMain.setTabOrder(self.ButtonUpdate, self.ButtonBackup)
- HostsUtlMain.setTabOrder(self.ButtonBackup, self.ButtonRestore)
- HostsUtlMain.setTabOrder(self.ButtonRestore, self.ButtonANSI)
- HostsUtlMain.setTabOrder(self.ButtonANSI, self.ButtonUTF)
- HostsUtlMain.setTabOrder(self.ButtonUTF, self.SelectLang)
+ self.retranslateUi(Util)
+ QtCore.QObject.connect(self.ButtonExit, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.close)
+ QtCore.QObject.connect(self.SelectIP, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), Util.on_IPVersion_changed)
+ QtCore.QObject.connect(self.SelectMirror, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(int)")), Util.on_Mirror_changed)
+ QtCore.QObject.connect(self.Functionlist, QtCore.SIGNAL(_fromUtf8("itemChanged(QListWidgetItem*)")), Util.on_Selection_changed)
+ QtCore.QObject.connect(self.ButtonApply, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_MakeHosts_clicked)
+ QtCore.QObject.connect(self.ButtonBackup, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_Backup_clicked)
+ QtCore.QObject.connect(self.ButtonRestore, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_Restore_clicked)
+ QtCore.QObject.connect(self.ButtonCheck, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_CheckUpdate_clicked)
+ QtCore.QObject.connect(self.ButtonUpdate, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_FetchUpdate_clicked)
+ QtCore.QObject.connect(self.SelectLang, QtCore.SIGNAL(_fromUtf8("currentIndexChanged(QString)")), Util.on_Lang_changed)
+ QtCore.QObject.connect(self.ButtonANSI, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_MakeANSI_clicked)
+ QtCore.QObject.connect(self.ButtonUTF, QtCore.SIGNAL(_fromUtf8("clicked()")), Util.on_MakeUTF8_clicked)
+ QtCore.QObject.connect(self.Copyright, QtCore.SIGNAL(_fromUtf8("linkActivated(QString)")), Util.on_LinkActivated)
+ QtCore.QMetaObject.connectSlotsByName(Util)
+ Util.setTabOrder(self.SelectMirror, self.SelectIP)
+ Util.setTabOrder(self.SelectIP, self.Functionlist)
+ Util.setTabOrder(self.Functionlist, self.ButtonApply)
+ Util.setTabOrder(self.ButtonApply, self.ButtonExit)
+ Util.setTabOrder(self.ButtonExit, self.ButtonCheck)
+ Util.setTabOrder(self.ButtonCheck, self.ButtonUpdate)
+ Util.setTabOrder(self.ButtonUpdate, self.ButtonBackup)
+ Util.setTabOrder(self.ButtonBackup, self.ButtonRestore)
+ Util.setTabOrder(self.ButtonRestore, self.ButtonANSI)
+ Util.setTabOrder(self.ButtonANSI, self.ButtonUTF)
+ Util.setTabOrder(self.ButtonUTF, self.SelectLang)
- def retranslateUi(self, HostsUtlMain):
- HostsUtlMain.setWindowTitle(_translate("HostsUtlMain", "Hosts Setup Utility", None))
- self.ConfigBox.setTitle(_translate("HostsUtlMain", "Config", None))
- self.labelIP.setText(_translate("HostsUtlMain", "Server", None))
- self.labelMirror.setText(_translate("HostsUtlMain", "IP Version", None))
- self.StatusBox.setTitle(_translate("HostsUtlMain", "Status", None))
- self.labelConn.setText(_translate("HostsUtlMain", "Connection", None))
- self.labelConnStat.setText(_translate("HostsUtlMain", "N/A", None))
- self.labelOS.setText(_translate("HostsUtlMain", "OS", None))
- self.labelOSStat.setText(_translate("HostsUtlMain", "N/A", None))
- self.InfoBox.setTitle(_translate("HostsUtlMain", "Hosts Info", None))
- self.labelVersion.setText(_translate("HostsUtlMain", "Version", None))
- self.labelVersionData.setText(_translate("HostsUtlMain", "N/A", None))
- self.labelRelease.setText(_translate("HostsUtlMain", "Release", None))
- self.labelReleaseData.setText(_translate("HostsUtlMain", "N/A", None))
- self.labelLatest.setText(_translate("HostsUtlMain", "Latest", None))
- self.labelLatestData.setText(_translate("HostsUtlMain", "N/A", None))
- self.FunctionsBox.setTitle(_translate("HostsUtlMain", "Functions", None))
- self.ButtonBackup.setToolTip(_translate("HostsUtlMain", "Backup hosts", None))
- self.ButtonBackup.setWhatsThis(_translate("HostsUtlMain", "Backup the hosts file of current system.", None))
- self.ButtonUpdate.setToolTip(_translate("HostsUtlMain", "Download data file", None))
- self.ButtonUpdate.setWhatsThis(_translate("HostsUtlMain", "Download the latest data file.", None))
- self.ButtonRestore.setToolTip(_translate("HostsUtlMain", "Restore backup", None))
- self.ButtonRestore.setWhatsThis(_translate("HostsUtlMain", "Restore a previous backup of hosts file.", None))
- self.ButtonApply.setToolTip(_translate("HostsUtlMain", "Apply hosts", None))
- self.ButtonApply.setWhatsThis(_translate("HostsUtlMain", "Apply changes to the hosts file.", None))
- self.ButtonExit.setToolTip(_translate("HostsUtlMain", "Exit", None))
- self.ButtonExit.setWhatsThis(_translate("HostsUtlMain", "Close this tool.", None))
- self.ButtonCheck.setToolTip(_translate("HostsUtlMain", "Check update / Refresh", None))
- self.ButtonCheck.setWhatsThis(_translate("HostsUtlMain", "Check the latest version of hosts data file.", None))
- self.ButtonANSI.setToolTip(_translate("HostsUtlMain", "Save with ANSI", None))
- self.ButtonANSI.setWhatsThis(_translate("HostsUtlMain", "Export to hosts file encoding by ANSI.", None))
- self.ButtonUTF.setToolTip(_translate("HostsUtlMain", "Save with UTF-8", None))
- self.ButtonUTF.setWhatsThis(_translate("HostsUtlMain", "Export to hosts file encoding by UTF-8.", None))
- self.TitleLabel.setText(_translate("HostsUtlMain", "Hosts Setup Utility", None))
- self.Copyright.setText(_translate("HostsUtlMain", "Copyleft (C) 2011-2014 huhamhire-hosts", None))
- self.label.setText(_translate("HostsUtlMain", "Powered by PyQT", None))
+ def retranslateUi(self, Util):
+ Util.setWindowTitle(_translate("Util", "Hosts Setup Utility", None))
+ self.ConfigBox.setTitle(_translate("Util", "Config", None))
+ self.labelIP.setText(_translate("Util", "Server", None))
+ self.labelMirror.setText(_translate("Util", "IP Version", None))
+ self.StatusBox.setTitle(_translate("Util", "Status", None))
+ self.labelConn.setText(_translate("Util", "Connection", None))
+ self.labelConnStat.setText(_translate("Util", "N/A", None))
+ self.labelOS.setText(_translate("Util", "OS", None))
+ self.labelOSStat.setText(_translate("Util", "N/A", None))
+ self.InfoBox.setTitle(_translate("Util", "Hosts Info", None))
+ self.labelVersion.setText(_translate("Util", "Version", None))
+ self.labelVersionData.setText(_translate("Util", "N/A", None))
+ self.labelRelease.setText(_translate("Util", "Release", None))
+ self.labelReleaseData.setText(_translate("Util", "N/A", None))
+ self.labelLatest.setText(_translate("Util", "Latest", None))
+ self.labelLatestData.setText(_translate("Util", "N/A", None))
+ self.FunctionsBox.setTitle(_translate("Util", "Functions", None))
+ self.ButtonBackup.setToolTip(_translate("Util", "Backup hosts", None))
+ self.ButtonBackup.setWhatsThis(_translate("Util", "Backup the hosts file of current system.", None))
+ self.ButtonUpdate.setToolTip(_translate("Util", "Download data file", None))
+ self.ButtonUpdate.setWhatsThis(_translate("Util", "Download the latest data file.", None))
+ self.ButtonRestore.setToolTip(_translate("Util", "Restore backup", None))
+ self.ButtonRestore.setWhatsThis(_translate("Util", "Restore a previous backup of hosts file.", None))
+ self.ButtonApply.setToolTip(_translate("Util", "Apply hosts", None))
+ self.ButtonApply.setWhatsThis(_translate("Util", "Apply changes to the hosts file.", None))
+ self.ButtonExit.setToolTip(_translate("Util", "Exit", None))
+ self.ButtonExit.setWhatsThis(_translate("Util", "Close this tool.", None))
+ self.ButtonCheck.setToolTip(_translate("Util", "Check update / Refresh", None))
+ self.ButtonCheck.setWhatsThis(_translate("Util", "Check the latest version of hosts data file.", None))
+ self.ButtonANSI.setToolTip(_translate("Util", "Save with ANSI", None))
+ self.ButtonANSI.setWhatsThis(_translate("Util", "Export to hosts file encoding by ANSI.", None))
+ self.ButtonUTF.setToolTip(_translate("Util", "Save with UTF-8", None))
+ self.ButtonUTF.setWhatsThis(_translate("Util", "Export to hosts file encoding by UTF-8.", None))
+ self.TitleLabel.setText(_translate("Util", "Hosts Setup Utility", None))
+ self.Copyright.setText(_translate("Util", "Copyleft (C) 2011-2014 huhamhire-hosts", None))
+ self.label.setText(_translate("Util", "Powered by PyQT", None))
-import qthosts_rc
+import util_rc
import style_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
- HostsUtlMain = QtGui.QDialog()
- ui = Ui_HostsUtlMain()
- ui.setupUi(HostsUtlMain)
- HostsUtlMain.show()
+ Util = QtGui.QDialog()
+ ui = Ui_Util()
+ ui.setupUi(Util)
+ Util.show()
sys.exit(app.exec_())
diff --git a/hoststool.py b/hoststool.py
new file mode 100644
index 0000000..505b8d3
--- /dev/null
+++ b/hoststool.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# hoststool.py : Launch the utility.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+
+from optparse import OptionParser
+
+import gui
+import tui
+
+from __version__ import __version__
+
+import sys
+sys.path.append("..")
+from util import CommonUtil
+
+
+class UtilLauncher(object):
+ """
+ HostsUtil class is the entrance to launch `Hosts Setup Utility`. This
+ class contains methods for user to decide whether to start a session in
+ Graphical User Interface (GUI) mode or in Text-based User Interface (TUI)
+ mode.
+
+ Usage can be printed to screen via terminal by using ``-h`` or ``--help``
+ argument. The help message would be like this::
+
+ Usage: hoststool.py [-g] [-t] [-h] [--version]
+
+ Options:
+ --version show program's version number and exit
+ -h, --help show this help message and exit
+ -g, --graphicui launch in GUI(QT) mode
+ -t, --textui launch in TUI(Curses) mode
+
+ .. seealso:: :ref:`Get Started `.
+
+ .. note:: All methods from this class are declared as `classmethod`.
+ """
+
+ @classmethod
+ def launch(cls):
+ """
+ Launch `Hosts Setup Utility`.
+
+ .. note:: This is a `classmethod`.
+ """
+ options, args = cls.set_commands()
+ if options.tui:
+ cls.launch_tui()
+ elif options.gui:
+ cls.launch_gui()
+
+ @classmethod
+ def set_commands(cls):
+ """
+ Set the command options and arguments to launch `Hosts Setup Utility`.
+
+ .. note:: This is a `classmethod`.
+
+ :return: :meth:`hoststool.UtilLauncher.set_commands` returns two
+ values:
+
+ (:attr:`options`, :attr:`args`)
+
+ * options(`optparse.Values`): An instance of
+ :class:`optparse.Values` containing values for all of your
+ options—e.g. if --file takes a single string argument, then
+ options.file will be the filename supplied by the user, or None
+ if the user did not supply that option args, the list of
+ positional arguments leftover after parsing options.
+ * args(`list`): Positional arguments leftover after parsing
+ options.
+
+ .. seealso:: `OptionParser
+ `_.
+
+ :rtype: :class:`optparse.Values`, list
+ """
+ usage = "usage: %prog [-g] [-t] [-h] [--version]"
+ version = "Hosts Setup Utility v" + __version__
+ parser = OptionParser(usage=usage, version=version)
+ parser.add_option("-g", "--graphicui", dest="gui",
+ default=True, action="store_true",
+ help="launch in GUI(QT) mode")
+ parser.add_option("-t", "--textui", dest="tui",
+ default=False, action="store_true",
+ help="launch in TUI(Curses) mode ")
+ return parser.parse_args()
+
+ @classmethod
+ def launch_gui(cls):
+ """
+ Start a Graphical User Interface (GUI) session of
+ `Hosts Setup Utility`.
+
+ .. note:: This is a `classmethod`.
+ """
+ main = gui.HostsUtil()
+ main.start()
+
+ @classmethod
+ def launch_tui(cls):
+ """
+ Start a Text-based User Interface (TUI) session of
+ `Hosts Setup Utility`.
+
+ .. note:: This is a `classmethod`.
+ """
+
+ # Set code page for Windows
+ system = CommonUtil.check_platform()[0]
+ cp = "437"
+ if system == "Windows":
+ chcp = os.popen("chcp")
+ cp = chcp.read().split()[-1]
+ chcp.close()
+ os.popen("chcp 437")
+
+ main = tui.HostsUtil()
+ main.start()
+
+ # Restore the default code page for Windows
+ if system == "Windows":
+ os.popen("chcp " + cp)
+
+if __name__ == "__main__":
+ UtilLauncher.launch()
\ No newline at end of file
diff --git a/hostsutl.pro b/hostsutl.pro
deleted file mode 100644
index 11f4793..0000000
--- a/hostsutl.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-SOURCES = hostsutl.py \
- qthostsui.py
-TRANSLATIONS = lang/de_DE.ts \
- lang/en_US.ts \
- lang/zh_CN.ts \
- lang/zh_TW.ts
-INTERFACES = qthostsui.ui
-RESOURCES = qthosts.qrc
-CODECFORTR = UTF-8
-CODECFORSRC = UTF-8
diff --git a/hostsutl.py b/hostsutl.py
deleted file mode 100644
index 753501e..0000000
--- a/hostsutl.py
+++ /dev/null
@@ -1,1559 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# hostsutl.py : Main parts of Hosts Setup Utility
-#
-# Copyleft (C) 2013 - huhamhire hosts team
-# =====================================================================
-# Licensed under the GNU General Public License, version 3. You should
-# have received a copy of the GNU General Public License along with
-# this program. If not, see .
-#
-# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
-# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE.
-# =====================================================================
-
-__version__ = "1.9.7"
-__revision__ = "$Id$"
-__author__ = "huhamhire "
-
-__all__ = [
- "LANG_DIR", "MainDialog", "QSubChkConnection", "QSubFetchUpdate",
- "QSubMakeHosts", "QSubChkUpdate",]
-
-import json
-import os
-import shutil
-import socket
-import sys
-import time
-import urllib
-
-from PyQt4 import QtCore, QtGui
-from zipfile import BadZipfile
-
-from qthostsui import Ui_HostsUtlMain, _fromUtf8, _encoding, _translate
-from retrievedata import RetrieveData, make_hosts
-from utilities import Utilities, LangUtilities
-
-# Path to store language files
-LANG_DIR = "./lang/"
-
-class MainDialog(QtGui.QDialog):
- """A class to manage the operations and UI of Hosts Setup Utility
-
- MainDialog class is a subclasse of PyQt4.QtGui.QDialog which is used to
- make the main dialog of this hosts setup utility.
- This class contains a set of tools used to manage the operations while
- modifying the hosts file of current operating system. Including methods
- to manage operations to update data file, download data file, configure
- hosts, make hosts file, backup hosts file, and restore backup.
- The MainDialog class also provides QT slots to deal with the QT singles
- emitted by the widgets on the main dialog operated by users. Extend
- methods dealing with the user interface is also given by this class.
-
- Attributes:
- _cur_ver (str): A string indicating the current version of hosts data
- file.
- _ipv_id (int): An integer indicating current IP version setting. The
- value could be 1 or 0. 1 represents IPv6 while 1 represents IPv4.
- _is_root (int): An integer indicating whether the program is run with
- admin/root privileges. The value could be 1 or 0.
- _down_flag (int) An integer indicating the downloading status of
- current session. 1 represents data file is being downloaded.
- _funcs (list): A list containing two lists with the information of
- function list for IPv4 and IPv6 environment.
- _make_cfg (dict): A dictionary containing the selection control bytes
- to make a hosts file.
- _make_mode (str): A string indicating the operation mode for making
- hosts file.
- _make_path (str): A string indicating the path to store the hosts file
- in export mode.
- _sys_eol (str): A string indicating the End-Of-Line marker.
- _update (dict): A dictionary containing the update information of the
- current data file on server.
- _trans (obj): A QtCore.QTranslator object indicating the current UI
- language setting.
- choice (list): A list containing two lists with the selection of
- functions for IPv4 and IPv6 environment.
- slices (list): A list containing two lists with integers indicating
- the number of function items from different parts listed in the
- function list.
- initd (int): An integer indicating how many times has the main dialog
- been initialized. This value would be referenced for translator
- to set the language of the main dialog.
- platform (str): A string indicating the platform of current operating
- system. The value could be "Windows", "Linux", "Unix", "OS X", and
- of course "Unkown".
- plat_flag (bool): A boolean flag indicating whether the current os is
- supported or not.
- hostname (str): A string indicating the hostname of current operating
- system. This attribute would be used for linux clients.
- hostspath (str): A string indicating the absolute path of the hosts
- file on current operating system.
- Ui (str): A user interface object indicating the main dialog of this
- program.
- _mirr_id (int): An integer indicating current index number of mirrors.
- mirrors (list): A dictionary containing tag, test url, and update url
- of mirrors.
- __list_trans (list): A list containing names of function list items
- for translator to translate.
- filename (str): A string indicating the filename of the data file
- containing data to make a hosts file.
- infofile (str): A string indicating the filename of the info file
- containing metadata of the data file in JSON format.
- """
- _cur_ver = ""
- _ipv_id = 0
- _is_root = 0
- _down_flag = 0
- _funcs = [[], []]
- _make_cfg = {}
- _make_mode = ""
- _make_path = "./hosts"
- _sys_eol = ""
- _update = {}
- _trans = None
-
- choice = [[], []]
- slices = [[], []]
-
- initd = 0
-
- Ui = None
- # OS related configuration
- platform = ''
- plat_flag = True
- hostname = ''
- hostspath = ''
- # Mirror related configuration
- _mirr_id = 0
- mirrors = []
- # Name of items from the function list to be localized
- __list_trans = [
- _translate("HostsUtlMain", "google(cn)", None),
- _translate("HostsUtlMain", "google(hk)", None),
- _translate("HostsUtlMain", "google(us)", None),
- _translate("HostsUtlMain", "google-apis(cn)", None),
- _translate("HostsUtlMain", "google-apis(us)", None),
- _translate("HostsUtlMain", "activation-helper", None),
- _translate("HostsUtlMain", "facebook", None),
- _translate("HostsUtlMain", "twitter", None),
- _translate("HostsUtlMain", "youtube", None),
- _translate("HostsUtlMain", "wikipedia", None),
- _translate("HostsUtlMain", "institutions", None),
- _translate("HostsUtlMain", "steam", None),
- _translate("HostsUtlMain", "others", None),
- _translate("HostsUtlMain", "adblock-hostsx", None),
- _translate("HostsUtlMain", "adblock-mvps", None),
- _translate("HostsUtlMain", "adblock-mwsl", None),
- _translate("HostsUtlMain", "adblock-yoyo", None),
- ]
- # Data file related configuration
- filename = "hostslist.data"
- infofile = "hostsinfo.json"
-
- def __init__(self, Ui, trans):
- """Initialize a new instance of this class - Private Method
-
- Set the UI object and current translator of the main dialog.
-
- Args:
- Ui (obj): A user interface object indicating the main dialog of
- this program.
- trans (obj): A PyQt4.QtCore.QTranslator object indicating the
- current UI language setting.
- """
- super(MainDialog, self).__init__()
- self.Ui = Ui
- self._trans = trans
- self.set_platform()
- self.set_style()
- self.set_stylesheet()
-
- def on_Mirror_changed(self, mirr_id):
- """Change the current mirror setting - Public Method
-
- The slot response to the signal ({mirr_id}) from SelectMirror widget
- while the value is changed.
-
- Args:
- mirr_id (int): An integer indicating current index number of
- mirrors.
- """
- self._mirr_id = mirr_id
- self.check_connection()
-
- def on_IPVersion_changed(self, ipv_id):
- """Change the current IP version setting - Public Method
-
- The slot response to the signal ({ipv_id}) from SelectIP widget while
- the value is changed.
-
- Args:
- ipv_id (int): An integer indicating current IP version setting.
- The value could be 1 or 0. 1 represents IPv6 while 1
- represents IPv4.
- """
- if self._ipv_id != ipv_id:
- self._ipv_id = ipv_id
- if not RetrieveData.db_exists():
- self.warning_no_datafile()
- else:
- self.set_func_list(0)
- self.refresh_func_list()
-
- def on_Selection_changed(self, item):
- """Change the function selection setting - Public Method
-
- The slot response to the signal ({item}) from Functionlist widget
- while the selection of the items is changed. This method would change
- the current selection of functions.
-
- Args:
- item (int): An integer indicating the row number of the item
- listed in Functionlist which is changed by user.
- """
- ip_flag = self._ipv_id
- func_id = item.listWidget().row(item)
- if self._funcs[ip_flag][func_id] == 0:
- self._funcs[ip_flag][func_id] = 1
- else:
- self._funcs[ip_flag][func_id] = 0
- mutex = RetrieveData.get_ids(self.choice[ip_flag][func_id][2])
- for c_id, c in enumerate(self.choice[ip_flag]):
- if c[0] == self.choice[ip_flag][func_id][0]:
- if c[1] in mutex and self._funcs[ip_flag][c_id] == 1:
- self._funcs[ip_flag][c_id] = 0
- item = self.Ui.Functionlist.item(c_id)
- self.refresh_func_list()
-
- def on_Lang_changed(self, lang):
- """Change the UI language setting - Public Method
-
- The slot response to the signal ({lang}) from SelectLang widget while
- the value is changed. This method would change the language of the UI.
-
- Args:
- lang (str): A string indicating the language which is selected by
- user.
- This string uses the for of IETF language tag. For example:
- en_US, en_GB, etc.
- """
- new_lang = LangUtilities.get_locale_by_language(unicode(lang))
- trans = QtCore.QTranslator()
- global LANG_DIR
- trans.load(LANG_DIR + new_lang)
- QtGui.QApplication.removeTranslator(self._trans)
- QtGui.QApplication.installTranslator(trans)
- self._trans = trans
- self.Ui.retranslateUi(self)
- self.init_main()
- self.check_connection()
-
- def on_MakeHosts_clicked(self):
- """Start making hosts file - Public Method
-
- The slot response to the signal from ButtonApply widget while the
- button is clicked. This method would call operations to make a hosts
- file.
- No operations would be called if current session does not have the
- privileges to change the hosts file.
- """
- if not self._is_root:
- self.warning_permission()
- return
- if self.question_apply():
- self._make_path = "./hosts"
- self.make_hosts("system")
- else:
- return
-
- def on_MakeANSI_clicked(self):
- """Export hosts ANSI - Public Method
-
- The slot response to the signal from ButtonANSI widget while the
- button is clicked. This method would call operations to export a hosts
- file encoding in ANSI.
- """
- self._make_path = self.export_hosts()
- if unicode(self._make_path) != u'':
- self.make_hosts("ansi")
-
- def on_MakeUTF8_clicked(self):
- """Export hosts in UTF-8 - Public Method
-
- The slot response to the signal from ButtonUTF widget while the
- button is clicked. This method would call operations to export a hosts
- file encoding in UTF-8.
- """
- self._make_path = self.export_hosts()
- if unicode(self._make_path) != u'':
- self.make_hosts("utf-8")
-
- def on_Backup_clicked(self):
- """Backup system hosts file - Public Method
-
- The slot response to the signal from ButtonBackup widget while the
- button is clicked. This method would call operations to backup the
- hosts file of current operating system.
- """
- l_time = time.localtime(time.time())
- backtime = time.strftime("%Y-%m-%d-%H%M%S", l_time)
- filename = "hosts_" + backtime + ".bak"
- if self.platform == "OS X":
- filename = "/Users/" + filename
- filepath = QtGui.QFileDialog.getSaveFileName(
- self, _translate("HostsUtlMain", "Backup hosts", None),
- QtCore.QString(filename),
- _translate("HostsUtlMain", "Backup File(*.bak)", None))
- if unicode(filepath) != u'':
- shutil.copy2(self.hostspath, unicode(filepath))
- self.info_complete()
-
- def on_Restore_clicked(self):
- """Restore hosts file - Public Method
-
- The slot response to the signal from ButtonRestore widget while the
- button is clicked. This method would call operations to restore a
- previously backed up hosts file.
- No operations would be called if current session does not have the
- privileges to change the hosts file.
- """
- if not self._is_root:
- self.warning_permission()
- return
- filename = ''
- if self.platform == "OS X":
- filename = "/Users/" + filename
- filepath = QtGui.QFileDialog.getOpenFileName(
- self, _translate("HostsUtlMain", "Restore hosts", None),
- QtCore.QString(filename),
- _translate("HostsUtlMain", "Backup File(*.bak)", None))
- if unicode(filepath) != u'':
- shutil.copy2(unicode(filepath), self.hostspath)
- self.info_complete()
-
- def on_CheckUpdate_clicked(self):
- """Check data file update - Public Method
-
- The slot response to the signal from ButtonCheck widget while the
- button is clicked. This method would call operations to fetch update
- information of the latest data file.
- """
- if self.choice != [[], []]:
- self.refresh_func_list()
- self.Ui.ButtonApply.setEnabled(True)
- self.Ui.ButtonANSI.setEnabled(True)
- self.Ui.ButtonUTF.setEnabled(True)
- if self._update == {} or self._update["version"] == \
- unicode(_translate("HostsUtlMain", "[Error]", None)):
- self.check_update()
-
- def on_FetchUpdate_clicked(self):
- """Fetch data file update - Public Method
-
- The slot response to the signal from ButtonUpdate widget while the
- button is clicked. This method would call operations to fetch the
- latest data file.
- If no update information has been got from the server, the method to
- check the update would be called.
- If the current data is up-to-date, no data file would be retrieved.
- """
- self._down_flag = 1
- self.Ui.Functionlist.setEnabled(False)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
- if self._update == {} or self._update["version"] == \
- unicode(_translate("HostsUtlMain", "[Error]", None)):
- self.check_update()
- elif self.new_version():
- self.fetch_update()
- else:
- self.info_uptodate()
- self.finish_fetch()
-
- def on_LinkActivated(self, url):
- """Open external link in browser - Public Method
-
- The slot response to the signal from Label widget while the text with
- a hyperlink is clicked by user.
- """
- QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
-
- def init_main(self):
- """Initialize the main dialog - Public Method
-
- Set up the elements on the main dialog. Check the environment of
- current operating system and current session.
- """
- self.Ui.SelectMirror.clear()
- # Set mirrors
- self.mirrors = Utilities.set_network("network.conf")
- for i, mirror in enumerate(self.mirrors):
- self.Ui.SelectMirror.addItem(_fromUtf8(""))
- self.Ui.SelectMirror.setItemText(
- i, _translate("HostsUtlMain", mirror["tag"], None))
- self.set_platform_label()
- # Read data file and set function list
- try:
- RetrieveData.unpack()
- RetrieveData.connect_db()
- self.set_func_list(1)
- self.refresh_func_list()
- self.set_info()
- except IOError:
- self.warning_no_datafile()
- except BadZipfile:
- self.warning_incorrect_datafile()
- # Check if current session have root privileges
- self.check_root()
- self.initd += 1
-
- def reject(self):
- """Response to the reject signal - Public Method
-
- The slot response to the reject signal from an instance of the main
- dialog. Close this program while the reject signal is emitted.
- """
- self.close()
- return QtGui.QDialog.reject(self)
-
- def close(self):
- """Response to the close signal - Public Method
-
- The slot response to the close signal from an instance of the main
- dialog. Close this program while the reject signal is emitted.
- """
- try:
- RetrieveData.clear()
- except:
- pass
- super(MainDialog, self).close()
-
- def check_root(self):
- """Check root privileges - Public Method
-
- Check if current session is ran with root privileges.
- """
- is_root = Utilities.check_privileges()[1]
- self._is_root = is_root
- if not is_root:
- self.warning_permission()
-
- def check_connection(self):
- """Operations to check connection - Public Method
-
- Call operations to check the connection to current server.
- """
- thread = QSubChkConnection(self)
- thread.trigger.connect(self.set_conn_status)
- thread.start()
-
- def check_update(self):
- """Operations to check data file update - Public Method
-
- Call operations to retrieve the metadata of the latest data file from
- a server.
- """
- self.Ui.SelectMirror.setEnabled(False)
- self.Ui.ButtonCheck.setEnabled(False)
- self.Ui.ButtonUpdate.setEnabled(False)
- self.set_label_text(self.Ui.labelLatestData, unicode(
- _translate("HostsUtlMain", "Checking...", None)))
- thread = QSubChkUpdate(self)
- thread.trigger.connect(self.finish_update)
- thread.start()
-
- def fetch_update(self):
- """Operations to fetch new data file - Public Method
-
- Call operations to retrieve a new hosts data file from a server.
- """
- self.Ui.SelectMirror.setEnabled(False)
- self.Ui.ButtonCheck.setEnabled(False)
- self.Ui.ButtonUpdate.setEnabled(False)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
- self.Ui.ButtonExit.setEnabled(False)
- thread = QSubFetchUpdate(self)
- thread.prog_trigger.connect(self.set_downprogbar)
- thread.finish_trigger.connect(self.finish_fetch)
- thread.start()
-
- def fetch_update_aftercheck(self):
- """Check to fetch data file after check for update - Public Method
-
- Decide whether to retrieve a new data file from server or not after
- checking update information from a mirror.
- """
- if self._update["version"] == \
- unicode(_translate("HostsUtlMain", "[Error]", None)):
- self.finish_fetch(error=1)
- elif self.new_version():
- self.fetch_update()
- else:
- self.info_uptodate()
- self.finish_fetch()
-
- def export_hosts(self):
- """Draw export hosts dialog - Public Method
-
- Show the export dialog and get the path to save the exported hosts
- file.
-
- Returns:
- A string indicating the path to export a hosts file
- """
- filename = "hosts"
- if self.platform == "OS X":
- filename = "/Users/" + filename
- filepath = QtGui.QFileDialog.getSaveFileName(
- self, _translate("HostsUtlMain", "Export hosts", None),
- QtCore.QString(filename),
- _translate("HostsUtlMain", "hosts File", None))
- return filepath
-
- def make_hosts(self, mode="system"):
- """Operations to make hosts file - Public Method
-
- Call operations to make a new hosts file for current system.
-
- Args:
- mode (str): A string indicating the operation mode for making
- hosts file.
- """
- self.Ui.Functionlist.setEnabled(False)
- self.Ui.SelectIP.setEnabled(False)
- self.Ui.ButtonCheck.setEnabled(False)
- self.Ui.ButtonUpdate.setEnabled(False)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
- self.Ui.ButtonExit.setEnabled(False)
- self.set_makemsg(unicode(_translate(
- "HostsUtlMain", "Building hosts file...", None)), 1)
- # Avoid conflict while making hosts file
- RetrieveData.disconnect_db()
- self._make_mode = mode
- self.set_cfgbytes(mode)
- thread = QSubMakeHosts(self)
- thread.info_trigger.connect(self.set_makeprog)
- thread.fina_trigger.connect(self.set_makefina)
- thread.move_trigger.connect(self.move_hosts)
- thread.start()
-
- def move_hosts(self):
- """Move hosts file to the system path after making - Public Method
-
- The slot response to the move_trigger signal from an instance of
- QSubMakeHosts class while making operations are finished.
- """
- filepath = "hosts"
- msg = unicode(_translate("HostsUtlMain",
- "Copying new hosts file to\n"
- " %s", None)) % self.hostspath
- self.set_makemsg(msg)
- try:
- shutil.copy2(filepath, self.hostspath)
- except IOError:
- self.warning_permission()
- os.remove(filepath)
- return
- msg = unicode(_translate("HostsUtlMain",
- "Remove temporary file", None))
- self.set_makemsg(msg)
- os.remove(filepath)
- msg = unicode(_translate("HostsUtlMain",
- "Operation completed", None))
- self.set_makemsg(msg)
- self.info_complete()
-
- def set_languages(self):
- """Set items in SelectLang widget - Public Method
-
- Set optional language selection items in the SelectLang widget.
- """
- self.Ui.SelectLang.clear()
- langs = LangUtilities.language
- langs_not_found = []
- for locale in langs:
- if not os.path.isfile(LANG_DIR + locale + ".qm"):
- langs_not_found.append(locale)
- for locale in langs_not_found:
- langs.pop(locale)
- LangUtilities.language = langs
- if len(langs) <= 1:
- self.Ui.SelectLang.setEnabled(False)
- # Block the signal while set the language selecions.
- self.Ui.SelectLang.blockSignals(True)
- sys_locale = LangUtilities.get_locale()
- if sys_locale not in langs.keys():
- sys_locale = "en_US"
- for i, locale in enumerate(sorted(langs.keys())):
- if sys_locale == locale:
- select = i
- lang = langs[locale]
- self.Ui.SelectLang.addItem(_fromUtf8(""))
- self.Ui.SelectLang.setItemText(i, lang)
- self.Ui.SelectLang.blockSignals(False)
- self.Ui.SelectLang.setCurrentIndex(select)
-
- def set_platform(self):
- """Set OS info - Public Method
-
- Set the information of current operating system platform.
- """
- system, hostname, path, encode, flag = Utilities.check_platform()
- self.platform = system
- self.hostname = hostname
- self.hostspath = path
- self.plat_flag = flag
- if encode == "win_ansi":
- self._sys_eol = "\r\n"
- else:
- self._sys_eol = "\n"
-
- def set_platform_label(self):
- """Set label of OS info - Public Method
-
- Set the information of the label indicating current operating system
- platform.
- """
- color = "GREEN" if self.plat_flag else "RED"
- self.set_label_color(self.Ui.labelOSStat, color)
- self.set_label_text(self.Ui.labelOSStat, "[%s]" % self.platform)
-
-
- def set_style(self):
- """Set window style - Public Method
-
- Set the main dialog with a window style depending on the os platform.
- """
- self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
- system = self.platform
- if system == "Windows":
- pass
- elif system == "Linux":
- # Set window style for sudo users.
- QtGui.QApplication.setStyle(
- QtGui.QStyleFactory.create("Cleanlooks"))
- elif system == "OS X":
- pass
-
- def set_stylesheet(self):
- """Set Stylesheet for main frame - Public Method
-
- Define the style sheet of main dialog.
- """
- app = QtGui.QApplication.instance()
- with open("./theme/darkdefault.qss", "r") as qss:
- app.setStyleSheet(qss.read())
-
- def mouseMoveEvent(self, e):
- """Set mouse drag event - Public Method
-
- Allow drag operations to set the new position for current dialog.
-
- Args:
- e (QMouseEvent): A QMouseEvent object indicating current mouse
- event.
- """
- if e.buttons() & QtCore.Qt.LeftButton:
- try:
- self.move(e.globalPos() - self.dragPos)
- except AttributeError:
- pass
- e.accept()
-
- def mousePressEvent(self, e):
- """Set mouse press event - Public Method
-
- Allow drag operations to set the new position for current dialog.
-
- Args:
- e (QMouseEvent): A QMouseEvent object indicating current mouse
- event.
- """
- if e.button() == QtCore.Qt.LeftButton:
- self.dragPos = e.globalPos() - self.frameGeometry().topLeft()
- e.accept()
-
-
- def set_label_color(self, label, color):
- """Set the color of a label - Public Method
-
- Set a specified label ({label}) to show with specified color
- ({color}).
-
- Args:
- label (obj): An instance of PyQt4.QtGui.QLabel class on the main
- dialog.
- color (str): A string indicating the color to be shown on the
- lable.
- """
- if color == "GREEN":
- rgb = "#37b158"
- elif color == "RED":
- rgb = "#e27867"
- elif color == "BLACK":
- rgb = "#b1b1b1"
- label.setStyleSheet("QLabel {color: %s}" % rgb)
-
- def set_label_text(self, label, text):
- """Set the text of a label - Public Method
-
- Set a specified label ({label}) to show specified text ({text}).
-
- Args:
- label (obj): An instance of PyQt4.QtGui.QLabel class on the main
- dialog.
- text (str): A string indicating the message to be shown on the
- lable.
- """
- label.setText(_translate("HostsUtlMain", text, None))
-
- def set_conn_status(self, status):
- """Set connection status info - Public Method
-
- Set the information of connection status to the current server
- selected.
- """
- if status == -1:
- self.set_label_color(self.Ui.labelConnStat, "BLACK")
- self.set_label_text(self.Ui.labelConnStat, unicode(
- _translate("HostsUtlMain", "Checking...", None)))
- elif status in [0, 1]:
- if status:
- color, stat = "GREEN", unicode(_translate(
- "HostsUtlMain", "[OK]", None))
- else:
- color, stat = "RED", unicode(_translate(
- "HostsUtlMain", "[Failed]", None))
- self.set_label_color(self.Ui.labelConnStat, color)
- self.set_label_text(self.Ui.labelConnStat, stat)
-
- def set_func_list(self, new=0):
- """Set the function list - Public Method
-
- Draw the function list and decide whether to load the default
- selection configuration or not.
-
- Arg:
- new (int): A flag integer indicating whether to load the default
- selection configuration or not. 0 -> user user config,
- 1 -> use default config. Default by 0.
- """
- ip_flag = self._ipv_id
- self.Ui.Functionlist.clear()
- self.Ui.FunctionsBox.setTitle(_translate(
- "HostsUtlMain", "Functions", None))
- if new:
- for ip in range(2):
- choice, defaults, slices = RetrieveData.get_choice(ip)
- self.choice[ip] = choice
- self.slices[ip] = slices
- funcs = []
- for func in choice:
- item = QtGui.QListWidgetItem()
- if func[1] in defaults[func[0]]:
- funcs.append(1)
- else:
- funcs.append(0)
- self._funcs[ip] = funcs
-
- def refresh_func_list(self):
- """Refresh the function list - Public Method
-
- Refresh the items in the function list by user settings.
- """
- ip_flag = self._ipv_id
- self.Ui.Functionlist.clear()
- for f_id, func in enumerate(self.choice[self._ipv_id]):
- item = QtGui.QListWidgetItem()
- if self._funcs[ip_flag][f_id] == 1:
- check = QtCore.Qt.Checked
- else:
- check = QtCore.Qt.Unchecked
- item.setCheckState(check)
- item.setText(_translate("HostsUtlMain", func[3], None))
- self.Ui.Functionlist.addItem(item)
-
- def set_message(self, title, msg):
- """Set a message box - Public Method
-
- Show a message box with a specified message ({msg}) with a specified
- title ({title}).
-
- Args:
- title (str): A string indicating the title of the message box.
- msg (str): A string indicating the message to be shown in the
- message box.
- """
- self.Ui.FunctionsBox.setTitle(_translate(
- "HostsUtlMain", title, None))
- self.Ui.Functionlist.clear()
- item = QtGui.QListWidgetItem()
- item.setText(msg)
- item.setFlags(QtCore.Qt.ItemIsEnabled)
- self.Ui.Functionlist.addItem(item)
-
- def set_info(self):
- """Set data file info - Public Method
-
- Set the information of the current local data file.
- """
- info = RetrieveData.get_info()
- ver = info["Version"]
- self._cur_ver = ver
- self.set_label_text(self.Ui.labelVersionData, ver)
- build = info["Buildtime"]
- build = Utilities.timestamp_to_date(build)
- self.set_label_text(self.Ui.labelReleaseData, build)
-
- def set_downprogbar(self, prog, msg):
- """Set progress bar - Public Method
-
- Set the progress bar to a specified progress position ({prog}) with a
- specified message ({msg}).
-
- Args:
- prog (int): An integer indicating the progress to be set on the
- progress bar.
- msg (str): A string indicating the message to be shown on the
- progress bar.
- """
- self.Ui.Prog.setProperty("value", prog)
- self.set_conn_status(1)
- self.Ui.Prog.setFormat(msg)
-
- def set_listitemunchecked(self, item_id):
- """Set list item to be unchecked - Public Method
-
- Set a specified item ({item_id}) to become unchecked in the function
- list.
-
- Arg:
- item_id (int): An integer indicating the id number of a specified
- item in the function list.
- """
- self._funcs[self._ipv_id][item_id] = 0
- item = self.Ui.Functionlist.item(item_id)
- item.setCheckState(QtCore.Qt.Unchecked)
-
- def set_cfgbytes(self, mode):
- """Set configuration byte words - Public Method
-
- Calculate the module configuration byte words by the selection from
- function list on the main dialog.
-
- Args:
- mode (str): A string indicating the operation mode for making
- hosts file.
- """
- ip_flag = self._ipv_id
- selection = {}
- if mode == "system":
- localhost_word = {
- "Windows": 0x0001, "Linux": 0x0002,
- "Unix": 0x0002, "OS X": 0x0004}[self.platform]
- else:
- localhost_word = 0x0008
- selection[0x02] = localhost_word
- ch_parts = (0x08, 0x20 if self._ipv_id else 0x10, 0x40)
- slices = self.slices[ip_flag]
- for i, part in enumerate(ch_parts):
- part_cfg = self._funcs[ip_flag][slices[i]:slices[i + 1]]
- part_word = 0
- for i, cfg in enumerate(part_cfg):
- part_word += cfg << i
- selection[part] = part_word
- self._make_cfg = selection
-
- def refresh_info(self, refresh=0):
- """Refresh data file information - Public Method
-
- Reload the data file information and show them on the main dialog. The
- information here includes both metadata and hosts module info from the
- data file.
-
- Arg:
- refresh (int): A flag integer indicating whether the information
- needs to be reloaded or not. 1: reload, 0: do not reload.
- Default by 0.
- """
- if refresh and RetrieveData._conn != None:
- RetrieveData.clear()
- try:
- RetrieveData.unpack()
- RetrieveData.connect_db()
- self.set_func_list(refresh)
- self.refresh_func_list()
- self.set_info()
- except (BadZipfile, IOError, OSError):
- self.warning_incorrect_datafile()
-
- def set_makeprog(self, mod_name, mod_num):
- """Operations to show progress while making hosts file - Public Method
-
- The slot response to the info_trigger signal ({mod_name}, {mod_num})
- from an instance of QSubMakeHosts class while making operations are
- proceeded.
-
- Args:
- mod_name (str): A string indicating the name of a specified hosts
- module in current progress.
- mod_num (int): An integer indicating the number of current module
- in the operation sequence.
- """
- total_mods_num = self._funcs[self._ipv_id].count(1) + 1
- prog = 100 * mod_num / total_mods_num
- self.Ui.Prog.setProperty("value", prog)
- format = unicode(_translate(
- "HostsUtlMain", "Applying module: %s(%s/%s)", None)) % (
- mod_name, mod_num, total_mods_num)
- self.Ui.Prog.setFormat(format)
- self.set_makemsg(format)
-
- def set_makemsg(self, msg, start=0):
- """Operations to show making progress in function list - Public Method
-
- List message for the current operating progress while making the new
- hosts file.
-
- Args:
- msg (str): A string indicating the message to show in the function
- list.
- start (int): A flag integer indicating whether the message is the
- first of the making progress or not. 1: first, 0: not the
- first. Default by 0.
- """
- if start:
- self.Ui.FunctionsBox.setTitle(_translate(
- "HostsUtlMain", "Progress", None))
- self.Ui.Functionlist.clear()
- item = QtGui.QListWidgetItem()
- item.setText("- " + msg)
- item.setFlags(QtCore.Qt.ItemIsEnabled)
- self.Ui.Functionlist.addItem(item)
-
- def set_makefina(self, time, count):
- """Operations after making new hosts file - Public Method
-
- The slot response to the fina_trigger signal ({time}, {count}) from
- an instance of QSubMakeHosts class while making operations are
- finished.
-
- Args:
- time (str): A string indicating the total time uesd to make the
- new hosts file.
- count (int): An integer indicating the total number of hosts
- entries inserted into the new hosts file.
- """
- self.Ui.Functionlist.setEnabled(True)
- self.Ui.SelectIP.setEnabled(True)
- self.Ui.ButtonCheck.setEnabled(True)
- self.Ui.ButtonUpdate.setEnabled(True)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
- self.Ui.ButtonExit.setEnabled(True)
- RetrieveData.connect_db()
- msg = unicode(_translate("HostsUtlMain",
- "Notice: %i hosts entries has "
- "\n been applied in %ssecs.", None)) % (count, time)
- self.set_makemsg(msg)
- self.set_downprogbar(100,
- unicode(_translate("HostsUtlMain",
- "Operation Completed Successfully!", None)))
-
- def finish_update(self, update):
- """Operations after checking update - Public Method
-
- The slot response to the trigger signal ({update}) from an instance
- of QSubChkUpdate class while checking operations are finished.
-
- Arg:
- update (dict): A dictionary containing metadata of the latest
- hosts file from the server.
- """
- self._update = update
- self.set_label_text(self.Ui.labelLatestData, update["version"])
- if self._update["version"] == \
- unicode(_translate("HostsUtlMain", "[Error]", None)):
- self.set_conn_status(0)
- else:
- self.set_conn_status(1)
- if self._down_flag:
- self.fetch_update_aftercheck()
- else:
- self.Ui.SelectMirror.setEnabled(True)
- self.Ui.ButtonCheck.setEnabled(True)
- self.Ui.ButtonUpdate.setEnabled(True)
-
- def finish_fetch(self, refresh=1, error=0):
- """Operations after downloading data file - Public Method
-
- The slot response to the finish_trigger signal ({refresh}, {error})
- from an instance of QSubFetchUpdate class while downloading is
- finished.
-
- Args:
- refresh (int): A flag integer indicating whether a refresh for
- function list is needed or not. 1: refresh, 0: no refresh.
- Default by 1.
- error (int): A flag integer indicating errors have occurred while
- downloading new data file. 1: error, 0:success. Default by 0.
- """
- self._down_flag = 0
- if error:
- # Error occurred while downloading
- self.set_downprogbar(0,
- unicode(_translate("HostsUtlMain",
- "Error", None)))
- try:
- os.remove(self.filename)
- except:
- pass
- self.warning_download()
- msg_title = "Warning"
- msg = unicode(_translate("HostsUtlMain",
- "Incorrect Data file!\n"
- "Please use the \"Download\" key to \n"
- "fetch a new data file.", None))
- self.set_message(msg_title, msg)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
- self.set_conn_status(0)
- else:
- # Data file retrieved successfully
- self.set_downprogbar(100,
- unicode(_translate("HostsUtlMain",
- "Download Complete", None)))
- self.refresh_info(refresh)
- self.Ui.ButtonApply.setEnabled(True)
- self.Ui.ButtonANSI.setEnabled(True)
- self.Ui.ButtonUTF.setEnabled(True)
- self.Ui.Functionlist.setEnabled(True)
- self.Ui.SelectMirror.setEnabled(True)
- self.Ui.ButtonCheck.setEnabled(True)
- self.Ui.ButtonUpdate.setEnabled(True)
- self.Ui.ButtonExit.setEnabled(True)
-
- def new_version(self):
- """Compare version of data file - Public Method
-
- Compare version of local data file to the version from the server.
-
- Returns:
- A flag integer indicating whether the local data file is
- up-to-date or not.
- 1 -> The version of data file on server is newer.
- 0 -> The local data file is up-to-date.
- """
- local_ver = self._cur_ver
- server_ver = self._update["version"]
- local_ver = local_ver.split('.')
- server_ver = server_ver.split('.')
- for i, ver_num in enumerate(local_ver):
- if server_ver[i] > ver_num:
- return 1
- return 0
-
- def warning_permission(self):
- """Show permission error warning - Public Method
-
- Draw permission error warning message box.
- """
- QtGui.QMessageBox.warning(
- self, _translate("HostsUtlMain", "Warning", None),
- _translate("HostsUtlMain",
- "You do not have permissions to change the \n"
- "hosts file.\n"
- "Please run this program as Administrator/root\n"
- "so it can modify your hosts file."
- , None))
-
- def warning_download(self):
- """Show download error warning - Public Method
-
- Draw download error warning message box.
- """
- QtGui.QMessageBox.warning(
- self, _translate("HostsUtlMain", "Warning", None),
- _translate("HostsUtlMain",
- "Error retrieving data from the server.\n"
- "Please try another server.", None))
-
- def warning_incorrect_datafile(self):
- """Show incorrect data file warning - Public Method
-
- Draw incorrect data file warning message box.
- """
- msg_title = "Warning"
- msg = unicode(_translate("HostsUtlMain",
- "Incorrect Data file!\n"
- "Please use the \"Download\" key to \n"
- "fetch a new data file.", None))
- self.set_message(msg_title, msg)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
-
- def warning_no_datafile(self):
- """Show no data file warning - Public Method
-
- Draw no data file warning message box.
- """
- msg_title = "Warning"
- msg = unicode(_translate("HostsUtlMain",
- "Data file not found!\n"
- "Please use the \"Download\" key to \n"
- "fetch a new data file.", None))
- self.set_message(msg_title, msg)
- self.Ui.ButtonApply.setEnabled(False)
- self.Ui.ButtonANSI.setEnabled(False)
- self.Ui.ButtonUTF.setEnabled(False)
-
- def question_apply(self):
- """Show confirm make question - Public Method
-
- Draw confirm make question message box.
-
- Returns:
- A bool flag indicating whether user has accepted to continue the
- operations or not. True: Continue, False: Cancel.
- """
- msg_title = unicode(_translate("HostsUtlMain", "Notice", None))
- msg = unicode(_translate("HostsUtlMain",
- "Are you sure you want to apply changes \n"
- "to the hosts file on your system?\n\n"
- "This operation could not be reverted if \n"
- "you have not made a backup of your \n"
- "current hosts file.", None))
- choice = QtGui.QMessageBox.question(self, msg_title, msg,
- QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
- QtGui.QMessageBox.No)
- if choice == QtGui.QMessageBox.Yes:
- return True
- else:
- return False
-
- def info_uptodate(self):
- """Show up-to-date message - Public Method
-
- Draw data file is up-to-date message box.
- """
- QtGui.QMessageBox.information(
- self, _translate("HostsUtlMain", "Notice", None),
- _translate("HostsUtlMain", "Data file is up-to-date.", None))
-
- def info_complete(self):
- """Show complete message - Public Method
-
- Draw operation complete message box.
- """
- QtGui.QMessageBox.information(
- self, _translate("HostsUtlMain", "Complete", None),
- _translate("HostsUtlMain", "Operation completed", None))
-
-
-class QSubChkConnection(QtCore.QThread):
- """A class to check connection with server
-
- QSubChkConnection is a subclasse of PyQt4.QtCore.QThread. This class
- contains methods to check the network connection with a specified mirror.
-
- The instance of this class should be created in an individual thread. And
- the object instance of MainDialog class should be set as parent here.
-
- Attribute:
- trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit suatus signal
- to the main dialog. The meaning of the signal arguments is listed
- here:
- -1 -> checking..., 0 -> Failed, 1 -> OK.
- """
- trigger = QtCore.pyqtSignal(int)
-
- def __init__(self, parent=None):
- """Initialize a new instance of this class - Private Method
-
- Get mirror settings from the main dialog to check the connection.
-
- Args:
- parent (obj): An instance of MainDialog object to get settings
- from.
- """
- super(QSubChkConnection, self).__init__(parent)
- self.link = parent.mirrors[parent._mirr_id]["test_url"]
-
- def run(self):
- """Check connection - Public Method
-
- Operations to check the network connection with a specified mirror.
- """
- self.trigger.emit(-1)
- status = Utilities.check_connection(self.link)
- self.trigger.emit(status)
-
-class QSubFetchUpdate(QtCore.QThread):
- """A class to fetch the latest data file
-
- QSubFetchUpdate is a subclasse of PyQt4.QtCore.QThread. This class
- contains methods to retrieve the latest hosts data file.
-
- The instance of this class should be created in an individual thread. And
- the object instance of MainDialog class should be set as parent here.
-
- Attributes:
- prog_trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit progress
- signal to the main dialog indicating the current download
- progress. The meaning of the signal arguments is listed here:
- (int, str) -> (progress, message)
- progress (int): An integer indicating the current download
- progress.
- message (str): A string indicating the message to be shown to
- users on the progress bar.
- finish_trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit finish
- signal to the main dialog. The meaning of the signal arguments is
- listed here:
- (int, int) -> (refresh_flag, error_flag)
- refresh_flag (int): An integer indicating whether to refresh
- the funcion list or not. 1: refresh, 0: do not refresh.
- error_flag (int): An integer indicating whether the
- downloading is successfully finished or not.
- 1: error, 0: success.
- """
- prog_trigger = QtCore.pyqtSignal(int, str)
- finish_trigger = QtCore.pyqtSignal(int, int)
-
- def __init__(self, parent=None):
- """Initialize a new instance of this class - Private Method
-
- Get download settings from the main dialog to retrieve new hosts data
- file.
-
- Args:
- parent (obj): An instance of MainDialog object to get settings
- from.
- """
- super(QSubFetchUpdate, self).__init__(parent)
- self.url = parent.mirrors[parent._mirr_id]["update"] + parent.filename
- self.path = "./" + parent.filename
- self.tmp_path = self.path + ".download"
- self.filesize = parent._update["size"]
-
- def run(self):
- """Fetch data file - Public Method
-
- Operations to retrieve the new hosts data file.
- """
- self.prog_trigger.emit(0, unicode(_translate(
- "HostsUtlMain", "Connecting...", None)))
- self.fetch_file(self.url, self.path)
-
- def fetch_file(self, url, path):
- """Fetch the data file - Public Method
-
- Retrieve the latest data file to a specified path ({path}) by url
- ({url}).
-
- Args:
- url (str): A string indicating the url to fetch the latest data
- file.
- path (str): A string indicating the path to save the data file on
- current machine.
- """
- socket.setdefaulttimeout(10)
- try:
- urllib.urlretrieve(url, self.tmp_path, self.set_progress)
- self.replace_old()
- self.finish_trigger.emit(1, 0)
- except:
- self.finish_trigger.emit(1, 1)
-
- def set_progress(self, done, blocksize, total):
- """Set progress bar in the main dialog - Public Method
-
- Send message to the main dialog to set the progress bar Prog.
-
- Args:
- done (int): An integer indicating the number of data blocks have
- been downloaded from the server.
- blocksize (int): An integer indicating the size of a data block.
- """
- done = done * blocksize
- if total <= 0:
- total = self.filesize
- prog = 100 * done / total
- done = Utilities.convert_size(done)
- total = Utilities.convert_size(total)
- text = unicode(_translate(
- "HostsUtlMain", "Downloading: %s / %s", None)) % (done, total)
- self.prog_trigger.emit(prog, text)
-
- def replace_old(self):
- """Replace the old data file - Public Method
-
- Overwrite the old hosts data file with the new one.
- """
- if os.path.isfile(self.path):
- os.remove(self.path)
- os.rename(self.tmp_path, self.path)
-
-
-class QSubMakeHosts(QtCore.QThread):
- """A class to make a new hosts file
-
- QSubMakeHosts is a subclasse of PyQt4.QtCore.QThread. This class contains
- methods to make a new hosts file for client.
-
- The instance of this class should be created in an individual thread. And
- the object instance of MainDialog class should be set as parent here.
-
- Attributes:
- info_trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit message
- signal to the main dialog indicating the current operation.The
- meaning of the signal arguments is listed here:
- (str, int) - (mod_name, mod_num)
- mod_name (str): A string indicating the name of a specified hosts
- module in current progress.
- mod_num (int): An integer indicating the number of current module
- in the operation sequence.
- fina_trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit message
- signal to the main dialog indicating finish information. The
- meaning of the signal arguments is listed here:
- (str, int) - (time, count)
- time (str): A string indicating the total time uesd to make the
- new hosts file.
- count (int): An integer indicating the total number of hosts
- entries inserted into the new hosts file.
- move_trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit signal
- to the main dialog while new hosts is being moved to specified
- path on current system. This signal does not
- count (int): An integer indicating id of the module being processed
- currently.
- mod_num (int): An integer indicating total number of modules being
- operated while making hosts file.
- make_cfg (dict): A dictionary containing the selection control bytes
- to make a hosts file.
- make_mode (str): A string indicating the operation mode for making
- hosts file.
- eol (str): A string indicating the End-Of-Line marker.
- """
- info_trigger = QtCore.pyqtSignal(str, int)
- fina_trigger = QtCore.pyqtSignal(str, int)
- move_trigger = QtCore.pyqtSignal()
-
- count = 0
- mod_num = 0
- make_cfg = {}
- make_mode = ""
- eol = ""
-
- def __init__(self, parent=None):
- """Initialize a new instance of this class - Private Method
-
- Fetch settings from the main dialog to make a new hosts file.
-
- Args:
- parent (obj): An instance of MainDialog object to get settings
- from.
- """
- super(QSubMakeHosts, self).__init__(parent)
- self.count = 0
- self.make_cfg = parent._make_cfg
- self.make_mode = parent._make_mode
- make_path = parent._make_path
- self.hostname = parent.hostname
- if parent._make_mode == "system":
- self.eol = parent._sys_eol
- self.hosts_file = open("hosts", "wb")
- elif parent._make_mode == "ansi":
- self.eol = "\r\n"
- self.hosts_file = open(unicode(make_path), "wb")
- elif parent._make_mode == "utf-8":
- self.eol = "\n"
- self.hosts_file = open(unicode(make_path), "wb")
-
- def run(self):
- """Make new hosts file - Public Method
-
- Operations to retrieve data from the data file and make the new hosts
- file for current system.
- """
- RetrieveData.connect_db()
- start_time = time.time()
- self.maketime = start_time
- self.write_head()
- self.write_info()
- self.get_hosts(self.make_cfg)
- self.hosts_file.close()
- end_time = time.time()
- total_time = "%.4f" % (end_time - start_time)
- self.fina_trigger.emit(total_time, self.count)
- if self.make_mode == "system":
- self.move_trigger.emit()
- RetrieveData.disconnect_db()
-
- def get_hosts(self, make_cfg):
- """Make hosts by user config - Public Method
-
- Make the new hosts file by the configuration ({make_cfg}) from
- function list on the main dialog.
-
- Args:
- make_cfg (dict): A dictionary containing module settings in byte
- word format.
- """
- for part_id in sorted(make_cfg.keys()):
- mod_cfg = make_cfg[part_id]
- if not RetrieveData.chk_mutex(part_id, mod_cfg):
- return
- mods = RetrieveData.get_ids(mod_cfg)
- for mod_id in mods:
- self.mod_num += 1
- if part_id == 0x02:
- self.write_localhost_mod(part_id, mod_id)
- else:
- self.write_common_mod(part_id, mod_id)
-
- def write_head(self):
- """Write head section - Public Method
-
- Write the head part of new hosts file.
- """
- for head_str in RetrieveData.get_head():
- self.hosts_file.write("%s%s" % (head_str[0], self.eol))
-
- def write_info(self):
- """Write info section - Public Method
-
- Write the information part of new hosts file.
- """
- info = RetrieveData.get_info()
- info_lines = ["#"]
- info_lines.append("# %s: %s" % ("Version", info["Version"]))
- info_lines.append("# %s: %s" % ("Buildtime", info["Buildtime"]))
- info_lines.append("# %s: %s" % ("Applytime", int(self.maketime)))
- info_lines.append("#")
- for line in info_lines:
- self.hosts_file.write("%s%s" % (line, self.eol))
-
- def write_common_mod(self, part_id, mod_id):
- """Write module section - Public Method
-
- Write hosts entries in a specified module ({mod_id}) from a specified
- part ({part_id}) of the data file to the new hosts file.
-
- Args:
- part_id (int): An integer indicating the index number of a part
- in the data file.
- mod_id (int): An integer indicating the index number of a module
- in the data file.
- """
- hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
- self.info_trigger.emit(mod_name, self.mod_num)
- self.hosts_file.write(
- "%s# Section Start: %s%s" % (self.eol, mod_name, self.eol))
- for host in hosts:
- self.hosts_file.write("%s %s%s" % (host[0], host[1], self.eol))
- self.count += 1
- self.hosts_file.write("# Section End: %s%s" % (mod_name, self.eol))
-
- def write_localhost_mod(self, part_id, mod_id):
- """Write localhost section - Public Method
-
- Write hosts entries in a localhost module ({mod_id}) from a specified
- part ({part_id}) of the data file to the new hosts file.
-
- Args:
- part_id (int): An integer indicating the index number of a part
- in the data file.
- mod_id (int): An integer indicating the index number of a module
- in the data file.
- """
- hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
- self.info_trigger.emit(mod_name, self.mod_num)
- self.hosts_file.write(
- "%s# Section Start: Localhost%s" % (self.eol, self.eol))
- for host in hosts:
- if "#Replace" in host[1]:
- host = (host[0], self.hostname)
- self.hosts_file.write("%s %s%s" % (host[0], host[1], self.eol))
- self.count += 1
- self.hosts_file.write("# Section End: Localhost%s" % (self.eol))
-
-
-class QSubChkUpdate(QtCore.QThread):
- """A class to check update info of the latest data file
-
- QSubChkConnection is a subclasse of PyQt4.QtCore.QThread. This class
- contains methods to retrieve the metadata of the latest hosts data file.
-
- The instance of this class should be created in an individual thread. And
- the object instance of MainDialog class should be set as parent here.
-
- Attribute:
- trigger (obj): A PyQt4.QtCore.pyqtSignal object to emit suatus signal
- to the main dialog. The meaning of the signal is listed here:
- (dict) -> (update_info)
- update_info (dict): A dictionary containing metadata of the
- latest hosts data file.
- """
- trigger = QtCore.pyqtSignal(dict)
-
- def __init__(self, parent=None):
- """Initialize a new instance of this class - Private Method
-
- Get mirror settings from the main dialog to check the connection.
-
- Args:
- parent (obj): An instance of MainDialog object to get settings
- from.
- """
- super(QSubChkUpdate, self).__init__(parent)
- self.url = parent.mirrors[parent._mirr_id]["update"] + parent.infofile
-
- def run(self):
- """Check update - Public Method
-
- Operations to retrieve the metadata of the latest hosts data file.
- """
- try:
- socket.setdefaulttimeout(5)
- urlobj = urllib.urlopen(self.url)
- j_str = urlobj.read()
- urlobj.close()
- info = json.loads(j_str)
- self.trigger.emit(info)
- except:
- info = {"version": unicode(_translate("HostsUtlMain",
- "[Error]", None))}
- self.trigger.emit(info)
-
-
-def qt_main():
- """Load main dialog
-
- Start the main dialog of Hosts Setup Utility.
- """
- trans = QtCore.QTranslator()
- trans.load("lang/en_US")
- app = QtGui.QApplication(sys.argv)
- app.installTranslator(trans)
- ui = Ui_HostsUtlMain()
- HostsUtlMain = MainDialog(ui, trans)
- ui.setupUi(HostsUtlMain)
- HostsUtlMain.set_languages()
- if not HostsUtlMain.initd:
- HostsUtlMain.init_main()
- HostsUtlMain.show()
- sys.exit(app.exec_())
-
-if __name__ == "__main__":
- qt_main()
diff --git a/img/hosts_utl.icns b/img/hosts_utl.icns
deleted file mode 100644
index 72b8f81..0000000
Binary files a/img/hosts_utl.icns and /dev/null differ
diff --git a/img/hosts_utl.ico b/img/hosts_utl.ico
deleted file mode 100644
index d2e3c4d..0000000
Binary files a/img/hosts_utl.ico and /dev/null differ
diff --git a/img/utl_icon.png b/img/utl_icon.png
deleted file mode 100644
index e6db3d8..0000000
Binary files a/img/utl_icon.png and /dev/null differ
diff --git a/img/utl_icon_256x256.png b/img/utl_icon_256x256.png
deleted file mode 100644
index 68d6d0e..0000000
Binary files a/img/utl_icon_256x256.png and /dev/null differ
diff --git a/lang/en_US.qm b/lang/en_US.qm
deleted file mode 100644
index 6ea3657..0000000
Binary files a/lang/en_US.qm and /dev/null differ
diff --git a/lang/zh_CN.qm b/lang/zh_CN.qm
deleted file mode 100644
index d842be4..0000000
Binary files a/lang/zh_CN.qm and /dev/null differ
diff --git a/lang/zh_TW.qm b/lang/zh_TW.qm
deleted file mode 100644
index ca362e0..0000000
Binary files a/lang/zh_TW.qm and /dev/null differ
diff --git a/mac_res/.DS_Store b/mac_res/.DS_Store
deleted file mode 100644
index 54998dc..0000000
Binary files a/mac_res/.DS_Store and /dev/null differ
diff --git a/qthosts.qrc b/qthosts.qrc
deleted file mode 100644
index fc4f101..0000000
--- a/qthosts.qrc
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- img/utl_icon.png
-
-
- img/buttons/button_ansi.png
- img/buttons/button_ansi_disabled.png
- img/buttons/button_apply.png
- img/buttons/button_apply_disabled.png
- img/buttons/button_backup.png
- img/buttons/button_backup_disabled.png
- img/buttons/button_download.png
- img/buttons/button_download_disabled.png
- img/buttons/button_exit.png
- img/buttons/button_exit_disabled.png
- img/buttons/button_restore.png
- img/buttons/button_restore_disabled.png
- img/buttons/button_update.png
- img/buttons/button_update_disabled.png
- img/buttons/button_utf8.png
- img/buttons/button_utf8_disabled.png
-
-
diff --git a/img/buttons/button_ansi.png b/res/img/buttons/button_ansi.png
similarity index 100%
rename from img/buttons/button_ansi.png
rename to res/img/buttons/button_ansi.png
diff --git a/img/buttons/button_ansi_disabled.png b/res/img/buttons/button_ansi_disabled.png
similarity index 100%
rename from img/buttons/button_ansi_disabled.png
rename to res/img/buttons/button_ansi_disabled.png
diff --git a/img/buttons/button_apply.png b/res/img/buttons/button_apply.png
similarity index 100%
rename from img/buttons/button_apply.png
rename to res/img/buttons/button_apply.png
diff --git a/img/buttons/button_apply_disabled.png b/res/img/buttons/button_apply_disabled.png
similarity index 100%
rename from img/buttons/button_apply_disabled.png
rename to res/img/buttons/button_apply_disabled.png
diff --git a/img/buttons/button_backup.png b/res/img/buttons/button_backup.png
similarity index 100%
rename from img/buttons/button_backup.png
rename to res/img/buttons/button_backup.png
diff --git a/img/buttons/button_backup_disabled.png b/res/img/buttons/button_backup_disabled.png
similarity index 100%
rename from img/buttons/button_backup_disabled.png
rename to res/img/buttons/button_backup_disabled.png
diff --git a/img/buttons/button_download.png b/res/img/buttons/button_download.png
similarity index 100%
rename from img/buttons/button_download.png
rename to res/img/buttons/button_download.png
diff --git a/img/buttons/button_download_disabled.png b/res/img/buttons/button_download_disabled.png
similarity index 100%
rename from img/buttons/button_download_disabled.png
rename to res/img/buttons/button_download_disabled.png
diff --git a/img/buttons/button_exit.png b/res/img/buttons/button_exit.png
similarity index 100%
rename from img/buttons/button_exit.png
rename to res/img/buttons/button_exit.png
diff --git a/img/buttons/button_exit_disabled.png b/res/img/buttons/button_exit_disabled.png
similarity index 100%
rename from img/buttons/button_exit_disabled.png
rename to res/img/buttons/button_exit_disabled.png
diff --git a/img/buttons/button_restore.png b/res/img/buttons/button_restore.png
similarity index 100%
rename from img/buttons/button_restore.png
rename to res/img/buttons/button_restore.png
diff --git a/img/buttons/button_restore_disabled.png b/res/img/buttons/button_restore_disabled.png
similarity index 100%
rename from img/buttons/button_restore_disabled.png
rename to res/img/buttons/button_restore_disabled.png
diff --git a/img/buttons/button_update.png b/res/img/buttons/button_update.png
similarity index 100%
rename from img/buttons/button_update.png
rename to res/img/buttons/button_update.png
diff --git a/img/buttons/button_update_disabled.png b/res/img/buttons/button_update_disabled.png
similarity index 100%
rename from img/buttons/button_update_disabled.png
rename to res/img/buttons/button_update_disabled.png
diff --git a/img/buttons/button_utf8.png b/res/img/buttons/button_utf8.png
similarity index 100%
rename from img/buttons/button_utf8.png
rename to res/img/buttons/button_utf8.png
diff --git a/img/buttons/button_utf8_disabled.png b/res/img/buttons/button_utf8_disabled.png
similarity index 100%
rename from img/buttons/button_utf8_disabled.png
rename to res/img/buttons/button_utf8_disabled.png
diff --git a/res/img/icons/hosts_utl.icns b/res/img/icons/hosts_utl.icns
new file mode 100644
index 0000000..4743cba
Binary files /dev/null and b/res/img/icons/hosts_utl.icns differ
diff --git a/res/img/icons/hosts_utl.ico b/res/img/icons/hosts_utl.ico
new file mode 100644
index 0000000..4a76b01
Binary files /dev/null and b/res/img/icons/hosts_utl.ico differ
diff --git a/res/img/icons/utl_icon@256x256.png b/res/img/icons/utl_icon@256x256.png
new file mode 100644
index 0000000..cbc99fa
Binary files /dev/null and b/res/img/icons/utl_icon@256x256.png differ
diff --git a/res/img/icons/utl_icon@512x512.png b/res/img/icons/utl_icon@512x512.png
new file mode 100644
index 0000000..53e479e
Binary files /dev/null and b/res/img/icons/utl_icon@512x512.png differ
diff --git a/img/style/checkbox.png b/res/img/style/checkbox.png
similarity index 100%
rename from img/style/checkbox.png
rename to res/img/style/checkbox.png
diff --git a/img/style/down_arrow.png b/res/img/style/down_arrow.png
similarity index 100%
rename from img/style/down_arrow.png
rename to res/img/style/down_arrow.png
diff --git a/mac_res/HostsUtl.scpt b/res/mac/HostsUtl.scpt
similarity index 100%
rename from mac_res/HostsUtl.scpt
rename to res/mac/HostsUtl.scpt
diff --git a/mac_res/Info.plist b/res/mac/Info.plist
similarity index 100%
rename from mac_res/Info.plist
rename to res/mac/Info.plist
diff --git a/mac_res/dmg_resource/DS_Store_dmg b/res/mac/dmg/DS_Store_dmg
similarity index 67%
rename from mac_res/dmg_resource/DS_Store_dmg
rename to res/mac/dmg/DS_Store_dmg
index 7655ef6..bd9d541 100644
Binary files a/mac_res/dmg_resource/DS_Store_dmg and b/res/mac/dmg/DS_Store_dmg differ
diff --git a/mac_res/dmg_resource/background.png b/res/mac/dmg/background.png
similarity index 100%
rename from mac_res/dmg_resource/background.png
rename to res/mac/dmg/background.png
diff --git a/retrievedata.py b/retrievedata.py
deleted file mode 100644
index 93cef40..0000000
--- a/retrievedata.py
+++ /dev/null
@@ -1,310 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# retrievedata.py : Read data from the hosts data file
-#
-# Copyleft (C) 2013 - huhamhire hosts team
-# =====================================================================
-# Licensed under the GNU General Public License, version 3. You should
-# have received a copy of the GNU General Public License along with
-# this program. If not, see .
-#
-# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
-# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE.
-# =====================================================================
-
-__version__ = "0.8"
-__revision__ = "$Id$"
-__author__ = "huhamhire "
-
-__all__ = ["RetrieveData", "make_hosts"]
-
-import os
-import sqlite3
-import time
-import zipfile
-
-class RetrieveData(object):
- """A class to fetch data from data file
-
- RetrieveData class contains a set of tools to retrieve information from
- the hosts data file. All methods from this class are defined as class
- methods.
-
- Attributes:
- _conn (obj): An instance of sqlite3.connect object to set the
- connection with a SQLite database.
- _cur (obj): An instance of sqlite3.connect.cursor object to operate
- SQL queries in the database.
- _database (str): A string indicating the filename of a SQLite database
- file.
- """
- _conn = None
- _cur = None
- _database = None
-
- @classmethod
- def db_exists(cls, database="hostslist.s3db"):
- """Check if database exists - Class Method
-
- Check whether the database file exists or not.
-
- Args:
- database (str): A string indicating the SQLite database file.
- "hostslist.s3db" by default.
-
- Returns:
- A boolean indicating if the database file exists.
- """
- return os.path.isfile(database)
-
- @classmethod
- def connect_db(cls, database="hostslist.s3db"):
- """Connect to database - Class Method
-
- Set up connection with a SQLite database.
-
- Args:
- database (str): A string indicating the SQLite database file.
- "hostslist.s3db" by default.
- """
- cls._conn = sqlite3.connect(database)
- cls._cur = cls._conn.cursor()
- cls._database = database
-
- @classmethod
- def disconnect_db(cls):
- """Disconnect to database - Class Method
-
- Close the connection with a SQLite database.
- """
- cls._conn.close()
-
- @classmethod
- def get_info(cls):
- """Get data file information - Class Method
-
- Retrieve the metadata of current data file.
-
- Returns:
- A dictionary containing the metadata of current data file.
- """
- cls._cur.execute("SELECT sect, info FROM info")
- info = dict(cls._cur.fetchall())
- return info
-
- @classmethod
- def get_head(cls):
- """Get head info from data file - Class Method
-
- Retrieve the head information from hosts data file.
-
- Returns:
- A list containing hosts head information.
- """
- cls._cur.execute("SELECT str FROM hosts_head ORDER BY ln")
- head = cls._cur.fetchall()
- return head
-
- @classmethod
- def get_ids(cls, id_cfg):
- """Get id numbers - Class Method
-
- Calculate the id numbers covered by config word ({id_cfg}).
-
- Args:
- id_cfg (int): A hex number indicating the config word of id
- selections.
-
- Returns:
- A list containing the id numbers covered by config word.
- """
- cfg = bin(id_cfg)[:1:-1]
- ids = []
- for i, id_en in enumerate(cfg):
- if int(id_en):
- ids.append(0b1 << i)
- return ids
-
- @classmethod
- def get_host(cls, part_id, mod_id):
- """Get hosts entries - Class Method
-
- Retrieve the hosts of a specified module ({mod_id}) from a specified
- part ({part_id}) in the data file.
-
- Args:
- part_id (int): An integer indicating the id number of a specified
- part from the hosts data file
- mod_id (int): An integer indicating the id number of a specified
- module number from a specified part.
-
- Returns:
- (hosts, mod_name)
- hosts (list): A list containing hosts entries of a specified
- module.
- mod_name (str): A string indicating the name of a specified
- module.
- """
- cls._cur.execute("SELECT part_name FROM parts "
- "WHERE part_id=%s" % part_id)
- part_name = cls._cur.fetchone()[0]
- cls._cur.execute("SELECT ip, host FROM %s "
- "WHERE cate=%s" % (part_name, mod_id))
- hosts = cls._cur.fetchall()
- cls._cur.execute("SELECT mod_name FROM modules "
- "WHERE part_id=%s AND mod_id = %s" % (part_id, mod_id))
- mod_name = cls._cur.fetchone()[0]
- return hosts, mod_name
-
- @classmethod
- def get_choice(cls, flag_v6=False):
- """Get module selection choices - Class Method
-
- Retrieve module selection items from the hosts data file with default
- selection for users.
-
- Args:
- flag_v6 (bool): A bool flag indicating whether to receive the IPv6
- entries or the IPv4 ones. True: IPv6, False: IPv4.
-
- Returns:
- (modules, defaults, slices)
- modules (list): A list containing information of modules for users
- to select.
- defaults (dict): A dictionary containing default selection for
- selected parts.
- slices (list): A list containing the number of modules in each
- part.
- """
- ch_parts = (0x08, 0x20 if flag_v6 else 0x10, 0x40)
- cls._cur.execute("SELECT * FROM modules "
- "WHERE part_id IN (?, ?, ?)", ch_parts)
- modules = cls._cur.fetchall()
- cls._cur.execute("SELECT part_id, part_default FROM parts "
- "WHERE part_id IN (?, ?, ?)", ch_parts)
- default_cfg = cls._cur.fetchall()
- defaults = {}
- for default in default_cfg:
- defaults[default[0]] = cls.get_ids(default[1])
- slices = [0]
- for ch_part in ch_parts:
- cls._cur.execute("SELECT COUNT(mod_id) FROM modules "
- "WHERE part_id=?", (ch_part, ))
- slices.append(cls._cur.fetchone()[0])
- for s in range(1, len(slices)):
- slices[s] = slices[s] + slices[s - 1]
- return modules, defaults, slices
-
- @classmethod
- def chk_mutex(cls, part_id, mod_cfg):
- """Check conflict in selections - Class Method
-
- Check if there is conflict in user selections ({mod_cfg}) of a
- specified part ({part_id})
-
- Args:
- part_id (int): An integer indicating the id number of a specified
- part from the hosts data file
- mod_cfg (int): A hex number indicating the config word of id
- selections for a specified part.
-
- Returns:
- A bool flag indicating whether there is a conflict or not.
- True : Conflict, False: No conflicts.
- """
- cls._cur.execute("SELECT mod_id, mutex FROM modules "
- "WHERE part_id=%s" % part_id)
- mutex_tuple = dict(cls._cur.fetchall())
- mutex_info = []
- mod_info = cls.get_ids(mod_cfg)
- for mod_id in mod_info:
- mutex_info.extend(cls.get_ids(mutex_tuple[mod_id]))
- mutex_info = set(mutex_info)
- for mod_id in mod_info:
- if mod_id in mutex_info:
- return False
- return True
-
- @classmethod
- def unpack(cls, packfile="hostslist.data", dbfile="hostslist.s3db"):
- """Unpack local data file - Class Method
-
- Unzip the zipped data file ({packfile}) to a SQLite database file
- ({dbfile}).
- """
- datafile = zipfile.ZipFile(packfile, "r")
- datafile.extract(dbfile)
-
- @classmethod
- def clear(cls):
- """Clear up workspace - Class Method
-
- Close the connection to the database and delete the database file.
- """
- cls._conn.close()
- os.remove(cls._database)
-
-def make_hosts(cfgs, hostname):
- """Operations to make a hosts filename
-
- Make a new hosts file by data from the local data file.
-
- Args:
- cfgs (dict): A dictionary containing the hex config words for
- different parts of the data file.
- hostname (str): A string indicating the hostname of current operating
- system.
- """
- # Operations start
- start_time = time.time()
- hosts_file = open("hosts", "w")
- RetrieveData.unpack()
- RetrieveData.connect_db()
- # Fetches head section
- for head_str in RetrieveData.get_head():
- hosts_file.write("%s\n" % head_str[0])
- # Fetches info section
- info = RetrieveData.get_info()
- info_lines = ["#"]
- info_lines.append("# %s: %s" % ("Version", info["Version"]))
- info_lines.append("# %s: %s" % ("Buildtime", info["Buildtime"]))
- info_lines.append("# %s: %s" % ("Applytime", int(start_time)))
- info_lines.append("#")
- for line in info_lines:
- hosts_file.write("%s\n" % line)
- # Fetches hosts section
- for part_id in sorted(cfgs.keys()):
- mod_cfg = cfgs[part_id]
- if not RetrieveData.chk_mutex(part_id, mod_cfg):
- return
- mods = RetrieveData.get_ids(mod_cfg)
- if part_id == 0x02:
- # Retrieve localhost module
- for mod_id in mods:
- hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
- hosts_file.write("\n# Section Start: %s\n" % mod_name)
- for host in hosts:
- if hosts[1] == "#Replace Your Device Name Here!":
- hosts[1] = hostname
- hosts_file.write("%s %s\n" % (host[0], host[1]))
- hosts_file.write("# Section End: %s\n" % mod_name)
- else:
- # Retrieve common modules
- for mod_id in mods:
- hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
- hosts_file.write("\n# Section Start: %s\n" % mod_name)
- for host in hosts:
- hosts_file.write("%s %s\n" % (host[0], host[1]))
- hosts_file.write("# Section End: %s\n" % mod_name)
- hosts_file.close()
- RetrieveData.clear()
- # Operations end
- end_time = time.time()
- total_time = "%.4f" % (end_time - start_time)
-
-if __name__ == "__main__":
- # Module Test
- selection = {0x02: 0x0001, 0x08: 0x0001, 0x10: 0x003F, 0x40: 0x000F}
- make_hosts(selection, "TEST-PC")
diff --git a/style.qrc b/style.qrc
deleted file mode 100644
index 9c88d05..0000000
--- a/style.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- img/style/checkbox.png
- img/style/down_arrow.png
-
-
diff --git a/tui/__doc__.py b/tui/__doc__.py
new file mode 100644
index 0000000..7849776
--- /dev/null
+++ b/tui/__doc__.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __doc__.py Document in reST format of tui module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+"""
+.. _tui-module:
+
+Text-based User Interface (TUI)
+===============================
+
+The following sections describe the objects and methods from the Text-based
+User Interface (TUI) module of huhamhire-hosts HostsUtil. The methods to make
+TUI here are based on
+`curses `_.
+
+
+HostsUtil(TUI)
+--------------
+.. autoclass:: tui.hostsutil.HostsUtil
+ :members:
+
+ .. automethod:: tui.hostsutil.HostsUtil.__init__
+ .. automethod:: tui.hostsutil.HostsUtil.__del__
+
+
+CursesDaemon
+------------
+.. autoclass:: tui.curses_d.CursesDaemon
+ :members:
+
+
+CursesUI
+--------
+.. autoclass:: tui.curses_ui.CursesUI
+ :members:
+
+ .. automethod:: tui.curses_ui.CursesUI.__init__
+ .. automethod:: tui.curses_ui.CursesUI.__del__
+
+
+FetchUpdate
+-----------
+
+.. autoclass:: tui._update.FetchUpdate
+ :members:
+
+ .. automethod:: tui._update.FetchUpdate.__init__
+"""
\ No newline at end of file
diff --git a/tui/__init__.py b/tui/__init__.py
new file mode 100644
index 0000000..d53ebf1
--- /dev/null
+++ b/tui/__init__.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __init__.py: Declare modules to be called in tui module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+from hostsutil import HostsUtil
+
+__all__ = ["HostsUtil"]
diff --git a/tui/_update.py b/tui/_update.py
new file mode 100644
index 0000000..831bd6c
--- /dev/null
+++ b/tui/_update.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# _update.py: Retrieve hosts data file.
+#
+# Copyleft (C) 2013 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+import socket
+import urllib
+
+
+class FetchUpdate(object):
+ """
+ FetchUpdate class contains methods to retrieve the latest hosts data file
+ from the project server.
+
+ :ivar str url: The URL of the latest hosts data file.
+ :ivar str path: Destination path to save the data file downloaded.
+ :ivar str tmp_path: Temporary path to save the data file while
+ downloading.
+ :ivar int file_size: Size of the data file in bytes.
+ :ivar CursesDaemon parent: An instance of
+ :class:`~tui.curses_d.CursesDaemon` class to get configuration with.
+ """
+
+ def __init__(self, parent):
+ """
+ Initialize a new instance of this class
+
+ :param parent: An instance of :class:`~tui.curses_d.CursesDaemon`
+ class to get configuration with.
+ :type parent: :class:`~tui.curses_d.CursesDaemon`
+ """
+ mirror_id = parent.settings[0][1]
+ mirror = parent.settings[0][2][mirror_id]
+ self.url = mirror["update"] + parent.filename
+ self.path = "./" + parent.filename
+ self.tmp_path = self.path + ".download"
+ self.file_size = parent._update["size"]
+ self.parent = parent
+
+ def get_file(self):
+ """
+ Fetch the latest hosts data file from project server.
+ """
+ socket.setdefaulttimeout(10)
+ try:
+ urllib.urlretrieve(self.url, self.tmp_path,
+ self.parent.process_bar)
+ self.replace_old()
+ except Exception, e:
+ raise e
+
+ def replace_old(self):
+ """
+ Replace the old hosts data file with the new one.
+ """
+ if os.path.isfile(self.path):
+ os.remove(self.path)
+ os.rename(self.tmp_path, self.path)
\ No newline at end of file
diff --git a/tui/curses_d.py b/tui/curses_d.py
new file mode 100644
index 0000000..4031cd3
--- /dev/null
+++ b/tui/curses_d.py
@@ -0,0 +1,511 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# curses_d.py: Operations for TUI window.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import curses
+import json
+import os
+import shutil
+import socket
+import urllib
+import sys
+
+from curses_ui import CursesUI
+from _update import FetchUpdate
+
+sys.path.append("..")
+from util import CommonUtil, RetrieveData
+from util import MakeHosts
+
+
+class CursesDaemon(CursesUI):
+ """
+ CursesDaemon class contains methods to deal with the operations related to
+ the TUI window of `Hosts Setup Utility`. Including methods to interactive
+ with users.
+
+ .. note:: This class is subclass of :class:`~tui.curses_ui.CursesUI`
+ class.
+
+ :ivar dict _update: Update information of the current data file on server.
+ :ivar int _writable: Indicating whether the program is run with admin/root
+ privileges. The value could be `1` or `0`.
+
+ .. seealso:: `_update` and `_writable` in
+ :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ :ivar dict make_cfg: A set of module selection control bytes used to
+ control whether a specified method is used or not while generate a
+ hosts file.
+
+ * `Keys` of :attr:`make_cfg` are typically 8-bit control byte
+ indicating which part of the hosts data file would be effected by
+ the corresponding `Value`.
+
+ +----+----------------+
+ |Key |Part |
+ +====+================+
+ |0x02|Localhost |
+ +----+----------------+
+ |0x08|Shared hosts |
+ +----+----------------+
+ |0x10|IPv4 hosts |
+ +----+----------------+
+ |0x20|IPv6 hosts |
+ +----+----------------+
+ |0x40|AD block hosts |
+ +----+----------------+
+
+ * `Values` of :attr:`make_cfg` are typically 16-bit control bytes that
+ decides which of the modules in a specified part would be inserted
+ into the `hosts` file.
+
+ * `Value` of `Localhost` part. The Value used in `Localhost` part
+ are usually bytes indicating the current operating system.
+
+ +---------------+-------------------+
+ |Hex |OS |
+ +===============+===================+
+ |0x0001 |Windows |
+ +---------------+-------------------+
+ |0x0002 |Linux, Unix |
+ +---------------+-------------------+
+ |0x0004 |Mac OS X |
+ +---------------+-------------------+
+
+ * `Values` of `Shared hosts`, `IPv4 hosts`, `IPv6 hosts`, and
+ `AD block hosts` parts are usually sum of module IDs selected
+ by user.
+
+ .. note::
+ If modules in specified part whose IDs are `0x0002` and
+ `0x0010`, the value here should be `0x0002 + 0x0010 = 0x0012`,
+ which is `0b0000000000000010 + 0b0000000000010000 =
+ 0b0000000000010010` in binary.
+
+ .. warning::
+ Only one bit could be `1` in the binary form of a module ID,
+ which means `0b0000000000010010` is an INVALID module ID while
+ it could be a VALID `Value` in `make_cfg`.
+
+ :ivar str platform: Platform of current operating system. The value could
+ be `Windows`, `Linux`, `Unix`, `OS X`, and of course `Unknown`.
+ :ivar str hostname: The hostname of current operating system.
+
+ .. note:: This attribute would only be used on linux.
+
+ :ivar str hosts_path: The absolute path to the hosts file on current
+ operating system.
+ :ivar str make_mode: Operation mode for making hosts file. The valid value
+ could be one of `system`, `ansi`, and `utf-8`.
+
+ .. seealso:: :attr:`make_mode` in
+ :class:`~util.makehosts.MakeHosts` class.
+
+ :ivar str make_path: Temporary path to store generated hosts file. The
+ default value of :attr:`make_path` is "`./hosts`".
+ :ivar list _ops_keys: Hot keys used to start a specified operation.
+ Default operation keys are `F5`, `F6`, and `F10`.
+ :ivar list _hot_keys: Hot keys used to select a item or confirm an
+ operation. And the default :attr:`_hot_keys` is defined as::
+
+ _hot_keys = [curses.KEY_UP, curses.KEY_DOWN, 10, 32]
+
+ .. seealso:: :attr:`~tui.curses_ui.CursesUI.funckeys` in
+ :class:`~tui.curses_ui.CursesUI` class.
+ """
+ _update = {}
+ _writable = 0
+
+ make_cfg = {}
+ platform = ''
+ hostname = ''
+ hosts_path = ''
+
+ make_mode = ''
+ make_path = "./hosts"
+
+ _ops_keys = [curses.KEY_F5, curses.KEY_F6, curses.KEY_F10]
+ _hot_keys = [curses.KEY_UP, curses.KEY_DOWN, 10, 32]
+
+ def __init__(self):
+ super(CursesDaemon, self).__init__()
+ self.check_writable()
+
+ def check_writable(self):
+ """
+ Check if current session has write privileges to the hosts file.
+
+ .. note:: IF current session does not has the write privileges to the
+ hosts file of current system, a warning message box would popup.
+
+ .. note:: ALL operation would change the `hosts` file on current
+ system could only be done while current session has write
+ privileges to the file.
+ """
+ self._writable = CommonUtil.check_privileges()[1]
+ if not self._writable:
+ self.messagebox("Please check if you have writing\n"
+ "privileges to the hosts file!", 1)
+ exit()
+
+ def session_daemon(self):
+ """
+ Operations processed while running a TUI session of `Hosts Setup
+ Utility`.
+
+ :return: A flag indicating whether to reload the current session or
+ all operations have been finished. The return value could only be
+ `0` or `1`. To be specific:
+
+ ==== =========
+ flag operation
+ ==== =========
+ 0 Finish
+ 1 Reload
+ ==== =========
+
+ .. note:: Reload operation is called only when a new data file is
+ retrieved from server.
+
+ :rtype: int
+
+ .. note:: IF hosts data file does not exists in current working
+ directory, a warning message box would popup. And operations to
+ change the hosts file on current system could be done only until
+ a new data file has been downloaded.
+ """
+
+ screen = self._stdscr.subwin(0, 0, 0, 0)
+ screen.keypad(1)
+ # Draw Menu
+ self.banner()
+ self.footer()
+ # Key Press Operations
+ key_in = None
+ tab = 0
+ pos = 0
+ tab_entry = [self.configure_settings, self.select_func]
+ while key_in != 27:
+ self.setup_menu()
+ self.status()
+ self.process_bar(0, 0, 0, 0)
+ for i, sec in enumerate(tab_entry):
+ tab_entry[i](pos if i == tab else None)
+ if key_in is None:
+ test = self.settings[0][2][0]["test_url"]
+ self.check_connection(test)
+ key_in = screen.getch()
+ if key_in == 9:
+ if self.choice == [[], []]:
+ tab = 0
+ else:
+ tab = not tab
+ pos = 0
+ elif key_in in self._hot_keys:
+ pos = tab_entry[tab](pos, key_in)
+ elif key_in in self._ops_keys:
+ if key_in == curses.KEY_F10:
+ if self._funcs == [[], []]:
+ self.messagebox("No data file found! Press F6 to get "
+ "data file first.", 1)
+ else:
+ msg = "Apply Changes to hosts file?"
+ confirm = self.messagebox(msg, 2)
+ if confirm:
+ self.set_config_bytes()
+ self.make_mode = "system"
+ maker = MakeHosts(self)
+ maker.make()
+ self.move_hosts()
+ elif key_in == curses.KEY_F5:
+ self._update = self.check_update()
+ elif key_in == curses.KEY_F6:
+ if self._update == {}:
+ self._update = self.check_update()
+ # Check if data file up-to-date
+ if self.new_version():
+ self.fetch_update()
+ return 1
+ else:
+ self.messagebox("Data file is up-to-date!", 1)
+ else:
+ pass
+ return 0
+
+ def configure_settings(self, pos=None, key_in=None):
+ """
+ Perform operations to config settings if `Configure Setting` frame is
+ active, or just draw the `Configure Setting` frame with no items
+ selected while it is inactive.
+
+ .. note:: Whether the `Configure Setting` frame is inactive is decided
+ by if :attr:`pos` is `None` or not.
+
+ =========== ========
+ :attr:`pos` Status
+ =========== ========
+ None Inactive
+ int Active
+ =========== ========
+
+ :param pos: Index of selected item in `Configure Setting` frame. The
+ default value of `pos` is `None`.
+ :type pos: int or None
+ :param key_in: A flag indicating the key pressed by user. The default
+ value of `key_in` is `None`.
+ :type key_in: int or None
+ :return: Index of selected item in `Configure Setting` frame.
+ :rtype: int or None
+ """
+ id_num = range(len(self.settings))
+ if pos is not None:
+ if key_in == curses.KEY_DOWN:
+ pos = list(id_num[1:] + id_num[:1])[pos]
+ elif key_in == curses.KEY_UP:
+ pos = list(id_num[-1:] + id_num[:-1])[pos]
+ elif key_in in [10, 32]:
+ self.sub_selection(pos)
+ self.info(pos, 0)
+ self.configure_settings_frame(pos)
+ return pos
+
+ def select_func(self, pos=None, key_in=None):
+ """
+ Perform operations if `function selection list` is active, or just
+ draw the `function selection list` with no items selected while it is
+ inactive.
+
+ .. note:: Whether the `function selection list` is inactive is decided
+ by if :attr:`pos` is `None` or not.
+
+ .. seealso:: :meth:`~tui.curses_d.CursesDaemon.configure_settings`.
+
+ :param pos: Index of selected item in `function selection list`. The
+ default value of `pos` is `None`.
+ :type pos: int or None
+ :param key_in: A flag indicating the key pressed by user. The default
+ value of `key_in` is `None`.
+ :type key_in: int or None
+ :return: Index of selected item in `function selection list`.
+ :rtype: int or None
+ """
+ list_height = 15
+ ip = self.settings[1][1]
+ # Key Press Operations
+ item_len = len(self.choice[ip])
+ item_sup, item_inf = self._item_sup, self._item_inf
+ if pos is not None:
+ if item_len > list_height:
+ if pos <= 1:
+ item_sup = 0
+ item_inf = list_height - 1
+ elif pos >= item_len - 2:
+ item_sup = item_len - list_height + 1
+ item_inf = item_len
+ else:
+ item_sup = 0
+ item_inf = item_len
+ if key_in == curses.KEY_DOWN:
+ pos += 1
+ if pos >= item_len:
+ pos = 0
+ if pos not in range(item_sup, item_inf):
+ item_sup += 2 if item_sup == 0 else 1
+ item_inf += 1
+ elif key_in == curses.KEY_UP:
+ pos -= 1
+ if pos < 0:
+ pos = item_len - 1
+ if pos not in range(item_sup, item_inf):
+ item_inf -= 2 if item_inf == item_len else 1
+ item_sup -= 1
+ elif key_in in [10, 32]:
+ self._funcs[ip][pos] = not self._funcs[ip][pos]
+ mutex = RetrieveData.get_ids(self.choice[ip][pos][2])
+ for c_id, c in enumerate(self.choice[ip]):
+ if c[0] == self.choice[ip][pos][0]:
+ if c[1] in mutex and self._funcs[ip][c_id] == 1:
+ self._funcs[ip][c_id] = 0
+ self.info(pos, 1)
+ else:
+ item_sup = 0
+ if item_len > list_height:
+ item_inf = list_height - 1
+ else:
+ item_inf = item_len
+ self.show_funclist(pos, item_sup, item_inf)
+ return pos
+
+ def sub_selection(self, pos):
+ """
+ Let user to choose settings from `Selection Dialog` specified by
+ :attr:`pos`.
+
+ :param pos: Index of selected item in `Configure Setting` frame.
+ :type pos: int
+
+ .. warning:: The value of `pos` MUST NOT be `None`.
+
+ .. seealso:: :meth:`~tui.curses_ui.CursesUI.sub_selection_dialog` in
+ :class:`~tui.curses_ui.CursesUI`.
+ """
+ screen = self.sub_selection_dialog(pos)
+ i_pos = self.settings[pos][1]
+ # Key Press Operations
+ id_num = range(len(self.settings[pos][2]))
+ key_in = None
+ while key_in != 27:
+ self.sub_selection_dialog_items(pos, i_pos, screen)
+ key_in = screen.getch()
+ if key_in == curses.KEY_DOWN:
+ i_pos = list(id_num[1:] + id_num[:1])[i_pos]
+ elif key_in == curses.KEY_UP:
+ i_pos = list(id_num[-1:] + id_num[:-1])[i_pos]
+ elif key_in in [10, 32]:
+ if pos == 0 and i_pos != self.settings[pos][1]:
+ test = self.settings[pos][2][i_pos]["test_url"]
+ self.check_connection(test)
+ self.settings[pos][1] = i_pos
+ return
+
+ def check_connection(self, url):
+ """
+ Check connection status to the server currently selected by user and
+ show a status box indicating current operation.
+
+ :param url: The link of the server chose by user.This string could be
+ a domain name or the IP address of a server.
+
+ .. seealso:: :attr:`link` in
+ :meth:`~util.common.CommonUtil.check_connection`.
+ :type url: str
+ :return: A flag indicating connection status is good or not.
+
+ .. seealso:: :meth:`~util.common.CommonUtil.check_connection`. in
+ :class:`~util.common.CommonUtil` class.
+ :rtype: int
+ """
+ self.messagebox("Checking Server Status...")
+ conn = CommonUtil.check_connection(url)
+ if conn:
+ self.statusinfo[0][1] = "OK"
+ self.statusinfo[0][2] = "GREEN"
+ else:
+ self.statusinfo[0][1] = "Error"
+ self.statusinfo[0][2] = "RED"
+ self.status()
+ return conn
+
+ def check_update(self):
+ """
+ Check the metadata of the latest hosts data file from server and
+ show a status box indicating current operation.
+
+ :return: A dictionary containing the `Version`, `Release Date` of
+ current hosts data file and the `Latest Version` of the data file
+ on server.
+
+ IF error occurs while checking update, the dictionary would be
+ defined as::
+
+ {"version": "[Error]"}
+ :rtype: dict
+ """
+ self.messagebox("Checking Update...")
+ srv_id = self.settings[0][1]
+ url = self.settings[0][2][srv_id]["update"] + self.infofile
+ try:
+ socket.setdefaulttimeout(5)
+ url_obj = urllib.urlopen(url)
+ j_str = url_obj.read()
+ url_obj.close()
+ info = json.loads(j_str)
+ except:
+ info = {"version": "[Error]"}
+ self.hostsinfo["Latest"] = info["version"]
+ self.status()
+ return info
+
+ def new_version(self):
+ """
+ Compare version of local data file to the version from the server.
+
+ :return: A flag indicating whether the local data file is up-to-date
+ or not.
+
+ ====== ============================================
+ Return Data file status
+ ====== ============================================
+ 1 The version of data file on server is newer.
+ 0 The local data file is up-to-date.
+ ====== ============================================
+ :rtype: int
+ """
+ local_ver = self.hostsinfo["Version"]
+ if local_ver == "N/A":
+ return 1
+ server_ver = self._update["version"]
+ local_ver = local_ver.split('.')
+ server_ver = server_ver.split('.')
+ for i, ver_num in enumerate(local_ver):
+ if server_ver[i] > ver_num:
+ return 1
+ return 0
+
+ def fetch_update(self):
+ """
+ Retrieve the latest hosts data file from server and show a status box
+ indicating current operation.
+ """
+ self.messagebox("Downloading...")
+ fetch_d = FetchUpdate(self)
+ fetch_d.get_file()
+
+ def set_config_bytes(self):
+ """
+ Calculate the module configuration byte words by the selection from
+ function list on the main dialog.
+ """
+ ip_flag = self.settings[1][1]
+ selection = {}
+ localhost_word = {
+ "Windows": 0x0001, "Linux": 0x0002,
+ "Unix": 0x0002, "OS X": 0x0004}[self.platform]
+ selection[0x02] = localhost_word
+ ch_parts = [0x08, 0x20 if ip_flag else 0x10, 0x40]
+ # Set customized module if exists
+ if os.path.isfile(self.custom):
+ ch_parts.insert(0, 0x04)
+ slices = self.slices[ip_flag]
+ for i, part in enumerate(ch_parts):
+ part_cfg = self._funcs[ip_flag][slices[i]:slices[i + 1]]
+ part_word = 0
+ for i, cfg in enumerate(part_cfg):
+ part_word += cfg << i
+ selection[part] = part_word
+ self.make_cfg = selection
+
+ def move_hosts(self):
+ """
+ Move hosts file to the system path after making operations are
+ finished.
+ """
+ filepath = "hosts"
+ try:
+ shutil.copy2(filepath, self.hosts_path)
+ except IOError:
+ os.remove(filepath)
+ return
+ os.remove(filepath)
+ self.messagebox("Operation completed!", 1)
diff --git a/tui/curses_ui.py b/tui/curses_ui.py
new file mode 100644
index 0000000..3e0328b
--- /dev/null
+++ b/tui/curses_ui.py
@@ -0,0 +1,584 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# curses_ui.py: Draw TUI using curses.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import curses
+import locale
+
+import sys
+sys.path.append("..")
+from util import CommonUtil
+from __version__ import __version__, __release__
+
+
+class CursesUI(object):
+ """
+ CursesUI class contains methods to draw the Text-based User Interface
+ (TUI) of Hosts Setup Utility. The methods to make TUI here are based on
+ `curses `_.
+
+ :ivar str __title: Title of the TUI utility.
+ :ivar str __copyleft: Copyleft information of the TUI utility.
+ :ivar WindowObject _stdscr: A **WindowObject** which represents the whole
+ screen.
+ :ivar int _item_sup: Upper bound of item index from `function selection
+ list`.
+ :ivar int _item_inf: Lower bound of item index from `function selection
+ list`.
+ :ivar str _make_path: Temporary path to store the hosts file in while
+ building. The default _make_path is `./hosts`.
+ :ivar list _funcs: Two lists with the information of function list both
+ for IPv4 and IPv6 environment.
+ :ivar list choice: Two lists with the selection of functions both
+ for IPv4 and IPv6 environment.
+ :ivar list slices: Two lists with integers indicating the number of
+ function items from different parts listed in the function list.
+
+ .. seealso:: `_funcs`, `choice`, and `slices` in
+ :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ :ivar str sys_eol: The End-Of-Line marker. This maker could typically be
+ one of `CR`, `LF`, or `CRLF`.
+
+ .. seealso:: :attr:`sys_eol` in
+ :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ :ivar list colorpairs: Tuples of `(foreground-color, background-color)`
+ used while drawing TUI.
+
+ The default colorpairs is defined as::
+
+ colorpairs = [(curses.COLOR_WHITE, curses.COLOR_BLUE),
+ (curses.COLOR_WHITE, curses.COLOR_RED),
+ (curses.COLOR_YELLOW, curses.COLOR_BLUE),
+ (curses.COLOR_BLUE, curses.COLOR_WHITE),
+ (curses.COLOR_WHITE, curses.COLOR_WHITE),
+ (curses.COLOR_BLACK, curses.COLOR_WHITE),
+ (curses.COLOR_GREEN, curses.COLOR_WHITE),
+ (curses.COLOR_WHITE, curses.COLOR_BLACK),
+ (curses.COLOR_RED, curses.COLOR_WHITE), ]
+
+ :ivar list settings: Two list containing the server selection and IP
+ protocol version of current session.
+
+ The settings should be like::
+
+ settings = [["Server", 0, []],
+ ["IP Version", 0, ["IPv4", "IPv6"]]]
+
+ :ivar list funckeys: Lists of hot keys with their function to be shown on
+ TUI.
+
+ The default :attr:`funckeys` is defined as::
+
+ funckeys = [["", "Select Item"], ["Tab", "Select Field"],
+ ["Enter", "Set Item"], ["F5", "Check Update"],
+ ["F6", "Fetch Update"], ["F10", "Apply Changes"],
+ ["Esc", "Exit"]]
+
+ :ivar list statusinfo: Two lists containing the connection and OS checking
+ status of current session.
+
+ The default :attr:`statusinfo` is defined as::
+
+ statusinfo = [["Connection", "N/A", "GREEN"],
+ ["OS", "N/A", "GREEN"]]
+
+ :ivar dict hostsinfo: Containing the `Version`, `Release Date` of current
+ hosts data file and the `Latest Version` of the data file on server.
+
+ The default hostsinfo is defined as::
+
+ hostsinfo = {"Version": "N/A", "Release": "N/A", "Latest": "N/A"}
+
+ .. note:: IF the hosts data file does NOT exist in current working
+ directory, OR the file metadata has NOT been checked, the values
+ here would just be `N/A`.
+
+ :ivar str filename: Filename of the hosts data file containing data to
+ make hosts files from. Default by "`hostslist.data`".
+ :ivar str infofile: Filename of the info file containing metadata of the
+ hosts data file formatted in JSON. Default by "`hostslist.json`".
+
+ .. seealso:: :attr:`filename` and :attr:`infofile` in
+ :class:`~gui.hostsutil.HostsUtil` class.
+
+ :ivar str custom: File name of User Customized Hosts File. Customized
+ hosts would be able to select if this file exists. The default file
+ name is ``custom.hosts``.
+
+ .. seealso:: :ref:`User Customized Hosts`.
+ """
+ __title = "HOSTS SETUP UTILITY"
+ version = "".join(['v', __version__, ' ', __release__])
+ __copyleft = "%s Copyleft 2011-2014, huhamhire-hosts Team" % version
+
+ _stdscr = None
+ _item_sup = 0
+ _item_inf = 0
+
+ _make_path = "./hosts"
+ _funcs = [[], []]
+ choice = [[], []]
+ slices = [[], []]
+ sys_eol = ""
+
+ colorpairs = [(curses.COLOR_WHITE, curses.COLOR_BLUE),
+ (curses.COLOR_WHITE, curses.COLOR_RED),
+ (curses.COLOR_YELLOW, curses.COLOR_BLUE),
+ (curses.COLOR_BLUE, curses.COLOR_WHITE),
+ (curses.COLOR_WHITE, curses.COLOR_WHITE),
+ (curses.COLOR_BLACK, curses.COLOR_WHITE),
+ (curses.COLOR_GREEN, curses.COLOR_WHITE),
+ (curses.COLOR_WHITE, curses.COLOR_BLACK),
+ (curses.COLOR_RED, curses.COLOR_WHITE), ]
+
+ settings = [["Server", 0, []],
+ ["IP Version", 0, ["IPv4", "IPv6"]]]
+ funckeys = [["", "Select Item"], ["Tab", "Select Field"],
+ ["Enter", "Set Item"], ["F5", "Check Update"],
+ ["F6", "Fetch Update"], ["F10", "Apply Changes"],
+ ["Esc", "Exit"]]
+ statusinfo = [["Connection", "N/A", "GREEN"], ["OS", "N/A", "GREEN"]]
+ hostsinfo = {"Version": "N/A", "Release": "N/A", "Latest": "N/A"}
+
+ filename = "hostslist.data"
+ infofile = "hostsinfo.json"
+ custom = "custom.hosts"
+
+ def __init__(self):
+ """
+ Initialize a new TUI window in terminal.
+ """
+ locale.setlocale(locale.LC_ALL, '')
+ self._stdscr = curses.initscr()
+ curses.start_color()
+ curses.noecho()
+ curses.cbreak()
+ curses.curs_set(0)
+ # Set colors
+ curses.use_default_colors()
+ for i, color in enumerate(self.colorpairs):
+ curses.init_pair(i + 1, *color)
+
+ def __del__(self):
+ """
+ Reset terminal before quit.
+ """
+ curses.nocbreak()
+ curses.echo()
+ curses.endwin()
+
+ def banner(self):
+ """
+ Draw the banner in the TUI window.
+ """
+ screen = self._stdscr.subwin(2, 80, 0, 0)
+ screen.bkgd(' ', curses.color_pair(1))
+ # Set local variable
+ title = curses.A_NORMAL
+ title += curses.A_BOLD
+ normal = curses.color_pair(4)
+ # Print title
+ screen.addstr(0, 0, self.__title.center(79), title)
+ screen.addstr(1, 0, "Setup".center(10), normal)
+ screen.refresh()
+
+ def footer(self):
+ """
+ Draw the footer in the TUI window.
+ """
+ screen = self._stdscr.subwin(1, 80, 23, 0)
+ screen.bkgd(' ', curses.color_pair(1))
+ # Set local variable
+ normal = curses.A_NORMAL
+ # Copyright info
+ copyleft = self.__copyleft
+ screen.addstr(0, 0, copyleft.center(79), normal)
+ screen.refresh()
+
+ def configure_settings_frame(self, pos=None):
+ """
+ Draw `Configure Setting` frame with a index number (`pos`) of the item
+ selected.
+
+ :param pos: Index of selected item in `Configure Setting` frame. The
+ default value of `pos` is `None`.
+ :type pos: int or None
+
+ .. note:: None of the items in `Configure Setting` frame would be
+ selected if pos is `None`.
+ """
+ self._stdscr.keypad(1)
+ screen = self._stdscr.subwin(8, 25, 2, 0)
+ screen.bkgd(' ', curses.color_pair(4))
+ # Set local variable
+ normal = curses.A_NORMAL
+ select = curses.color_pair(5)
+ select += curses.A_BOLD
+
+ for p, item in enumerate(self.settings):
+ item_str = item[0].ljust(12)
+ screen.addstr(3 + p, 2, item_str, select if p == pos else normal)
+ if p:
+ choice = "[%s]" % item[2][item[1]]
+ else:
+ choice = "[%s]" % item[2][item[1]]["label"]
+ screen.addstr(3 + p, 15, ''.ljust(10), normal)
+ screen.addstr(3 + p, 15, choice, select if p == pos else normal)
+ screen.refresh()
+
+ def status(self):
+ """
+ Draw `Status` frame and `Hosts File` frame in the TUI window.
+ """
+ screen = self._stdscr.subwin(11, 25, 10, 0)
+ screen.bkgd(' ', curses.color_pair(4))
+ # Set local variable
+ normal = curses.A_NORMAL
+ green = curses.color_pair(7)
+ red = curses.color_pair(9)
+ # Status info
+ for i, stat in enumerate(self.statusinfo):
+ screen.addstr(2 + i, 2, stat[0], normal)
+ stat_str = ''.join(['[', stat[1], ']']).ljust(9)
+ screen.addstr(2 + i, 15, stat_str,
+ green if stat[2] == "GREEN" else red)
+ # Hosts file info
+ i = 0
+ for key, info in self.hostsinfo.items():
+ screen.addstr(7 + i, 2, key, normal)
+ screen.addstr(7 + i, 15, info, normal)
+ i += 1
+ screen.refresh()
+
+ def show_funclist(self, pos, item_sup, item_inf):
+ """
+ Draw `function selection list` frame with a index number of the item
+ selected and the range of items to be displayed.
+
+ :param pos: Index of selected item in `function selection list`.
+ :type pos: int or None
+ :param item_sup: Upper bound of item index from `function selection
+ list`.
+ :type item_sup: int
+ :param item_inf: Lower bound of item index from `function selection
+ list`.
+ :type item_inf: int
+ """
+ # Set UI variable
+ screen = self._stdscr.subwin(18, 26, 2, 26)
+ screen.bkgd(' ', curses.color_pair(4))
+ normal = curses.A_NORMAL
+ select = curses.color_pair(5)
+ select += curses.A_BOLD
+ list_height = 15
+ # Set local variable
+ ip = self.settings[1][1]
+ item_len = len(self.choice[ip])
+ # Function list
+ items_show = self.choice[ip][item_sup:item_inf]
+ items_selec = self._funcs[ip][item_sup:item_inf]
+ for p, item in enumerate(items_show):
+ sel_ch = '+' if items_selec[p] else ' '
+ item_str = ("[%s] %s" % (sel_ch, item[3])).ljust(23)
+ item_pos = pos - item_sup if pos is not None else None
+ highlight = select if p == item_pos else normal
+ if item_len > list_height:
+ if item_inf - item_sup == list_height - 2:
+ screen.addstr(4 + p, 2, item_str, highlight)
+ elif item_inf == item_len:
+ screen.addstr(4 + p, 2, item_str, highlight)
+ elif item_sup == 0:
+ screen.addstr(3 + p, 2, item_str, highlight)
+ else:
+ screen.addstr(3 + p, 2, item_str, highlight)
+ if item_len > list_height:
+ if item_inf - item_sup == list_height - 2:
+ screen.addstr(3, 2, " More ".center(23, '.'), normal)
+ screen.addch(3, 15, curses.ACS_UARROW)
+ screen.addstr(17, 2, " More ".center(23, '.'), normal)
+ screen.addch(17, 15, curses.ACS_DARROW)
+ elif item_inf == item_len:
+ screen.addstr(3, 2, " More ".center(23, '.'), normal)
+ screen.addch(3, 15, curses.ACS_UARROW)
+ elif item_sup == 0:
+ screen.addstr(17, 2, " More ".center(23, '.'), normal)
+ screen.addch(17, 15, curses.ACS_DARROW)
+ else:
+ for line_i in range(list_height - item_len):
+ screen.addstr(17 - line_i, 2, ' ' * 23, normal)
+ if not items_show:
+ screen.addstr(4, 2, "No data file!".center(23), normal)
+ screen.refresh()
+ self._item_sup, self._item_inf = item_sup, item_inf
+
+ def info(self, pos, tab):
+ """
+ Draw `Information` frame with a index number (`pos`) of the item
+ selected from the frame specified by `tab`.
+
+ :param pos: Index of selected item in a specified frame.
+ :type pos: int
+ :param tab: Index of the frame to select items from.
+ :type tab: int
+
+ .. warning:: Both of `pos` and `tab` in this method could not be
+ set to `None`.
+ """
+ screen = self._stdscr.subwin(18, 24, 2, 52)
+ screen.bkgd(' ', curses.color_pair(4))
+ normal = curses.A_NORMAL
+ if tab:
+ ip = self.settings[1][1]
+ info_str = self.choice[ip][pos][3]
+ else:
+ info_str = self.settings[pos][0]
+ # Clear Expired Infomotion
+ for i in range(6):
+ screen.addstr(1 + i, 2, ''.ljust(22), normal)
+ screen.addstr(1, 2, info_str, normal)
+ # Key Info Offset
+ k_info_y = 10
+ k_info_x_key = 2
+ k_info_x_text = 10
+ # Arrow Keys
+ screen.addch(k_info_y, k_info_x_key, curses.ACS_UARROW, normal)
+ screen.addch(k_info_y, k_info_x_key + 1, curses.ACS_DARROW, normal)
+ # Show Key Info
+ for i, keyinfo in enumerate(self.funckeys):
+ screen.addstr(k_info_y + i, k_info_x_key, keyinfo[0], normal)
+ screen.addstr(k_info_y + i, k_info_x_text, keyinfo[1], normal)
+ screen.refresh()
+
+ def process_bar(self, done, block, total, mode=1):
+ """
+ Draw `Process Bar` at the bottom which is used to indicate progress of
+ downloading operation.
+
+ .. note:: This method is a callback function responses to
+ :meth:`urllib.urlretrieve` method while fetching hosts data
+ file.
+
+ :param done: Block count of packaged retrieved.
+ :type done: int
+ :param block: Block size of the data pack retrieved.
+ :type block: int
+ :param total: Total size of the hosts data file.
+ :type total: int
+ :param mode: A flag indicating the status of `Process Bar`.
+ The default value of `mode` is `1`.
+
+ ==== ====================================
+ mode `Process Bar` status
+ ==== ====================================
+ 1 Downloading operation is processing.
+ 0 Not downloading.
+ ==== ====================================
+ :type mode: int
+ """
+ screen = self._stdscr.subwin(2, 80, 20, 0)
+ screen.bkgd(' ', curses.color_pair(4))
+ normal = curses.A_NORMAL
+ line_width = 76
+ prog_len = line_width - 20
+ # Progress Bar
+ if mode:
+ done *= block
+ prog = 1.0 * prog_len * done / total
+ progress = ''.join(['=' * int(prog), '-' * int(2 * prog % 2)])
+ progress = progress.ljust(prog_len)
+ total = CommonUtil.convert_size(total).ljust(7)
+ done = CommonUtil.convert_size(done).rjust(7)
+ else:
+ progress = ' ' * prog_len
+ done = total = 'N/A'.center(7)
+ # Show Progress
+ prog_bar = "[%s] %s | %s" % (progress, done, total)
+ screen.addstr(1, 2, prog_bar, normal)
+ screen.refresh()
+
+ def sub_selection_dialog(self, pos):
+ """
+ Draw a `Selection Dialog` on screen used to make configurations.
+
+ :param pos: Index of selected item in `Configure Setting` frame.
+ :type pos: int
+
+ .. warning:: The value of `pos` MUST NOT be `None`.
+
+ :return: A **WindowObject** which represents the selection dialog.
+ :rtype: WindowObject
+ """
+ i_len = len(self.settings[pos][2])
+ # Draw Shadow
+ shadow = curses.newwin(i_len + 2, 18, 13 - i_len / 2, 31)
+ shadow.bkgd(' ', curses.color_pair(8))
+ shadow.refresh()
+ # Draw Subwindow
+ screen = curses.newwin(i_len + 2, 18, 12 - i_len / 2, 30)
+ screen.box()
+ screen.bkgd(' ', curses.color_pair(1))
+ screen.keypad(1)
+ # Set local variable
+ normal = curses.A_NORMAL
+ # Title of Subwindow
+ screen.addstr(0, 3, self.settings[pos][0].center(12), normal)
+ return screen
+
+ def sub_selection_dialog_items(self, pos, i_pos, screen):
+ """
+ Draw items in `Selection Dialog`.
+
+ :param pos: Index of selected item in `Configure Setting` frame.
+ :type pos: int
+ :param i_pos: Index of selected item in `Selection Dialog`.
+ :type i_pos: int
+ :param screen: A **WindowObject** which represents the selection
+ dialog.
+ :type screen: WindowObject
+ """
+ # Set local variable
+ normal = curses.A_NORMAL
+ select = normal + curses.A_BOLD
+ for p, item in enumerate(self.settings[pos][2]):
+ item_str = item if pos else item["tag"]
+ screen.addstr(1 + p, 2, item_str,
+ select if p == i_pos else normal)
+ screen.refresh()
+
+ def setup_menu(self):
+ """
+ Draw the main frame of `Setup` tab in the TUI window.
+ """
+ screen = self._stdscr.subwin(21, 80, 2, 0)
+ screen.box()
+ screen.bkgd(' ', curses.color_pair(4))
+ # Configuration Section
+ screen.addch(0, 26, curses.ACS_BSSS)
+ screen.vline(1, 26, curses.ACS_VLINE, 17)
+ # Status Section
+ screen.addch(7, 0, curses.ACS_SSSB)
+ screen.addch(7, 26, curses.ACS_SBSS)
+ screen.hline(7, 1, curses.ACS_HLINE, 25)
+ # Select Functions Section
+ screen.addch(0, 52, curses.ACS_BSSS)
+ screen.vline(1, 52, curses.ACS_VLINE, 17)
+ # Process Bar Section
+ screen.addch(18, 0, curses.ACS_SSSB)
+ screen.addch(18, 79, curses.ACS_SBSS)
+ screen.hline(18, 1, curses.ACS_HLINE, 78)
+ screen.addch(18, 26, curses.ACS_SSBS)
+ screen.addch(18, 52, curses.ACS_SSBS)
+ # Section Titles
+ title = curses.color_pair(6)
+ subtitles = [["Configure Settings", (1, 2)], ["Status", (8, 2)],
+ ["Hosts File", (13, 2)], ["Select Functions", (1, 28)]]
+ for s_title in subtitles:
+ cord = s_title[1]
+ screen.addstr(cord[0], cord[1], s_title[0], title)
+ screen.hline(cord[0] + 1, cord[1], curses.ACS_HLINE, 23)
+ screen.refresh()
+
+ @staticmethod
+ def messagebox(msg, mode=0):
+ """
+ Draw a `Message Box` with :attr:`msg` in a specified type defined by
+ :attr:`mode`.
+
+ .. note:: This is a `static` method.
+
+ :param msg: The information to be displayed in message box.
+ :type msg: str
+ :param mode: A flag indicating the type of message box to be
+ displayed. The default value of `mode` is `0`.
+
+ ==== ===========================================================
+ mode Message Box Type
+ ==== ===========================================================
+ 0 A simple message box showing a message without any buttons.
+ 1 A message box with an `OK` button for user to confirm.
+ 2 A message box with `OK` and `Cancel` buttons for user to
+ choose.
+ ==== ===========================================================
+ :type mode: int
+ :return: A flag indicating the choice made by user.
+
+ ====== =====================================================
+ Return Condition
+ ====== =====================================================
+ 0 #. No button is pressed while :attr:`mode` is `0`.
+ #. `OK` button pressed while :attr:`mode` is `1`.
+ #. `Cancel` button pressed while :attr:`mode` is `2`.
+ 1 `OK` button pressed while :attr:`mode` is `2`.
+ ====== =====================================================
+ :rtype: int
+ """
+ pos_x = 24 if mode == 0 else 20
+ pos_y = 10
+ width = 30 if mode == 0 else 40
+ height = 2
+ messages = CommonUtil.cut_message(msg, width - 4)
+ height += len(messages)
+ if mode:
+ height += 2
+ # Draw Shadow
+ shadow = curses.newwin(height, width, pos_y + 1, pos_x + 1)
+ shadow.bkgd(' ', curses.color_pair(8))
+ shadow.refresh()
+ # Draw Subwindow
+ screen = curses.newwin(height, width, pos_y, pos_x)
+ screen.box()
+ screen.bkgd(' ', curses.color_pair(2))
+ screen.keypad(1)
+ # Set local variable
+ normal = curses.A_NORMAL
+ select = curses.A_REVERSE
+ # Insert messages
+ for i in range(len(messages)):
+ screen.addstr(1 + i, 2, messages[i].center(width - 4), normal)
+ if mode == 0:
+ screen.refresh()
+ else:
+ # Draw subwindow frame
+ line_height = 1 + len(messages)
+ screen.hline(line_height, 1, curses.ACS_HLINE, width - 2)
+ screen.addch(line_height, 0, curses.ACS_SSSB)
+ screen.addch(line_height, width - 1, curses.ACS_SBSS)
+ tab = 0
+ key_in = None
+ while key_in != 27:
+ if mode == 1:
+ choices = ["OK"]
+ elif mode == 2:
+ choices = ["OK", "Cancel"]
+ else:
+ return 0
+ for i, item in enumerate(choices):
+ item_str = ''.join(['[', item, ']'])
+ tab_pos_x = 6 + 20 * i if mode == 2 else 18
+ screen.addstr(line_height + 1, tab_pos_x, item_str,
+ select if i == tab else normal)
+ screen.refresh()
+ key_in = screen.getch()
+ if mode == 2:
+ # OK or Cancel
+ if key_in in [9, curses.KEY_LEFT, curses.KEY_RIGHT]:
+ tab = [1, 0][tab]
+ if key_in in [ord('a'), ord('c')]:
+ key_in -= (ord('a') - ord('A'))
+ if key_in in [ord('C'), ord('O')]:
+ return [ord('C'), ord('O')].index(key_in)
+ if key_in in [10, 32]:
+ return not tab
+ return 0
diff --git a/tui/hostsutil.py b/tui/hostsutil.py
new file mode 100644
index 0000000..063323b
--- /dev/null
+++ b/tui/hostsutil.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# hostsutil.py: Start a TUI session of `Hosts Setup Utility`.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+
+from zipfile import BadZipfile
+
+from curses_d import CursesDaemon
+
+import sys
+sys.path.append("..")
+from util import CommonUtil, RetrieveData
+
+
+class HostsUtil(CursesDaemon):
+ """
+ HostsUtil class in :mod:`tui` module is the main entrance to the
+ Text-based User Interface (TUI) mode of `Hosts Setup Utility`. This class
+ contains methods to start a TUI session of `Hosts Setup Utility`.
+
+ .. note:: This class is subclass of :class:`~tui.curses_d.CursesDaemon`
+ class.
+
+ .. inheritance-diagram:: tui.hostsutil.HostsUtil
+ :parts: 2
+
+ Typical usage to start a TUI session::
+
+ import tui
+
+ util = tui.HostsUtil()
+ util.start()
+
+ :ivar str platform: Platform of current operating system. The value could
+ be `Windows`, `Linux`, `Unix`, `OS X`, and of course `Unknown`.
+ :ivar str hostname: The hostname of current operating system.
+
+ .. note:: This attribute would only be used on linux.
+ :ivar str hosts_path: The absolute path to the hosts file on current
+ operating system.
+
+ .. seealso:: :attr:`platform`, :attr:`hostname`, :attr:`hosts_path` in
+ :class:`~tui.curses_d.CursesDaemon` class.
+ :ivar str sys_eol: The End-Of-Line marker. This maker could typically be
+ one of `CR`, `LF`, or `CRLF`.
+
+ .. seealso:: :attr:`sys_eol` in :class:`~tui.curses_ui.CursesUI`
+ class.
+ """
+ platform = ""
+ hostname = ""
+ hosts_path = ""
+ sys_eol = ""
+
+ def __init__(self):
+ """
+ Initialize a new TUI session.
+
+ * Load server list from a configuration file under working directory.
+ * Try to load the hosts data file under working directory if it
+ exists.
+
+ .. note:: IF hosts data file does not exists correctly in current
+ working directory, a warning message box would popup. And
+ operations to change the hosts file on current system could be
+ done only until a new data file has been downloaded.
+
+ .. seealso:: :meth:`~tui.curses_d.CursesDaemon.session_daemon` method
+ in :class:`~tui.curses_d.CursesDaemon`.
+
+ .. seealso:: :meth:`~gui.hostsutil.HostsUtil.init_main` in
+ :class:`~gui.hostsutil.HostsUtil` class.
+ """
+ super(HostsUtil, self).__init__()
+ # Set mirrors
+ self.settings[0][2] = CommonUtil.set_network("network.conf")
+ # Read data file and set function list
+ try:
+ self.set_platform()
+ RetrieveData.unpack()
+ RetrieveData.connect_db()
+ self.set_info()
+ self.set_func_list()
+ except IOError:
+ self.messagebox("No data file found! Press F6 to get data file "
+ "first.", 1)
+ except BadZipfile:
+ self.messagebox("Incorrect Data file! Press F6 to get a new data "
+ "file first.", 1)
+
+ def __del__(self):
+ """
+ Reset the terminal and clear up the temporary data file while TUI
+ session is finished.
+ """
+ super(HostsUtil, self).__del__()
+ try:
+ RetrieveData.clear()
+ except:
+ pass
+
+ def start(self):
+ """
+ Start the TUI session.
+
+ .. note:: This method is the trigger to start a TUI session of
+ `Hosts Setup Utility`.
+ """
+ while True:
+ # Reload
+ if self.session_daemon():
+ self.__del__()
+ self.__init__()
+ else:
+ break
+
+ def set_platform(self):
+ """
+ Set the information about current operating system.
+ """
+ system, hostname, path, encode, flag = CommonUtil.check_platform()
+ color = "GREEN" if flag else "RED"
+ self.platform = system
+ self.statusinfo[1][1] = system
+ self.hostname = hostname
+ self.hosts_path = path
+ self.statusinfo[1][2] = color
+ if encode == "win_ansi":
+ self.sys_eol = "\r\n"
+ else:
+ self.sys_eol = "\n"
+
+ def set_func_list(self):
+ """
+ Set the function selection list in TUI session.
+ """
+ for ip in range(2):
+ choice, defaults, slices = RetrieveData.get_choice(ip)
+ if os.path.isfile(self.custom):
+ choice.insert(0, [4, 1, 0, "customize"])
+ defaults[0x04] = [1]
+ for i in range(len(slices)):
+ slices[i] += 1
+ slices.insert(0, 0)
+ self.choice[ip] = choice
+ self.slices[ip] = slices
+ funcs = []
+ for func in choice:
+ if func[1] in defaults[func[0]]:
+ funcs.append(1)
+ else:
+ funcs.append(0)
+ self._funcs[ip] = funcs
+
+ def set_info(self):
+ """
+ Set the information of the current local data file.
+ """
+ info = RetrieveData.get_info()
+ build = info["Buildtime"]
+ self.hostsinfo["Version"] = info["Version"]
+ self.hostsinfo["Release"] = CommonUtil.timestamp_to_date(build)
+
+if __name__ == "__main__":
+ main = HostsUtil()
+ main.start()
diff --git a/util/__doc__.py b/util/__doc__.py
new file mode 100644
index 0000000..7e87193
--- /dev/null
+++ b/util/__doc__.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __doc__.py : Document in reST format of util module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+"""
+Shared Utilities
+================
+
+The module described in this chapter provides shared utilities
+
+
+CommonUtil
+----------
+.. autoclass:: util.common.CommonUtil
+ :members:
+
+
+MakeHosts
+---------
+
+.. autoclass:: util.makehosts.MakeHosts
+ :members:
+
+ .. automethod:: util.makehosts.MakeHosts.__init__
+
+
+RetrieveData
+------------
+.. autoclass:: util.retrievedata.RetrieveData
+ :members:
+"""
\ No newline at end of file
diff --git a/util/__init__.py b/util/__init__.py
new file mode 100644
index 0000000..25acfea
--- /dev/null
+++ b/util/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# __init__.py: Declare modules to be called in util module.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+from common import CommonUtil
+from makehosts import MakeHosts
+from retrievedata import RetrieveData
+
+__all__ = ["CommonUtil", "MakeHosts", "RetrieveData"]
\ No newline at end of file
diff --git a/util/common.py b/util/common.py
new file mode 100644
index 0000000..12e8115
--- /dev/null
+++ b/util/common.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# common.py : Basic utilities used by Hosts Setup Utility.
+#
+# Copyleft (C) 2013 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import ConfigParser
+import math
+import os
+import sys
+import socket
+import time
+
+
+class CommonUtil(object):
+ """
+ CommonUtil class contains a set of basic tools for Hosts Setup Utility to
+ use.
+
+ .. note:: All methods from this class are declared as `classmethod`.
+ """
+ @classmethod
+ def check_connection(cls, link):
+ """
+ Check connect to a specified server by :attr:`link`.
+
+ .. note:: This is a `classmethod`.
+
+ :param link: The link to a specified server. This string could be a
+ domain name or the IP address of a server.
+ :type link: str
+ :return: A flag indicating whether the connection status is good or
+ not.
+
+ ==== ======
+ flag Status
+ ==== ======
+ 1 OK
+ 0 Error
+ ==== ======
+ :rtype: int
+ """
+ try:
+ timeout = 3
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(timeout)
+ sock.connect((link, 80))
+ sock.close()
+ return 1
+ except:
+ return 0
+
+ @classmethod
+ def check_platform(cls):
+ """
+ Check information about current operating system.
+
+ .. note:: This is a `classmethod`.
+
+ :return: system, hostname, path, encode, flag
+
+ * system(`str`): Operating system of current session.
+ * hostname(`str`): Hostname of current machine.
+ * path(`str`): Path to hosts on current operating system.
+ * encode(`str`): Default encoding of current OS.
+ * flag(`int`): A flag indicating whether the current OS is
+ supported or not.
+
+ ==== ===========
+ flag Status
+ ==== ===========
+ 1 supported
+ 2 unsupported
+ ==== ===========
+
+ :rtype: str, str, str, str, int
+ """
+ hostname = socket.gethostname()
+ if os.name == "nt":
+ system = "Windows"
+ path = os.getenv("WINDIR") + "\\System32\\drivers\\etc\\hosts"
+ encode = "win_ansi"
+ elif os.name == "posix":
+ path = "/etc/hosts"
+ encode = "unix_utf8"
+ if sys.platform == "darwin":
+ system = "OS X"
+ # Remove the ".local" suffix
+ hostname = hostname[0:-6]
+ else:
+ system = ["Unix", "Linux"][sys.platform.startswith('linux')]
+ else:
+ return "Unknown", '', '', 0
+ return system, hostname, path, encode, 1
+
+ @classmethod
+ def check_privileges(cls):
+ """
+ Check whether the current session has privileges to change the hosts
+ file of current operating system.
+
+ .. note:: This is a `classmethod`.
+
+ :return: username, flag
+
+ * username(`str`): Username of the user running current session.
+ * flag(`bool`): A flag indicating whether the current session has
+ write privileges to the hosts file or not.
+ :rtype: str, bool
+ """
+ if os.name == 'nt':
+ try:
+ # Only windows users with admin privileges can read the
+ # C:\windows\temp
+ os.listdir(os.sep.join([
+ os.environ.get('SystemRoot', 'C:\windows'), 'temp']))
+ except:
+ return os.environ['USERNAME'], False
+ else:
+ return os.environ['USERNAME'], True
+ else:
+ # Check wirte privileges to the hosts file for current user
+ w_flag = os.access("/etc/hosts", os.W_OK)
+ try:
+ return os.environ['USERNAME'], w_flag
+ except KeyError:
+ return os.environ['USER'], w_flag
+
+ @classmethod
+ def set_network(cls, conf_file="network.conf"):
+ """
+ Get configurations for mirrors to connect to.
+
+ .. note:: This is a `classmethod`.
+
+ :param conf_file: Path to a configuration file containing which
+ contains the server list.
+ :type conf_file: str
+ :return: `tag`, `test url`, and `update url` of the servers listed in
+ the configuration file.
+
+ Definition of the dictionary returned:
+
+ ======== ===================================================
+ Key Value
+ ======== ===================================================
+ tag `Tag` string of a specified server.
+ label Name of a specified server.
+ test_url `URL` to test the connection to a server.
+ update `URL` containing the directory to get latest hosts\
+ data file.
+ ======== ===================================================
+
+ :rtype: dict
+ """
+ conf = ConfigParser.ConfigParser()
+ conf.read(conf_file)
+ mirrors = []
+ for sec in conf.sections():
+ mirror = {"tag": sec,
+ "label": conf.get(sec, "label"),
+ "test_url": conf.get(sec, "server"),
+ "update": conf.get(sec, "update"), }
+ mirrors.append(mirror)
+ return mirrors
+
+ @classmethod
+ def timestamp_to_date(cls, timestamp):
+ """
+ Transform unix :attr:`timestamp` to a data string in ISO format.
+
+ .. note:: This is a `classmethod`.
+
+ :param timestamp: A unix timestamp indicating a specified time.
+ :type timestamp: number
+
+ .. note:: The :attr:`timestamp` could be `int` or `float`.
+
+ :return: Date in ISO format, which is `YY-mm-dd` in specific.
+ :rtype: str
+ """
+ l_time = time.localtime(float(timestamp))
+ iso_format = "%Y-%m-%d"
+ date = time.strftime(iso_format, l_time)
+ return date
+
+ @classmethod
+ def convert_size(cls, bufferbytes):
+ """
+ Convert byte size :attr:`bufferbytes` of a file into a size string.
+
+ .. note:: This is a `classmethod`.
+
+ :param bufferbytes: The size of a file counted in bytes.
+ :type bufferbytes: int
+ :return: A readable size string.
+ :rtype: str
+ """
+ if bufferbytes == 0:
+ return "0 B"
+ l_unit = int(math.log(bufferbytes, 0x400))
+ units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
+ formats = ['%.2f', '%.1f', '%d']
+ size = bufferbytes / math.pow(0x400, l_unit)
+ l_num = int(math.log(size, 10))
+ if l_unit >= len(units):
+ l_unit = len(units) - 1
+ if l_num >= len(formats):
+ l_num = len(formats) - 1
+ return ''.join([formats[l_num], ' ', units[l_unit]]) % size
+
+ @classmethod
+ def cut_message(cls, msg, cut):
+ """
+ Cut english message (:attr:`msg`) into lines with specified length
+ (:attr:`cut`).
+
+ .. note:: This is a `classmethod`.
+
+ :param msg: The message to be cut.
+ :type msg: str
+ :param cut: The length for each line of the message.
+ :type cut: int
+ :return: Lines cut from the message.
+ :rtype: list
+ """
+ delimiter = [" ", "\n"]
+ msgs = []
+ while len(msg) >= cut:
+ if "\n" in msg[:cut-1]:
+ [line, msg] = msg.split("\n", 1)
+ else:
+ if (msg[cut-1] not in delimiter) and \
+ (msg[cut] not in delimiter):
+ cut_len = cut - 1
+ hyphen = " " if msg[cut-2] in delimiter else "-"
+ else:
+ cut_len = cut
+ hyphen = " "
+ line = msg[:cut_len] + hyphen
+ msg = msg[cut_len:]
+ msgs.append(line)
+ msgs.append(msg)
+ return msgs
diff --git a/util/makehosts.py b/util/makehosts.py
new file mode 100644
index 0000000..c506f25
--- /dev/null
+++ b/util/makehosts.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# makehosts.py: Make a hosts file.
+#
+# Copyleft (C) 2014 - huhamhire
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+import time
+
+from retrievedata import RetrieveData
+
+
+class MakeHosts(object):
+ """
+ MakeHosts class contains methods to make a hosts file with host entries
+ from a single data file.
+
+ :ivar str make_mode: Operation mode for making hosts file. The valid value
+ could be one of `system`, `ansi`, and `utf-8`.
+
+ .. seealso:: :attr:`make_mode` in
+ :class:`~gui.qdialog_d.QDialogDaemon` class.
+
+ :ivar str custom: File name of User Customized Hosts file.
+
+ .. seealso:: :ref:`User Customized Hosts`.
+
+ :ivar str hostname: File Name of hosts file.
+ :ivar file hosts_file: The hosts file to write hosts to.
+ :ivar int mod_num: Total number of modules written to hosts file.
+ :ivar int count: Number of the module being processed currently in the
+ operation sequence.
+ :ivar dict make_cfg: Configuration to make a new hosts file.
+ :ivar str eol: End-of-Line used by the hosts file created.
+ :ivar time make_time: Timestamp of making hosts file.
+
+ .. seealso:: :class:`gui._make.QSubMakeHosts` class and
+ :class:`tui.curses_d.CursesDaemon` class.
+ """
+ make_mode = ""
+ custom = ""
+ hostname = ""
+ hosts_file = None
+ make_cfg = {}
+ mod_num = 0
+ count = 0
+ eol = ""
+ make_time = None
+
+ def __init__(self, parent):
+ """
+ Retrieve configuration from the main dialog to make a new hosts file.
+
+ :param parent: An instance of :class:`~tui.curses_d.CursesDaemon`
+ class to retrieve configuration with.
+ :type parent: :class:`~tui.curses_d.CursesDaemon`
+
+ .. warning:: :attr:`parent` MUST NOT be set as `None`.
+ """
+ self.count = 0
+ self.make_cfg = parent.make_cfg
+ self.hostname = parent.hostname
+ self.custom = parent.custom
+ make_path = parent.make_path
+ self.make_mode = parent.make_mode
+ if self.make_mode == "system":
+ self.eol = parent.sys_eol
+ self.hosts_file = open("hosts", "wb")
+ elif self.make_mode == "ansi":
+ self.eol = "\r\n"
+ self.hosts_file = open(unicode(make_path), "wb")
+ elif self.make_mode == "utf-8":
+ self.eol = "\n"
+ self.hosts_file = open(unicode(make_path), "wb")
+
+ def make(self):
+ """
+ Start operations to retrieve data from the data file and make the new
+ hosts file for the current operating system.
+ """
+ RetrieveData.connect_db()
+ self.make_time = time.time()
+ self.write_head()
+ self.write_info()
+ self.get_hosts(self.make_cfg)
+ self.hosts_file.close()
+ RetrieveData.disconnect_db()
+
+ def get_hosts(self, make_cfg):
+ """
+ Make the new hosts file by the configuration defined by `make_cfg`
+ from function list on the main dialog.
+
+ :param make_cfg: Module settings in byte word format.
+ :type make_cfg: dict
+
+ .. seealso:: :attr:`make_cfg` in :class:`~tui.curses_d.CursesDaemon`
+ class.
+ """
+ for part_id in sorted(make_cfg.keys()):
+ mod_cfg = make_cfg[part_id]
+ if not RetrieveData.chk_mutex(part_id, mod_cfg):
+ return
+ mods = RetrieveData.get_ids(mod_cfg)
+ for mod_id in mods:
+ self.mod_num += 1
+ hosts, mod_name = RetrieveData.get_host(part_id, mod_id)
+ if part_id == 0x02:
+ self.write_localhost_mod(hosts)
+ elif part_id == 0x04:
+ self.write_customized()
+ else:
+ self.write_common_mod(hosts, mod_name)
+
+ def write_head(self):
+ """
+ Write the head part into the new hosts file.
+ """
+ for head_str in RetrieveData.get_head():
+ self.hosts_file.write("%s%s" % (head_str[0], self.eol))
+
+ def write_info(self):
+ """
+ Write the information part into the new hosts file.
+ """
+ info = RetrieveData.get_info()
+ info_lines = [
+ "#",
+ "# %s: %s" % ("Version", info["Version"]),
+ "# %s: %s" % ("BuildTime", info["Buildtime"]),
+ "# %s: %s" % ("ApplyTime", int(self.make_time)),
+ "#"
+ ]
+ for line in info_lines:
+ self.hosts_file.write("%s%s" % (line, self.eol))
+
+ def write_common_mod(self, hosts, mod_name):
+ """
+ Write hosts entries :attr:`hosts` from a module named `hosts` in the
+ hosts data file..
+
+ :param hosts: Hosts entries from a part in the data file.
+ :type hosts: list
+ :param mod_name: Name of a module from the data file.
+ :type mod_name: str
+ """
+ self.hosts_file.write(
+ "%s# Section Start: %s%s" % (self.eol, mod_name, self.eol))
+ for host in hosts:
+ ip = host[0]
+ if len(ip) < 16:
+ ip = ip.ljust(16)
+ self.hosts_file.write("%s %s%s" % (ip, host[1], self.eol))
+ self.count += 1
+ self.hosts_file.write("# Section End: %s%s" % (mod_name, self.eol))
+
+ def write_customized(self):
+ """
+ Write user customized hosts list into the hosts file if the customized
+ hosts file exists.
+ """
+ if os.path.isfile(self.custom):
+ custom_file = open(unicode(self.custom), "r")
+ lines = custom_file.readlines()
+ self.hosts_file.write(
+ "%s# Section Start: Customized%s" % (self.eol, self.eol))
+ for line in lines:
+ line = line.strip("\n")
+ entry = line.split(" ", 1)
+ if line.startswith("#"):
+ self.hosts_file.write(line + self.eol)
+ elif len(entry) > 1:
+ ip = entry[0]
+ if len(ip) < 16:
+ ip = entry[0].ljust(16)
+ self.hosts_file.write(
+ "%s %s%s" % (ip, entry[1], self.eol)
+ )
+ else:
+ pass
+ self.hosts_file.write("# Section End: Customized%s" % self.eol)
+
+ def write_localhost_mod(self, hosts):
+ """
+ Write localhost entries :attr:`hosts` into the hosts file.
+
+ :param hosts: Hosts entries from a part in the data file.
+ :type hosts: list
+ """
+ self.hosts_file.write(
+ "%s# Section Start: Localhost%s" % (self.eol, self.eol))
+ for host in hosts:
+ if "#Replace" in host[1]:
+ host = (host[0], self.hostname)
+ ip = host[0]
+ if len(ip) < 16:
+ ip = ip.ljust(16)
+ self.hosts_file.write("%s %s%s" % (ip, host[1], self.eol))
+ self.count += 1
+ self.hosts_file.write("# Section End: Localhost%s" % self.eol)
\ No newline at end of file
diff --git a/util/retrievedata.py b/util/retrievedata.py
new file mode 100644
index 0000000..bd15bd8
--- /dev/null
+++ b/util/retrievedata.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# retrievedata.py : Retrieve data from the hosts data file.
+#
+# Copyleft (C) 2014 - huhamhire hosts team
+# =====================================================================
+# Licensed under the GNU General Public License, version 3. You should
+# have received a copy of the GNU General Public License along with
+# this program. If not, see .
+#
+# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
+# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE.
+# =====================================================================
+
+__author__ = "huhamhire "
+
+import os
+import sqlite3
+import zipfile
+
+DATAFILE = "./hostslist.data"
+DATABASE = "./hostslist.s3db"
+
+
+class RetrieveData(object):
+ """
+ RetrieveData class contains a set of tools to retrieve information from
+ the hosts data file.
+
+ .. note:: All methods from this class are declared as `classmethod`.
+
+ :ivar sqlite3.connect conn: An instance of :class:`sqlite3.connect`
+ object to set up connection to a SQLite database.
+ :ivar sqlite3.connect.cursor _cur: An instance of
+ :class:`sqlite3.connect.cursor` object to process SQL queries in the
+ database.
+ :ivar str _db: Filename of a SQLite database file.
+ """
+ conn = None
+ _cur = None
+ _database = None
+
+ @classmethod
+ def db_exists(cls, database=DATABASE):
+ """
+ Check whether the :attr:`database` file exists or not.
+
+ .. note:: This is a `classmethod`.
+
+ :param database: Path to a SQLite database file.
+ `./hostslist.s3db` by default.
+ :type database: str
+ :return: A flag indicating whether the database file exists or not.
+ :rtype: bool
+ """
+ return os.path.isfile(database)
+
+ @classmethod
+ def connect_db(cls, database=DATABASE):
+ """
+ Set up connection with a SQLite :attr:`database`.
+
+ .. note:: This is a `classmethod`.
+
+ :param database: Path to a SQLite database file.
+ `./hostslist.s3db` by default.
+ :type database: str
+ """
+ cls.conn = sqlite3.connect(database)
+ cls._cur = cls.conn.cursor()
+ cls._database = database
+
+ @classmethod
+ def disconnect_db(cls):
+ """
+ Close the connection with a SQLite database.
+
+ .. note:: This is a `classmethod`.
+ """
+ cls.conn.close()
+
+ @classmethod
+ def get_info(cls):
+ """
+ Retrieve the metadata of current data file.
+
+ .. note:: This is a `classmethod`.
+
+ :return: Metadata of current data file. The metadata here is a
+ dictionary while the `Keys` are made of `Section Name` and
+ `Values` are made of `Information` defined in the hosts data file.
+ :rtype: dict
+ """
+ cls._cur.execute("SELECT sect, info FROM info;")
+ info = dict(cls._cur.fetchall())
+ return info
+
+ @classmethod
+ def get_head(cls):
+ """
+ Retrieve the head information from hosts data file.
+
+ .. note:: This is a `classmethod`.
+
+ :return: Lines of hosts head information.
+ :rtype: list
+ """
+ cls._cur.execute("SELECT str FROM hosts_head ORDER BY ln;")
+ head = cls._cur.fetchall()
+ return head
+
+ @classmethod
+ def get_ids(cls, id_cfg):
+ """
+ Calculate the id numbers covered by config word :attr:`id_cfg`.
+
+ .. note:: This is a `classmethod`.
+
+ :param id_cfg: A hexadecimal config word of id selections.
+ :type id_cfg: int
+ :return: ID numbers covered by config word.
+ :rtype: list
+ """
+ cfg = bin(id_cfg)[:1:-1]
+ ids = []
+ for i, id_en in enumerate(cfg):
+ if int(id_en):
+ ids.append(0b1 << i)
+ return ids
+
+ @classmethod
+ def get_host(cls, part_id, mod_id):
+ """
+ Retrieve the hosts module specified by :attr:`mod_id` from a part
+ specified by :attr:`part_id` in the data file.
+
+ .. note:: This is a `classmethod`.
+
+ :param part_id: ID number of a specified part from the hosts data
+ file.
+
+ .. note:: ID number is usually an 8-bit control byte.
+ :type part_id: int
+ :param mod_id: ID number of a specified module from a specified part.
+
+ .. note:: ID number is usually an 8-bit control byte.
+ :type mod_id: int
+
+ .. seealso:: :attr:`make_cfg` in
+ :class:`~tui.curses_d.CursesDaemon` class.
+
+ :return: hosts, mod_name
+
+ * hosts(`list`): Hosts entries from a specified module.
+ * mod_name(`str`): Name of a specified module.
+ :rtype: list, str
+ """
+ if part_id == 0x04:
+ return None, "customize"
+ cls._cur.execute("""
+ SELECT part_name FROM parts
+ WHERE part_id=:part_id;
+ """, (part_id, ))
+ part_name = cls._cur.fetchone()[0]
+ cls._cur.execute("""
+ SELECT ip, host FROM %s
+ WHERE cate=%s;
+ """ % (part_name, mod_id))
+ hosts = cls._cur.fetchall()
+ cls._cur.execute("""
+ SELECT mod_name FROM modules
+ WHERE part_id=:part_id AND mod_id=:mod_id;
+ """, (part_id, mod_id))
+ mod_name = cls._cur.fetchone()[0]
+ return hosts, mod_name
+
+ @classmethod
+ def get_choice(cls, flag_v6=False):
+ """
+ Retrieve module selection items from the hosts data file with default
+ selection for users.
+
+ .. note:: This is a `classmethod`.
+
+ :param flag_v6: A flag indicating whether to receive IPv6 hosts
+ entries or the IPv4 ones. Default by `False`.
+
+ =============== =======
+ :attr:`flag_v6` hosts
+ =============== =======
+ True IPv6
+ False IPv4
+ =============== =======
+ :type flag_v6: bool
+ :return: modules, defaults, slices
+
+ * modules(`list`): Information of modules for users to select.
+ * defaults(`dict`): Default selection config for selected parts.
+ * slices(`list`): Numbers of modules in each part.
+ :rtype: list, dict, list
+ """
+ ch_parts = (0x08, 0x20 if flag_v6 else 0x10, 0x40)
+ cls._cur.execute("""
+ SELECT * FROM modules
+ WHERE part_id IN (:id_shared, :id_ipv, :id_adblock);
+ """, ch_parts)
+ modules = cls._cur.fetchall()
+ cls._cur.execute("""
+ SELECT part_id, part_default FROM parts
+ WHERE part_id IN (:id_shared, :id_ipv, :id_adblock);
+ """, ch_parts)
+ default_cfg = cls._cur.fetchall()
+ defaults = {}
+ for default in default_cfg:
+ defaults[default[0]] = cls.get_ids(default[1])
+ slices = [0]
+ for ch_part in ch_parts:
+ cls._cur.execute("""
+ SELECT COUNT(mod_id) FROM modules
+ WHERE part_id=:ch_part;
+ """, (ch_part, ))
+ slices.append(cls._cur.fetchone()[0])
+ for s in range(1, len(slices)):
+ slices[s] += slices[s - 1]
+ return modules, defaults, slices
+
+ @classmethod
+ def chk_mutex(cls, part_id, mod_cfg):
+ """
+ Check if there is conflict in user selections :attr:`mod_cfg` from a
+ part specified by :attr:`part_id` in the data file.
+
+ .. note:: A conflict may happen while one module selected is declared
+ in `mutex` word of ano module selected at the same time.
+
+ .. note:: This is a `classmethod`.
+
+ :param part_id: ID number of a specified part from the hosts data
+ file.
+
+ .. note:: ID number is usually an 8-bit control byte.
+
+ .. seealso:: :meth:`~util.retrievedata.get_host`.
+
+ :type part_id: int
+ :param mod_cfg: A 16-bit config word indicating module selections of a
+ specified part.
+
+ .. note::
+ If modules in specified part whose IDs are `0x0002` and
+ `0x0010`, the value here should be `0x0002 + 0x0010 = 0x0012`,
+ which is `0b0000000000000010 + 0b0000000000010000 =
+ 0b0000000000010010` in binary.
+
+ :type: int
+
+ .. seealso:: :attr:`make_cfg` in
+ :class:`~tui.curses_d.CursesDaemon` class.
+
+ :return: A flag indicating whether there is a conflict or not.
+
+ ====== ============
+ Return Status
+ ====== ============
+ True Conflict
+ False No conflicts
+ ====== ============
+
+ :rtype: bool
+ """
+ if part_id == 0x04:
+ return True
+ cls._cur.execute("""
+ SELECT mod_id, mutex FROM modules
+ WHERE part_id=:part_id;
+ """, (part_id, ))
+ mutex_tuple = dict(cls._cur.fetchall())
+ mutex_info = []
+ mod_info = cls.get_ids(mod_cfg)
+ for mod_id in mod_info:
+ mutex_info.extend(cls.get_ids(mutex_tuple[mod_id]))
+ mutex_info = set(mutex_info)
+ for mod_id in mod_info:
+ if mod_id in mutex_info:
+ return False
+ return True
+
+ @classmethod
+ def unpack(cls, datafile=DATAFILE, database=DATABASE):
+ """
+ Unzip the archived :attr:`datafile` to a SQLite database file
+ :attr:`database`.
+
+ .. note:: This is a `classmethod`.
+
+ :param datafile: Path to the zipped data file. `./hostslist.data` by
+ default.
+ :type datafile: str
+ :param database: Path to a SQLite database file. `./hostslist.s3db` by
+ default.
+ :type database: str
+ """
+ datafile = zipfile.ZipFile(datafile, "r")
+ path, filename = os.path.split(database)
+ datafile.extract(filename, path)
+
+ @classmethod
+ def clear(cls):
+ """
+ Close connection to the database and delete the database file.
+
+ .. note:: This is a `classmethod`.
+ """
+ cls.conn.close()
+ os.remove(cls._database)
diff --git a/utilities.py b/utilities.py
deleted file mode 100644
index e8a5f78..0000000
--- a/utilities.py
+++ /dev/null
@@ -1,262 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# utilities.py : Basic utilities used by Hosts Setup Utility
-#
-# Copyleft (C) 2013 - huhamhire hosts team
-# =====================================================================
-# Licensed under the GNU General Public License, version 3. You should
-# have received a copy of the GNU General Public License along with
-# this program. If not, see .
-#
-# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
-# THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE.
-# =====================================================================
-
-__version__ = "0.9"
-__revision__ = "$Id$"
-__author__ = "huhamhire "
-
-__all__ = ["Utilities", "LangUtilities"]
-
-import ConfigParser
-import locale
-import math
-import os
-import sys
-import socket
-import time
-
-class Utilities(object):
- """Basic tools for Hosts Setup Utility
-
- Utilities class contains a set of basic tools for Hosts Setup Utility to
- use.
- """
- @classmethod
- def check_connection(cls, link):
- """ Check connect to a server - Class Method
-
- Check connect to a specified server by link ({link}).
-
- Args:
- link (str): A string indicating the link to a specified server.
- This string could be a domain name or the IP address of a
- server.
-
- Returns:
- A flag integer indicating whether the connection is good or not.
- 1: OK, 0: Error.
- """
- try:
- timeout = 3
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.settimeout(timeout)
- sock.connect((link, 80))
- sock.close()
- return 1
- except:
- return 0
-
- @classmethod
- def check_platform(cls):
- """Check OS - Class Method
-
- Check information about current operating system.
-
- Returns:
- (system, hostname, path, encode, flag)
- system (str): A string indicating the platform of current OS.
- hostname (str): A string indicating the hostname of current OS.
- path (str): A string indicating the path to hosts on current
- operating system.
- encode (str): A string indicating the encoding of current OS.
- flag (int): A flag integer indicating whether the current OS is
- supported or not. 1: supported, 2, unsupported.
- """
- hostname = socket.gethostname()
- if os.name == "nt":
- system = "Windows"
- path = os.getenv("WINDIR") + "\\System32\\drivers\\etc\\hosts"
- encode = "win_ansi"
- elif os.name == "posix":
- path = "/etc/hosts"
- encode = "unix_utf8"
- if sys.platform == "darwin":
- system = "OS X"
- # Remove the ".local" suffix
- hostname = hostname[0:-6]
- else:
- system = ["Unix", "Linux"][sys.platform.startswith('linux')]
- else:
- return "Unknown", '', '', 0
- return system, hostname, path, encode, 1
-
- @classmethod
- def check_privileges(cls):
- """Check user privileges - Class Method
-
- Check whether the current session has privileges to change the hosts
- file of current operating system.
-
- Returns:
- (username, flag)
- username (str): A string indicating username of the user running
- current session.
- flag (bool): A bool flag indicating whether the current session
- has write privileges to the hosts file or not.
- """
- if os.name == 'nt':
- try:
- # Only windows users with admin privileges can read the
- # C:\windows\temp
- temp = os.listdir(os.sep.join([
- os.environ.get('SystemRoot', 'C:\windows'), 'temp']))
- except:
- return (os.environ['USERNAME'], False)
- else:
- return (os.environ['USERNAME'], True)
- else:
- # Check wirte privileges to the hosts file for current user
- w_flag = os.access("/etc/hosts", os.W_OK)
- try:
- return (os.environ['USERNAME'], w_flag)
- except KeyError:
- return (os.environ['USER'], w_flag)
-
- @classmethod
- def set_network(cls, conf_file="network.conf"):
- """Read network config file
-
- Get configurations for mirrors to connect to.
-
- Returns:
- A dictionary containing tag, test url, and update url of mirrors.
- """
- conf = ConfigParser.ConfigParser()
- conf.read(conf_file)
- mirrors = []
- for sec in conf.sections():
- mirror = {}
- mirror["tag"] = sec
- mirror["label"] = conf.get(sec, "label")
- mirror["test_url"] = conf.get(sec, "server")
- mirror["update"] = conf.get(sec, "update")
- mirrors.append(mirror)
- return mirrors
-
- @classmethod
- def timestamp_to_date(cls, timestamp):
- """Transform timestamp to readable string - Class Method
-
- Transform unix timestamp ({timestamp}) to a data string in ISO format.
-
- Args:
- timestamp (int/float): A number indicating a unix timestamp.
-
- Returns:
- A data string in ISO format.
- """
- l_time = time.localtime(float(timestamp))
- iso_format = "%Y-%m-%d"
- date = time.strftime(iso_format , l_time)
- return date
-
- @classmethod
- def convert_size(cls, bytes):
- """Transform file size to readable string - Class Method
-
- Convert byte size ({bytes}) of a file into a size string.
-
- Args:
- bytes (int): A integer indicating the size of a file counted by
- byte.
-
- Returns:
- A readable size string.
- """
- if bytes == 0:
- return "0 B"
- l_unit = int(math.log(bytes, 0x400))
- units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
- formats = ['%.2f', '%.1f', '%d']
- size = bytes / math.pow(0x400, l_unit)
- l_num = int(math.log(size, 10))
- if l_unit >= len(units):
- l_unit = len(units) - 1
- if l_num >= len(formats):
- l_num = len(formats) - 1
- return ''.join([formats[l_num], ' ', units[l_unit]]) % (size)
-
-
-class LangUtilities(object):
- """language tools for Hosts Setup Utility
-
- LangUtilities contains a set of language tools for Hosts Setup Utility to
- use.
-
- Attributes:
- language (dict): A dictionary containing supported localized language
- name for a specified locale.
- """
- language = {"de_DE": u"Deutsch",
- "en_US": u"English",
- "ja_JP": u"日本語",
- "ko_KR": u"한글",
- "ru_RU": u"Русский",
- "zh_CN": u"简体中文",
- "zh_TW": u"正體中文", }
-
- @classmethod
- def get_locale(cls):
- """Get locale string - Class Method
-
- Get the default locale of current operating system.
-
- Returns:
- locale (str): A string indicating the locale of current operating
- system. If the locale is not in cls.dictionary dictionary, it
- will return "en_US" as default.
- """
- lc = locale.getdefaultlocale()[0]
- if lc == None:
- lc = "en_US"
- return lc
-
- @classmethod
- def get_language_by_locale(cls, l_locale):
- """Get language name by locale - Class Method
-
- Return the name of a specified language by a locale string
- ({l_locale}).
-
- Args:
- l_locale (str): A string indicating a specified locale.
-
- Returns:
- A string indicating the localized name of a language.
- """
- try:
- return cls.language[l_locale]
- except KeyError:
- return cls.language["en_US"]
-
- @classmethod
- def get_locale_by_language(cls, l_lang):
- """Get locale string by language name - Class Method
-
- Return the locale string connecting with a specified language
- ({l_lang}).
-
- Args:
- l_lang (str): A string indicating the localized name of a
- language.
-
- Returns:
- A string indicating a specified locale.
- """
- for locl, lang in cls.language.items():
- if l_lang == lang:
- return locl
- return "en_US"